Apply dependencies update
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16IncomingRequestService.ts
CommitLineData
c8eeb62b
JB
1// Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
2
e7aeea18
JB
3import {
4 ChangeAvailabilityRequest,
5 ChangeConfigurationRequest,
6 ClearChargingProfileRequest,
ef6fa3fb 7 DiagnosticsStatusNotificationRequest,
e7aeea18
JB
8 GetConfigurationRequest,
9 GetDiagnosticsRequest,
10 MessageTrigger,
11 OCPP16AvailabilityType,
ef6fa3fb
JB
12 OCPP16BootNotificationRequest,
13 OCPP16HeartbeatRequest,
e7aeea18 14 OCPP16IncomingRequestCommand,
94a464f9 15 OCPP16RequestCommand,
ef6fa3fb 16 OCPP16StatusNotificationRequest,
e7aeea18
JB
17 OCPP16TriggerMessageRequest,
18 RemoteStartTransactionRequest,
19 RemoteStopTransactionRequest,
20 ResetRequest,
21 SetChargingProfileRequest,
22 UnlockConnectorRequest,
23} from '../../../types/ocpp/1.6/Requests';
24import {
25 ChangeAvailabilityResponse,
26 ChangeConfigurationResponse,
27 ClearChargingProfileResponse,
f22266fd 28 DiagnosticsStatusNotificationResponse,
e7aeea18
JB
29 GetConfigurationResponse,
30 GetDiagnosticsResponse,
f22266fd
JB
31 OCPP16BootNotificationResponse,
32 OCPP16HeartbeatResponse,
33 OCPP16StatusNotificationResponse,
e7aeea18
JB
34 OCPP16TriggerMessageResponse,
35 SetChargingProfileResponse,
36 UnlockConnectorResponse,
37} from '../../../types/ocpp/1.6/Responses';
38import {
39 ChargingProfilePurposeType,
40 OCPP16ChargingProfile,
41} from '../../../types/ocpp/1.6/ChargingProfile';
47e22477 42import { Client, FTPResponse } from 'basic-ftp';
e7aeea18
JB
43import {
44 OCPP16AuthorizationStatus,
ef6fa3fb 45 OCPP16AuthorizeRequest,
2e3d65ae 46 OCPP16AuthorizeResponse,
ef6fa3fb 47 OCPP16StartTransactionRequest,
e7454a1f 48 OCPP16StartTransactionResponse,
e7aeea18 49 OCPP16StopTransactionReason,
ef6fa3fb 50 OCPP16StopTransactionRequest,
68c993d5 51 OCPP16StopTransactionResponse,
e7aeea18 52} from '../../../types/ocpp/1.6/Transaction';
ef6fa3fb
JB
53import {
54 OCPP16MeterValuesRequest,
55 OCPP16MeterValuesResponse,
56} from '../../../types/ocpp/1.6/MeterValues';
68cb8b91
JB
57import {
58 OCPP16StandardParametersKey,
59 OCPP16SupportedFeatureProfiles,
60} from '../../../types/ocpp/1.6/Configuration';
c0560973 61
73b9adec 62import type ChargingStation from '../../ChargingStation';
c0560973 63import Constants from '../../../utils/Constants';
9ccca265 64import { DefaultResponse } from '../../../types/ocpp/Responses';
c0560973 65import { ErrorType } from '../../../types/ocpp/ErrorType';
58144adb 66import { IncomingRequestHandler } from '../../../types/ocpp/Requests';
5cc4b63b 67import { JsonType } from '../../../types/JsonType';
93b4a429 68import { OCPP16ChargePointErrorCode } from '../../../types/ocpp/1.6/ChargePointErrorCode';
c0560973 69import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
47e22477 70import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStatus';
68c993d5 71import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
c0560973 72import { OCPPConfigurationKey } from '../../../types/ocpp/Configuration';
e58068fd 73import OCPPError from '../../../exception/OCPPError';
c0560973 74import OCPPIncomingRequestService from '../OCPPIncomingRequestService';
a3868ec4 75import { URL } from 'url';
c0560973 76import Utils from '../../../utils/Utils';
47e22477 77import fs from 'fs';
9f2e3130 78import logger from '../../../utils/Logger';
47e22477
JB
79import path from 'path';
80import tar from 'tar';
c0560973 81
2a115f87 82const moduleName = 'OCPP16IncomingRequestService';
909dcf2d 83
c0560973 84export default class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
58144adb
JB
85 private incomingRequestHandlers: Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>;
86
08f130a0 87 public constructor() {
909dcf2d 88 if (new.target?.name === moduleName) {
06127450 89 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
9f2e3130 90 }
08f130a0 91 super();
58144adb
JB
92 this.incomingRequestHandlers = new Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>([
93 [OCPP16IncomingRequestCommand.RESET, this.handleRequestReset.bind(this)],
94 [OCPP16IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)],
95 [OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, this.handleRequestUnlockConnector.bind(this)],
e7aeea18
JB
96 [
97 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
98 this.handleRequestGetConfiguration.bind(this),
99 ],
100 [
101 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
102 this.handleRequestChangeConfiguration.bind(this),
103 ],
104 [
105 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
106 this.handleRequestSetChargingProfile.bind(this),
107 ],
108 [
109 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
110 this.handleRequestClearChargingProfile.bind(this),
111 ],
112 [
113 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
114 this.handleRequestChangeAvailability.bind(this),
115 ],
116 [
117 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
118 this.handleRequestRemoteStartTransaction.bind(this),
119 ],
120 [
121 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
122 this.handleRequestRemoteStopTransaction.bind(this),
123 ],
734d790d 124 [OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, this.handleRequestGetDiagnostics.bind(this)],
e7aeea18 125 [OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, this.handleRequestTriggerMessage.bind(this)],
58144adb
JB
126 ]);
127 }
128
f7f98c68 129 public async incomingRequestHandler(
08f130a0 130 chargingStation: ChargingStation,
e7aeea18
JB
131 messageId: string,
132 commandName: OCPP16IncomingRequestCommand,
5cc4b63b 133 commandPayload: JsonType
e7aeea18 134 ): Promise<void> {
5cc4b63b 135 let response: JsonType;
e7aeea18 136 if (
08f130a0
JB
137 chargingStation.getOcppStrictCompliance() &&
138 chargingStation.isInPendingState() &&
e7aeea18
JB
139 (commandName === OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION ||
140 commandName === OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION)
141 ) {
142 throw new OCPPError(
143 ErrorType.SECURITY_ERROR,
144 `${commandName} cannot be issued to handle request payload ${JSON.stringify(
145 commandPayload,
146 null,
147 2
148 )} while the charging station is in pending state on the central server`,
149 commandName
150 );
caad9d6b 151 }
e7aeea18 152 if (
08f130a0
JB
153 chargingStation.isRegistered() ||
154 (!chargingStation.getOcppStrictCompliance() && chargingStation.isInUnknownState())
e7aeea18 155 ) {
124f3553
JB
156 if (this.incomingRequestHandlers.has(commandName)) {
157 try {
c75a6675 158 // Call the method to build the response
08f130a0
JB
159 response = await this.incomingRequestHandlers.get(commandName)(
160 chargingStation,
161 commandPayload
162 );
124f3553
JB
163 } catch (error) {
164 // Log
08f130a0 165 logger.error(chargingStation.logPrefix() + ' Handle request error: %j', error);
124f3553
JB
166 throw error;
167 }
168 } else {
169 // Throw exception
e7aeea18
JB
170 throw new OCPPError(
171 ErrorType.NOT_IMPLEMENTED,
172 `${commandName} is not implemented to handle request payload ${JSON.stringify(
173 commandPayload,
174 null,
175 2
176 )}`,
177 commandName
178 );
c0560973
JB
179 }
180 } else {
e7aeea18
JB
181 throw new OCPPError(
182 ErrorType.SECURITY_ERROR,
183 `${commandName} cannot be issued to handle request payload ${JSON.stringify(
184 commandPayload,
185 null,
186 2
187 )} while the charging station is not registered on the central server.`,
188 commandName
189 );
c0560973 190 }
c75a6675 191 // Send the built response
08f130a0
JB
192 await chargingStation.ocppRequestService.sendResponse(
193 chargingStation,
194 messageId,
195 response,
196 commandName
197 );
c0560973
JB
198 }
199
200 // Simulate charging station restart
08f130a0
JB
201 private handleRequestReset(
202 chargingStation: ChargingStation,
203 commandPayload: ResetRequest
204 ): DefaultResponse {
71623267
JB
205 // eslint-disable-next-line @typescript-eslint/no-misused-promises
206 setImmediate(async (): Promise<void> => {
08f130a0 207 await chargingStation.reset((commandPayload.type + 'Reset') as OCPP16StopTransactionReason);
c0560973 208 });
e7aeea18 209 logger.info(
08f130a0 210 `${chargingStation.logPrefix()} ${
e7aeea18
JB
211 commandPayload.type
212 } reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds(
08f130a0 213 chargingStation.stationInfo.resetTime
e7aeea18
JB
214 )}`
215 );
c0560973
JB
216 return Constants.OCPP_RESPONSE_ACCEPTED;
217 }
218
219 private handleRequestClearCache(): DefaultResponse {
220 return Constants.OCPP_RESPONSE_ACCEPTED;
221 }
222
e7aeea18 223 private async handleRequestUnlockConnector(
08f130a0 224 chargingStation: ChargingStation,
e7aeea18
JB
225 commandPayload: UnlockConnectorRequest
226 ): Promise<UnlockConnectorResponse> {
c0560973
JB
227 const connectorId = commandPayload.connectorId;
228 if (connectorId === 0) {
e7aeea18 229 logger.error(
08f130a0 230 chargingStation.logPrefix() + ' Trying to unlock connector ' + connectorId.toString()
e7aeea18 231 );
c0560973
JB
232 return Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
233 }
08f130a0
JB
234 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted) {
235 const transactionId = chargingStation.getConnectorStatus(connectorId).transactionId;
68c993d5 236 if (
08f130a0
JB
237 chargingStation.getBeginEndMeterValues() &&
238 chargingStation.getOcppStrictCompliance() &&
239 !chargingStation.getOutOfOrderEndMeterValues()
68c993d5
JB
240 ) {
241 // FIXME: Implement OCPP version agnostic helpers
242 const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(
08f130a0 243 chargingStation,
68c993d5 244 connectorId,
08f130a0 245 chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId)
68c993d5 246 );
08f130a0 247 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
248 OCPP16MeterValuesRequest,
249 OCPP16MeterValuesResponse
08f130a0 250 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
ef6fa3fb
JB
251 connectorId,
252 transactionId,
253 meterValue: transactionEndMeterValue,
254 });
68c993d5 255 }
08f130a0 256 const stopResponse = await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
257 OCPP16StopTransactionRequest,
258 OCPP16StopTransactionResponse
08f130a0 259 >(chargingStation, OCPP16RequestCommand.STOP_TRANSACTION, {
ef6fa3fb 260 transactionId,
08f130a0
JB
261 meterStop: chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId),
262 idTag: chargingStation.getTransactionIdTag(transactionId),
ef6fa3fb
JB
263 reason: OCPP16StopTransactionReason.UNLOCK_COMMAND,
264 });
c0560973
JB
265 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
266 return Constants.OCPP_RESPONSE_UNLOCKED;
267 }
268 return Constants.OCPP_RESPONSE_UNLOCK_FAILED;
269 }
08f130a0 270 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
271 OCPP16StatusNotificationRequest,
272 OCPP16StatusNotificationResponse
08f130a0 273 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
274 connectorId,
275 status: OCPP16ChargePointStatus.AVAILABLE,
276 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
277 });
08f130a0 278 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
c0560973
JB
279 return Constants.OCPP_RESPONSE_UNLOCKED;
280 }
281
e7aeea18 282 private handleRequestGetConfiguration(
08f130a0 283 chargingStation: ChargingStation,
e7aeea18
JB
284 commandPayload: GetConfigurationRequest
285 ): GetConfigurationResponse {
c0560973
JB
286 const configurationKey: OCPPConfigurationKey[] = [];
287 const unknownKey: string[] = [];
288 if (Utils.isEmptyArray(commandPayload.key)) {
08f130a0 289 for (const configuration of chargingStation.ocppConfiguration.configurationKey) {
7f7b65ca
JB
290 if (Utils.isUndefined(configuration.visible)) {
291 configuration.visible = true;
c0560973 292 }
7f7b65ca 293 if (!configuration.visible) {
c0560973
JB
294 continue;
295 }
296 configurationKey.push({
7f7b65ca
JB
297 key: configuration.key,
298 readonly: configuration.readonly,
299 value: configuration.value,
c0560973
JB
300 });
301 }
302 } else {
303 for (const key of commandPayload.key) {
08f130a0 304 const keyFound = chargingStation.getConfigurationKey(key);
c0560973
JB
305 if (keyFound) {
306 if (Utils.isUndefined(keyFound.visible)) {
307 keyFound.visible = true;
308 }
309 if (!keyFound.visible) {
310 continue;
311 }
312 configurationKey.push({
313 key: keyFound.key,
314 readonly: keyFound.readonly,
315 value: keyFound.value,
316 });
317 } else {
318 unknownKey.push(key);
319 }
320 }
321 }
322 return {
323 configurationKey,
324 unknownKey,
325 };
326 }
327
e7aeea18 328 private handleRequestChangeConfiguration(
08f130a0 329 chargingStation: ChargingStation,
e7aeea18
JB
330 commandPayload: ChangeConfigurationRequest
331 ): ChangeConfigurationResponse {
c0560973
JB
332 // JSON request fields type sanity check
333 if (!Utils.isString(commandPayload.key)) {
e7aeea18 334 logger.error(
08f130a0 335 `${chargingStation.logPrefix()} ${
78085c42 336 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION
e7aeea18
JB
337 } request key field is not a string:`,
338 commandPayload
339 );
c0560973
JB
340 }
341 if (!Utils.isString(commandPayload.value)) {
e7aeea18 342 logger.error(
08f130a0 343 `${chargingStation.logPrefix()} ${
78085c42 344 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION
e7aeea18
JB
345 } request value field is not a string:`,
346 commandPayload
347 );
c0560973 348 }
08f130a0 349 const keyToChange = chargingStation.getConfigurationKey(commandPayload.key, true);
c0560973
JB
350 if (!keyToChange) {
351 return Constants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED;
352 } else if (keyToChange && keyToChange.readonly) {
353 return Constants.OCPP_CONFIGURATION_RESPONSE_REJECTED;
354 } else if (keyToChange && !keyToChange.readonly) {
c0560973 355 let valueChanged = false;
a95873d8 356 if (keyToChange.value !== commandPayload.value) {
08f130a0 357 chargingStation.setConfigurationKeyValue(commandPayload.key, commandPayload.value, true);
c0560973
JB
358 valueChanged = true;
359 }
360 let triggerHeartbeatRestart = false;
361 if (keyToChange.key === OCPP16StandardParametersKey.HeartBeatInterval && valueChanged) {
08f130a0 362 chargingStation.setConfigurationKeyValue(
e7aeea18
JB
363 OCPP16StandardParametersKey.HeartbeatInterval,
364 commandPayload.value
365 );
c0560973
JB
366 triggerHeartbeatRestart = true;
367 }
368 if (keyToChange.key === OCPP16StandardParametersKey.HeartbeatInterval && valueChanged) {
08f130a0 369 chargingStation.setConfigurationKeyValue(
e7aeea18
JB
370 OCPP16StandardParametersKey.HeartBeatInterval,
371 commandPayload.value
372 );
c0560973
JB
373 triggerHeartbeatRestart = true;
374 }
375 if (triggerHeartbeatRestart) {
08f130a0 376 chargingStation.restartHeartbeat();
c0560973
JB
377 }
378 if (keyToChange.key === OCPP16StandardParametersKey.WebSocketPingInterval && valueChanged) {
08f130a0 379 chargingStation.restartWebSocketPing();
c0560973
JB
380 }
381 if (keyToChange.reboot) {
382 return Constants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED;
383 }
384 return Constants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED;
385 }
386 }
387
e7aeea18 388 private handleRequestSetChargingProfile(
08f130a0 389 chargingStation: ChargingStation,
e7aeea18
JB
390 commandPayload: SetChargingProfileRequest
391 ): SetChargingProfileResponse {
370ae4ee
JB
392 if (
393 !OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 394 chargingStation,
370ae4ee
JB
395 OCPP16SupportedFeatureProfiles.SmartCharging,
396 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE
397 )
398 ) {
68cb8b91
JB
399 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED;
400 }
08f130a0 401 if (!chargingStation.getConnectorStatus(commandPayload.connectorId)) {
e7aeea18 402 logger.error(
08f130a0 403 `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${
e7aeea18
JB
404 commandPayload.connectorId
405 }`
406 );
c0560973
JB
407 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
408 }
e7aeea18
JB
409 if (
410 commandPayload.csChargingProfiles.chargingProfilePurpose ===
411 ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE &&
412 commandPayload.connectorId !== 0
413 ) {
c0560973
JB
414 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
415 }
e7aeea18
JB
416 if (
417 commandPayload.csChargingProfiles.chargingProfilePurpose ===
418 ChargingProfilePurposeType.TX_PROFILE &&
419 (commandPayload.connectorId === 0 ||
08f130a0 420 !chargingStation.getConnectorStatus(commandPayload.connectorId)?.transactionStarted)
e7aeea18 421 ) {
c0560973
JB
422 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
423 }
08f130a0 424 chargingStation.setChargingProfile(
e7aeea18
JB
425 commandPayload.connectorId,
426 commandPayload.csChargingProfiles
427 );
428 logger.debug(
08f130a0 429 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${
ad8537a7
JB
430 commandPayload.connectorId
431 }, dump their stack: %j`,
08f130a0 432 chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles
e7aeea18 433 );
c0560973
JB
434 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED;
435 }
436
e7aeea18 437 private handleRequestClearChargingProfile(
08f130a0 438 chargingStation: ChargingStation,
e7aeea18
JB
439 commandPayload: ClearChargingProfileRequest
440 ): ClearChargingProfileResponse {
370ae4ee
JB
441 if (
442 !OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 443 chargingStation,
370ae4ee
JB
444 OCPP16SupportedFeatureProfiles.SmartCharging,
445 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE
446 )
447 ) {
68cb8b91
JB
448 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
449 }
08f130a0 450 const connectorStatus = chargingStation.getConnectorStatus(commandPayload.connectorId);
658e2d16 451 if (!connectorStatus) {
e7aeea18 452 logger.error(
08f130a0 453 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${
e7aeea18
JB
454 commandPayload.connectorId
455 }`
456 );
c0560973
JB
457 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
458 }
658e2d16
JB
459 if (commandPayload.connectorId && !Utils.isEmptyArray(connectorStatus.chargingProfiles)) {
460 connectorStatus.chargingProfiles = [];
e7aeea18 461 logger.debug(
08f130a0 462 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${
ad8537a7
JB
463 commandPayload.connectorId
464 }, dump their stack: %j`,
658e2d16 465 connectorStatus.chargingProfiles
e7aeea18 466 );
c0560973
JB
467 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
468 }
469 if (!commandPayload.connectorId) {
470 let clearedCP = false;
08f130a0
JB
471 for (const connectorId of chargingStation.connectors.keys()) {
472 if (!Utils.isEmptyArray(chargingStation.getConnectorStatus(connectorId).chargingProfiles)) {
473 chargingStation
e7aeea18
JB
474 .getConnectorStatus(connectorId)
475 .chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
476 let clearCurrentCP = false;
477 if (chargingProfile.chargingProfileId === commandPayload.id) {
478 clearCurrentCP = true;
479 }
480 if (
481 !commandPayload.chargingProfilePurpose &&
482 chargingProfile.stackLevel === commandPayload.stackLevel
483 ) {
484 clearCurrentCP = true;
485 }
486 if (
487 !chargingProfile.stackLevel &&
488 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
489 ) {
490 clearCurrentCP = true;
491 }
492 if (
493 chargingProfile.stackLevel === commandPayload.stackLevel &&
494 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
495 ) {
496 clearCurrentCP = true;
497 }
498 if (clearCurrentCP) {
658e2d16 499 connectorStatus.chargingProfiles[index] = {} as OCPP16ChargingProfile;
e7aeea18 500 logger.debug(
08f130a0 501 `${chargingStation.logPrefix()} Matching charging profile(s) cleared on connector id ${
ad8537a7
JB
502 commandPayload.connectorId
503 }, dump their stack: %j`,
658e2d16 504 connectorStatus.chargingProfiles
e7aeea18
JB
505 );
506 clearedCP = true;
507 }
508 });
c0560973
JB
509 }
510 }
511 if (clearedCP) {
512 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
513 }
514 }
515 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
516 }
517
e7aeea18 518 private async handleRequestChangeAvailability(
08f130a0 519 chargingStation: ChargingStation,
e7aeea18
JB
520 commandPayload: ChangeAvailabilityRequest
521 ): Promise<ChangeAvailabilityResponse> {
c0560973 522 const connectorId: number = commandPayload.connectorId;
08f130a0 523 if (!chargingStation.getConnectorStatus(connectorId)) {
e7aeea18 524 logger.error(
08f130a0 525 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`
e7aeea18 526 );
c0560973
JB
527 return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
528 }
e7aeea18
JB
529 const chargePointStatus: OCPP16ChargePointStatus =
530 commandPayload.type === OCPP16AvailabilityType.OPERATIVE
531 ? OCPP16ChargePointStatus.AVAILABLE
532 : OCPP16ChargePointStatus.UNAVAILABLE;
c0560973
JB
533 if (connectorId === 0) {
534 let response: ChangeAvailabilityResponse = Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
08f130a0
JB
535 for (const id of chargingStation.connectors.keys()) {
536 if (chargingStation.getConnectorStatus(id)?.transactionStarted) {
c0560973
JB
537 response = Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
538 }
08f130a0 539 chargingStation.getConnectorStatus(id).availability = commandPayload.type;
c0560973 540 if (response === Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) {
08f130a0 541 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
542 OCPP16StatusNotificationRequest,
543 OCPP16StatusNotificationResponse
08f130a0 544 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
545 connectorId: id,
546 status: chargePointStatus,
547 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
548 });
08f130a0 549 chargingStation.getConnectorStatus(id).status = chargePointStatus;
c0560973
JB
550 }
551 }
552 return response;
e7aeea18
JB
553 } else if (
554 connectorId > 0 &&
08f130a0
JB
555 (chargingStation.getConnectorStatus(0).availability === OCPP16AvailabilityType.OPERATIVE ||
556 (chargingStation.getConnectorStatus(0).availability ===
e7aeea18
JB
557 OCPP16AvailabilityType.INOPERATIVE &&
558 commandPayload.type === OCPP16AvailabilityType.INOPERATIVE))
559 ) {
08f130a0
JB
560 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted) {
561 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
c0560973
JB
562 return Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
563 }
08f130a0
JB
564 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
565 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
566 OCPP16StatusNotificationRequest,
567 OCPP16StatusNotificationResponse
08f130a0 568 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
569 connectorId,
570 status: chargePointStatus,
571 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
572 });
08f130a0 573 chargingStation.getConnectorStatus(connectorId).status = chargePointStatus;
c0560973
JB
574 return Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
575 }
576 return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
577 }
578
e7aeea18 579 private async handleRequestRemoteStartTransaction(
08f130a0 580 chargingStation: ChargingStation,
e7aeea18
JB
581 commandPayload: RemoteStartTransactionRequest
582 ): Promise<DefaultResponse> {
658e2d16 583 const transactionConnectorId = commandPayload.connectorId;
08f130a0 584 const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId);
a7fc8211 585 if (transactionConnectorId) {
08f130a0 586 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
587 OCPP16StatusNotificationRequest,
588 OCPP16StatusNotificationResponse
08f130a0 589 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
590 connectorId: transactionConnectorId,
591 status: OCPP16ChargePointStatus.PREPARING,
592 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
593 });
658e2d16 594 connectorStatus.status = OCPP16ChargePointStatus.PREPARING;
08f130a0 595 if (chargingStation.isChargingStationAvailable() && connectorStatus) {
e060fe58 596 // Check if authorized
08f130a0 597 if (chargingStation.getAuthorizeRemoteTxRequests()) {
a7fc8211 598 let authorized = false;
e7aeea18 599 if (
08f130a0
JB
600 chargingStation.getLocalAuthListEnabled() &&
601 chargingStation.hasAuthorizedTags() &&
602 chargingStation.authorizedTags.find((value) => value === commandPayload.idTag)
e7aeea18 603 ) {
658e2d16
JB
604 connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
605 connectorStatus.idTagLocalAuthorized = true;
36f6a92e 606 authorized = true;
08f130a0 607 } else if (chargingStation.getMayAuthorizeAtRemoteStart()) {
658e2d16 608 connectorStatus.authorizeIdTag = commandPayload.idTag;
2e3d65ae 609 const authorizeResponse: OCPP16AuthorizeResponse =
08f130a0 610 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
611 OCPP16AuthorizeRequest,
612 OCPP16AuthorizeResponse
08f130a0 613 >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
ef6fa3fb
JB
614 idTag: commandPayload.idTag,
615 });
a7fc8211
JB
616 if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
617 authorized = true;
a7fc8211 618 }
71068fb9 619 } else {
e7aeea18 620 logger.warn(
08f130a0 621 `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
e7aeea18 622 );
a7fc8211
JB
623 }
624 if (authorized) {
625 // Authorization successful, start transaction
e7aeea18
JB
626 if (
627 this.setRemoteStartTransactionChargingProfile(
08f130a0 628 chargingStation,
e7aeea18
JB
629 transactionConnectorId,
630 commandPayload.chargingProfile
631 )
632 ) {
658e2d16 633 connectorStatus.transactionRemoteStarted = true;
e7aeea18
JB
634 if (
635 (
08f130a0 636 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
637 OCPP16StartTransactionRequest,
638 OCPP16StartTransactionResponse
08f130a0 639 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
ef6fa3fb
JB
640 connectorId: transactionConnectorId,
641 idTag: commandPayload.idTag,
642 })
e7aeea18
JB
643 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
644 ) {
645 logger.debug(
08f130a0 646 chargingStation.logPrefix() +
e7aeea18 647 ' Transaction remotely STARTED on ' +
08f130a0 648 chargingStation.stationInfo.chargingStationId +
e7aeea18
JB
649 '#' +
650 transactionConnectorId.toString() +
651 ' for idTag ' +
652 commandPayload.idTag
653 );
e060fe58
JB
654 return Constants.OCPP_RESPONSE_ACCEPTED;
655 }
e7aeea18 656 return this.notifyRemoteStartTransactionRejected(
08f130a0 657 chargingStation,
e7aeea18
JB
658 transactionConnectorId,
659 commandPayload.idTag
660 );
e060fe58 661 }
e7aeea18 662 return this.notifyRemoteStartTransactionRejected(
08f130a0 663 chargingStation,
e7aeea18
JB
664 transactionConnectorId,
665 commandPayload.idTag
666 );
a7fc8211 667 }
e7aeea18 668 return this.notifyRemoteStartTransactionRejected(
08f130a0 669 chargingStation,
e7aeea18
JB
670 transactionConnectorId,
671 commandPayload.idTag
672 );
36f6a92e 673 }
a7fc8211 674 // No authorization check required, start transaction
e7aeea18
JB
675 if (
676 this.setRemoteStartTransactionChargingProfile(
08f130a0 677 chargingStation,
e7aeea18
JB
678 transactionConnectorId,
679 commandPayload.chargingProfile
680 )
681 ) {
658e2d16 682 connectorStatus.transactionRemoteStarted = true;
e7aeea18
JB
683 if (
684 (
08f130a0 685 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
686 OCPP16StartTransactionRequest,
687 OCPP16StartTransactionResponse
08f130a0 688 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
ef6fa3fb
JB
689 connectorId: transactionConnectorId,
690 idTag: commandPayload.idTag,
691 })
e7aeea18
JB
692 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
693 ) {
694 logger.debug(
08f130a0 695 chargingStation.logPrefix() +
e7aeea18 696 ' Transaction remotely STARTED on ' +
08f130a0 697 chargingStation.stationInfo.chargingStationId +
e7aeea18
JB
698 '#' +
699 transactionConnectorId.toString() +
700 ' for idTag ' +
701 commandPayload.idTag
702 );
e060fe58
JB
703 return Constants.OCPP_RESPONSE_ACCEPTED;
704 }
e7aeea18 705 return this.notifyRemoteStartTransactionRejected(
08f130a0 706 chargingStation,
e7aeea18
JB
707 transactionConnectorId,
708 commandPayload.idTag
709 );
e060fe58 710 }
e7aeea18 711 return this.notifyRemoteStartTransactionRejected(
08f130a0 712 chargingStation,
e7aeea18
JB
713 transactionConnectorId,
714 commandPayload.idTag
715 );
c0560973 716 }
e7aeea18 717 return this.notifyRemoteStartTransactionRejected(
08f130a0 718 chargingStation,
e7aeea18
JB
719 transactionConnectorId,
720 commandPayload.idTag
721 );
c0560973 722 }
08f130a0
JB
723 return this.notifyRemoteStartTransactionRejected(
724 chargingStation,
725 transactionConnectorId,
726 commandPayload.idTag
727 );
a7fc8211
JB
728 }
729
e7aeea18 730 private async notifyRemoteStartTransactionRejected(
08f130a0 731 chargingStation: ChargingStation,
e7aeea18
JB
732 connectorId: number,
733 idTag: string
734 ): Promise<DefaultResponse> {
735 if (
08f130a0 736 chargingStation.getConnectorStatus(connectorId).status !== OCPP16ChargePointStatus.AVAILABLE
e7aeea18 737 ) {
08f130a0 738 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
739 OCPP16StatusNotificationRequest,
740 OCPP16StatusNotificationResponse
08f130a0 741 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
742 connectorId,
743 status: OCPP16ChargePointStatus.AVAILABLE,
744 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
745 });
08f130a0 746 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
e060fe58 747 }
e7aeea18 748 logger.warn(
08f130a0 749 chargingStation.logPrefix() +
e7aeea18
JB
750 ' Remote starting transaction REJECTED on connector Id ' +
751 connectorId.toString() +
752 ', idTag ' +
753 idTag +
754 ', availability ' +
08f130a0 755 chargingStation.getConnectorStatus(connectorId).availability +
e7aeea18 756 ', status ' +
08f130a0 757 chargingStation.getConnectorStatus(connectorId).status
e7aeea18 758 );
c0560973
JB
759 return Constants.OCPP_RESPONSE_REJECTED;
760 }
761
e7aeea18 762 private setRemoteStartTransactionChargingProfile(
08f130a0 763 chargingStation: ChargingStation,
e7aeea18
JB
764 connectorId: number,
765 cp: OCPP16ChargingProfile
766 ): boolean {
a7fc8211 767 if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) {
08f130a0 768 chargingStation.setChargingProfile(connectorId, cp);
e7aeea18 769 logger.debug(
08f130a0
JB
770 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}, dump their stack: %j`,
771 chargingStation.getConnectorStatus(connectorId).chargingProfiles
e7aeea18 772 );
a7fc8211
JB
773 return true;
774 } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) {
e7aeea18 775 logger.warn(
08f130a0 776 `${chargingStation.logPrefix()} Not allowed to set ${
e7aeea18
JB
777 cp.chargingProfilePurpose
778 } charging profile(s) at remote start transaction`
779 );
a7fc8211 780 return false;
e060fe58
JB
781 } else if (!cp) {
782 return true;
a7fc8211
JB
783 }
784 }
785
e7aeea18 786 private async handleRequestRemoteStopTransaction(
08f130a0 787 chargingStation: ChargingStation,
e7aeea18
JB
788 commandPayload: RemoteStopTransactionRequest
789 ): Promise<DefaultResponse> {
c0560973 790 const transactionId = commandPayload.transactionId;
08f130a0 791 for (const connectorId of chargingStation.connectors.keys()) {
e7aeea18
JB
792 if (
793 connectorId > 0 &&
08f130a0 794 chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId
e7aeea18 795 ) {
08f130a0 796 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
797 OCPP16StatusNotificationRequest,
798 OCPP16StatusNotificationResponse
08f130a0 799 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
800 connectorId,
801 status: OCPP16ChargePointStatus.FINISHING,
802 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
803 });
08f130a0 804 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.FINISHING;
68c993d5 805 if (
08f130a0
JB
806 chargingStation.getBeginEndMeterValues() &&
807 chargingStation.getOcppStrictCompliance() &&
808 !chargingStation.getOutOfOrderEndMeterValues()
68c993d5
JB
809 ) {
810 // FIXME: Implement OCPP version agnostic helpers
811 const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(
08f130a0 812 chargingStation,
68c993d5 813 connectorId,
08f130a0 814 chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId)
68c993d5 815 );
08f130a0 816 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
817 OCPP16MeterValuesRequest,
818 OCPP16MeterValuesResponse
08f130a0 819 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
ef6fa3fb 820 connectorId,
68c993d5 821 transactionId,
ef6fa3fb
JB
822 meterValue: transactionEndMeterValue,
823 });
824 }
08f130a0 825 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
826 OCPP16StopTransactionRequest,
827 OCPP16StopTransactionResponse
08f130a0 828 >(chargingStation, OCPP16RequestCommand.STOP_TRANSACTION, {
ef6fa3fb 829 transactionId,
08f130a0
JB
830 meterStop: chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId),
831 idTag: chargingStation.getTransactionIdTag(transactionId),
ef6fa3fb 832 });
c0560973
JB
833 return Constants.OCPP_RESPONSE_ACCEPTED;
834 }
835 }
e7aeea18 836 logger.info(
08f130a0 837 chargingStation.logPrefix() +
e7aeea18
JB
838 ' Trying to remote stop a non existing transaction ' +
839 transactionId.toString()
840 );
c0560973
JB
841 return Constants.OCPP_RESPONSE_REJECTED;
842 }
47e22477 843
e7aeea18 844 private async handleRequestGetDiagnostics(
08f130a0 845 chargingStation: ChargingStation,
e7aeea18
JB
846 commandPayload: GetDiagnosticsRequest
847 ): Promise<GetDiagnosticsResponse> {
68cb8b91 848 if (
370ae4ee 849 !OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 850 chargingStation,
370ae4ee
JB
851 OCPP16SupportedFeatureProfiles.FirmwareManagement,
852 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
853 )
68cb8b91 854 ) {
68cb8b91
JB
855 return Constants.OCPP_RESPONSE_EMPTY;
856 }
e7aeea18 857 logger.debug(
08f130a0 858 chargingStation.logPrefix() +
e7aeea18
JB
859 ' ' +
860 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS +
861 ' request received: %j',
862 commandPayload
863 );
a3868ec4 864 const uri = new URL(commandPayload.location);
47e22477
JB
865 if (uri.protocol.startsWith('ftp:')) {
866 let ftpClient: Client;
867 try {
e7aeea18
JB
868 const logFiles = fs
869 .readdirSync(path.resolve(__dirname, '../../../../'))
870 .filter((file) => file.endsWith('.log'))
871 .map((file) => path.join('./', file));
08f130a0 872 const diagnosticsArchive = chargingStation.stationInfo.chargingStationId + '_logs.tar.gz';
47e22477
JB
873 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
874 ftpClient = new Client();
875 const accessResponse = await ftpClient.access({
876 host: uri.host,
e8191622
JB
877 ...(!Utils.isEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
878 ...(!Utils.isEmptyString(uri.username) && { user: uri.username }),
879 ...(!Utils.isEmptyString(uri.password) && { password: uri.password }),
47e22477
JB
880 });
881 let uploadResponse: FTPResponse;
882 if (accessResponse.code === 220) {
883 // eslint-disable-next-line @typescript-eslint/no-misused-promises
884 ftpClient.trackProgress(async (info) => {
e7aeea18 885 logger.info(
08f130a0 886 `${chargingStation.logPrefix()} ${
e7aeea18
JB
887 info.bytes / 1024
888 } bytes transferred from diagnostics archive ${info.name}`
889 );
08f130a0 890 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
891 DiagnosticsStatusNotificationRequest,
892 DiagnosticsStatusNotificationResponse
08f130a0 893 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
894 status: OCPP16DiagnosticsStatus.Uploading,
895 });
47e22477 896 });
e7aeea18
JB
897 uploadResponse = await ftpClient.uploadFrom(
898 path.join(path.resolve(__dirname, '../../../../'), diagnosticsArchive),
899 uri.pathname + diagnosticsArchive
900 );
47e22477 901 if (uploadResponse.code === 226) {
08f130a0 902 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
903 DiagnosticsStatusNotificationRequest,
904 DiagnosticsStatusNotificationResponse
08f130a0 905 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
906 status: OCPP16DiagnosticsStatus.Uploaded,
907 });
47e22477
JB
908 if (ftpClient) {
909 ftpClient.close();
910 }
911 return { fileName: diagnosticsArchive };
912 }
e7aeea18
JB
913 throw new OCPPError(
914 ErrorType.GENERIC_ERROR,
915 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
916 uploadResponse?.code && '|' + uploadResponse?.code.toString()
917 }`,
918 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
919 );
47e22477 920 }
e7aeea18
JB
921 throw new OCPPError(
922 ErrorType.GENERIC_ERROR,
923 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
924 uploadResponse?.code && '|' + uploadResponse?.code.toString()
925 }`,
926 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
927 );
47e22477 928 } catch (error) {
08f130a0 929 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
930 DiagnosticsStatusNotificationRequest,
931 DiagnosticsStatusNotificationResponse
08f130a0 932 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
933 status: OCPP16DiagnosticsStatus.UploadFailed,
934 });
47e22477
JB
935 if (ftpClient) {
936 ftpClient.close();
937 }
e7aeea18 938 return this.handleIncomingRequestError(
08f130a0 939 chargingStation,
e7aeea18
JB
940 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
941 error as Error,
942 { errorResponse: Constants.OCPP_RESPONSE_EMPTY }
943 );
47e22477
JB
944 }
945 } else {
e7aeea18 946 logger.error(
08f130a0 947 `${chargingStation.logPrefix()} Unsupported protocol ${
e7aeea18
JB
948 uri.protocol
949 } to transfer the diagnostic logs archive`
950 );
08f130a0 951 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
952 DiagnosticsStatusNotificationRequest,
953 DiagnosticsStatusNotificationResponse
08f130a0 954 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
955 status: OCPP16DiagnosticsStatus.UploadFailed,
956 });
47e22477
JB
957 return Constants.OCPP_RESPONSE_EMPTY;
958 }
959 }
802cfa13 960
e7aeea18 961 private handleRequestTriggerMessage(
08f130a0 962 chargingStation: ChargingStation,
e7aeea18
JB
963 commandPayload: OCPP16TriggerMessageRequest
964 ): OCPP16TriggerMessageResponse {
370ae4ee
JB
965 if (
966 !OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 967 chargingStation,
370ae4ee
JB
968 OCPP16SupportedFeatureProfiles.RemoteTrigger,
969 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
970 )
971 ) {
68cb8b91
JB
972 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
973 }
dc661702
JB
974 // TODO: factor out the check on connector id
975 if (commandPayload?.connectorId < 0) {
976 logger.warn(
08f130a0 977 `${chargingStation.logPrefix()} ${
dc661702
JB
978 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
979 } incoming request received with invalid connectorId ${commandPayload.connectorId}`
980 );
981 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
982 }
802cfa13
JB
983 try {
984 switch (commandPayload.requestedMessage) {
985 case MessageTrigger.BootNotification:
986 setTimeout(() => {
08f130a0 987 chargingStation.ocppRequestService
f7f98c68 988 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
08f130a0 989 chargingStation,
6a8b180d
JB
990 OCPP16RequestCommand.BOOT_NOTIFICATION,
991 {
08f130a0
JB
992 chargePointModel: chargingStation.getBootNotificationRequest().chargePointModel,
993 chargePointVendor: chargingStation.getBootNotificationRequest().chargePointVendor,
6a8b180d 994 chargeBoxSerialNumber:
08f130a0
JB
995 chargingStation.getBootNotificationRequest().chargeBoxSerialNumber,
996 firmwareVersion: chargingStation.getBootNotificationRequest().firmwareVersion,
6a8b180d 997 chargePointSerialNumber:
08f130a0
JB
998 chargingStation.getBootNotificationRequest().chargePointSerialNumber,
999 iccid: chargingStation.getBootNotificationRequest().iccid,
1000 imsi: chargingStation.getBootNotificationRequest().imsi,
1001 meterSerialNumber: chargingStation.getBootNotificationRequest().meterSerialNumber,
1002 meterType: chargingStation.getBootNotificationRequest().meterType,
6a8b180d
JB
1003 },
1004 { skipBufferingOnError: true, triggerMessage: true }
e7aeea18 1005 )
ae711c83 1006 .then((value) => {
08f130a0 1007 chargingStation.bootNotificationResponse = value;
ae711c83 1008 })
e7aeea18
JB
1009 .catch(() => {
1010 /* This is intentional */
1011 });
802cfa13
JB
1012 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1013 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1014 case MessageTrigger.Heartbeat:
1015 setTimeout(() => {
08f130a0 1016 chargingStation.ocppRequestService
f7f98c68 1017 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
08f130a0 1018 chargingStation,
ef6fa3fb
JB
1019 OCPP16RequestCommand.HEARTBEAT,
1020 null,
1021 {
1022 triggerMessage: true,
1023 }
1024 )
e7aeea18
JB
1025 .catch(() => {
1026 /* This is intentional */
1027 });
802cfa13
JB
1028 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1029 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
dc661702
JB
1030 case MessageTrigger.StatusNotification:
1031 setTimeout(() => {
1032 if (commandPayload?.connectorId) {
08f130a0 1033 chargingStation.ocppRequestService
dc661702 1034 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
08f130a0 1035 chargingStation,
dc661702
JB
1036 OCPP16RequestCommand.STATUS_NOTIFICATION,
1037 {
1038 connectorId: commandPayload.connectorId,
1039 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
08f130a0 1040 status: chargingStation.getConnectorStatus(commandPayload.connectorId).status,
dc661702
JB
1041 },
1042 {
1043 triggerMessage: true,
1044 }
1045 )
1046 .catch(() => {
1047 /* This is intentional */
1048 });
1049 } else {
08f130a0
JB
1050 for (const connectorId of chargingStation.connectors.keys()) {
1051 chargingStation.ocppRequestService
dc661702
JB
1052 .requestHandler<
1053 OCPP16StatusNotificationRequest,
1054 OCPP16StatusNotificationResponse
1055 >(
08f130a0 1056 chargingStation,
dc661702
JB
1057 OCPP16RequestCommand.STATUS_NOTIFICATION,
1058 {
1059 connectorId,
1060 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
08f130a0 1061 status: chargingStation.getConnectorStatus(connectorId).status,
dc661702
JB
1062 },
1063 {
1064 triggerMessage: true,
1065 }
1066 )
1067 .catch(() => {
1068 /* This is intentional */
1069 });
1070 }
1071 }
1072 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1073 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
802cfa13
JB
1074 default:
1075 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1076 }
1077 } catch (error) {
e7aeea18 1078 return this.handleIncomingRequestError(
08f130a0 1079 chargingStation,
e7aeea18
JB
1080 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1081 error as Error,
1082 { errorResponse: Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
1083 );
802cfa13
JB
1084 }
1085 }
c0560973 1086}