1 // Partial Copyright Jerome Benoit. 2021-2024. 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 { ValidateFunction
} from
'ajv'
8 import { Client
, type FTPResponse
} from
'basic-ftp'
16 import { maxTime
} from
'date-fns/constants'
17 import { create
} from
'tar'
19 import { OCPP16Constants
} from
'./OCPP16Constants.js'
20 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils.js'
23 canProceedChargingProfile
,
26 getConnectorChargingProfiles
,
27 prepareChargingProfileKind
,
28 removeExpiredReservations
,
29 setConfigurationKeyValue
30 } from
'../../../charging-station/index.js'
31 import { OCPPError
} from
'../../../exception/index.js'
33 type ChangeConfigurationRequest
,
34 type ChangeConfigurationResponse
,
38 type GetConfigurationRequest
,
39 type GetConfigurationResponse
,
40 type GetDiagnosticsRequest
,
41 type GetDiagnosticsResponse
,
42 type IncomingRequestHandler
,
44 OCPP16AuthorizationStatus
,
45 OCPP16AvailabilityType
,
46 type OCPP16BootNotificationRequest
,
47 type OCPP16BootNotificationResponse
,
48 type OCPP16CancelReservationRequest
,
49 type OCPP16ChangeAvailabilityRequest
,
50 type OCPP16ChangeAvailabilityResponse
,
51 OCPP16ChargePointErrorCode
,
52 OCPP16ChargePointStatus
,
53 type OCPP16ChargingProfile
,
54 OCPP16ChargingProfilePurposeType
,
55 type OCPP16ChargingSchedule
,
56 type OCPP16ClearCacheRequest
,
57 type OCPP16ClearChargingProfileRequest
,
58 type OCPP16ClearChargingProfileResponse
,
59 type OCPP16DataTransferRequest
,
60 type OCPP16DataTransferResponse
,
61 OCPP16DataTransferVendorId
,
62 OCPP16DiagnosticsStatus
,
63 type OCPP16DiagnosticsStatusNotificationRequest
,
64 type OCPP16DiagnosticsStatusNotificationResponse
,
66 type OCPP16FirmwareStatusNotificationRequest
,
67 type OCPP16FirmwareStatusNotificationResponse
,
68 type OCPP16GetCompositeScheduleRequest
,
69 type OCPP16GetCompositeScheduleResponse
,
70 type OCPP16HeartbeatRequest
,
71 type OCPP16HeartbeatResponse
,
72 OCPP16IncomingRequestCommand
,
75 type OCPP16ReserveNowRequest
,
76 type OCPP16ReserveNowResponse
,
77 OCPP16StandardParametersKey
,
78 type OCPP16StartTransactionRequest
,
79 type OCPP16StartTransactionResponse
,
80 type OCPP16StatusNotificationRequest
,
81 type OCPP16StatusNotificationResponse
,
82 OCPP16StopTransactionReason
,
83 OCPP16SupportedFeatureProfiles
,
84 type OCPP16TriggerMessageRequest
,
85 type OCPP16TriggerMessageResponse
,
86 OCPP16TriggerMessageStatus
,
87 type OCPP16UpdateFirmwareRequest
,
88 type OCPP16UpdateFirmwareResponse
,
89 type OCPPConfigurationKey
,
91 type RemoteStartTransactionRequest
,
92 type RemoteStopTransactionRequest
,
93 ReservationTerminationReason
,
95 type SetChargingProfileRequest
,
96 type SetChargingProfileResponse
,
97 type UnlockConnectorRequest
,
98 type UnlockConnectorResponse
99 } from
'../../../types/index.js'
104 formatDurationMilliSeconds
,
112 } from
'../../../utils/index.js'
113 import { OCPPIncomingRequestService
} from
'../OCPPIncomingRequestService.js'
115 const moduleName
= 'OCPP16IncomingRequestService'
117 export class OCPP16IncomingRequestService
extends OCPPIncomingRequestService
{
118 protected payloadValidateFunctions
: Map
<OCPP16IncomingRequestCommand
, ValidateFunction
<JsonType
>>
120 private readonly incomingRequestHandlers
: Map
<
121 OCPP16IncomingRequestCommand
,
122 IncomingRequestHandler
125 public constructor () {
126 // if (new.target.name === moduleName) {
127 // throw new TypeError(`Cannot construct ${new.target.name} instances directly`)
129 super(OCPPVersion
.VERSION_16
)
130 this.incomingRequestHandlers
= new Map
<OCPP16IncomingRequestCommand
, IncomingRequestHandler
>([
132 OCPP16IncomingRequestCommand
.RESET
,
133 this.handleRequestReset
.bind(this) as unknown
as IncomingRequestHandler
136 OCPP16IncomingRequestCommand
.CLEAR_CACHE
,
137 this.handleRequestClearCache
.bind(this) as IncomingRequestHandler
140 OCPP16IncomingRequestCommand
.UNLOCK_CONNECTOR
,
141 this.handleRequestUnlockConnector
.bind(this) as unknown
as IncomingRequestHandler
144 OCPP16IncomingRequestCommand
.GET_CONFIGURATION
,
145 this.handleRequestGetConfiguration
.bind(this) as IncomingRequestHandler
148 OCPP16IncomingRequestCommand
.CHANGE_CONFIGURATION
,
149 this.handleRequestChangeConfiguration
.bind(this) as unknown
as IncomingRequestHandler
152 OCPP16IncomingRequestCommand
.GET_COMPOSITE_SCHEDULE
,
153 this.handleRequestGetCompositeSchedule
.bind(this) as unknown
as IncomingRequestHandler
156 OCPP16IncomingRequestCommand
.SET_CHARGING_PROFILE
,
157 this.handleRequestSetChargingProfile
.bind(this) as unknown
as IncomingRequestHandler
160 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
,
161 this.handleRequestClearChargingProfile
.bind(this) as IncomingRequestHandler
164 OCPP16IncomingRequestCommand
.CHANGE_AVAILABILITY
,
165 this.handleRequestChangeAvailability
.bind(this) as unknown
as IncomingRequestHandler
168 OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
,
169 this.handleRequestRemoteStartTransaction
.bind(this) as unknown
as IncomingRequestHandler
172 OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
,
173 this.handleRequestRemoteStopTransaction
.bind(this) as unknown
as IncomingRequestHandler
176 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
177 this.handleRequestGetDiagnostics
.bind(this) as IncomingRequestHandler
180 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
181 this.handleRequestTriggerMessage
.bind(this) as unknown
as IncomingRequestHandler
184 OCPP16IncomingRequestCommand
.DATA_TRANSFER
,
185 this.handleRequestDataTransfer
.bind(this) as unknown
as IncomingRequestHandler
188 OCPP16IncomingRequestCommand
.UPDATE_FIRMWARE
,
189 this.handleRequestUpdateFirmware
.bind(this) as unknown
as IncomingRequestHandler
192 OCPP16IncomingRequestCommand
.RESERVE_NOW
,
193 this.handleRequestReserveNow
.bind(this) as unknown
as IncomingRequestHandler
196 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
,
197 this.handleRequestCancelReservation
.bind(this) as unknown
as IncomingRequestHandler
200 this.payloadValidateFunctions
= new Map
<
201 OCPP16IncomingRequestCommand
,
202 ValidateFunction
<JsonType
>
205 OCPP16IncomingRequestCommand
.RESET
,
208 OCPP16ServiceUtils
.parseJsonSchemaFile
<ResetRequest
>(
209 'assets/json-schemas/ocpp/1.6/Reset.json',
217 OCPP16IncomingRequestCommand
.CLEAR_CACHE
,
220 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16ClearCacheRequest
>(
221 'assets/json-schemas/ocpp/1.6/ClearCache.json',
229 OCPP16IncomingRequestCommand
.UNLOCK_CONNECTOR
,
232 OCPP16ServiceUtils
.parseJsonSchemaFile
<UnlockConnectorRequest
>(
233 'assets/json-schemas/ocpp/1.6/UnlockConnector.json',
241 OCPP16IncomingRequestCommand
.GET_CONFIGURATION
,
244 OCPP16ServiceUtils
.parseJsonSchemaFile
<GetConfigurationRequest
>(
245 'assets/json-schemas/ocpp/1.6/GetConfiguration.json',
253 OCPP16IncomingRequestCommand
.CHANGE_CONFIGURATION
,
256 OCPP16ServiceUtils
.parseJsonSchemaFile
<ChangeConfigurationRequest
>(
257 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json',
265 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
268 OCPP16ServiceUtils
.parseJsonSchemaFile
<GetDiagnosticsRequest
>(
269 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json',
277 OCPP16IncomingRequestCommand
.GET_COMPOSITE_SCHEDULE
,
280 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16GetCompositeScheduleRequest
>(
281 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json',
289 OCPP16IncomingRequestCommand
.SET_CHARGING_PROFILE
,
292 OCPP16ServiceUtils
.parseJsonSchemaFile
<SetChargingProfileRequest
>(
293 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json',
301 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
,
304 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16ClearChargingProfileRequest
>(
305 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json',
313 OCPP16IncomingRequestCommand
.CHANGE_AVAILABILITY
,
316 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16ChangeAvailabilityRequest
>(
317 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json',
325 OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
,
328 OCPP16ServiceUtils
.parseJsonSchemaFile
<RemoteStartTransactionRequest
>(
329 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json',
337 OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
,
340 OCPP16ServiceUtils
.parseJsonSchemaFile
<RemoteStopTransactionRequest
>(
341 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json',
349 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
352 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16TriggerMessageRequest
>(
353 'assets/json-schemas/ocpp/1.6/TriggerMessage.json',
361 OCPP16IncomingRequestCommand
.DATA_TRANSFER
,
364 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16DataTransferRequest
>(
365 'assets/json-schemas/ocpp/1.6/DataTransfer.json',
373 OCPP16IncomingRequestCommand
.UPDATE_FIRMWARE
,
376 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16UpdateFirmwareRequest
>(
377 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json',
385 OCPP16IncomingRequestCommand
.RESERVE_NOW
,
388 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16ReserveNowRequest
>(
389 'assets/json-schemas/ocpp/1.6/ReserveNow.json',
397 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
,
400 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16CancelReservationRequest
>(
401 'assets/json-schemas/ocpp/1.6/CancelReservation.json',
409 // Handle incoming request events
411 OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
,
413 chargingStation
: ChargingStation
,
414 request
: RemoteStartTransactionRequest
,
415 response
: GenericResponse
417 if (response
.status === GenericStatus
.Accepted
) {
418 const { connectorId
, idTag
} = request
419 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
420 chargingStation
.getConnectorStatus(connectorId
)!.transactionRemoteStarted
= true
421 chargingStation
.ocppRequestService
422 .requestHandler
<OCPP16StartTransactionRequest
, OCPP16StartTransactionResponse
>(
424 OCPP16RequestCommand
.START_TRANSACTION
,
431 if (response
.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
433 `${chargingStation.logPrefix()} Remote start transaction ACCEPTED on ${chargingStation.stationInfo?.chargingStationId}#${connectorId} for idTag '${idTag}'`
437 `${chargingStation.logPrefix()} Remote start transaction REJECTED on ${chargingStation.stationInfo?.chargingStationId}#${connectorId} for idTag '${idTag}'`
443 `${chargingStation.logPrefix()} ${moduleName}.constructor: Remote start transaction error:`,
451 OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
,
453 chargingStation
: ChargingStation
,
454 request
: RemoteStopTransactionRequest
,
455 response
: GenericResponse
457 if (response
.status === GenericStatus
.Accepted
) {
458 const { transactionId
} = request
459 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
460 const connectorId
= chargingStation
.getConnectorIdByTransactionId(transactionId
)!
461 OCPP16ServiceUtils
.remoteStopTransaction(chargingStation
, connectorId
)
463 if (response
.status === GenericStatus
.Accepted
) {
465 `${chargingStation.logPrefix()} Remote stop transaction ACCEPTED on ${chargingStation.stationInfo?.chargingStationId}#${connectorId} for transaction '${transactionId}'`
469 `${chargingStation.logPrefix()} Remote stop transaction REJECTED on ${chargingStation.stationInfo?.chargingStationId}#${connectorId} for transaction '${transactionId}'`
475 `${chargingStation.logPrefix()} ${moduleName}.constructor: Remote stop transaction error:`,
483 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
485 chargingStation
: ChargingStation
,
486 request
: OCPP16TriggerMessageRequest
,
487 response
: OCPP16TriggerMessageResponse
489 if (response
.status !== OCPP16TriggerMessageStatus
.ACCEPTED
) {
492 const { requestedMessage
, connectorId
} = request
493 const errorHandler
= (error
: Error): void => {
495 `${chargingStation.logPrefix()} ${moduleName}.constructor: Trigger ${requestedMessage} error:`,
499 switch (requestedMessage
) {
500 case OCPP16MessageTrigger
.BootNotification
:
501 chargingStation
.ocppRequestService
502 .requestHandler
<OCPP16BootNotificationRequest
, OCPP16BootNotificationResponse
>(
504 OCPP16RequestCommand
.BOOT_NOTIFICATION
,
505 chargingStation
.bootNotificationRequest
,
506 { skipBufferingOnError
: true, triggerMessage
: true }
509 chargingStation
.bootNotificationResponse
= response
513 case OCPP16MessageTrigger
.Heartbeat
:
514 chargingStation
.ocppRequestService
515 .requestHandler
<OCPP16HeartbeatRequest
, OCPP16HeartbeatResponse
>(
517 OCPP16RequestCommand
.HEARTBEAT
,
525 case OCPP16MessageTrigger
.StatusNotification
:
526 if (connectorId
!= null) {
527 chargingStation
.ocppRequestService
528 .requestHandler
<OCPP16StatusNotificationRequest
, OCPP16StatusNotificationResponse
>(
530 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
533 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
534 status: chargingStation
.getConnectorStatus(connectorId
)?.status
541 } else if (chargingStation
.hasEvses
) {
542 for (const evseStatus
of chargingStation
.evses
.values()) {
543 for (const [id
, connectorStatus
] of evseStatus
.connectors
) {
544 chargingStation
.ocppRequestService
546 OCPP16StatusNotificationRequest
,
547 OCPP16StatusNotificationResponse
550 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
553 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
554 status: connectorStatus
.status
564 for (const [id
, connectorStatus
] of chargingStation
.connectors
) {
565 chargingStation
.ocppRequestService
567 OCPP16StatusNotificationRequest
,
568 OCPP16StatusNotificationResponse
571 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
574 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
575 status: connectorStatus
.status
588 this.validatePayload
= this.validatePayload
.bind(this)
591 public async incomingRequestHandler
<ReqType
extends JsonType
, ResType
extends JsonType
>(
592 chargingStation
: ChargingStation
,
594 commandName
: OCPP16IncomingRequestCommand
,
595 commandPayload
: ReqType
597 let response
: ResType
599 chargingStation
.stationInfo
?.ocppStrictCompliance
=== true &&
600 chargingStation
.inPendingState() &&
601 (commandName
=== OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
||
602 commandName
=== OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
)
605 ErrorType
.SECURITY_ERROR
,
606 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
610 )} while the charging station is in pending state on the central server`,
616 chargingStation
.isRegistered() ||
617 (chargingStation
.stationInfo
?.ocppStrictCompliance
=== false &&
618 chargingStation
.inUnknownState())
621 this.incomingRequestHandlers
.has(commandName
) &&
622 OCPP16ServiceUtils
.isIncomingRequestCommandSupported(chargingStation
, commandName
)
625 this.validatePayload(chargingStation
, commandName
, commandPayload
)
626 // Call the method to build the response
627 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
628 const incomingRequestHandler
= this.incomingRequestHandlers
.get(commandName
)!
629 if (isAsyncFunction(incomingRequestHandler
)) {
630 response
= (await incomingRequestHandler(chargingStation
, commandPayload
)) as ResType
632 response
= incomingRequestHandler(chargingStation
, commandPayload
) as ResType
637 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
645 ErrorType
.NOT_IMPLEMENTED
,
646 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
657 ErrorType
.SECURITY_ERROR
,
658 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
662 )} while the charging station is not registered on the central server.`,
667 // Send the built response
668 await chargingStation
.ocppRequestService
.sendResponse(
674 // Emit command name event to allow delayed handling
675 this.emit(commandName
, chargingStation
, commandPayload
, response
)
678 private validatePayload (
679 chargingStation
: ChargingStation
,
680 commandName
: OCPP16IncomingRequestCommand
,
681 commandPayload
: JsonType
683 if (this.payloadValidateFunctions
.has(commandName
)) {
684 return this.validateIncomingRequestPayload(chargingStation
, commandName
, commandPayload
)
687 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema validation function found for command '${commandName}' PDU validation`
692 // Simulate charging station restart
693 private handleRequestReset (
694 chargingStation
: ChargingStation
,
695 commandPayload
: ResetRequest
697 const { type } = commandPayload
699 .reset(`${type}Reset` as OCPP16StopTransactionReason
)
700 .catch(Constants
.EMPTY_FUNCTION
)
702 `${chargingStation.logPrefix()} ${type} reset command received, simulating it. The station will be back online in ${formatDurationMilliSeconds(
703 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
704 chargingStation.stationInfo!.resetTime!
707 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
710 private async handleRequestUnlockConnector (
711 chargingStation
: ChargingStation
,
712 commandPayload
: UnlockConnectorRequest
713 ): Promise
<UnlockConnectorResponse
> {
714 const { connectorId
} = commandPayload
715 if (!chargingStation
.hasConnector(connectorId
)) {
717 `${chargingStation.logPrefix()} Trying to unlock a non existing connector id ${connectorId}`
719 return OCPP16Constants
.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED
721 if (connectorId
=== 0) {
722 logger
.error(`${chargingStation.logPrefix()} Trying to unlock connector id ${connectorId}`)
723 return OCPP16Constants
.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED
725 if (chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== true) {
726 const stopResponse
= await chargingStation
.stopTransactionOnConnector(
728 OCPP16StopTransactionReason
.UNLOCK_COMMAND
730 if (stopResponse
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
731 return OCPP16Constants
.OCPP_RESPONSE_UNLOCKED
733 return OCPP16Constants
.OCPP_RESPONSE_UNLOCK_FAILED
735 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
738 OCPP16ChargePointStatus
.Available
740 return OCPP16Constants
.OCPP_RESPONSE_UNLOCKED
743 private handleRequestGetConfiguration (
744 chargingStation
: ChargingStation
,
745 commandPayload
: GetConfigurationRequest
746 ): GetConfigurationResponse
{
747 const { key
} = commandPayload
748 const configurationKey
: OCPPConfigurationKey
[] = []
749 const unknownKey
: string[] = []
751 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
752 for (const configKey
of chargingStation
.ocppConfiguration
!.configurationKey
!) {
753 if (!OCPP16ServiceUtils
.isConfigurationKeyVisible(configKey
)) {
756 configurationKey
.push({
758 readonly: configKey
.readonly,
759 value
: configKey
.value
762 } else if (isNotEmptyArray(key
)) {
763 for (const k
of key
) {
764 const keyFound
= getConfigurationKey(chargingStation
, k
, true)
765 if (keyFound
!= null) {
766 if (!OCPP16ServiceUtils
.isConfigurationKeyVisible(keyFound
)) {
769 configurationKey
.push({
771 readonly: keyFound
.readonly,
772 value
: keyFound
.value
785 private handleRequestChangeConfiguration (
786 chargingStation
: ChargingStation
,
787 commandPayload
: ChangeConfigurationRequest
788 ): ChangeConfigurationResponse
{
789 const { key
, value
} = commandPayload
790 const keyToChange
= getConfigurationKey(chargingStation
, key
, true)
791 if (keyToChange
?.readonly === true) {
792 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_REJECTED
793 } else if (keyToChange
?.readonly === false) {
794 let valueChanged
= false
795 if (keyToChange
.value
!== value
) {
796 setConfigurationKeyValue(chargingStation
, key
, value
, true)
799 let triggerHeartbeatRestart
= false
801 (keyToChange
.key
as OCPP16StandardParametersKey
) ===
802 OCPP16StandardParametersKey
.HeartBeatInterval
&&
805 setConfigurationKeyValue(
807 OCPP16StandardParametersKey
.HeartbeatInterval
,
810 triggerHeartbeatRestart
= true
813 (keyToChange
.key
as OCPP16StandardParametersKey
) ===
814 OCPP16StandardParametersKey
.HeartbeatInterval
&&
817 setConfigurationKeyValue(
819 OCPP16StandardParametersKey
.HeartBeatInterval
,
822 triggerHeartbeatRestart
= true
824 if (triggerHeartbeatRestart
) {
825 chargingStation
.restartHeartbeat()
828 (keyToChange
.key
as OCPP16StandardParametersKey
) ===
829 OCPP16StandardParametersKey
.WebSocketPingInterval
&&
832 chargingStation
.restartWebSocketPing()
834 if (keyToChange
.reboot
=== true) {
835 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED
837 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_ACCEPTED
839 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED
842 private handleRequestSetChargingProfile (
843 chargingStation
: ChargingStation
,
844 commandPayload
: SetChargingProfileRequest
845 ): SetChargingProfileResponse
{
847 !OCPP16ServiceUtils
.checkFeatureProfile(
849 OCPP16SupportedFeatureProfiles
.SmartCharging
,
850 OCPP16IncomingRequestCommand
.SET_CHARGING_PROFILE
853 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED
855 const { connectorId
, csChargingProfiles
} = commandPayload
856 if (!chargingStation
.hasConnector(connectorId
)) {
858 `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector id ${connectorId}`
860 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
863 csChargingProfiles
.chargingProfilePurpose
===
864 OCPP16ChargingProfilePurposeType
.CHARGE_POINT_MAX_PROFILE
&&
867 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
870 csChargingProfiles
.chargingProfilePurpose
=== OCPP16ChargingProfilePurposeType
.TX_PROFILE
&&
874 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId}`
876 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
878 const connectorStatus
= chargingStation
.getConnectorStatus(connectorId
)
880 csChargingProfiles
.chargingProfilePurpose
=== OCPP16ChargingProfilePurposeType
.TX_PROFILE
&&
882 connectorStatus
?.transactionStarted
=== false
885 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId} without a started transaction`
887 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
890 csChargingProfiles
.chargingProfilePurpose
=== OCPP16ChargingProfilePurposeType
.TX_PROFILE
&&
892 connectorStatus
?.transactionStarted
=== true &&
893 csChargingProfiles
.transactionId
!== connectorStatus
.transactionId
896 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId} with a different transaction id ${
897 csChargingProfiles.transactionId
898 } than the started transaction id ${connectorStatus.transactionId}`
900 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
902 OCPP16ServiceUtils
.setChargingProfile(chargingStation
, connectorId
, csChargingProfiles
)
904 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${connectorId}: %j`,
907 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED
910 private handleRequestGetCompositeSchedule (
911 chargingStation
: ChargingStation
,
912 commandPayload
: OCPP16GetCompositeScheduleRequest
913 ): OCPP16GetCompositeScheduleResponse
{
915 !OCPP16ServiceUtils
.checkFeatureProfile(
917 OCPP16SupportedFeatureProfiles
.SmartCharging
,
918 OCPP16IncomingRequestCommand
.GET_COMPOSITE_SCHEDULE
921 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
923 const { connectorId
, duration
, chargingRateUnit
} = commandPayload
924 if (!chargingStation
.hasConnector(connectorId
)) {
926 `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector id ${connectorId}`
928 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
930 if (connectorId
=== 0) {
932 `${chargingStation.logPrefix()} Get composite schedule on connector id ${connectorId} is not yet supported`
934 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
936 if (chargingRateUnit
!= null) {
938 `${chargingStation.logPrefix()} Get composite schedule with a specified rate unit is not yet supported, no conversion will be done`
941 const connectorStatus
= chargingStation
.getConnectorStatus(connectorId
)
943 isEmptyArray(connectorStatus
?.chargingProfiles
) &&
944 isEmptyArray(chargingStation
.getConnectorStatus(0)?.chargingProfiles
)
946 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
948 const currentDate
= new Date()
949 const compositeScheduleInterval
: Interval
= {
951 end
: addSeconds(currentDate
, duration
)
953 // Get charging profiles sorted by connector id then stack level
954 const chargingProfiles
: OCPP16ChargingProfile
[] = getConnectorChargingProfiles(
958 let previousCompositeSchedule
: OCPP16ChargingSchedule
| undefined
959 let compositeSchedule
: OCPP16ChargingSchedule
| undefined
960 for (const chargingProfile
of chargingProfiles
) {
961 if (chargingProfile
.chargingSchedule
.startSchedule
== null) {
963 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${
964 chargingProfile.chargingProfileId
965 } has no startSchedule defined. Trying to set it to the connector current transaction start date`
967 // OCPP specifies that if startSchedule is not defined, it should be relative to start of the connector transaction
968 chargingProfile
.chargingSchedule
.startSchedule
= connectorStatus
?.transactionStart
970 if (!isDate(chargingProfile
.chargingSchedule
.startSchedule
)) {
972 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${
973 chargingProfile.chargingProfileId
974 } startSchedule property is not a Date instance. Trying to convert it to a Date instance`
976 chargingProfile
.chargingSchedule
.startSchedule
= convertToDate(
977 chargingProfile
.chargingSchedule
.startSchedule
980 if (chargingProfile
.chargingSchedule
.duration
== null) {
982 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${
983 chargingProfile.chargingProfileId
984 } has no duration defined and will be set to the maximum time allowed`
986 // OCPP specifies that if duration is not defined, it should be infinite
987 chargingProfile
.chargingSchedule
.duration
= differenceInSeconds(
989 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
990 chargingProfile
.chargingSchedule
.startSchedule
!
994 !prepareChargingProfileKind(
997 compositeScheduleInterval
.start
,
998 chargingStation
.logPrefix()
1004 !canProceedChargingProfile(
1006 compositeScheduleInterval
.start
,
1007 chargingStation
.logPrefix()
1012 compositeSchedule
= OCPP16ServiceUtils
.composeChargingSchedules(
1013 previousCompositeSchedule
,
1014 chargingProfile
.chargingSchedule
,
1015 compositeScheduleInterval
1017 previousCompositeSchedule
= compositeSchedule
1019 if (compositeSchedule
!= null) {
1021 status: GenericStatus
.Accepted
,
1022 scheduleStart
: compositeSchedule
.startSchedule
,
1024 chargingSchedule
: compositeSchedule
1027 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
1030 private handleRequestClearChargingProfile (
1031 chargingStation
: ChargingStation
,
1032 commandPayload
: OCPP16ClearChargingProfileRequest
1033 ): OCPP16ClearChargingProfileResponse
{
1035 !OCPP16ServiceUtils
.checkFeatureProfile(
1037 OCPP16SupportedFeatureProfiles
.SmartCharging
,
1038 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
1041 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
1043 const { connectorId
} = commandPayload
1044 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1045 if (!chargingStation
.hasConnector(connectorId
!)) {
1047 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector id ${connectorId}`
1049 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
1051 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1052 const connectorStatus
= chargingStation
.getConnectorStatus(connectorId
!)
1053 if (connectorId
!= null && isNotEmptyArray(connectorStatus
?.chargingProfiles
)) {
1054 connectorStatus
.chargingProfiles
= []
1056 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${connectorId}`
1058 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED
1060 if (connectorId
== null) {
1061 let clearedCP
= false
1062 if (chargingStation
.hasEvses
) {
1063 for (const evseStatus
of chargingStation
.evses
.values()) {
1064 for (const status of evseStatus
.connectors
.values()) {
1065 clearedCP
= OCPP16ServiceUtils
.clearChargingProfiles(
1068 status.chargingProfiles
1073 for (const id
of chargingStation
.connectors
.keys()) {
1074 clearedCP
= OCPP16ServiceUtils
.clearChargingProfiles(
1077 chargingStation
.getConnectorStatus(id
)?.chargingProfiles
1082 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED
1085 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
1088 private async handleRequestChangeAvailability (
1089 chargingStation
: ChargingStation
,
1090 commandPayload
: OCPP16ChangeAvailabilityRequest
1091 ): Promise
<OCPP16ChangeAvailabilityResponse
> {
1092 const { connectorId
, type } = commandPayload
1093 if (!chargingStation
.hasConnector(connectorId
)) {
1095 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector id ${connectorId}`
1097 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_REJECTED
1099 const chargePointStatus
: OCPP16ChargePointStatus
=
1100 type === OCPP16AvailabilityType
.Operative
1101 ? OCPP16ChargePointStatus
.Available
1102 : OCPP16ChargePointStatus
.Unavailable
1103 if (connectorId
=== 0) {
1104 let response
: OCPP16ChangeAvailabilityResponse
| undefined
1105 if (chargingStation
.hasEvses
) {
1106 for (const evseStatus
of chargingStation
.evses
.values()) {
1107 response
= await OCPP16ServiceUtils
.changeAvailability(
1109 [...evseStatus
.connectors
.keys()],
1115 response
= await OCPP16ServiceUtils
.changeAvailability(
1117 [...chargingStation
.connectors
.keys()],
1122 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1126 (chargingStation
.isChargingStationAvailable() ||
1127 (!chargingStation
.isChargingStationAvailable() &&
1128 type === OCPP16AvailabilityType
.Inoperative
))
1130 if (chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== true) {
1131 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1132 chargingStation
.getConnectorStatus(connectorId
)!.availability
= type
1133 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_SCHEDULED
1135 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1136 chargingStation
.getConnectorStatus(connectorId
)!.availability
= type
1137 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1142 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_ACCEPTED
1144 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_REJECTED
1147 private async handleRequestRemoteStartTransaction (
1148 chargingStation
: ChargingStation
,
1149 commandPayload
: RemoteStartTransactionRequest
1150 ): Promise
<GenericResponse
> {
1151 const { connectorId
: transactionConnectorId
, idTag
, chargingProfile
} = commandPayload
1152 if (!chargingStation
.hasConnector(transactionConnectorId
)) {
1153 return await this.notifyRemoteStartTransactionRejected(
1155 transactionConnectorId
,
1160 !chargingStation
.isChargingStationAvailable() ||
1161 !chargingStation
.isConnectorAvailable(transactionConnectorId
)
1163 return await this.notifyRemoteStartTransactionRejected(
1165 transactionConnectorId
,
1169 // idTag authorization check required
1171 chargingStation
.getAuthorizeRemoteTxRequests() &&
1172 !(await OCPP16ServiceUtils
.isIdTagAuthorized(chargingStation
, transactionConnectorId
, idTag
))
1174 return await this.notifyRemoteStartTransactionRejected(
1176 transactionConnectorId
,
1180 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1182 transactionConnectorId
,
1183 OCPP16ChargePointStatus
.Preparing
1186 chargingProfile
!= null &&
1187 !this.setRemoteStartTransactionChargingProfile(
1189 transactionConnectorId
,
1193 return await this.notifyRemoteStartTransactionRejected(
1195 transactionConnectorId
,
1200 `${chargingStation.logPrefix()} Remote start transaction ACCEPTED on connector id ${transactionConnectorId}, idTag '${idTag}'`
1202 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
1205 private async notifyRemoteStartTransactionRejected (
1206 chargingStation
: ChargingStation
,
1207 connectorId
: number,
1209 ): Promise
<GenericResponse
> {
1210 const connectorStatus
= chargingStation
.getConnectorStatus(connectorId
)
1211 if (connectorStatus
?.status !== OCPP16ChargePointStatus
.Available
) {
1212 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1215 OCPP16ChargePointStatus
.Available
1219 `${chargingStation.logPrefix()} Remote start transaction REJECTED on connector id ${connectorId}, idTag '${idTag}', availability '${connectorStatus?.availability}', status '${connectorStatus?.status}'`
1221 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
1224 private setRemoteStartTransactionChargingProfile (
1225 chargingStation
: ChargingStation
,
1226 connectorId
: number,
1227 chargingProfile
: OCPP16ChargingProfile
1229 if (chargingProfile
.chargingProfilePurpose
=== OCPP16ChargingProfilePurposeType
.TX_PROFILE
) {
1230 OCPP16ServiceUtils
.setChargingProfile(chargingStation
, connectorId
, chargingProfile
)
1232 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`,
1238 `${chargingStation.logPrefix()} Not allowed to set ${
1239 chargingProfile.chargingProfilePurpose
1240 } charging profile(s) at remote start transaction`
1245 private handleRequestRemoteStopTransaction (
1246 chargingStation
: ChargingStation
,
1247 commandPayload
: RemoteStopTransactionRequest
1248 ): GenericResponse
{
1249 const { transactionId
} = commandPayload
1250 if (chargingStation
.getConnectorIdByTransactionId(transactionId
) != null) {
1252 `${chargingStation.logPrefix()} Remote stop transaction ACCEPTED for transactionId '${transactionId}'`
1254 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
1257 `${chargingStation.logPrefix()} Remote stop transaction REJECTED for transactionId '${transactionId}'`
1259 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
1262 private handleRequestUpdateFirmware (
1263 chargingStation
: ChargingStation
,
1264 commandPayload
: OCPP16UpdateFirmwareRequest
1265 ): OCPP16UpdateFirmwareResponse
{
1267 !OCPP16ServiceUtils
.checkFeatureProfile(
1269 OCPP16SupportedFeatureProfiles
.FirmwareManagement
,
1270 OCPP16IncomingRequestCommand
.UPDATE_FIRMWARE
1274 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported`
1276 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
1278 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1279 commandPayload
.retrieveDate
= convertToDate(commandPayload
.retrieveDate
)!
1280 const { retrieveDate
} = commandPayload
1281 if (chargingStation
.stationInfo
?.firmwareStatus
!== OCPP16FirmwareStatus
.Installed
) {
1283 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`
1285 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
1287 const now
= Date.now()
1288 if (retrieveDate
.getTime() <= now
) {
1289 this.updateFirmwareSimulation(chargingStation
).catch(Constants
.EMPTY_FUNCTION
)
1292 this.updateFirmwareSimulation(chargingStation
).catch(Constants
.EMPTY_FUNCTION
)
1293 }, retrieveDate
.getTime() - now
)
1295 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
1298 private async updateFirmwareSimulation (
1299 chargingStation
: ChargingStation
,
1303 if (!checkChargingStation(chargingStation
, chargingStation
.logPrefix())) {
1306 if (chargingStation
.hasEvses
) {
1307 for (const [evseId
, evseStatus
] of chargingStation
.evses
) {
1309 for (const [connectorId
, connectorStatus
] of evseStatus
.connectors
) {
1310 if (connectorStatus
.transactionStarted
=== false) {
1311 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1314 OCPP16ChargePointStatus
.Unavailable
1321 for (const connectorId
of chargingStation
.connectors
.keys()) {
1324 chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== false
1326 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1329 OCPP16ChargePointStatus
.Unavailable
1334 await chargingStation
.ocppRequestService
.requestHandler
<
1335 OCPP16FirmwareStatusNotificationRequest
,
1336 OCPP16FirmwareStatusNotificationResponse
1337 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1338 status: OCPP16FirmwareStatus
.Downloading
1340 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1341 chargingStation
.stationInfo
!.firmwareStatus
= OCPP16FirmwareStatus
.Downloading
1343 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
===
1344 OCPP16FirmwareStatus
.DownloadFailed
1346 await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
)))
1347 await chargingStation
.ocppRequestService
.requestHandler
<
1348 OCPP16FirmwareStatusNotificationRequest
,
1349 OCPP16FirmwareStatusNotificationResponse
1350 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1351 status: chargingStation
.stationInfo
.firmwareUpgrade
.failureStatus
1353 chargingStation
.stationInfo
.firmwareStatus
=
1354 chargingStation
.stationInfo
.firmwareUpgrade
.failureStatus
1357 await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
)))
1358 await chargingStation
.ocppRequestService
.requestHandler
<
1359 OCPP16FirmwareStatusNotificationRequest
,
1360 OCPP16FirmwareStatusNotificationResponse
1361 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1362 status: OCPP16FirmwareStatus
.Downloaded
1364 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1365 chargingStation
.stationInfo
!.firmwareStatus
= OCPP16FirmwareStatus
.Downloaded
1366 let wasTransactionsStarted
= false
1367 let transactionsStarted
: boolean
1369 const runningTransactions
= chargingStation
.getNumberOfRunningTransactions()
1370 if (runningTransactions
> 0) {
1371 const waitTime
= secondsToMilliseconds(15)
1373 `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: ${runningTransactions} transaction(s) in progress, waiting ${formatDurationMilliSeconds(
1375 )} before continuing firmware update simulation`
1377 await sleep(waitTime
)
1378 transactionsStarted
= true
1379 wasTransactionsStarted
= true
1381 if (chargingStation
.hasEvses
) {
1382 for (const [evseId
, evseStatus
] of chargingStation
.evses
) {
1384 for (const [connectorId
, connectorStatus
] of evseStatus
.connectors
) {
1385 if (connectorStatus
.status !== OCPP16ChargePointStatus
.Unavailable
) {
1386 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1389 OCPP16ChargePointStatus
.Unavailable
1396 for (const connectorId
of chargingStation
.connectors
.keys()) {
1399 chargingStation
.getConnectorStatus(connectorId
)?.status !==
1400 OCPP16ChargePointStatus
.Unavailable
1402 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1405 OCPP16ChargePointStatus
.Unavailable
1410 transactionsStarted
= false
1412 } while (transactionsStarted
)
1413 !wasTransactionsStarted
&&
1414 (await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
))))
1415 if (!checkChargingStation(chargingStation
, chargingStation
.logPrefix())) {
1418 await chargingStation
.ocppRequestService
.requestHandler
<
1419 OCPP16FirmwareStatusNotificationRequest
,
1420 OCPP16FirmwareStatusNotificationResponse
1421 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1422 status: OCPP16FirmwareStatus
.Installing
1424 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1425 chargingStation
.stationInfo
!.firmwareStatus
= OCPP16FirmwareStatus
.Installing
1427 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
===
1428 OCPP16FirmwareStatus
.InstallationFailed
1430 await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
)))
1431 await chargingStation
.ocppRequestService
.requestHandler
<
1432 OCPP16FirmwareStatusNotificationRequest
,
1433 OCPP16FirmwareStatusNotificationResponse
1434 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1435 status: chargingStation
.stationInfo
.firmwareUpgrade
.failureStatus
1437 chargingStation
.stationInfo
.firmwareStatus
=
1438 chargingStation
.stationInfo
.firmwareUpgrade
.failureStatus
1441 if (chargingStation
.stationInfo
?.firmwareUpgrade
?.reset
=== true) {
1442 await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
)))
1443 await chargingStation
.reset(OCPP16StopTransactionReason
.REBOOT
)
1447 private async handleRequestGetDiagnostics (
1448 chargingStation
: ChargingStation
,
1449 commandPayload
: GetDiagnosticsRequest
1450 ): Promise
<GetDiagnosticsResponse
> {
1452 !OCPP16ServiceUtils
.checkFeatureProfile(
1454 OCPP16SupportedFeatureProfiles
.FirmwareManagement
,
1455 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
1459 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`
1461 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
1463 const { location
} = commandPayload
1464 const uri
= new URL(location
)
1465 if (uri
.protocol
.startsWith('ftp:')) {
1466 let ftpClient
: Client
| undefined
1468 const logFiles
= readdirSync(resolve(dirname(fileURLToPath(import.meta
.url
)), '../'))
1469 .filter(file
=> file
.endsWith('.log'))
1470 .map(file
=> join('./', file
))
1471 const diagnosticsArchive
= `${chargingStation.stationInfo?.chargingStationId}_logs.tar.gz`
1472 create({ gzip
: true }, logFiles
).pipe(createWriteStream(diagnosticsArchive
))
1473 ftpClient
= new Client()
1474 const accessResponse
= await ftpClient
.access({
1476 ...(isNotEmptyString(uri
.port
) && { port
: convertToInt(uri
.port
) }),
1477 ...(isNotEmptyString(uri
.username
) && { user
: uri
.username
}),
1478 ...(isNotEmptyString(uri
.password
) && { password
: uri
.password
})
1480 let uploadResponse
: FTPResponse
| undefined
1481 if (accessResponse
.code
=== 220) {
1482 ftpClient
.trackProgress(info
=> {
1484 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: ${
1486 } bytes transferred from diagnostics archive ${info.name}`
1488 chargingStation
.ocppRequestService
1490 OCPP16DiagnosticsStatusNotificationRequest
,
1491 OCPP16DiagnosticsStatusNotificationResponse
1492 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1493 status: OCPP16DiagnosticsStatus
.Uploading
1497 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
1498 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
1504 uploadResponse
= await ftpClient
.uploadFrom(
1505 join(resolve(dirname(fileURLToPath(import.meta
.url
)), '../'), diagnosticsArchive
),
1506 `${uri.pathname}${diagnosticsArchive}`
1508 if (uploadResponse
.code
=== 226) {
1509 await chargingStation
.ocppRequestService
.requestHandler
<
1510 OCPP16DiagnosticsStatusNotificationRequest
,
1511 OCPP16DiagnosticsStatusNotificationResponse
1512 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1513 status: OCPP16DiagnosticsStatus
.Uploaded
1516 return { fileName
: diagnosticsArchive
}
1518 throw new OCPPError(
1519 ErrorType
.GENERIC_ERROR
,
1520 `Diagnostics transfer failed with error code ${accessResponse.code}|${uploadResponse.code}`,
1521 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
1524 throw new OCPPError(
1525 ErrorType
.GENERIC_ERROR
,
1526 `Diagnostics transfer failed with error code ${accessResponse.code}|${uploadResponse?.code}`,
1527 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
1530 await chargingStation
.ocppRequestService
.requestHandler
<
1531 OCPP16DiagnosticsStatusNotificationRequest
,
1532 OCPP16DiagnosticsStatusNotificationResponse
1533 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1534 status: OCPP16DiagnosticsStatus
.UploadFailed
1537 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1538 return this.handleIncomingRequestError
<GetDiagnosticsResponse
>(
1540 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
1542 { errorResponse
: OCPP16Constants
.OCPP_RESPONSE_EMPTY
}
1547 `${chargingStation.logPrefix()} Unsupported protocol ${
1549 } to transfer the diagnostic logs archive`
1551 await chargingStation
.ocppRequestService
.requestHandler
<
1552 OCPP16DiagnosticsStatusNotificationRequest
,
1553 OCPP16DiagnosticsStatusNotificationResponse
1554 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1555 status: OCPP16DiagnosticsStatus
.UploadFailed
1557 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
1561 private handleRequestTriggerMessage (
1562 chargingStation
: ChargingStation
,
1563 commandPayload
: OCPP16TriggerMessageRequest
1564 ): OCPP16TriggerMessageResponse
{
1565 const { requestedMessage
, connectorId
} = commandPayload
1567 !OCPP16ServiceUtils
.checkFeatureProfile(
1569 OCPP16SupportedFeatureProfiles
.RemoteTrigger
,
1570 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
1572 !OCPP16ServiceUtils
.isMessageTriggerSupported(chargingStation
, requestedMessage
)
1574 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED
1577 !OCPP16ServiceUtils
.isConnectorIdValid(
1579 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
1580 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1584 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED
1586 switch (requestedMessage
) {
1587 case OCPP16MessageTrigger
.BootNotification
:
1588 case OCPP16MessageTrigger
.Heartbeat
:
1589 case OCPP16MessageTrigger
.StatusNotification
:
1590 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED
1592 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED
1596 private handleRequestDataTransfer (
1597 chargingStation
: ChargingStation
,
1598 commandPayload
: OCPP16DataTransferRequest
1599 ): OCPP16DataTransferResponse
{
1600 const { vendorId
} = commandPayload
1602 if (Object.values(OCPP16DataTransferVendorId
).includes(vendorId
)) {
1603 return OCPP16Constants
.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED
1605 return OCPP16Constants
.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID
1607 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1608 return this.handleIncomingRequestError
<OCPP16DataTransferResponse
>(
1610 OCPP16IncomingRequestCommand
.DATA_TRANSFER
,
1612 { errorResponse
: OCPP16Constants
.OCPP_DATA_TRANSFER_RESPONSE_REJECTED
}
1617 private async handleRequestReserveNow (
1618 chargingStation
: ChargingStation
,
1619 commandPayload
: OCPP16ReserveNowRequest
1620 ): Promise
<OCPP16ReserveNowResponse
> {
1622 !OCPP16ServiceUtils
.checkFeatureProfile(
1624 OCPP16SupportedFeatureProfiles
.Reservation
,
1625 OCPP16IncomingRequestCommand
.RESERVE_NOW
1628 return OCPP16Constants
.OCPP_RESERVATION_RESPONSE_REJECTED
1630 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1631 commandPayload
.expiryDate
= convertToDate(commandPayload
.expiryDate
)!
1632 const { reservationId
, idTag
, connectorId
} = commandPayload
1633 let response
: OCPP16ReserveNowResponse
1635 if (connectorId
> 0 && !chargingStation
.isConnectorAvailable(connectorId
)) {
1636 return OCPP16Constants
.OCPP_RESERVATION_RESPONSE_REJECTED
1638 if (connectorId
=== 0 && !chargingStation
.getReserveConnectorZeroSupported()) {
1639 return OCPP16Constants
.OCPP_RESERVATION_RESPONSE_REJECTED
1641 if (!(await OCPP16ServiceUtils
.isIdTagAuthorized(chargingStation
, connectorId
, idTag
))) {
1642 return OCPP16Constants
.OCPP_RESERVATION_RESPONSE_REJECTED
1644 await removeExpiredReservations(chargingStation
)
1645 switch (chargingStation
.getConnectorStatus(connectorId
)?.status) {
1646 case OCPP16ChargePointStatus
.Faulted
:
1647 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_FAULTED
1649 case OCPP16ChargePointStatus
.Preparing
:
1650 case OCPP16ChargePointStatus
.Charging
:
1651 case OCPP16ChargePointStatus
.SuspendedEV
:
1652 case OCPP16ChargePointStatus
.SuspendedEVSE
:
1653 case OCPP16ChargePointStatus
.Finishing
:
1654 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_OCCUPIED
1656 case OCPP16ChargePointStatus
.Unavailable
:
1657 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_UNAVAILABLE
1659 case OCPP16ChargePointStatus
.Reserved
:
1660 if (!chargingStation
.isConnectorReservable(reservationId
, idTag
, connectorId
)) {
1661 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_OCCUPIED
1664 // eslint-disable-next-line no-fallthrough
1666 if (!chargingStation
.isConnectorReservable(reservationId
, idTag
)) {
1667 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_OCCUPIED
1670 await chargingStation
.addReservation({
1671 id
: commandPayload
.reservationId
,
1674 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_ACCEPTED
1679 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1680 chargingStation
.getConnectorStatus(connectorId
)!.status = OCPP16ChargePointStatus
.Available
1681 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1682 return this.handleIncomingRequestError
<OCPP16ReserveNowResponse
>(
1684 OCPP16IncomingRequestCommand
.RESERVE_NOW
,
1686 { errorResponse
: OCPP16Constants
.OCPP_RESERVATION_RESPONSE_FAULTED
}
1691 private async handleRequestCancelReservation (
1692 chargingStation
: ChargingStation
,
1693 commandPayload
: OCPP16CancelReservationRequest
1694 ): Promise
<GenericResponse
> {
1696 !OCPP16ServiceUtils
.checkFeatureProfile(
1698 OCPP16SupportedFeatureProfiles
.Reservation
,
1699 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
1702 return OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
1705 const { reservationId
} = commandPayload
1706 const reservation
= chargingStation
.getReservationBy('reservationId', reservationId
)
1707 if (reservation
== null) {
1709 `${chargingStation.logPrefix()} Reservation with id ${reservationId} does not exist on charging station`
1711 return OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
1713 await chargingStation
.removeReservation(
1715 ReservationTerminationReason
.RESERVATION_CANCELED
1717 return OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED
1719 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1720 return this.handleIncomingRequestError
<GenericResponse
>(
1722 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
,
1724 { errorResponse
: OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
}