refactor(simulator): switch utils to internal module export/import
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16IncomingRequestService.ts
1 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
2
3 import fs from 'node:fs';
4 import path from 'node:path';
5 import { URL, fileURLToPath } from 'node:url';
6
7 import type { JSONSchemaType } from 'ajv';
8 import { Client, type FTPResponse } from 'basic-ftp';
9 import tar from 'tar';
10
11 import {
12 type ChargingStation,
13 ChargingStationConfigurationUtils,
14 ChargingStationUtils,
15 } from '../../../charging-station';
16 import { OCPPError } from '../../../exception';
17 import {
18 type ChangeAvailabilityRequest,
19 type ChangeAvailabilityResponse,
20 type ChangeConfigurationRequest,
21 type ChangeConfigurationResponse,
22 ChargingProfilePurposeType,
23 type ClearChargingProfileRequest,
24 type ClearChargingProfileResponse,
25 ErrorType,
26 type GenericResponse,
27 type GetConfigurationRequest,
28 type GetConfigurationResponse,
29 type GetDiagnosticsRequest,
30 type GetDiagnosticsResponse,
31 type IncomingRequestHandler,
32 type JsonObject,
33 type JsonType,
34 OCPP16AuthorizationStatus,
35 type OCPP16AuthorizeRequest,
36 type OCPP16AuthorizeResponse,
37 OCPP16AvailabilityType,
38 type OCPP16BootNotificationRequest,
39 type OCPP16BootNotificationResponse,
40 OCPP16ChargePointErrorCode,
41 OCPP16ChargePointStatus,
42 type OCPP16ChargingProfile,
43 type OCPP16ClearCacheRequest,
44 type OCPP16DataTransferRequest,
45 type OCPP16DataTransferResponse,
46 OCPP16DataTransferStatus,
47 OCPP16DataTransferVendorId,
48 OCPP16DiagnosticsStatus,
49 type OCPP16DiagnosticsStatusNotificationRequest,
50 type OCPP16DiagnosticsStatusNotificationResponse,
51 OCPP16FirmwareStatus,
52 type OCPP16FirmwareStatusNotificationRequest,
53 type OCPP16FirmwareStatusNotificationResponse,
54 type OCPP16HeartbeatRequest,
55 type OCPP16HeartbeatResponse,
56 OCPP16IncomingRequestCommand,
57 OCPP16MessageTrigger,
58 OCPP16RequestCommand,
59 OCPP16StandardParametersKey,
60 type OCPP16StartTransactionRequest,
61 type OCPP16StartTransactionResponse,
62 type OCPP16StatusNotificationRequest,
63 type OCPP16StatusNotificationResponse,
64 OCPP16StopTransactionReason,
65 OCPP16SupportedFeatureProfiles,
66 type OCPP16TriggerMessageRequest,
67 type OCPP16TriggerMessageResponse,
68 type OCPP16UpdateFirmwareRequest,
69 type OCPP16UpdateFirmwareResponse,
70 type OCPPConfigurationKey,
71 OCPPVersion,
72 type RemoteStartTransactionRequest,
73 type RemoteStopTransactionRequest,
74 type ResetRequest,
75 type SetChargingProfileRequest,
76 type SetChargingProfileResponse,
77 type UnlockConnectorRequest,
78 type UnlockConnectorResponse,
79 } from '../../../types';
80 import { Constants, Utils, logger } from '../../../utils';
81 import { OCPP16ServiceUtils, OCPPConstants, OCPPIncomingRequestService } from '../internal';
82
83 const moduleName = 'OCPP16IncomingRequestService';
84
85 export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
86 protected jsonSchemas: Map<OCPP16IncomingRequestCommand, JSONSchemaType<JsonObject>>;
87 private incomingRequestHandlers: Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>;
88
89 public constructor() {
90 if (new.target?.name === moduleName) {
91 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
92 }
93 super(OCPPVersion.VERSION_16);
94 this.incomingRequestHandlers = new Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>([
95 [OCPP16IncomingRequestCommand.RESET, this.handleRequestReset.bind(this)],
96 [OCPP16IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)],
97 [OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, this.handleRequestUnlockConnector.bind(this)],
98 [
99 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
100 this.handleRequestGetConfiguration.bind(this),
101 ],
102 [
103 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
104 this.handleRequestChangeConfiguration.bind(this),
105 ],
106 [
107 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
108 this.handleRequestSetChargingProfile.bind(this),
109 ],
110 [
111 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
112 this.handleRequestClearChargingProfile.bind(this),
113 ],
114 [
115 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
116 this.handleRequestChangeAvailability.bind(this),
117 ],
118 [
119 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
120 this.handleRequestRemoteStartTransaction.bind(this),
121 ],
122 [
123 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
124 this.handleRequestRemoteStopTransaction.bind(this),
125 ],
126 [OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, this.handleRequestGetDiagnostics.bind(this)],
127 [OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, this.handleRequestTriggerMessage.bind(this)],
128 [OCPP16IncomingRequestCommand.DATA_TRANSFER, this.handleRequestDataTransfer.bind(this)],
129 [OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, this.handleRequestUpdateFirmware.bind(this)],
130 ]);
131 this.jsonSchemas = new Map<OCPP16IncomingRequestCommand, JSONSchemaType<JsonObject>>([
132 [
133 OCPP16IncomingRequestCommand.RESET,
134 OCPP16ServiceUtils.parseJsonSchemaFile<ResetRequest>(
135 '../../../assets/json-schemas/ocpp/1.6/Reset.json',
136 moduleName,
137 'constructor'
138 ),
139 ],
140 [
141 OCPP16IncomingRequestCommand.CLEAR_CACHE,
142 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ClearCacheRequest>(
143 '../../../assets/json-schemas/ocpp/1.6/ClearCache.json',
144 moduleName,
145 'constructor'
146 ),
147 ],
148 [
149 OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
150 OCPP16ServiceUtils.parseJsonSchemaFile<UnlockConnectorRequest>(
151 '../../../assets/json-schemas/ocpp/1.6/UnlockConnector.json',
152 moduleName,
153 'constructor'
154 ),
155 ],
156 [
157 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
158 OCPP16ServiceUtils.parseJsonSchemaFile<GetConfigurationRequest>(
159 '../../../assets/json-schemas/ocpp/1.6/GetConfiguration.json',
160 moduleName,
161 'constructor'
162 ),
163 ],
164 [
165 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
166 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeConfigurationRequest>(
167 '../../../assets/json-schemas/ocpp/1.6/ChangeConfiguration.json',
168 moduleName,
169 'constructor'
170 ),
171 ],
172 [
173 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
174 OCPP16ServiceUtils.parseJsonSchemaFile<GetDiagnosticsRequest>(
175 '../../../assets/json-schemas/ocpp/1.6/GetDiagnostics.json',
176 moduleName,
177 'constructor'
178 ),
179 ],
180 [
181 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
182 OCPP16ServiceUtils.parseJsonSchemaFile<SetChargingProfileRequest>(
183 '../../../assets/json-schemas/ocpp/1.6/SetChargingProfile.json',
184 moduleName,
185 'constructor'
186 ),
187 ],
188 [
189 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
190 OCPP16ServiceUtils.parseJsonSchemaFile<ClearChargingProfileRequest>(
191 '../../../assets/json-schemas/ocpp/1.6/ClearChargingProfile.json',
192 moduleName,
193 'constructor'
194 ),
195 ],
196 [
197 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
198 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeAvailabilityRequest>(
199 '../../../assets/json-schemas/ocpp/1.6/ChangeAvailability.json',
200 moduleName,
201 'constructor'
202 ),
203 ],
204 [
205 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
206 OCPP16ServiceUtils.parseJsonSchemaFile<RemoteStartTransactionRequest>(
207 '../../../assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json',
208 moduleName,
209 'constructor'
210 ),
211 ],
212 [
213 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
214 OCPP16ServiceUtils.parseJsonSchemaFile<RemoteStopTransactionRequest>(
215 '../../../assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json',
216 moduleName,
217 'constructor'
218 ),
219 ],
220 [
221 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
222 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16TriggerMessageRequest>(
223 '../../../assets/json-schemas/ocpp/1.6/TriggerMessage.json',
224 moduleName,
225 'constructor'
226 ),
227 ],
228 [
229 OCPP16IncomingRequestCommand.DATA_TRANSFER,
230 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferRequest>(
231 '../../../assets/json-schemas/ocpp/1.6/DataTransfer.json',
232 moduleName,
233 'constructor'
234 ),
235 ],
236 [
237 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE,
238 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16UpdateFirmwareRequest>(
239 '../../../assets/json-schemas/ocpp/1.6/UpdateFirmware.json',
240 moduleName,
241 'constructor'
242 ),
243 ],
244 ]);
245 this.validatePayload.bind(this);
246 }
247
248 public async incomingRequestHandler(
249 chargingStation: ChargingStation,
250 messageId: string,
251 commandName: OCPP16IncomingRequestCommand,
252 commandPayload: JsonType
253 ): Promise<void> {
254 let response: JsonType;
255 if (
256 chargingStation.getOcppStrictCompliance() === true &&
257 chargingStation.isInPendingState() === true &&
258 (commandName === OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION ||
259 commandName === OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION)
260 ) {
261 throw new OCPPError(
262 ErrorType.SECURITY_ERROR,
263 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
264 commandPayload,
265 null,
266 2
267 )} while the charging station is in pending state on the central server`,
268 commandName,
269 commandPayload
270 );
271 }
272 if (
273 chargingStation.isRegistered() === true ||
274 (chargingStation.getOcppStrictCompliance() === false &&
275 chargingStation.isInUnknownState() === true)
276 ) {
277 if (
278 this.incomingRequestHandlers.has(commandName) === true &&
279 OCPP16ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName) === true
280 ) {
281 try {
282 this.validatePayload(chargingStation, commandName, commandPayload);
283 // Call the method to build the response
284 response = await this.incomingRequestHandlers.get(commandName)(
285 chargingStation,
286 commandPayload
287 );
288 } catch (error) {
289 // Log
290 logger.error(
291 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
292 error
293 );
294 throw error;
295 }
296 } else {
297 // Throw exception
298 throw new OCPPError(
299 ErrorType.NOT_IMPLEMENTED,
300 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
301 commandPayload,
302 null,
303 2
304 )}`,
305 commandName,
306 commandPayload
307 );
308 }
309 } else {
310 throw new OCPPError(
311 ErrorType.SECURITY_ERROR,
312 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
313 commandPayload,
314 null,
315 2
316 )} while the charging station is not registered on the central server.`,
317 commandName,
318 commandPayload
319 );
320 }
321 // Send the built response
322 await chargingStation.ocppRequestService.sendResponse(
323 chargingStation,
324 messageId,
325 response,
326 commandName
327 );
328 }
329
330 private validatePayload(
331 chargingStation: ChargingStation,
332 commandName: OCPP16IncomingRequestCommand,
333 commandPayload: JsonType
334 ): boolean {
335 if (this.jsonSchemas.has(commandName) === true) {
336 return this.validateIncomingRequestPayload(
337 chargingStation,
338 commandName,
339 this.jsonSchemas.get(commandName),
340 commandPayload
341 );
342 }
343 logger.warn(
344 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
345 );
346 return false;
347 }
348
349 // Simulate charging station restart
350 private handleRequestReset(
351 chargingStation: ChargingStation,
352 commandPayload: ResetRequest
353 ): GenericResponse {
354 this.runInAsyncScope(
355 chargingStation.reset.bind(chargingStation) as (
356 this: ChargingStation,
357 ...args: any[]
358 ) => Promise<void>,
359 chargingStation,
360 `${commandPayload.type}Reset` as OCPP16StopTransactionReason
361 ).catch(() => {
362 /* This is intentional */
363 });
364 logger.info(
365 `${chargingStation.logPrefix()} ${
366 commandPayload.type
367 } reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds(
368 chargingStation.stationInfo.resetTime
369 )}`
370 );
371 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
372 }
373
374 private async handleRequestUnlockConnector(
375 chargingStation: ChargingStation,
376 commandPayload: UnlockConnectorRequest
377 ): Promise<UnlockConnectorResponse> {
378 const connectorId = commandPayload.connectorId;
379 if (chargingStation.connectors.has(connectorId) === false) {
380 logger.error(
381 `${chargingStation.logPrefix()} Trying to unlock a non existing connector Id ${connectorId.toString()}`
382 );
383 return OCPPConstants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
384 }
385 if (connectorId === 0) {
386 logger.error(
387 `${chargingStation.logPrefix()} Trying to unlock connector Id ${connectorId.toString()}`
388 );
389 return OCPPConstants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
390 }
391 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
392 const stopResponse = await chargingStation.stopTransactionOnConnector(
393 connectorId,
394 OCPP16StopTransactionReason.UNLOCK_COMMAND
395 );
396 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
397 return OCPPConstants.OCPP_RESPONSE_UNLOCKED;
398 }
399 return OCPPConstants.OCPP_RESPONSE_UNLOCK_FAILED;
400 }
401 await chargingStation.ocppRequestService.requestHandler<
402 OCPP16StatusNotificationRequest,
403 OCPP16StatusNotificationResponse
404 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
405 connectorId,
406 status: OCPP16ChargePointStatus.AVAILABLE,
407 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
408 });
409 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
410 return OCPPConstants.OCPP_RESPONSE_UNLOCKED;
411 }
412
413 private handleRequestGetConfiguration(
414 chargingStation: ChargingStation,
415 commandPayload: GetConfigurationRequest
416 ): GetConfigurationResponse {
417 const configurationKey: OCPPConfigurationKey[] = [];
418 const unknownKey: string[] = [];
419 if (Utils.isUndefined(commandPayload.key) === true) {
420 for (const configuration of chargingStation.ocppConfiguration.configurationKey) {
421 if (Utils.isUndefined(configuration.visible) === true) {
422 configuration.visible = true;
423 }
424 if (configuration.visible === false) {
425 continue;
426 }
427 configurationKey.push({
428 key: configuration.key,
429 readonly: configuration.readonly,
430 value: configuration.value,
431 });
432 }
433 } else if (Utils.isNotEmptyArray(commandPayload.key) === true) {
434 for (const key of commandPayload.key) {
435 const keyFound = ChargingStationConfigurationUtils.getConfigurationKey(
436 chargingStation,
437 key,
438 true
439 );
440 if (keyFound) {
441 if (Utils.isUndefined(keyFound.visible) === true) {
442 keyFound.visible = true;
443 }
444 if (keyFound.visible === false) {
445 continue;
446 }
447 configurationKey.push({
448 key: keyFound.key,
449 readonly: keyFound.readonly,
450 value: keyFound.value,
451 });
452 } else {
453 unknownKey.push(key);
454 }
455 }
456 }
457 return {
458 configurationKey,
459 unknownKey,
460 };
461 }
462
463 private handleRequestChangeConfiguration(
464 chargingStation: ChargingStation,
465 commandPayload: ChangeConfigurationRequest
466 ): ChangeConfigurationResponse {
467 const keyToChange = ChargingStationConfigurationUtils.getConfigurationKey(
468 chargingStation,
469 commandPayload.key,
470 true
471 );
472 if (!keyToChange) {
473 return OCPPConstants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED;
474 } else if (keyToChange && keyToChange.readonly) {
475 return OCPPConstants.OCPP_CONFIGURATION_RESPONSE_REJECTED;
476 } else if (keyToChange && !keyToChange.readonly) {
477 let valueChanged = false;
478 if (keyToChange.value !== commandPayload.value) {
479 ChargingStationConfigurationUtils.setConfigurationKeyValue(
480 chargingStation,
481 commandPayload.key,
482 commandPayload.value,
483 true
484 );
485 valueChanged = true;
486 }
487 let triggerHeartbeatRestart = false;
488 if (keyToChange.key === OCPP16StandardParametersKey.HeartBeatInterval && valueChanged) {
489 ChargingStationConfigurationUtils.setConfigurationKeyValue(
490 chargingStation,
491 OCPP16StandardParametersKey.HeartbeatInterval,
492 commandPayload.value
493 );
494 triggerHeartbeatRestart = true;
495 }
496 if (keyToChange.key === OCPP16StandardParametersKey.HeartbeatInterval && valueChanged) {
497 ChargingStationConfigurationUtils.setConfigurationKeyValue(
498 chargingStation,
499 OCPP16StandardParametersKey.HeartBeatInterval,
500 commandPayload.value
501 );
502 triggerHeartbeatRestart = true;
503 }
504 if (triggerHeartbeatRestart) {
505 chargingStation.restartHeartbeat();
506 }
507 if (keyToChange.key === OCPP16StandardParametersKey.WebSocketPingInterval && valueChanged) {
508 chargingStation.restartWebSocketPing();
509 }
510 if (keyToChange.reboot) {
511 return OCPPConstants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED;
512 }
513 return OCPPConstants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED;
514 }
515 }
516
517 private handleRequestSetChargingProfile(
518 chargingStation: ChargingStation,
519 commandPayload: SetChargingProfileRequest
520 ): SetChargingProfileResponse {
521 if (
522 OCPP16ServiceUtils.checkFeatureProfile(
523 chargingStation,
524 OCPP16SupportedFeatureProfiles.SmartCharging,
525 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE
526 ) === false
527 ) {
528 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED;
529 }
530 if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
531 logger.error(
532 `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${
533 commandPayload.connectorId
534 }`
535 );
536 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
537 }
538 if (
539 commandPayload.csChargingProfiles.chargingProfilePurpose ===
540 ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE &&
541 commandPayload.connectorId !== 0
542 ) {
543 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
544 }
545 if (
546 commandPayload.csChargingProfiles.chargingProfilePurpose ===
547 ChargingProfilePurposeType.TX_PROFILE &&
548 (commandPayload.connectorId === 0 ||
549 chargingStation.getConnectorStatus(commandPayload.connectorId)?.transactionStarted ===
550 false)
551 ) {
552 logger.error(
553 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${
554 commandPayload.connectorId
555 } without a started transaction`
556 );
557 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
558 }
559 OCPP16ServiceUtils.setChargingProfile(
560 chargingStation,
561 commandPayload.connectorId,
562 commandPayload.csChargingProfiles
563 );
564 logger.debug(
565 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${
566 commandPayload.connectorId
567 }: %j`,
568 commandPayload.csChargingProfiles
569 );
570 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED;
571 }
572
573 private handleRequestClearChargingProfile(
574 chargingStation: ChargingStation,
575 commandPayload: ClearChargingProfileRequest
576 ): ClearChargingProfileResponse {
577 if (
578 OCPP16ServiceUtils.checkFeatureProfile(
579 chargingStation,
580 OCPP16SupportedFeatureProfiles.SmartCharging,
581 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE
582 ) === false
583 ) {
584 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
585 }
586 if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
587 logger.error(
588 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${
589 commandPayload.connectorId
590 }`
591 );
592 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
593 }
594 const connectorStatus = chargingStation.getConnectorStatus(commandPayload.connectorId);
595 if (
596 !Utils.isNullOrUndefined(commandPayload.connectorId) &&
597 Utils.isNotEmptyArray(connectorStatus?.chargingProfiles)
598 ) {
599 connectorStatus.chargingProfiles = [];
600 logger.debug(
601 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${
602 commandPayload.connectorId
603 }`
604 );
605 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
606 }
607 if (Utils.isNullOrUndefined(commandPayload.connectorId)) {
608 let clearedCP = false;
609 for (const connectorId of chargingStation.connectors.keys()) {
610 if (
611 Utils.isNotEmptyArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)
612 ) {
613 chargingStation
614 .getConnectorStatus(connectorId)
615 ?.chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
616 let clearCurrentCP = false;
617 if (chargingProfile.chargingProfileId === commandPayload.id) {
618 clearCurrentCP = true;
619 }
620 if (
621 !commandPayload.chargingProfilePurpose &&
622 chargingProfile.stackLevel === commandPayload.stackLevel
623 ) {
624 clearCurrentCP = true;
625 }
626 if (
627 !chargingProfile.stackLevel &&
628 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
629 ) {
630 clearCurrentCP = true;
631 }
632 if (
633 chargingProfile.stackLevel === commandPayload.stackLevel &&
634 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
635 ) {
636 clearCurrentCP = true;
637 }
638 if (clearCurrentCP) {
639 connectorStatus?.chargingProfiles?.splice(index, 1);
640 logger.debug(
641 `${chargingStation.logPrefix()} Matching charging profile(s) cleared: %j`,
642 chargingProfile
643 );
644 clearedCP = true;
645 }
646 });
647 }
648 }
649 if (clearedCP) {
650 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
651 }
652 }
653 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
654 }
655
656 private async handleRequestChangeAvailability(
657 chargingStation: ChargingStation,
658 commandPayload: ChangeAvailabilityRequest
659 ): Promise<ChangeAvailabilityResponse> {
660 const connectorId: number = commandPayload.connectorId;
661 if (chargingStation.connectors.has(connectorId) === false) {
662 logger.error(
663 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`
664 );
665 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
666 }
667 const chargePointStatus: OCPP16ChargePointStatus =
668 commandPayload.type === OCPP16AvailabilityType.OPERATIVE
669 ? OCPP16ChargePointStatus.AVAILABLE
670 : OCPP16ChargePointStatus.UNAVAILABLE;
671 if (connectorId === 0) {
672 let response: ChangeAvailabilityResponse = OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
673 for (const id of chargingStation.connectors.keys()) {
674 if (chargingStation.getConnectorStatus(id)?.transactionStarted === true) {
675 response = OCPPConstants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
676 }
677 chargingStation.getConnectorStatus(id).availability = commandPayload.type;
678 if (response === OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) {
679 await chargingStation.ocppRequestService.requestHandler<
680 OCPP16StatusNotificationRequest,
681 OCPP16StatusNotificationResponse
682 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
683 connectorId: id,
684 status: chargePointStatus,
685 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
686 });
687 chargingStation.getConnectorStatus(id).status = chargePointStatus;
688 }
689 }
690 return response;
691 } else if (
692 connectorId > 0 &&
693 (chargingStation.isChargingStationAvailable() === true ||
694 (chargingStation.isChargingStationAvailable() === false &&
695 commandPayload.type === OCPP16AvailabilityType.INOPERATIVE))
696 ) {
697 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
698 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
699 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
700 }
701 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
702 await chargingStation.ocppRequestService.requestHandler<
703 OCPP16StatusNotificationRequest,
704 OCPP16StatusNotificationResponse
705 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
706 connectorId,
707 status: chargePointStatus,
708 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
709 });
710 chargingStation.getConnectorStatus(connectorId).status = chargePointStatus;
711 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
712 }
713 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
714 }
715
716 private async handleRequestRemoteStartTransaction(
717 chargingStation: ChargingStation,
718 commandPayload: RemoteStartTransactionRequest
719 ): Promise<GenericResponse> {
720 const transactionConnectorId = commandPayload.connectorId;
721 if (chargingStation.connectors.has(transactionConnectorId) === true) {
722 const remoteStartTransactionLogMsg = `${chargingStation.logPrefix()} Transaction remotely STARTED on ${
723 chargingStation.stationInfo.chargingStationId
724 }#${transactionConnectorId.toString()} for idTag '${commandPayload.idTag}'`;
725 await chargingStation.ocppRequestService.requestHandler<
726 OCPP16StatusNotificationRequest,
727 OCPP16StatusNotificationResponse
728 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
729 connectorId: transactionConnectorId,
730 status: OCPP16ChargePointStatus.PREPARING,
731 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
732 });
733 const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId);
734 connectorStatus.status = OCPP16ChargePointStatus.PREPARING;
735 if (chargingStation.isChargingStationAvailable() === true) {
736 // Check if authorized
737 if (chargingStation.getAuthorizeRemoteTxRequests() === true) {
738 let authorized = false;
739 if (
740 chargingStation.getLocalAuthListEnabled() === true &&
741 chargingStation.hasAuthorizedTags() === true &&
742 Utils.isNotEmptyString(
743 chargingStation.authorizedTagsCache
744 .getAuthorizedTags(
745 ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
746 )
747 ?.find((idTag) => idTag === commandPayload.idTag)
748 )
749 ) {
750 connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
751 connectorStatus.idTagLocalAuthorized = true;
752 authorized = true;
753 } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
754 connectorStatus.authorizeIdTag = commandPayload.idTag;
755 const authorizeResponse: OCPP16AuthorizeResponse =
756 await chargingStation.ocppRequestService.requestHandler<
757 OCPP16AuthorizeRequest,
758 OCPP16AuthorizeResponse
759 >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
760 idTag: commandPayload.idTag,
761 });
762 if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
763 authorized = true;
764 }
765 } else {
766 logger.warn(
767 `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
768 );
769 }
770 if (authorized === true) {
771 // Authorization successful, start transaction
772 if (
773 this.setRemoteStartTransactionChargingProfile(
774 chargingStation,
775 transactionConnectorId,
776 commandPayload.chargingProfile
777 ) === true
778 ) {
779 connectorStatus.transactionRemoteStarted = true;
780 if (
781 (
782 await chargingStation.ocppRequestService.requestHandler<
783 OCPP16StartTransactionRequest,
784 OCPP16StartTransactionResponse
785 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
786 connectorId: transactionConnectorId,
787 idTag: commandPayload.idTag,
788 })
789 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
790 ) {
791 logger.debug(remoteStartTransactionLogMsg);
792 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
793 }
794 return this.notifyRemoteStartTransactionRejected(
795 chargingStation,
796 transactionConnectorId,
797 commandPayload.idTag
798 );
799 }
800 return this.notifyRemoteStartTransactionRejected(
801 chargingStation,
802 transactionConnectorId,
803 commandPayload.idTag
804 );
805 }
806 return this.notifyRemoteStartTransactionRejected(
807 chargingStation,
808 transactionConnectorId,
809 commandPayload.idTag
810 );
811 }
812 // No authorization check required, start transaction
813 if (
814 this.setRemoteStartTransactionChargingProfile(
815 chargingStation,
816 transactionConnectorId,
817 commandPayload.chargingProfile
818 ) === true
819 ) {
820 connectorStatus.transactionRemoteStarted = true;
821 if (
822 (
823 await chargingStation.ocppRequestService.requestHandler<
824 OCPP16StartTransactionRequest,
825 OCPP16StartTransactionResponse
826 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
827 connectorId: transactionConnectorId,
828 idTag: commandPayload.idTag,
829 })
830 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
831 ) {
832 logger.debug(remoteStartTransactionLogMsg);
833 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
834 }
835 return this.notifyRemoteStartTransactionRejected(
836 chargingStation,
837 transactionConnectorId,
838 commandPayload.idTag
839 );
840 }
841 return this.notifyRemoteStartTransactionRejected(
842 chargingStation,
843 transactionConnectorId,
844 commandPayload.idTag
845 );
846 }
847 return this.notifyRemoteStartTransactionRejected(
848 chargingStation,
849 transactionConnectorId,
850 commandPayload.idTag
851 );
852 }
853 return this.notifyRemoteStartTransactionRejected(
854 chargingStation,
855 transactionConnectorId,
856 commandPayload.idTag
857 );
858 }
859
860 private async notifyRemoteStartTransactionRejected(
861 chargingStation: ChargingStation,
862 connectorId: number,
863 idTag: string
864 ): Promise<GenericResponse> {
865 if (
866 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.AVAILABLE
867 ) {
868 await chargingStation.ocppRequestService.requestHandler<
869 OCPP16StatusNotificationRequest,
870 OCPP16StatusNotificationResponse
871 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
872 connectorId,
873 status: OCPP16ChargePointStatus.AVAILABLE,
874 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
875 });
876 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
877 }
878 logger.warn(
879 `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector Id ${connectorId.toString()}, idTag '${idTag}', availability '${
880 chargingStation.getConnectorStatus(connectorId)?.availability
881 }', status '${chargingStation.getConnectorStatus(connectorId)?.status}'`
882 );
883 return OCPPConstants.OCPP_RESPONSE_REJECTED;
884 }
885
886 private setRemoteStartTransactionChargingProfile(
887 chargingStation: ChargingStation,
888 connectorId: number,
889 cp: OCPP16ChargingProfile
890 ): boolean {
891 if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) {
892 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, cp);
893 logger.debug(
894 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`,
895 cp
896 );
897 return true;
898 } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) {
899 logger.warn(
900 `${chargingStation.logPrefix()} Not allowed to set ${
901 cp.chargingProfilePurpose
902 } charging profile(s) at remote start transaction`
903 );
904 return false;
905 } else if (!cp) {
906 return true;
907 }
908 }
909
910 private async handleRequestRemoteStopTransaction(
911 chargingStation: ChargingStation,
912 commandPayload: RemoteStopTransactionRequest
913 ): Promise<GenericResponse> {
914 const transactionId = commandPayload.transactionId;
915 for (const connectorId of chargingStation.connectors.keys()) {
916 if (
917 connectorId > 0 &&
918 chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId
919 ) {
920 await chargingStation.ocppRequestService.requestHandler<
921 OCPP16StatusNotificationRequest,
922 OCPP16StatusNotificationResponse
923 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
924 connectorId,
925 status: OCPP16ChargePointStatus.FINISHING,
926 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
927 });
928 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.FINISHING;
929 const stopResponse = await chargingStation.stopTransactionOnConnector(
930 connectorId,
931 OCPP16StopTransactionReason.REMOTE
932 );
933 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
934 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
935 }
936 return OCPPConstants.OCPP_RESPONSE_REJECTED;
937 }
938 }
939 logger.warn(
940 `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction ${transactionId.toString()}`
941 );
942 return OCPPConstants.OCPP_RESPONSE_REJECTED;
943 }
944
945 private handleRequestUpdateFirmware(
946 chargingStation: ChargingStation,
947 commandPayload: OCPP16UpdateFirmwareRequest
948 ): OCPP16UpdateFirmwareResponse {
949 if (
950 OCPP16ServiceUtils.checkFeatureProfile(
951 chargingStation,
952 OCPP16SupportedFeatureProfiles.FirmwareManagement,
953 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE
954 ) === false
955 ) {
956 logger.warn(
957 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported`
958 );
959 return OCPPConstants.OCPP_RESPONSE_EMPTY;
960 }
961 if (
962 !Utils.isNullOrUndefined(chargingStation.stationInfo.firmwareStatus) &&
963 chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed
964 ) {
965 logger.warn(
966 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`
967 );
968 return OCPPConstants.OCPP_RESPONSE_EMPTY;
969 }
970 const retrieveDate = Utils.convertToDate(commandPayload.retrieveDate);
971 const now = Date.now();
972 if (retrieveDate?.getTime() <= now) {
973 this.runInAsyncScope(
974 this.updateFirmware.bind(this) as (
975 this: OCPP16IncomingRequestService,
976 ...args: any[]
977 ) => Promise<void>,
978 this,
979 chargingStation
980 ).catch(() => {
981 /* This is intentional */
982 });
983 } else {
984 setTimeout(() => {
985 this.updateFirmware(chargingStation).catch(() => {
986 /* Intentional */
987 });
988 }, retrieveDate?.getTime() - now);
989 }
990 return OCPPConstants.OCPP_RESPONSE_EMPTY;
991 }
992
993 private async updateFirmware(
994 chargingStation: ChargingStation,
995 maxDelay = 30,
996 minDelay = 15
997 ): Promise<void> {
998 chargingStation.stopAutomaticTransactionGenerator();
999 for (const connectorId of chargingStation.connectors.keys()) {
1000 if (
1001 connectorId > 0 &&
1002 chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false
1003 ) {
1004 await chargingStation.ocppRequestService.requestHandler<
1005 OCPP16StatusNotificationRequest,
1006 OCPP16StatusNotificationResponse
1007 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
1008 connectorId,
1009 status: OCPP16ChargePointStatus.UNAVAILABLE,
1010 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1011 });
1012 chargingStation.getConnectorStatus(connectorId).status =
1013 OCPP16ChargePointStatus.UNAVAILABLE;
1014 }
1015 }
1016 if (
1017 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus &&
1018 Utils.isNotEmptyString(chargingStation.stationInfo?.firmwareUpgrade?.failureStatus)
1019 ) {
1020 await chargingStation.ocppRequestService.requestHandler<
1021 OCPP16FirmwareStatusNotificationRequest,
1022 OCPP16FirmwareStatusNotificationResponse
1023 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1024 status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus,
1025 });
1026 return;
1027 }
1028 await chargingStation.ocppRequestService.requestHandler<
1029 OCPP16FirmwareStatusNotificationRequest,
1030 OCPP16FirmwareStatusNotificationResponse
1031 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1032 status: OCPP16FirmwareStatus.Downloading,
1033 });
1034 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloading;
1035 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1036 await chargingStation.ocppRequestService.requestHandler<
1037 OCPP16FirmwareStatusNotificationRequest,
1038 OCPP16FirmwareStatusNotificationResponse
1039 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1040 status: OCPP16FirmwareStatus.Downloaded,
1041 });
1042 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloaded;
1043 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1044 await chargingStation.ocppRequestService.requestHandler<
1045 OCPP16FirmwareStatusNotificationRequest,
1046 OCPP16FirmwareStatusNotificationResponse
1047 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1048 status: OCPP16FirmwareStatus.Installing,
1049 });
1050 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Installing;
1051 if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) {
1052 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1053 await chargingStation.reset(OCPP16StopTransactionReason.REBOOT);
1054 }
1055 }
1056
1057 private async handleRequestGetDiagnostics(
1058 chargingStation: ChargingStation,
1059 commandPayload: GetDiagnosticsRequest
1060 ): Promise<GetDiagnosticsResponse> {
1061 if (
1062 OCPP16ServiceUtils.checkFeatureProfile(
1063 chargingStation,
1064 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1065 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1066 ) === false
1067 ) {
1068 logger.warn(
1069 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`
1070 );
1071 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1072 }
1073 const uri = new URL(commandPayload.location);
1074 if (uri.protocol.startsWith('ftp:')) {
1075 let ftpClient: Client;
1076 try {
1077 const logFiles = fs
1078 .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'))
1079 .filter((file) => file.endsWith('.log'))
1080 .map((file) => path.join('./', file));
1081 const diagnosticsArchive = `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`;
1082 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
1083 ftpClient = new Client();
1084 const accessResponse = await ftpClient.access({
1085 host: uri.host,
1086 ...(Utils.isNotEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
1087 ...(Utils.isNotEmptyString(uri.username) && { user: uri.username }),
1088 ...(Utils.isNotEmptyString(uri.password) && { password: uri.password }),
1089 });
1090 let uploadResponse: FTPResponse;
1091 if (accessResponse.code === 220) {
1092 ftpClient.trackProgress((info) => {
1093 logger.info(
1094 `${chargingStation.logPrefix()} ${
1095 info.bytes / 1024
1096 } bytes transferred from diagnostics archive ${info.name}`
1097 );
1098 chargingStation.ocppRequestService
1099 .requestHandler<
1100 OCPP16DiagnosticsStatusNotificationRequest,
1101 OCPP16DiagnosticsStatusNotificationResponse
1102 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1103 status: OCPP16DiagnosticsStatus.Uploading,
1104 })
1105 .catch((error) => {
1106 logger.error(
1107 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
1108 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
1109 }'`,
1110 error
1111 );
1112 });
1113 });
1114 uploadResponse = await ftpClient.uploadFrom(
1115 path.join(
1116 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'),
1117 diagnosticsArchive
1118 ),
1119 `${uri.pathname}${diagnosticsArchive}`
1120 );
1121 if (uploadResponse.code === 226) {
1122 await chargingStation.ocppRequestService.requestHandler<
1123 OCPP16DiagnosticsStatusNotificationRequest,
1124 OCPP16DiagnosticsStatusNotificationResponse
1125 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1126 status: OCPP16DiagnosticsStatus.Uploaded,
1127 });
1128 if (ftpClient) {
1129 ftpClient.close();
1130 }
1131 return { fileName: diagnosticsArchive };
1132 }
1133 throw new OCPPError(
1134 ErrorType.GENERIC_ERROR,
1135 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1136 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
1137 }`,
1138 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1139 );
1140 }
1141 throw new OCPPError(
1142 ErrorType.GENERIC_ERROR,
1143 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1144 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
1145 }`,
1146 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1147 );
1148 } catch (error) {
1149 await chargingStation.ocppRequestService.requestHandler<
1150 OCPP16DiagnosticsStatusNotificationRequest,
1151 OCPP16DiagnosticsStatusNotificationResponse
1152 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1153 status: OCPP16DiagnosticsStatus.UploadFailed,
1154 });
1155 if (ftpClient) {
1156 ftpClient.close();
1157 }
1158 return this.handleIncomingRequestError(
1159 chargingStation,
1160 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1161 error as Error,
1162 { errorResponse: OCPPConstants.OCPP_RESPONSE_EMPTY }
1163 );
1164 }
1165 } else {
1166 logger.error(
1167 `${chargingStation.logPrefix()} Unsupported protocol ${
1168 uri.protocol
1169 } to transfer the diagnostic logs archive`
1170 );
1171 await chargingStation.ocppRequestService.requestHandler<
1172 OCPP16DiagnosticsStatusNotificationRequest,
1173 OCPP16DiagnosticsStatusNotificationResponse
1174 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1175 status: OCPP16DiagnosticsStatus.UploadFailed,
1176 });
1177 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1178 }
1179 }
1180
1181 private handleRequestTriggerMessage(
1182 chargingStation: ChargingStation,
1183 commandPayload: OCPP16TriggerMessageRequest
1184 ): OCPP16TriggerMessageResponse {
1185 if (
1186 !OCPP16ServiceUtils.checkFeatureProfile(
1187 chargingStation,
1188 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1189 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1190 ) ||
1191 !OCPP16ServiceUtils.isMessageTriggerSupported(
1192 chargingStation,
1193 commandPayload.requestedMessage
1194 )
1195 ) {
1196 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1197 }
1198 if (
1199 !OCPP16ServiceUtils.isConnectorIdValid(
1200 chargingStation,
1201 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1202 commandPayload.connectorId
1203 )
1204 ) {
1205 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
1206 }
1207 try {
1208 switch (commandPayload.requestedMessage) {
1209 case OCPP16MessageTrigger.BootNotification:
1210 setTimeout(() => {
1211 chargingStation.ocppRequestService
1212 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
1213 chargingStation,
1214 OCPP16RequestCommand.BOOT_NOTIFICATION,
1215 chargingStation.bootNotificationRequest,
1216 { skipBufferingOnError: true, triggerMessage: true }
1217 )
1218 .then((response) => {
1219 chargingStation.bootNotificationResponse = response;
1220 })
1221 .catch(() => {
1222 /* This is intentional */
1223 });
1224 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1225 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1226 case OCPP16MessageTrigger.Heartbeat:
1227 setTimeout(() => {
1228 chargingStation.ocppRequestService
1229 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
1230 chargingStation,
1231 OCPP16RequestCommand.HEARTBEAT,
1232 null,
1233 {
1234 triggerMessage: true,
1235 }
1236 )
1237 .catch(() => {
1238 /* This is intentional */
1239 });
1240 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1241 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1242 case OCPP16MessageTrigger.StatusNotification:
1243 setTimeout(() => {
1244 if (!Utils.isNullOrUndefined(commandPayload?.connectorId)) {
1245 chargingStation.ocppRequestService
1246 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
1247 chargingStation,
1248 OCPP16RequestCommand.STATUS_NOTIFICATION,
1249 {
1250 connectorId: commandPayload.connectorId,
1251 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1252 status: chargingStation.getConnectorStatus(commandPayload.connectorId)?.status,
1253 },
1254 {
1255 triggerMessage: true,
1256 }
1257 )
1258 .catch(() => {
1259 /* This is intentional */
1260 });
1261 } else {
1262 for (const connectorId of chargingStation.connectors.keys()) {
1263 chargingStation.ocppRequestService
1264 .requestHandler<
1265 OCPP16StatusNotificationRequest,
1266 OCPP16StatusNotificationResponse
1267 >(
1268 chargingStation,
1269 OCPP16RequestCommand.STATUS_NOTIFICATION,
1270 {
1271 connectorId,
1272 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1273 status: chargingStation.getConnectorStatus(connectorId)?.status,
1274 },
1275 {
1276 triggerMessage: true,
1277 }
1278 )
1279 .catch(() => {
1280 /* This is intentional */
1281 });
1282 }
1283 }
1284 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1285 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1286 default:
1287 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1288 }
1289 } catch (error) {
1290 return this.handleIncomingRequestError(
1291 chargingStation,
1292 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1293 error as Error,
1294 { errorResponse: OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
1295 );
1296 }
1297 }
1298
1299 private handleRequestDataTransfer(
1300 chargingStation: ChargingStation,
1301 commandPayload: OCPP16DataTransferRequest
1302 ): OCPP16DataTransferResponse {
1303 try {
1304 if (Object.values(OCPP16DataTransferVendorId).includes(commandPayload.vendorId)) {
1305 return {
1306 status: OCPP16DataTransferStatus.ACCEPTED,
1307 };
1308 }
1309 return {
1310 status: OCPP16DataTransferStatus.UNKNOWN_VENDOR_ID,
1311 };
1312 } catch (error) {
1313 return this.handleIncomingRequestError(
1314 chargingStation,
1315 OCPP16IncomingRequestCommand.DATA_TRANSFER,
1316 error as Error,
1317 { errorResponse: OCPPConstants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }
1318 );
1319 }
1320 }
1321 }