1 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
3 import { createWriteStream
, readdirSync
} from
'node:fs';
4 import { dirname
, join
, resolve
} from
'node:path';
5 import { URL
, fileURLToPath
} from
'node:url';
7 import type { JSONSchemaType
} from
'ajv';
8 import { Client
, type FTPResponse
} from
'basic-ftp';
9 import { isWithinInterval
, secondsToMilliseconds
} from
'date-fns';
10 import { create
} from
'tar';
12 import { OCPP16Constants
} from
'./OCPP16Constants';
13 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
18 removeExpiredReservations
,
19 setConfigurationKeyValue
,
20 } from
'../../../charging-station';
21 import { OCPPError
} from
'../../../exception';
23 type ChangeConfigurationRequest
,
24 type ChangeConfigurationResponse
,
25 type ClearChargingProfileRequest
,
26 type ClearChargingProfileResponse
,
30 type GetConfigurationRequest
,
31 type GetConfigurationResponse
,
32 type GetDiagnosticsRequest
,
33 type GetDiagnosticsResponse
,
34 type IncomingRequestHandler
,
37 OCPP16AuthorizationStatus
,
38 OCPP16AvailabilityType
,
39 type OCPP16BootNotificationRequest
,
40 type OCPP16BootNotificationResponse
,
41 type OCPP16CancelReservationRequest
,
42 type OCPP16ChangeAvailabilityRequest
,
43 type OCPP16ChangeAvailabilityResponse
,
44 OCPP16ChargePointErrorCode
,
45 OCPP16ChargePointStatus
,
46 type OCPP16ChargingProfile
,
47 OCPP16ChargingProfilePurposeType
,
48 type OCPP16ChargingSchedule
,
49 type OCPP16ClearCacheRequest
,
50 type OCPP16DataTransferRequest
,
51 type OCPP16DataTransferResponse
,
52 OCPP16DataTransferVendorId
,
53 OCPP16DiagnosticsStatus
,
54 type OCPP16DiagnosticsStatusNotificationRequest
,
55 type OCPP16DiagnosticsStatusNotificationResponse
,
57 type OCPP16FirmwareStatusNotificationRequest
,
58 type OCPP16FirmwareStatusNotificationResponse
,
59 type OCPP16GetCompositeScheduleRequest
,
60 type OCPP16GetCompositeScheduleResponse
,
61 type OCPP16HeartbeatRequest
,
62 type OCPP16HeartbeatResponse
,
63 OCPP16IncomingRequestCommand
,
66 type OCPP16ReserveNowRequest
,
67 type OCPP16ReserveNowResponse
,
68 OCPP16StandardParametersKey
,
69 type OCPP16StartTransactionRequest
,
70 type OCPP16StartTransactionResponse
,
71 type OCPP16StatusNotificationRequest
,
72 type OCPP16StatusNotificationResponse
,
73 OCPP16StopTransactionReason
,
74 OCPP16SupportedFeatureProfiles
,
75 type OCPP16TriggerMessageRequest
,
76 type OCPP16TriggerMessageResponse
,
77 type OCPP16UpdateFirmwareRequest
,
78 type OCPP16UpdateFirmwareResponse
,
79 type OCPPConfigurationKey
,
81 type RemoteStartTransactionRequest
,
82 type RemoteStopTransactionRequest
,
83 ReservationTerminationReason
,
85 type SetChargingProfileRequest
,
86 type SetChargingProfileResponse
,
87 type UnlockConnectorRequest
,
88 type UnlockConnectorResponse
,
89 } from
'../../../types';
94 formatDurationMilliSeconds
,
103 } from
'../../../utils';
104 import { OCPPIncomingRequestService
} from
'../OCPPIncomingRequestService';
106 const moduleName
= 'OCPP16IncomingRequestService';
108 export class OCPP16IncomingRequestService
extends OCPPIncomingRequestService
{
109 protected jsonSchemas
: Map
<OCPP16IncomingRequestCommand
, JSONSchemaType
<JsonObject
>>;
110 private incomingRequestHandlers
: Map
<OCPP16IncomingRequestCommand
, IncomingRequestHandler
>;
112 public constructor() {
113 // if (new.target?.name === moduleName) {
114 // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
116 super(OCPPVersion
.VERSION_16
);
117 this.incomingRequestHandlers
= new Map
<OCPP16IncomingRequestCommand
, IncomingRequestHandler
>([
119 OCPP16IncomingRequestCommand
.RESET
,
120 this.handleRequestReset
.bind(this) as unknown
as IncomingRequestHandler
,
123 OCPP16IncomingRequestCommand
.CLEAR_CACHE
,
124 this.handleRequestClearCache
.bind(this) as IncomingRequestHandler
,
127 OCPP16IncomingRequestCommand
.UNLOCK_CONNECTOR
,
128 this.handleRequestUnlockConnector
.bind(this) as unknown
as IncomingRequestHandler
,
131 OCPP16IncomingRequestCommand
.GET_CONFIGURATION
,
132 this.handleRequestGetConfiguration
.bind(this) as IncomingRequestHandler
,
135 OCPP16IncomingRequestCommand
.CHANGE_CONFIGURATION
,
136 this.handleRequestChangeConfiguration
.bind(this) as unknown
as IncomingRequestHandler
,
139 OCPP16IncomingRequestCommand
.GET_COMPOSITE_SCHEDULE
,
140 this.handleRequestGetCompositeSchedule
.bind(this) as unknown
as IncomingRequestHandler
,
143 OCPP16IncomingRequestCommand
.SET_CHARGING_PROFILE
,
144 this.handleRequestSetChargingProfile
.bind(this) as unknown
as IncomingRequestHandler
,
147 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
,
148 this.handleRequestClearChargingProfile
.bind(this) as IncomingRequestHandler
,
151 OCPP16IncomingRequestCommand
.CHANGE_AVAILABILITY
,
152 this.handleRequestChangeAvailability
.bind(this) as unknown
as IncomingRequestHandler
,
155 OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
,
156 this.handleRequestRemoteStartTransaction
.bind(this) as unknown
as IncomingRequestHandler
,
159 OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
,
160 this.handleRequestRemoteStopTransaction
.bind(this) as unknown
as IncomingRequestHandler
,
163 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
164 this.handleRequestGetDiagnostics
.bind(this) as IncomingRequestHandler
,
167 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
168 this.handleRequestTriggerMessage
.bind(this) as unknown
as IncomingRequestHandler
,
171 OCPP16IncomingRequestCommand
.DATA_TRANSFER
,
172 this.handleRequestDataTransfer
.bind(this) as unknown
as IncomingRequestHandler
,
175 OCPP16IncomingRequestCommand
.UPDATE_FIRMWARE
,
176 this.handleRequestUpdateFirmware
.bind(this) as unknown
as IncomingRequestHandler
,
179 OCPP16IncomingRequestCommand
.RESERVE_NOW
,
180 this.handleRequestReserveNow
.bind(this) as unknown
as IncomingRequestHandler
,
183 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
,
184 this.handleRequestCancelReservation
.bind(this) as unknown
as IncomingRequestHandler
,
187 this.jsonSchemas
= new Map
<OCPP16IncomingRequestCommand
, JSONSchemaType
<JsonObject
>>([
189 OCPP16IncomingRequestCommand
.RESET
,
190 OCPP16ServiceUtils
.parseJsonSchemaFile
<ResetRequest
>(
191 'assets/json-schemas/ocpp/1.6/Reset.json',
197 OCPP16IncomingRequestCommand
.CLEAR_CACHE
,
198 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16ClearCacheRequest
>(
199 'assets/json-schemas/ocpp/1.6/ClearCache.json',
205 OCPP16IncomingRequestCommand
.UNLOCK_CONNECTOR
,
206 OCPP16ServiceUtils
.parseJsonSchemaFile
<UnlockConnectorRequest
>(
207 'assets/json-schemas/ocpp/1.6/UnlockConnector.json',
213 OCPP16IncomingRequestCommand
.GET_CONFIGURATION
,
214 OCPP16ServiceUtils
.parseJsonSchemaFile
<GetConfigurationRequest
>(
215 'assets/json-schemas/ocpp/1.6/GetConfiguration.json',
221 OCPP16IncomingRequestCommand
.CHANGE_CONFIGURATION
,
222 OCPP16ServiceUtils
.parseJsonSchemaFile
<ChangeConfigurationRequest
>(
223 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json',
229 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
230 OCPP16ServiceUtils
.parseJsonSchemaFile
<GetDiagnosticsRequest
>(
231 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json',
237 OCPP16IncomingRequestCommand
.GET_COMPOSITE_SCHEDULE
,
238 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16GetCompositeScheduleRequest
>(
239 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json',
245 OCPP16IncomingRequestCommand
.SET_CHARGING_PROFILE
,
246 OCPP16ServiceUtils
.parseJsonSchemaFile
<SetChargingProfileRequest
>(
247 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json',
253 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
,
254 OCPP16ServiceUtils
.parseJsonSchemaFile
<ClearChargingProfileRequest
>(
255 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json',
261 OCPP16IncomingRequestCommand
.CHANGE_AVAILABILITY
,
262 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16ChangeAvailabilityRequest
>(
263 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json',
269 OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
,
270 OCPP16ServiceUtils
.parseJsonSchemaFile
<RemoteStartTransactionRequest
>(
271 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json',
277 OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
,
278 OCPP16ServiceUtils
.parseJsonSchemaFile
<RemoteStopTransactionRequest
>(
279 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json',
285 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
286 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16TriggerMessageRequest
>(
287 'assets/json-schemas/ocpp/1.6/TriggerMessage.json',
293 OCPP16IncomingRequestCommand
.DATA_TRANSFER
,
294 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16DataTransferRequest
>(
295 'assets/json-schemas/ocpp/1.6/DataTransfer.json',
301 OCPP16IncomingRequestCommand
.UPDATE_FIRMWARE
,
302 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16UpdateFirmwareRequest
>(
303 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json',
309 OCPP16IncomingRequestCommand
.RESERVE_NOW
,
310 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16ReserveNowRequest
>(
311 'assets/json-schemas/ocpp/1.6/ReserveNow.json',
317 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
,
318 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16CancelReservationRequest
>(
319 'assets/json-schemas/ocpp/1.6/CancelReservation.json',
325 this.validatePayload
= this.validatePayload
.bind(this) as (
326 chargingStation
: ChargingStation
,
327 commandName
: OCPP16IncomingRequestCommand
,
328 commandPayload
: JsonType
,
332 public async incomingRequestHandler
<ReqType
extends JsonType
, ResType
extends JsonType
>(
333 chargingStation
: ChargingStation
,
335 commandName
: OCPP16IncomingRequestCommand
,
336 commandPayload
: ReqType
,
338 let response
: ResType
;
340 chargingStation
.getOcppStrictCompliance() === true &&
341 chargingStation
.inPendingState() === true &&
342 (commandName
=== OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
||
343 commandName
=== OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
)
346 ErrorType
.SECURITY_ERROR
,
347 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
351 )} while the charging station is in pending state on the central server`,
357 chargingStation
.isRegistered() === true ||
358 (chargingStation
.getOcppStrictCompliance() === false &&
359 chargingStation
.inUnknownState() === true)
362 this.incomingRequestHandlers
.has(commandName
) === true &&
363 OCPP16ServiceUtils
.isIncomingRequestCommandSupported(chargingStation
, commandName
) === true
366 this.validatePayload(chargingStation
, commandName
, commandPayload
);
367 // Call the method to build the response
368 response
= (await this.incomingRequestHandlers
.get(commandName
)!(
375 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler:
376 Handle incoming request error:`,
384 ErrorType
.NOT_IMPLEMENTED
,
385 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
396 ErrorType
.SECURITY_ERROR
,
397 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
401 )} while the charging station is not registered on the central server.`,
406 // Send the built response
407 await chargingStation
.ocppRequestService
.sendResponse(
415 private validatePayload(
416 chargingStation
: ChargingStation
,
417 commandName
: OCPP16IncomingRequestCommand
,
418 commandPayload
: JsonType
,
420 if (this.jsonSchemas
.has(commandName
) === true) {
421 return this.validateIncomingRequestPayload(
424 this.jsonSchemas
.get(commandName
)!,
429 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found
430 for command '${commandName}' PDU validation`,
435 // Simulate charging station restart
436 private handleRequestReset(
437 chargingStation
: ChargingStation
,
438 commandPayload
: ResetRequest
,
440 const { type } = commandPayload
;
441 this.runInAsyncScope(
442 chargingStation
.reset
.bind(chargingStation
) as (
443 this: ChargingStation
,
447 `${type}Reset` as OCPP16StopTransactionReason
,
448 ).catch(Constants
.EMPTY_FUNCTION
);
450 `${chargingStation.logPrefix()} ${type} reset command received, simulating it. The station will be
451 back online in ${formatDurationMilliSeconds(chargingStation.stationInfo.resetTime!)}`,
453 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
;
456 private async handleRequestUnlockConnector(
457 chargingStation
: ChargingStation
,
458 commandPayload
: UnlockConnectorRequest
,
459 ): Promise
<UnlockConnectorResponse
> {
460 const { connectorId
} = commandPayload
;
461 if (chargingStation
.hasConnector(connectorId
) === false) {
463 `${chargingStation.logPrefix()} Trying to unlock a non existing
464 connector id ${connectorId.toString()}`,
466 return OCPP16Constants
.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED
;
468 if (connectorId
=== 0) {
470 `${chargingStation.logPrefix()} Trying to unlock connector id ${connectorId.toString()}`,
472 return OCPP16Constants
.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED
;
474 if (chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== true) {
475 const stopResponse
= await chargingStation
.stopTransactionOnConnector(
477 OCPP16StopTransactionReason
.UNLOCK_COMMAND
,
479 if (stopResponse
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
480 return OCPP16Constants
.OCPP_RESPONSE_UNLOCKED
;
482 return OCPP16Constants
.OCPP_RESPONSE_UNLOCK_FAILED
;
484 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
487 OCPP16ChargePointStatus
.Available
,
489 return OCPP16Constants
.OCPP_RESPONSE_UNLOCKED
;
492 private handleRequestGetConfiguration(
493 chargingStation
: ChargingStation
,
494 commandPayload
: GetConfigurationRequest
,
495 ): GetConfigurationResponse
{
496 const { key
} = commandPayload
;
497 const configurationKey
: OCPPConfigurationKey
[] = [];
498 const unknownKey
: string[] = [];
499 if (isUndefined(key
) === true) {
500 for (const configuration
of chargingStation
.ocppConfiguration
!.configurationKey
!) {
501 if (isUndefined(configuration
.visible
) === true) {
502 configuration
.visible
= true;
504 if (configuration
.visible
=== false) {
507 configurationKey
.push({
508 key
: configuration
.key
,
509 readonly: configuration
.readonly,
510 value
: configuration
.value
,
513 } else if (isNotEmptyArray(key
) === true) {
514 for (const k
of key
!) {
515 const keyFound
= getConfigurationKey(chargingStation
, k
, true);
517 if (isUndefined(keyFound
.visible
) === true) {
518 keyFound
.visible
= true;
520 if (keyFound
.visible
=== false) {
523 configurationKey
.push({
525 readonly: keyFound
.readonly,
526 value
: keyFound
.value
,
539 private handleRequestChangeConfiguration(
540 chargingStation
: ChargingStation
,
541 commandPayload
: ChangeConfigurationRequest
,
542 ): ChangeConfigurationResponse
{
543 const { key
, value
} = commandPayload
;
544 const keyToChange
= getConfigurationKey(chargingStation
, key
, true);
545 if (keyToChange
?.readonly === true) {
546 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_REJECTED
;
547 } else if (keyToChange
?.readonly === false) {
548 let valueChanged
= false;
549 if (keyToChange
.value
!== value
) {
550 setConfigurationKeyValue(chargingStation
, key
, value
, true);
553 let triggerHeartbeatRestart
= false;
555 (keyToChange
.key
as OCPP16StandardParametersKey
) ===
556 OCPP16StandardParametersKey
.HeartBeatInterval
&&
559 setConfigurationKeyValue(
561 OCPP16StandardParametersKey
.HeartbeatInterval
,
564 triggerHeartbeatRestart
= true;
567 (keyToChange
.key
as OCPP16StandardParametersKey
) ===
568 OCPP16StandardParametersKey
.HeartbeatInterval
&&
571 setConfigurationKeyValue(
573 OCPP16StandardParametersKey
.HeartBeatInterval
,
576 triggerHeartbeatRestart
= true;
578 if (triggerHeartbeatRestart
) {
579 chargingStation
.restartHeartbeat();
582 (keyToChange
.key
as OCPP16StandardParametersKey
) ===
583 OCPP16StandardParametersKey
.WebSocketPingInterval
&&
586 chargingStation
.restartWebSocketPing();
588 if (keyToChange
.reboot
) {
589 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED
;
591 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_ACCEPTED
;
593 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED
;
596 private handleRequestSetChargingProfile(
597 chargingStation
: ChargingStation
,
598 commandPayload
: SetChargingProfileRequest
,
599 ): SetChargingProfileResponse
{
601 OCPP16ServiceUtils
.checkFeatureProfile(
603 OCPP16SupportedFeatureProfiles
.SmartCharging
,
604 OCPP16IncomingRequestCommand
.SET_CHARGING_PROFILE
,
607 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED
;
609 const { connectorId
, csChargingProfiles
} = commandPayload
;
610 if (chargingStation
.hasConnector(connectorId
) === false) {
612 `${chargingStation.logPrefix()} Trying to set charging profile(s) to a
613 non existing connector id ${connectorId}`,
615 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
;
618 csChargingProfiles
.chargingProfilePurpose
===
619 OCPP16ChargingProfilePurposeType
.CHARGE_POINT_MAX_PROFILE
&&
622 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
;
625 csChargingProfiles
.chargingProfilePurpose
=== OCPP16ChargingProfilePurposeType
.TX_PROFILE
&&
626 (connectorId
=== 0 ||
627 chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== false)
630 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s)
631 on connector ${connectorId} without a started transaction`,
633 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
;
635 OCPP16ServiceUtils
.setChargingProfile(chargingStation
, connectorId
, csChargingProfiles
);
637 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${connectorId}: %j`,
640 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED
;
643 private handleRequestGetCompositeSchedule(
644 chargingStation
: ChargingStation
,
645 commandPayload
: OCPP16GetCompositeScheduleRequest
,
646 ): OCPP16GetCompositeScheduleResponse
{
648 OCPP16ServiceUtils
.checkFeatureProfile(
650 OCPP16SupportedFeatureProfiles
.SmartCharging
,
651 OCPP16IncomingRequestCommand
.GET_COMPOSITE_SCHEDULE
,
654 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
;
656 const { connectorId
, duration
} = commandPayload
;
657 if (chargingStation
.hasConnector(connectorId
) === false) {
659 `${chargingStation.logPrefix()} Trying to get composite schedule to a
660 non existing connector id ${connectorId}`,
662 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
;
664 if (isEmptyArray(chargingStation
.getConnectorStatus(connectorId
)?.chargingProfiles
)) {
665 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
;
667 const startDate
= new Date();
668 const endDate
= new Date(startDate
.getTime() + secondsToMilliseconds(duration
));
669 let compositeSchedule
: OCPP16ChargingSchedule
| undefined;
670 for (const chargingProfile
of chargingStation
.getConnectorStatus(connectorId
)!
671 .chargingProfiles
!) {
672 // FIXME: build the composite schedule including the local power limit, the stack level, the charging rate unit, etc.
674 isWithinInterval(chargingProfile
.chargingSchedule
.startSchedule
!, {
679 compositeSchedule
= chargingProfile
.chargingSchedule
;
684 status: GenericStatus
.Accepted
,
685 scheduleStart
: compositeSchedule
?.startSchedule
,
687 chargingSchedule
: compositeSchedule
,
691 private handleRequestClearChargingProfile(
692 chargingStation
: ChargingStation
,
693 commandPayload
: ClearChargingProfileRequest
,
694 ): ClearChargingProfileResponse
{
696 OCPP16ServiceUtils
.checkFeatureProfile(
698 OCPP16SupportedFeatureProfiles
.SmartCharging
,
699 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
,
702 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
;
704 const { connectorId
} = commandPayload
;
705 if (chargingStation
.hasConnector(connectorId
!) === false) {
707 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to
708 a non existing connector id ${connectorId}`,
710 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
;
713 !isNullOrUndefined(connectorId
) &&
714 isNotEmptyArray(chargingStation
.getConnectorStatus(connectorId
!)?.chargingProfiles
)
716 chargingStation
.getConnectorStatus(connectorId
!)!.chargingProfiles
= [];
718 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${connectorId}`,
720 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED
;
722 if (isNullOrUndefined(connectorId
)) {
723 let clearedCP
= false;
724 if (chargingStation
.hasEvses
) {
725 for (const evseStatus
of chargingStation
.evses
.values()) {
726 for (const connectorStatus
of evseStatus
.connectors
.values()) {
727 clearedCP
= OCPP16ServiceUtils
.clearChargingProfiles(
730 connectorStatus
.chargingProfiles
,
735 for (const id
of chargingStation
.connectors
.keys()) {
736 clearedCP
= OCPP16ServiceUtils
.clearChargingProfiles(
739 chargingStation
.getConnectorStatus(id
)?.chargingProfiles
,
744 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED
;
747 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
;
750 private async handleRequestChangeAvailability(
751 chargingStation
: ChargingStation
,
752 commandPayload
: OCPP16ChangeAvailabilityRequest
,
753 ): Promise
<OCPP16ChangeAvailabilityResponse
> {
754 const { connectorId
, type } = commandPayload
;
755 if (chargingStation
.hasConnector(connectorId
) === false) {
757 `${chargingStation.logPrefix()} Trying to change the availability of a
758 non existing connector id ${connectorId.toString()}`,
760 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_REJECTED
;
762 const chargePointStatus
: OCPP16ChargePointStatus
=
763 type === OCPP16AvailabilityType
.Operative
764 ? OCPP16ChargePointStatus
.Available
765 : OCPP16ChargePointStatus
.Unavailable
;
766 if (connectorId
=== 0) {
767 let response
: OCPP16ChangeAvailabilityResponse
;
768 if (chargingStation
.hasEvses
) {
769 for (const evseStatus
of chargingStation
.evses
.values()) {
770 response
= await OCPP16ServiceUtils
.changeAvailability(
772 [...evseStatus
.connectors
.keys()],
778 response
= await OCPP16ServiceUtils
.changeAvailability(
780 [...chargingStation
.connectors
.keys()],
788 (chargingStation
.isChargingStationAvailable() === true ||
789 (chargingStation
.isChargingStationAvailable() === false &&
790 type === OCPP16AvailabilityType
.Inoperative
))
792 if (chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== true) {
793 chargingStation
.getConnectorStatus(connectorId
)!.availability
= type;
794 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_SCHEDULED
;
796 chargingStation
.getConnectorStatus(connectorId
)!.availability
= type;
797 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
802 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_ACCEPTED
;
804 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_REJECTED
;
807 private async handleRequestRemoteStartTransaction(
808 chargingStation
: ChargingStation
,
809 commandPayload
: RemoteStartTransactionRequest
,
810 ): Promise
<GenericResponse
> {
811 const { connectorId
: transactionConnectorId
, idTag
, chargingProfile
} = commandPayload
;
812 if (chargingStation
.hasConnector(transactionConnectorId
) === false) {
813 return this.notifyRemoteStartTransactionRejected(
815 transactionConnectorId
,
820 !chargingStation
.isChargingStationAvailable() ||
821 !chargingStation
.isConnectorAvailable(transactionConnectorId
)
823 return this.notifyRemoteStartTransactionRejected(
825 transactionConnectorId
,
829 const remoteStartTransactionLogMsg
= `
830 ${chargingStation.logPrefix()} Transaction remotely STARTED on ${
831 chargingStation.stationInfo.chargingStationId
832 }#${transactionConnectorId.toString()} for idTag '${idTag}'`;
833 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
835 transactionConnectorId
,
836 OCPP16ChargePointStatus
.Preparing
,
838 const connectorStatus
= chargingStation
.getConnectorStatus(transactionConnectorId
)!;
839 // Authorization check required
841 chargingStation
.getAuthorizeRemoteTxRequests() === true &&
842 (await OCPP16ServiceUtils
.isIdTagAuthorized(chargingStation
, transactionConnectorId
, idTag
))
844 // Authorization successful, start transaction
846 this.setRemoteStartTransactionChargingProfile(
848 transactionConnectorId
,
852 connectorStatus
.transactionRemoteStarted
= true;
855 await chargingStation
.ocppRequestService
.requestHandler
<
856 OCPP16StartTransactionRequest
,
857 OCPP16StartTransactionResponse
858 >(chargingStation
, OCPP16RequestCommand
.START_TRANSACTION
, {
859 connectorId
: transactionConnectorId
,
862 ).idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
864 logger
.debug(remoteStartTransactionLogMsg
);
865 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
;
867 return this.notifyRemoteStartTransactionRejected(
869 transactionConnectorId
,
873 return this.notifyRemoteStartTransactionRejected(
875 transactionConnectorId
,
879 // No authorization check required, start transaction
881 this.setRemoteStartTransactionChargingProfile(
883 transactionConnectorId
,
887 connectorStatus
.transactionRemoteStarted
= true;
890 await chargingStation
.ocppRequestService
.requestHandler
<
891 OCPP16StartTransactionRequest
,
892 OCPP16StartTransactionResponse
893 >(chargingStation
, OCPP16RequestCommand
.START_TRANSACTION
, {
894 connectorId
: transactionConnectorId
,
897 ).idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
899 logger
.debug(remoteStartTransactionLogMsg
);
900 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
;
902 return this.notifyRemoteStartTransactionRejected(
904 transactionConnectorId
,
908 return this.notifyRemoteStartTransactionRejected(
910 transactionConnectorId
,
915 private async notifyRemoteStartTransactionRejected(
916 chargingStation
: ChargingStation
,
919 ): Promise
<GenericResponse
> {
921 chargingStation
.getConnectorStatus(connectorId
)?.status !== OCPP16ChargePointStatus
.Available
923 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
926 OCPP16ChargePointStatus
.Available
,
930 `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id
931 ${connectorId.toString()}, idTag '${idTag}', availability '${chargingStation.getConnectorStatus(
933 )?.availability}', status '${chargingStation.getConnectorStatus(connectorId)?.status}'`,
935 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
;
938 private setRemoteStartTransactionChargingProfile(
939 chargingStation
: ChargingStation
,
941 chargingProfile
: OCPP16ChargingProfile
,
945 chargingProfile
.chargingProfilePurpose
=== OCPP16ChargingProfilePurposeType
.TX_PROFILE
947 OCPP16ServiceUtils
.setChargingProfile(chargingStation
, connectorId
, chargingProfile
);
949 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction
950 on connector id ${connectorId}: %j`,
956 chargingProfile
.chargingProfilePurpose
!== OCPP16ChargingProfilePurposeType
.TX_PROFILE
959 `${chargingStation.logPrefix()} Not allowed to set ${
960 chargingProfile.chargingProfilePurpose
961 } charging profile(s) at remote start transaction`,
968 private async handleRequestRemoteStopTransaction(
969 chargingStation
: ChargingStation
,
970 commandPayload
: RemoteStopTransactionRequest
,
971 ): Promise
<GenericResponse
> {
972 const { transactionId
} = commandPayload
;
973 if (chargingStation
.hasEvses
) {
974 for (const [evseId
, evseStatus
] of chargingStation
.evses
) {
976 for (const [connectorId
, connectorStatus
] of evseStatus
.connectors
) {
977 if (connectorStatus
.transactionId
=== transactionId
) {
978 return OCPP16ServiceUtils
.remoteStopTransaction(chargingStation
, connectorId
);
984 for (const connectorId
of chargingStation
.connectors
.keys()) {
987 chargingStation
.getConnectorStatus(connectorId
)?.transactionId
=== transactionId
989 return OCPP16ServiceUtils
.remoteStopTransaction(chargingStation
, connectorId
);
994 `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction with id
995 ${transactionId.toString()}`,
997 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
;
1000 private handleRequestUpdateFirmware(
1001 chargingStation
: ChargingStation
,
1002 commandPayload
: OCPP16UpdateFirmwareRequest
,
1003 ): OCPP16UpdateFirmwareResponse
{
1005 OCPP16ServiceUtils
.checkFeatureProfile(
1007 OCPP16SupportedFeatureProfiles
.FirmwareManagement
,
1008 OCPP16IncomingRequestCommand
.UPDATE_FIRMWARE
,
1012 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware:
1013 Cannot simulate firmware update: feature profile not supported`,
1015 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
;
1017 let { retrieveDate
} = commandPayload
;
1019 !isNullOrUndefined(chargingStation
.stationInfo
.firmwareStatus
) &&
1020 chargingStation
.stationInfo
.firmwareStatus
!== OCPP16FirmwareStatus
.Installed
1023 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware:
1024 Cannot simulate firmware update: firmware update is already in progress`,
1026 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
;
1028 retrieveDate
= convertToDate(retrieveDate
)!;
1029 const now
= Date.now();
1030 if (retrieveDate
?.getTime() <= now
) {
1031 this.runInAsyncScope(
1032 this.updateFirmwareSimulation
.bind(this) as (
1033 this: OCPP16IncomingRequestService
,
1038 ).catch(Constants
.EMPTY_FUNCTION
);
1042 this.runInAsyncScope(
1043 this.updateFirmwareSimulation
.bind(this) as (
1044 this: OCPP16IncomingRequestService
,
1049 ).catch(Constants
.EMPTY_FUNCTION
);
1051 retrieveDate
?.getTime() - now
,
1054 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
;
1057 private async updateFirmwareSimulation(
1058 chargingStation
: ChargingStation
,
1062 if (checkChargingStation(chargingStation
, chargingStation
.logPrefix()) === false) {
1065 if (chargingStation
.hasEvses
) {
1066 for (const [evseId
, evseStatus
] of chargingStation
.evses
) {
1068 for (const [connectorId
, connectorStatus
] of evseStatus
.connectors
) {
1069 if (connectorStatus
?.transactionStarted
=== false) {
1070 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1073 OCPP16ChargePointStatus
.Unavailable
,
1080 for (const connectorId
of chargingStation
.connectors
.keys()) {
1083 chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== false
1085 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1088 OCPP16ChargePointStatus
.Unavailable
,
1093 await chargingStation
.ocppRequestService
.requestHandler
<
1094 OCPP16FirmwareStatusNotificationRequest
,
1095 OCPP16FirmwareStatusNotificationResponse
1096 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1097 status: OCPP16FirmwareStatus
.Downloading
,
1099 chargingStation
.stationInfo
.firmwareStatus
= OCPP16FirmwareStatus
.Downloading
;
1101 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
===
1102 OCPP16FirmwareStatus
.DownloadFailed
1104 await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
)));
1105 await chargingStation
.ocppRequestService
.requestHandler
<
1106 OCPP16FirmwareStatusNotificationRequest
,
1107 OCPP16FirmwareStatusNotificationResponse
1108 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1109 status: chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
,
1111 chargingStation
.stationInfo
.firmwareStatus
=
1112 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
;
1115 await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
)));
1116 await chargingStation
.ocppRequestService
.requestHandler
<
1117 OCPP16FirmwareStatusNotificationRequest
,
1118 OCPP16FirmwareStatusNotificationResponse
1119 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1120 status: OCPP16FirmwareStatus
.Downloaded
,
1122 chargingStation
.stationInfo
.firmwareStatus
= OCPP16FirmwareStatus
.Downloaded
;
1123 let wasTransactionsStarted
= false;
1124 let transactionsStarted
: boolean;
1126 const runningTransactions
= chargingStation
.getNumberOfRunningTransactions();
1127 if (runningTransactions
> 0) {
1128 const waitTime
= secondsToMilliseconds(15);
1130 `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation:
1131 ${runningTransactions} transaction(s) in progress, waiting ${formatDurationMilliSeconds(
1133 )} before continuing firmware update simulation`,
1135 await sleep(waitTime
);
1136 transactionsStarted
= true;
1137 wasTransactionsStarted
= true;
1139 if (chargingStation
.hasEvses
) {
1140 for (const [evseId
, evseStatus
] of chargingStation
.evses
) {
1142 for (const [connectorId
, connectorStatus
] of evseStatus
.connectors
) {
1143 if (connectorStatus
?.status !== OCPP16ChargePointStatus
.Unavailable
) {
1144 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1147 OCPP16ChargePointStatus
.Unavailable
,
1154 for (const connectorId
of chargingStation
.connectors
.keys()) {
1157 chargingStation
.getConnectorStatus(connectorId
)?.status !==
1158 OCPP16ChargePointStatus
.Unavailable
1160 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1163 OCPP16ChargePointStatus
.Unavailable
,
1168 transactionsStarted
= false;
1170 } while (transactionsStarted
);
1171 !wasTransactionsStarted
&&
1172 (await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
))));
1173 if (checkChargingStation(chargingStation
, chargingStation
.logPrefix()) === false) {
1176 await chargingStation
.ocppRequestService
.requestHandler
<
1177 OCPP16FirmwareStatusNotificationRequest
,
1178 OCPP16FirmwareStatusNotificationResponse
1179 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1180 status: OCPP16FirmwareStatus
.Installing
,
1182 chargingStation
.stationInfo
.firmwareStatus
= OCPP16FirmwareStatus
.Installing
;
1184 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
===
1185 OCPP16FirmwareStatus
.InstallationFailed
1187 await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
)));
1188 await chargingStation
.ocppRequestService
.requestHandler
<
1189 OCPP16FirmwareStatusNotificationRequest
,
1190 OCPP16FirmwareStatusNotificationResponse
1191 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1192 status: chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
,
1194 chargingStation
.stationInfo
.firmwareStatus
=
1195 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
;
1198 if (chargingStation
.stationInfo
?.firmwareUpgrade
?.reset
=== true) {
1199 await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
)));
1200 await chargingStation
.reset(OCPP16StopTransactionReason
.REBOOT
);
1204 private async handleRequestGetDiagnostics(
1205 chargingStation
: ChargingStation
,
1206 commandPayload
: GetDiagnosticsRequest
,
1207 ): Promise
<GetDiagnosticsResponse
> {
1209 OCPP16ServiceUtils
.checkFeatureProfile(
1211 OCPP16SupportedFeatureProfiles
.FirmwareManagement
,
1212 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
1216 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics:
1217 Cannot get diagnostics: feature profile not supported`,
1219 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
;
1221 const { location
} = commandPayload
;
1222 const uri
= new URL(location
);
1223 if (uri
.protocol
.startsWith('ftp:')) {
1224 let ftpClient
: Client
| undefined;
1226 const logFiles
= readdirSync(resolve(dirname(fileURLToPath(import.meta
.url
)), '../'))
1227 .filter((file
) => file
.endsWith('.log'))
1228 .map((file
) => join('./', file
));
1229 const diagnosticsArchive
= `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`;
1230 create({ gzip
: true }, logFiles
).pipe(createWriteStream(diagnosticsArchive
));
1231 ftpClient
= new Client();
1232 const accessResponse
= await ftpClient
.access({
1234 ...(isNotEmptyString(uri
.port
) && { port
: convertToInt(uri
.port
) }),
1235 ...(isNotEmptyString(uri
.username
) && { user
: uri
.username
}),
1236 ...(isNotEmptyString(uri
.password
) && { password
: uri
.password
}),
1238 let uploadResponse
: FTPResponse
| undefined;
1239 if (accessResponse
.code
=== 220) {
1240 ftpClient
.trackProgress((info
) => {
1242 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: ${
1244 } bytes transferred from diagnostics archive ${info.name}`,
1246 chargingStation
.ocppRequestService
1248 OCPP16DiagnosticsStatusNotificationRequest
,
1249 OCPP16DiagnosticsStatusNotificationResponse
1250 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1251 status: OCPP16DiagnosticsStatus
.Uploading
,
1255 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics:
1256 Error while sending '${OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION}'`,
1261 uploadResponse
= await ftpClient
.uploadFrom(
1262 join(resolve(dirname(fileURLToPath(import.meta
.url
)), '../'), diagnosticsArchive
),
1263 `${uri.pathname}${diagnosticsArchive}`,
1265 if (uploadResponse
.code
=== 226) {
1266 await chargingStation
.ocppRequestService
.requestHandler
<
1267 OCPP16DiagnosticsStatusNotificationRequest
,
1268 OCPP16DiagnosticsStatusNotificationResponse
1269 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1270 status: OCPP16DiagnosticsStatus
.Uploaded
,
1275 return { fileName
: diagnosticsArchive
};
1277 throw new OCPPError(
1278 ErrorType
.GENERIC_ERROR
,
1279 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1280 uploadResponse?.code && `|${uploadResponse?.code.toString()}
`
1282 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
1285 throw new OCPPError(
1286 ErrorType
.GENERIC_ERROR
,
1287 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1288 uploadResponse?.code && `|${uploadResponse?.code.toString()}
`
1290 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
1293 await chargingStation
.ocppRequestService
.requestHandler
<
1294 OCPP16DiagnosticsStatusNotificationRequest
,
1295 OCPP16DiagnosticsStatusNotificationResponse
1296 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1297 status: OCPP16DiagnosticsStatus
.UploadFailed
,
1302 return this.handleIncomingRequestError
<GetDiagnosticsResponse
>(
1304 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
1306 { errorResponse
: OCPP16Constants
.OCPP_RESPONSE_EMPTY
},
1311 `${chargingStation.logPrefix()} Unsupported protocol ${
1313 } to transfer the diagnostic logs archive`,
1315 await chargingStation
.ocppRequestService
.requestHandler
<
1316 OCPP16DiagnosticsStatusNotificationRequest
,
1317 OCPP16DiagnosticsStatusNotificationResponse
1318 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1319 status: OCPP16DiagnosticsStatus
.UploadFailed
,
1321 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
;
1325 private handleRequestTriggerMessage(
1326 chargingStation
: ChargingStation
,
1327 commandPayload
: OCPP16TriggerMessageRequest
,
1328 ): OCPP16TriggerMessageResponse
{
1329 const { requestedMessage
, connectorId
} = commandPayload
;
1331 !OCPP16ServiceUtils
.checkFeatureProfile(
1333 OCPP16SupportedFeatureProfiles
.RemoteTrigger
,
1334 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
1336 !OCPP16ServiceUtils
.isMessageTriggerSupported(chargingStation
, requestedMessage
)
1338 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED
;
1341 !OCPP16ServiceUtils
.isConnectorIdValid(
1343 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
1347 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED
;
1350 switch (requestedMessage
) {
1351 case OCPP16MessageTrigger
.BootNotification
:
1353 chargingStation
.ocppRequestService
1354 .requestHandler
<OCPP16BootNotificationRequest
, OCPP16BootNotificationResponse
>(
1356 OCPP16RequestCommand
.BOOT_NOTIFICATION
,
1357 chargingStation
.bootNotificationRequest
,
1358 { skipBufferingOnError
: true, triggerMessage
: true },
1360 .then((response
) => {
1361 chargingStation
.bootNotificationResponse
= response
;
1363 .catch(Constants
.EMPTY_FUNCTION
);
1364 }, OCPP16Constants
.OCPP_TRIGGER_MESSAGE_DELAY
);
1365 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED
;
1366 case OCPP16MessageTrigger
.Heartbeat
:
1368 chargingStation
.ocppRequestService
1369 .requestHandler
<OCPP16HeartbeatRequest
, OCPP16HeartbeatResponse
>(
1371 OCPP16RequestCommand
.HEARTBEAT
,
1374 triggerMessage
: true,
1377 .catch(Constants
.EMPTY_FUNCTION
);
1378 }, OCPP16Constants
.OCPP_TRIGGER_MESSAGE_DELAY
);
1379 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED
;
1380 case OCPP16MessageTrigger
.StatusNotification
:
1382 if (!isNullOrUndefined(connectorId
)) {
1383 chargingStation
.ocppRequestService
1384 .requestHandler
<OCPP16StatusNotificationRequest
, OCPP16StatusNotificationResponse
>(
1386 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
1389 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
1390 status: chargingStation
.getConnectorStatus(connectorId
!)?.status,
1393 triggerMessage
: true,
1396 .catch(Constants
.EMPTY_FUNCTION
);
1398 // eslint-disable-next-line no-lonely-if
1399 if (chargingStation
.hasEvses
) {
1400 for (const evseStatus
of chargingStation
.evses
.values()) {
1401 for (const [id
, connectorStatus
] of evseStatus
.connectors
) {
1402 chargingStation
.ocppRequestService
1404 OCPP16StatusNotificationRequest
,
1405 OCPP16StatusNotificationResponse
1408 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
1411 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
1412 status: connectorStatus
.status,
1415 triggerMessage
: true,
1418 .catch(Constants
.EMPTY_FUNCTION
);
1422 for (const id
of chargingStation
.connectors
.keys()) {
1423 chargingStation
.ocppRequestService
1425 OCPP16StatusNotificationRequest
,
1426 OCPP16StatusNotificationResponse
1429 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
1432 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
1433 status: chargingStation
.getConnectorStatus(id
)?.status,
1436 triggerMessage
: true,
1439 .catch(Constants
.EMPTY_FUNCTION
);
1443 }, OCPP16Constants
.OCPP_TRIGGER_MESSAGE_DELAY
);
1444 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED
;
1446 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED
;
1449 return this.handleIncomingRequestError
<OCPP16TriggerMessageResponse
>(
1451 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
1453 { errorResponse
: OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED
},
1458 private handleRequestDataTransfer(
1459 chargingStation
: ChargingStation
,
1460 commandPayload
: OCPP16DataTransferRequest
,
1461 ): OCPP16DataTransferResponse
{
1462 const { vendorId
} = commandPayload
;
1464 if (Object.values(OCPP16DataTransferVendorId
).includes(vendorId
)) {
1465 return OCPP16Constants
.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED
;
1467 return OCPP16Constants
.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID
;
1469 return this.handleIncomingRequestError
<OCPP16DataTransferResponse
>(
1471 OCPP16IncomingRequestCommand
.DATA_TRANSFER
,
1473 { errorResponse
: OCPP16Constants
.OCPP_DATA_TRANSFER_RESPONSE_REJECTED
},
1478 private async handleRequestReserveNow(
1479 chargingStation
: ChargingStation
,
1480 commandPayload
: OCPP16ReserveNowRequest
,
1481 ): Promise
<OCPP16ReserveNowResponse
> {
1483 !OCPP16ServiceUtils
.checkFeatureProfile(
1485 OCPP16SupportedFeatureProfiles
.Reservation
,
1486 OCPP16IncomingRequestCommand
.RESERVE_NOW
,
1489 return OCPP16Constants
.OCPP_RESERVATION_RESPONSE_REJECTED
;
1491 const { reservationId
, idTag
, connectorId
} = commandPayload
;
1492 let response
: OCPP16ReserveNowResponse
;
1494 if (connectorId
> 0 && !chargingStation
.isConnectorAvailable(connectorId
)) {
1495 return OCPP16Constants
.OCPP_RESERVATION_RESPONSE_REJECTED
;
1497 if (connectorId
=== 0 && !chargingStation
.getReserveConnectorZeroSupported()) {
1498 return OCPP16Constants
.OCPP_RESERVATION_RESPONSE_REJECTED
;
1500 if (!(await OCPP16ServiceUtils
.isIdTagAuthorized(chargingStation
, connectorId
, idTag
))) {
1501 return OCPP16Constants
.OCPP_RESERVATION_RESPONSE_REJECTED
;
1503 await removeExpiredReservations(chargingStation
);
1504 switch (chargingStation
.getConnectorStatus(connectorId
)!.status) {
1505 case OCPP16ChargePointStatus
.Faulted
:
1506 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_FAULTED
;
1508 case OCPP16ChargePointStatus
.Preparing
:
1509 case OCPP16ChargePointStatus
.Charging
:
1510 case OCPP16ChargePointStatus
.SuspendedEV
:
1511 case OCPP16ChargePointStatus
.SuspendedEVSE
:
1512 case OCPP16ChargePointStatus
.Finishing
:
1513 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_OCCUPIED
;
1515 case OCPP16ChargePointStatus
.Unavailable
:
1516 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_UNAVAILABLE
;
1518 case OCPP16ChargePointStatus
.Reserved
:
1519 if (!chargingStation
.isConnectorReservable(reservationId
, idTag
, connectorId
)) {
1520 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_OCCUPIED
;
1523 // eslint-disable-next-line no-fallthrough
1525 if (!chargingStation
.isConnectorReservable(reservationId
, idTag
)) {
1526 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_OCCUPIED
;
1529 await chargingStation
.addReservation({
1530 id
: commandPayload
.reservationId
,
1533 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_ACCEPTED
;
1538 chargingStation
.getConnectorStatus(connectorId
)!.status = OCPP16ChargePointStatus
.Available
;
1539 return this.handleIncomingRequestError
<OCPP16ReserveNowResponse
>(
1541 OCPP16IncomingRequestCommand
.RESERVE_NOW
,
1543 { errorResponse
: OCPP16Constants
.OCPP_RESERVATION_RESPONSE_FAULTED
},
1548 private async handleRequestCancelReservation(
1549 chargingStation
: ChargingStation
,
1550 commandPayload
: OCPP16CancelReservationRequest
,
1551 ): Promise
<GenericResponse
> {
1553 !OCPP16ServiceUtils
.checkFeatureProfile(
1555 OCPP16SupportedFeatureProfiles
.Reservation
,
1556 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
,
1559 return OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
;
1562 const { reservationId
} = commandPayload
;
1563 const reservation
= chargingStation
.getReservationBy('reservationId', reservationId
);
1564 if (isUndefined(reservation
)) {
1566 `${chargingStation.logPrefix()} Reservation with id ${reservationId}
1567 does not exist on charging station`,
1569 return OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
;
1571 await chargingStation
.removeReservation(
1573 ReservationTerminationReason
.RESERVATION_CANCELED
,
1575 return OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED
;
1577 return this.handleIncomingRequestError
<GenericResponse
>(
1579 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
,
1581 { errorResponse
: OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
},