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'
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 type OCPP16UpdateFirmwareRequest
,
87 type OCPP16UpdateFirmwareResponse
,
88 type OCPPConfigurationKey
,
90 type RemoteStartTransactionRequest
,
91 type RemoteStopTransactionRequest
,
92 ReservationTerminationReason
,
94 type SetChargingProfileRequest
,
95 type SetChargingProfileResponse
,
96 type UnlockConnectorRequest
,
97 type UnlockConnectorResponse
98 } from
'../../../types/index.js'
103 formatDurationMilliSeconds
,
111 } from
'../../../utils/index.js'
112 import { OCPPIncomingRequestService
} from
'../OCPPIncomingRequestService.js'
114 const moduleName
= 'OCPP16IncomingRequestService'
116 export class OCPP16IncomingRequestService
extends OCPPIncomingRequestService
{
117 protected jsonSchemas
: Map
<OCPP16IncomingRequestCommand
, JSONSchemaType
<JsonType
>>
118 private readonly incomingRequestHandlers
: Map
<
119 OCPP16IncomingRequestCommand
,
120 IncomingRequestHandler
123 public constructor () {
124 // if (new.target?.name === moduleName) {
125 // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`)
127 super(OCPPVersion
.VERSION_16
)
128 this.incomingRequestHandlers
= new Map
<OCPP16IncomingRequestCommand
, IncomingRequestHandler
>([
130 OCPP16IncomingRequestCommand
.RESET
,
131 this.handleRequestReset
.bind(this) as unknown
as IncomingRequestHandler
134 OCPP16IncomingRequestCommand
.CLEAR_CACHE
,
135 this.handleRequestClearCache
.bind(this) as IncomingRequestHandler
138 OCPP16IncomingRequestCommand
.UNLOCK_CONNECTOR
,
139 this.handleRequestUnlockConnector
.bind(this) as unknown
as IncomingRequestHandler
142 OCPP16IncomingRequestCommand
.GET_CONFIGURATION
,
143 this.handleRequestGetConfiguration
.bind(this) as IncomingRequestHandler
146 OCPP16IncomingRequestCommand
.CHANGE_CONFIGURATION
,
147 this.handleRequestChangeConfiguration
.bind(this) as unknown
as IncomingRequestHandler
150 OCPP16IncomingRequestCommand
.GET_COMPOSITE_SCHEDULE
,
151 this.handleRequestGetCompositeSchedule
.bind(this) as unknown
as IncomingRequestHandler
154 OCPP16IncomingRequestCommand
.SET_CHARGING_PROFILE
,
155 this.handleRequestSetChargingProfile
.bind(this) as unknown
as IncomingRequestHandler
158 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
,
159 this.handleRequestClearChargingProfile
.bind(this) as IncomingRequestHandler
162 OCPP16IncomingRequestCommand
.CHANGE_AVAILABILITY
,
163 this.handleRequestChangeAvailability
.bind(this) as unknown
as IncomingRequestHandler
166 OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
,
167 this.handleRequestRemoteStartTransaction
.bind(this) as unknown
as IncomingRequestHandler
170 OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
,
171 this.handleRequestRemoteStopTransaction
.bind(this) as unknown
as IncomingRequestHandler
174 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
175 this.handleRequestGetDiagnostics
.bind(this) as IncomingRequestHandler
178 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
179 this.handleRequestTriggerMessage
.bind(this) as unknown
as IncomingRequestHandler
182 OCPP16IncomingRequestCommand
.DATA_TRANSFER
,
183 this.handleRequestDataTransfer
.bind(this) as unknown
as IncomingRequestHandler
186 OCPP16IncomingRequestCommand
.UPDATE_FIRMWARE
,
187 this.handleRequestUpdateFirmware
.bind(this) as unknown
as IncomingRequestHandler
190 OCPP16IncomingRequestCommand
.RESERVE_NOW
,
191 this.handleRequestReserveNow
.bind(this) as unknown
as IncomingRequestHandler
194 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
,
195 this.handleRequestCancelReservation
.bind(this) as unknown
as IncomingRequestHandler
198 this.jsonSchemas
= new Map
<OCPP16IncomingRequestCommand
, JSONSchemaType
<JsonType
>>([
200 OCPP16IncomingRequestCommand
.RESET
,
201 OCPP16ServiceUtils
.parseJsonSchemaFile
<ResetRequest
>(
202 'assets/json-schemas/ocpp/1.6/Reset.json',
208 OCPP16IncomingRequestCommand
.CLEAR_CACHE
,
209 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16ClearCacheRequest
>(
210 'assets/json-schemas/ocpp/1.6/ClearCache.json',
216 OCPP16IncomingRequestCommand
.UNLOCK_CONNECTOR
,
217 OCPP16ServiceUtils
.parseJsonSchemaFile
<UnlockConnectorRequest
>(
218 'assets/json-schemas/ocpp/1.6/UnlockConnector.json',
224 OCPP16IncomingRequestCommand
.GET_CONFIGURATION
,
225 OCPP16ServiceUtils
.parseJsonSchemaFile
<GetConfigurationRequest
>(
226 'assets/json-schemas/ocpp/1.6/GetConfiguration.json',
232 OCPP16IncomingRequestCommand
.CHANGE_CONFIGURATION
,
233 OCPP16ServiceUtils
.parseJsonSchemaFile
<ChangeConfigurationRequest
>(
234 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json',
240 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
241 OCPP16ServiceUtils
.parseJsonSchemaFile
<GetDiagnosticsRequest
>(
242 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json',
248 OCPP16IncomingRequestCommand
.GET_COMPOSITE_SCHEDULE
,
249 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16GetCompositeScheduleRequest
>(
250 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json',
256 OCPP16IncomingRequestCommand
.SET_CHARGING_PROFILE
,
257 OCPP16ServiceUtils
.parseJsonSchemaFile
<SetChargingProfileRequest
>(
258 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json',
264 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
,
265 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16ClearChargingProfileRequest
>(
266 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json',
272 OCPP16IncomingRequestCommand
.CHANGE_AVAILABILITY
,
273 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16ChangeAvailabilityRequest
>(
274 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json',
280 OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
,
281 OCPP16ServiceUtils
.parseJsonSchemaFile
<RemoteStartTransactionRequest
>(
282 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json',
288 OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
,
289 OCPP16ServiceUtils
.parseJsonSchemaFile
<RemoteStopTransactionRequest
>(
290 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json',
296 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
297 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16TriggerMessageRequest
>(
298 'assets/json-schemas/ocpp/1.6/TriggerMessage.json',
304 OCPP16IncomingRequestCommand
.DATA_TRANSFER
,
305 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16DataTransferRequest
>(
306 'assets/json-schemas/ocpp/1.6/DataTransfer.json',
312 OCPP16IncomingRequestCommand
.UPDATE_FIRMWARE
,
313 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16UpdateFirmwareRequest
>(
314 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json',
320 OCPP16IncomingRequestCommand
.RESERVE_NOW
,
321 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16ReserveNowRequest
>(
322 'assets/json-schemas/ocpp/1.6/ReserveNow.json',
328 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
,
329 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16CancelReservationRequest
>(
330 'assets/json-schemas/ocpp/1.6/CancelReservation.json',
336 this.validatePayload
= this.validatePayload
.bind(this) as (
337 chargingStation
: ChargingStation
,
338 commandName
: OCPP16IncomingRequestCommand
,
339 commandPayload
: JsonType
343 public async incomingRequestHandler
<ReqType
extends JsonType
, ResType
extends JsonType
>(
344 chargingStation
: ChargingStation
,
346 commandName
: OCPP16IncomingRequestCommand
,
347 commandPayload
: ReqType
349 let response
: ResType
351 chargingStation
.stationInfo
?.ocppStrictCompliance
=== true &&
352 chargingStation
.inPendingState() &&
353 (commandName
=== OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
||
354 commandName
=== OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
)
357 ErrorType
.SECURITY_ERROR
,
358 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
362 )} while the charging station is in pending state on the central server`,
368 chargingStation
.isRegistered() ||
369 (chargingStation
.stationInfo
?.ocppStrictCompliance
=== false &&
370 chargingStation
.inUnknownState())
373 this.incomingRequestHandlers
.has(commandName
) &&
374 OCPP16ServiceUtils
.isIncomingRequestCommandSupported(chargingStation
, commandName
)
377 this.validatePayload(chargingStation
, commandName
, commandPayload
)
378 // Call the method to build the response
379 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
380 response
= (await this.incomingRequestHandlers
.get(commandName
)!(
387 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
395 ErrorType
.NOT_IMPLEMENTED
,
396 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
407 ErrorType
.SECURITY_ERROR
,
408 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
412 )} while the charging station is not registered on the central server.`,
417 // Send the built response
418 await chargingStation
.ocppRequestService
.sendResponse(
426 private validatePayload (
427 chargingStation
: ChargingStation
,
428 commandName
: OCPP16IncomingRequestCommand
,
429 commandPayload
: JsonType
431 if (this.jsonSchemas
.has(commandName
)) {
432 return this.validateIncomingRequestPayload(
435 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
436 this.jsonSchemas
.get(commandName
)!,
441 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
446 // Simulate charging station restart
447 private handleRequestReset (
448 chargingStation
: ChargingStation
,
449 commandPayload
: ResetRequest
451 const { type } = commandPayload
453 .reset(`${type}Reset` as OCPP16StopTransactionReason
)
454 .catch(Constants
.EMPTY_FUNCTION
)
456 `${chargingStation.logPrefix()} ${type} reset command received, simulating it. The station will be back online in ${formatDurationMilliSeconds(
457 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
458 chargingStation.stationInfo.resetTime!
461 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
464 private async handleRequestUnlockConnector (
465 chargingStation
: ChargingStation
,
466 commandPayload
: UnlockConnectorRequest
467 ): Promise
<UnlockConnectorResponse
> {
468 const { connectorId
} = commandPayload
469 if (!chargingStation
.hasConnector(connectorId
)) {
471 `${chargingStation.logPrefix()} Trying to unlock a non existing connector id ${connectorId}`
473 return OCPP16Constants
.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED
475 if (connectorId
=== 0) {
476 logger
.error(`${chargingStation.logPrefix()} Trying to unlock connector id ${connectorId}`)
477 return OCPP16Constants
.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED
479 if (chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== true) {
480 const stopResponse
= await chargingStation
.stopTransactionOnConnector(
482 OCPP16StopTransactionReason
.UNLOCK_COMMAND
484 if (stopResponse
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
485 return OCPP16Constants
.OCPP_RESPONSE_UNLOCKED
487 return OCPP16Constants
.OCPP_RESPONSE_UNLOCK_FAILED
489 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
492 OCPP16ChargePointStatus
.Available
494 return OCPP16Constants
.OCPP_RESPONSE_UNLOCKED
497 private handleRequestGetConfiguration (
498 chargingStation
: ChargingStation
,
499 commandPayload
: GetConfigurationRequest
500 ): GetConfigurationResponse
{
501 const { key
} = commandPayload
502 const configurationKey
: OCPPConfigurationKey
[] = []
503 const unknownKey
: string[] = []
504 if (isUndefined(key
)) {
505 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
506 for (const configuration
of chargingStation
.ocppConfiguration
!.configurationKey
!) {
507 if (isUndefined(configuration
.visible
)) {
508 configuration
.visible
= true
510 if (configuration
.visible
=== false) {
513 configurationKey
.push({
514 key
: configuration
.key
,
515 readonly: configuration
.readonly,
516 value
: configuration
.value
519 } else if (isNotEmptyArray(key
)) {
520 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
521 for (const k
of key
!) {
522 const keyFound
= getConfigurationKey(chargingStation
, k
, true)
523 if (keyFound
!= null) {
524 if (isUndefined(keyFound
.visible
)) {
525 keyFound
.visible
= true
527 if (keyFound
.visible
=== false) {
530 configurationKey
.push({
532 readonly: keyFound
.readonly,
533 value
: keyFound
.value
546 private handleRequestChangeConfiguration (
547 chargingStation
: ChargingStation
,
548 commandPayload
: ChangeConfigurationRequest
549 ): ChangeConfigurationResponse
{
550 const { key
, value
} = commandPayload
551 const keyToChange
= getConfigurationKey(chargingStation
, key
, true)
552 if (keyToChange
?.readonly === true) {
553 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_REJECTED
554 } else if (keyToChange
?.readonly === false) {
555 let valueChanged
= false
556 if (keyToChange
.value
!== value
) {
557 setConfigurationKeyValue(chargingStation
, key
, value
, true)
560 let triggerHeartbeatRestart
= false
562 (keyToChange
.key
as OCPP16StandardParametersKey
) ===
563 OCPP16StandardParametersKey
.HeartBeatInterval
&&
566 setConfigurationKeyValue(
568 OCPP16StandardParametersKey
.HeartbeatInterval
,
571 triggerHeartbeatRestart
= true
574 (keyToChange
.key
as OCPP16StandardParametersKey
) ===
575 OCPP16StandardParametersKey
.HeartbeatInterval
&&
578 setConfigurationKeyValue(
580 OCPP16StandardParametersKey
.HeartBeatInterval
,
583 triggerHeartbeatRestart
= true
585 if (triggerHeartbeatRestart
) {
586 chargingStation
.restartHeartbeat()
589 (keyToChange
.key
as OCPP16StandardParametersKey
) ===
590 OCPP16StandardParametersKey
.WebSocketPingInterval
&&
593 chargingStation
.restartWebSocketPing()
595 if (keyToChange
.reboot
=== true) {
596 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED
598 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_ACCEPTED
600 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED
603 private handleRequestSetChargingProfile (
604 chargingStation
: ChargingStation
,
605 commandPayload
: SetChargingProfileRequest
606 ): SetChargingProfileResponse
{
608 !OCPP16ServiceUtils
.checkFeatureProfile(
610 OCPP16SupportedFeatureProfiles
.SmartCharging
,
611 OCPP16IncomingRequestCommand
.SET_CHARGING_PROFILE
614 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED
616 const { connectorId
, csChargingProfiles
} = commandPayload
617 if (!chargingStation
.hasConnector(connectorId
)) {
619 `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector id ${connectorId}`
621 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
624 csChargingProfiles
.chargingProfilePurpose
===
625 OCPP16ChargingProfilePurposeType
.CHARGE_POINT_MAX_PROFILE
&&
628 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
631 csChargingProfiles
.chargingProfilePurpose
=== OCPP16ChargingProfilePurposeType
.TX_PROFILE
&&
635 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId}`
637 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
639 const connectorStatus
= chargingStation
.getConnectorStatus(connectorId
)
641 csChargingProfiles
.chargingProfilePurpose
=== OCPP16ChargingProfilePurposeType
.TX_PROFILE
&&
643 connectorStatus
?.transactionStarted
=== false
646 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId} without a started transaction`
648 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
651 csChargingProfiles
.chargingProfilePurpose
=== OCPP16ChargingProfilePurposeType
.TX_PROFILE
&&
653 connectorStatus
?.transactionStarted
=== true &&
654 csChargingProfiles
.transactionId
!== connectorStatus
?.transactionId
657 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId} with a different transaction id ${
658 csChargingProfiles.transactionId
659 } than the started transaction id ${connectorStatus?.transactionId}`
661 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
663 OCPP16ServiceUtils
.setChargingProfile(chargingStation
, connectorId
, csChargingProfiles
)
665 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${connectorId}: %j`,
668 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED
671 private handleRequestGetCompositeSchedule (
672 chargingStation
: ChargingStation
,
673 commandPayload
: OCPP16GetCompositeScheduleRequest
674 ): OCPP16GetCompositeScheduleResponse
{
676 !OCPP16ServiceUtils
.checkFeatureProfile(
678 OCPP16SupportedFeatureProfiles
.SmartCharging
,
679 OCPP16IncomingRequestCommand
.GET_COMPOSITE_SCHEDULE
682 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
684 const { connectorId
, duration
, chargingRateUnit
} = commandPayload
685 if (!chargingStation
.hasConnector(connectorId
)) {
687 `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector id ${connectorId}`
689 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
691 if (connectorId
=== 0) {
693 `${chargingStation.logPrefix()} Get composite schedule on connector id ${connectorId} is not yet supported`
695 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
697 if (chargingRateUnit
!= null) {
699 `${chargingStation.logPrefix()} Get composite schedule with a specified rate unit is not yet supported, no conversion will be done`
702 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
703 const connectorStatus
= chargingStation
.getConnectorStatus(connectorId
)!
706 connectorStatus
?.chargingProfiles
!= null &&
707 isEmptyArray(chargingStation
.getConnectorStatus(0)?.chargingProfiles
)
710 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
712 const currentDate
= new Date()
713 const compositeScheduleInterval
: Interval
= {
715 end
: addSeconds(currentDate
, duration
)
717 // Get charging profiles sorted by connector id then stack level
718 const chargingProfiles
: OCPP16ChargingProfile
[] = getConnectorChargingProfiles(
722 let previousCompositeSchedule
: OCPP16ChargingSchedule
| undefined
723 let compositeSchedule
: OCPP16ChargingSchedule
| undefined
724 for (const chargingProfile
of chargingProfiles
) {
726 chargingProfile
.chargingSchedule
?.startSchedule
== null &&
727 connectorStatus
?.transactionStarted
=== true
730 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${
731 chargingProfile.chargingProfileId
732 } has no startSchedule defined. Trying to set it to the connector current transaction start date`
734 // OCPP specifies that if startSchedule is not defined, it should be relative to start of the connector transaction
735 chargingProfile
.chargingSchedule
.startSchedule
= connectorStatus
?.transactionStart
738 chargingProfile
.chargingSchedule
?.startSchedule
!= null &&
739 !isDate(chargingProfile
.chargingSchedule
?.startSchedule
)
742 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${
743 chargingProfile.chargingProfileId
744 } startSchedule property is not a Date instance. Trying to convert it to a Date instance`
746 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
747 chargingProfile
.chargingSchedule
.startSchedule
= convertToDate(
748 chargingProfile
.chargingSchedule
?.startSchedule
752 chargingProfile
.chargingSchedule
?.startSchedule
!= null &&
753 chargingProfile
.chargingSchedule
?.duration
== null
756 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${
757 chargingProfile.chargingProfileId
758 } has no duration defined and will be set to the maximum time allowed`
760 // OCPP specifies that if duration is not defined, it should be infinite
761 chargingProfile
.chargingSchedule
.duration
= differenceInSeconds(
763 chargingProfile
.chargingSchedule
.startSchedule
767 !prepareChargingProfileKind(
770 compositeScheduleInterval
.start
as Date,
771 chargingStation
.logPrefix()
777 !canProceedChargingProfile(
779 compositeScheduleInterval
.start
as Date,
780 chargingStation
.logPrefix()
785 compositeSchedule
= OCPP16ServiceUtils
.composeChargingSchedules(
786 previousCompositeSchedule
,
787 chargingProfile
.chargingSchedule
,
788 compositeScheduleInterval
790 previousCompositeSchedule
= compositeSchedule
792 if (compositeSchedule
!= null) {
794 status: GenericStatus
.Accepted
,
795 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
796 scheduleStart
: compositeSchedule
.startSchedule
!,
798 chargingSchedule
: compositeSchedule
801 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
804 private handleRequestClearChargingProfile (
805 chargingStation
: ChargingStation
,
806 commandPayload
: OCPP16ClearChargingProfileRequest
807 ): OCPP16ClearChargingProfileResponse
{
809 !OCPP16ServiceUtils
.checkFeatureProfile(
811 OCPP16SupportedFeatureProfiles
.SmartCharging
,
812 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
815 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
817 const { connectorId
} = commandPayload
818 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
819 if (!chargingStation
.hasConnector(connectorId
!)) {
821 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector id ${connectorId}`
823 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
825 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
826 const connectorStatus
= chargingStation
.getConnectorStatus(connectorId
!)
827 if (connectorId
!= null && isNotEmptyArray(connectorStatus
?.chargingProfiles
)) {
828 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
829 connectorStatus
!.chargingProfiles
= []
831 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${connectorId}`
833 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED
835 if (connectorId
== null) {
836 let clearedCP
= false
837 if (chargingStation
.hasEvses
) {
838 for (const evseStatus
of chargingStation
.evses
.values()) {
839 for (const status of evseStatus
.connectors
.values()) {
840 clearedCP
= OCPP16ServiceUtils
.clearChargingProfiles(
843 status.chargingProfiles
848 for (const id
of chargingStation
.connectors
.keys()) {
849 clearedCP
= OCPP16ServiceUtils
.clearChargingProfiles(
852 chargingStation
.getConnectorStatus(id
)?.chargingProfiles
857 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED
860 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
863 private async handleRequestChangeAvailability (
864 chargingStation
: ChargingStation
,
865 commandPayload
: OCPP16ChangeAvailabilityRequest
866 ): Promise
<OCPP16ChangeAvailabilityResponse
> {
867 const { connectorId
, type } = commandPayload
868 if (!chargingStation
.hasConnector(connectorId
)) {
870 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector id ${connectorId}`
872 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_REJECTED
874 const chargePointStatus
: OCPP16ChargePointStatus
=
875 type === OCPP16AvailabilityType
.Operative
876 ? OCPP16ChargePointStatus
.Available
877 : OCPP16ChargePointStatus
.Unavailable
878 if (connectorId
=== 0) {
879 let response
: OCPP16ChangeAvailabilityResponse
880 if (chargingStation
.hasEvses
) {
881 for (const evseStatus
of chargingStation
.evses
.values()) {
882 response
= await OCPP16ServiceUtils
.changeAvailability(
884 [...evseStatus
.connectors
.keys()],
890 response
= await OCPP16ServiceUtils
.changeAvailability(
892 [...chargingStation
.connectors
.keys()],
897 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
901 (chargingStation
.isChargingStationAvailable() ||
902 (!chargingStation
.isChargingStationAvailable() &&
903 type === OCPP16AvailabilityType
.Inoperative
))
905 if (chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== true) {
906 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
907 chargingStation
.getConnectorStatus(connectorId
)!.availability
= type
908 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_SCHEDULED
910 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
911 chargingStation
.getConnectorStatus(connectorId
)!.availability
= type
912 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
917 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_ACCEPTED
919 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_REJECTED
922 private async handleRequestRemoteStartTransaction (
923 chargingStation
: ChargingStation
,
924 commandPayload
: RemoteStartTransactionRequest
925 ): Promise
<GenericResponse
> {
926 const { connectorId
: transactionConnectorId
, idTag
, chargingProfile
} = commandPayload
927 if (!chargingStation
.hasConnector(transactionConnectorId
)) {
928 return await this.notifyRemoteStartTransactionRejected(
930 transactionConnectorId
,
935 !chargingStation
.isChargingStationAvailable() ||
936 !chargingStation
.isConnectorAvailable(transactionConnectorId
)
938 return await this.notifyRemoteStartTransactionRejected(
940 transactionConnectorId
,
944 const remoteStartTransactionLogMsg
= `
945 ${chargingStation.logPrefix()} Transaction remotely STARTED on ${
946 chargingStation.stationInfo.chargingStationId
947 }#${transactionConnectorId} for idTag '${idTag}'`
948 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
950 transactionConnectorId
,
951 OCPP16ChargePointStatus
.Preparing
953 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
954 const connectorStatus
= chargingStation
.getConnectorStatus(transactionConnectorId
)!
955 // Authorization check required
957 chargingStation
.getAuthorizeRemoteTxRequests() &&
958 (await OCPP16ServiceUtils
.isIdTagAuthorized(chargingStation
, transactionConnectorId
, idTag
))
960 // Authorization successful, start transaction
962 (chargingProfile
!= null &&
963 this.setRemoteStartTransactionChargingProfile(
965 transactionConnectorId
,
968 chargingProfile
== null
970 connectorStatus
.transactionRemoteStarted
= true
973 await chargingStation
.ocppRequestService
.requestHandler
<
974 OCPP16StartTransactionRequest
,
975 OCPP16StartTransactionResponse
976 >(chargingStation
, OCPP16RequestCommand
.START_TRANSACTION
, {
977 connectorId
: transactionConnectorId
,
980 ).idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
982 logger
.debug(remoteStartTransactionLogMsg
)
983 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
985 return await this.notifyRemoteStartTransactionRejected(
987 transactionConnectorId
,
991 return await this.notifyRemoteStartTransactionRejected(
993 transactionConnectorId
,
997 // No authorization check required, start transaction
999 (chargingProfile
!= null &&
1000 this.setRemoteStartTransactionChargingProfile(
1002 transactionConnectorId
,
1005 chargingProfile
== null
1007 connectorStatus
.transactionRemoteStarted
= true
1010 await chargingStation
.ocppRequestService
.requestHandler
<
1011 OCPP16StartTransactionRequest
,
1012 OCPP16StartTransactionResponse
1013 >(chargingStation
, OCPP16RequestCommand
.START_TRANSACTION
, {
1014 connectorId
: transactionConnectorId
,
1017 ).idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
1019 logger
.debug(remoteStartTransactionLogMsg
)
1020 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
1022 return await this.notifyRemoteStartTransactionRejected(
1024 transactionConnectorId
,
1028 return await this.notifyRemoteStartTransactionRejected(
1030 transactionConnectorId
,
1035 private async notifyRemoteStartTransactionRejected (
1036 chargingStation
: ChargingStation
,
1037 connectorId
: number,
1039 ): Promise
<GenericResponse
> {
1040 const connectorStatus
= chargingStation
.getConnectorStatus(connectorId
)
1041 if (connectorStatus
?.status !== OCPP16ChargePointStatus
.Available
) {
1042 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1045 OCPP16ChargePointStatus
.Available
1049 `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id ${connectorId}, idTag '${idTag}', availability '${connectorStatus?.availability}', status '${connectorStatus?.status}'`
1051 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
1054 private setRemoteStartTransactionChargingProfile (
1055 chargingStation
: ChargingStation
,
1056 connectorId
: number,
1057 chargingProfile
: OCPP16ChargingProfile
1059 if (chargingProfile
?.chargingProfilePurpose
=== OCPP16ChargingProfilePurposeType
.TX_PROFILE
) {
1060 OCPP16ServiceUtils
.setChargingProfile(chargingStation
, connectorId
, chargingProfile
)
1062 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`,
1068 `${chargingStation.logPrefix()} Not allowed to set ${
1069 chargingProfile.chargingProfilePurpose
1070 } charging profile(s) at remote start transaction`
1075 private async handleRequestRemoteStopTransaction (
1076 chargingStation
: ChargingStation
,
1077 commandPayload
: RemoteStopTransactionRequest
1078 ): Promise
<GenericResponse
> {
1079 const { transactionId
} = commandPayload
1080 if (chargingStation
.hasEvses
) {
1081 for (const [evseId
, evseStatus
] of chargingStation
.evses
) {
1083 for (const [connectorId
, connectorStatus
] of evseStatus
.connectors
) {
1084 if (connectorStatus
.transactionId
=== transactionId
) {
1085 return await OCPP16ServiceUtils
.remoteStopTransaction(chargingStation
, connectorId
)
1091 for (const connectorId
of chargingStation
.connectors
.keys()) {
1094 chargingStation
.getConnectorStatus(connectorId
)?.transactionId
=== transactionId
1096 return await OCPP16ServiceUtils
.remoteStopTransaction(chargingStation
, connectorId
)
1101 `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction with id ${transactionId}`
1103 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
1106 private handleRequestUpdateFirmware (
1107 chargingStation
: ChargingStation
,
1108 commandPayload
: OCPP16UpdateFirmwareRequest
1109 ): OCPP16UpdateFirmwareResponse
{
1111 !OCPP16ServiceUtils
.checkFeatureProfile(
1113 OCPP16SupportedFeatureProfiles
.FirmwareManagement
,
1114 OCPP16IncomingRequestCommand
.UPDATE_FIRMWARE
1118 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported`
1120 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
1122 let { retrieveDate
} = commandPayload
1124 chargingStation
.stationInfo
.firmwareStatus
!= null &&
1125 chargingStation
.stationInfo
.firmwareStatus
!== OCPP16FirmwareStatus
.Installed
1128 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`
1130 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
1132 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1133 retrieveDate
= convertToDate(retrieveDate
)!
1134 const now
= Date.now()
1135 if (retrieveDate
?.getTime() <= now
) {
1136 this.updateFirmwareSimulation(chargingStation
).catch(Constants
.EMPTY_FUNCTION
)
1140 this.updateFirmwareSimulation(chargingStation
).catch(Constants
.EMPTY_FUNCTION
)
1142 retrieveDate
?.getTime() - now
1145 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
1148 private async updateFirmwareSimulation (
1149 chargingStation
: ChargingStation
,
1153 if (!checkChargingStation(chargingStation
, chargingStation
.logPrefix())) {
1156 if (chargingStation
.hasEvses
) {
1157 for (const [evseId
, evseStatus
] of chargingStation
.evses
) {
1159 for (const [connectorId
, connectorStatus
] of evseStatus
.connectors
) {
1160 if (connectorStatus
?.transactionStarted
=== false) {
1161 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1164 OCPP16ChargePointStatus
.Unavailable
1171 for (const connectorId
of chargingStation
.connectors
.keys()) {
1174 chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== false
1176 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1179 OCPP16ChargePointStatus
.Unavailable
1184 await chargingStation
.ocppRequestService
.requestHandler
<
1185 OCPP16FirmwareStatusNotificationRequest
,
1186 OCPP16FirmwareStatusNotificationResponse
1187 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1188 status: OCPP16FirmwareStatus
.Downloading
1190 chargingStation
.stationInfo
.firmwareStatus
= OCPP16FirmwareStatus
.Downloading
1192 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
===
1193 OCPP16FirmwareStatus
.DownloadFailed
1195 await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
)))
1196 await chargingStation
.ocppRequestService
.requestHandler
<
1197 OCPP16FirmwareStatusNotificationRequest
,
1198 OCPP16FirmwareStatusNotificationResponse
1199 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1200 status: chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
1202 chargingStation
.stationInfo
.firmwareStatus
=
1203 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
1206 await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
)))
1207 await chargingStation
.ocppRequestService
.requestHandler
<
1208 OCPP16FirmwareStatusNotificationRequest
,
1209 OCPP16FirmwareStatusNotificationResponse
1210 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1211 status: OCPP16FirmwareStatus
.Downloaded
1213 chargingStation
.stationInfo
.firmwareStatus
= OCPP16FirmwareStatus
.Downloaded
1214 let wasTransactionsStarted
= false
1215 let transactionsStarted
: boolean
1217 const runningTransactions
= chargingStation
.getNumberOfRunningTransactions()
1218 if (runningTransactions
> 0) {
1219 const waitTime
= secondsToMilliseconds(15)
1221 `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: ${runningTransactions} transaction(s) in progress, waiting ${formatDurationMilliSeconds(
1223 )} before continuing firmware update simulation`
1225 await sleep(waitTime
)
1226 transactionsStarted
= true
1227 wasTransactionsStarted
= true
1229 if (chargingStation
.hasEvses
) {
1230 for (const [evseId
, evseStatus
] of chargingStation
.evses
) {
1232 for (const [connectorId
, connectorStatus
] of evseStatus
.connectors
) {
1233 if (connectorStatus
?.status !== OCPP16ChargePointStatus
.Unavailable
) {
1234 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1237 OCPP16ChargePointStatus
.Unavailable
1244 for (const connectorId
of chargingStation
.connectors
.keys()) {
1247 chargingStation
.getConnectorStatus(connectorId
)?.status !==
1248 OCPP16ChargePointStatus
.Unavailable
1250 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1253 OCPP16ChargePointStatus
.Unavailable
1258 transactionsStarted
= false
1260 } while (transactionsStarted
)
1261 !wasTransactionsStarted
&&
1262 (await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
))))
1263 if (!checkChargingStation(chargingStation
, chargingStation
.logPrefix())) {
1266 await chargingStation
.ocppRequestService
.requestHandler
<
1267 OCPP16FirmwareStatusNotificationRequest
,
1268 OCPP16FirmwareStatusNotificationResponse
1269 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1270 status: OCPP16FirmwareStatus
.Installing
1272 chargingStation
.stationInfo
.firmwareStatus
= OCPP16FirmwareStatus
.Installing
1274 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
===
1275 OCPP16FirmwareStatus
.InstallationFailed
1277 await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
)))
1278 await chargingStation
.ocppRequestService
.requestHandler
<
1279 OCPP16FirmwareStatusNotificationRequest
,
1280 OCPP16FirmwareStatusNotificationResponse
1281 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1282 status: chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
1284 chargingStation
.stationInfo
.firmwareStatus
=
1285 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
1288 if (chargingStation
.stationInfo
?.firmwareUpgrade
?.reset
=== true) {
1289 await sleep(secondsToMilliseconds(getRandomInteger(maxDelay
, minDelay
)))
1290 await chargingStation
.reset(OCPP16StopTransactionReason
.REBOOT
)
1294 private async handleRequestGetDiagnostics (
1295 chargingStation
: ChargingStation
,
1296 commandPayload
: GetDiagnosticsRequest
1297 ): Promise
<GetDiagnosticsResponse
> {
1299 !OCPP16ServiceUtils
.checkFeatureProfile(
1301 OCPP16SupportedFeatureProfiles
.FirmwareManagement
,
1302 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
1306 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`
1308 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
1310 const { location
} = commandPayload
1311 const uri
= new URL(location
)
1312 if (uri
.protocol
.startsWith('ftp:')) {
1313 let ftpClient
: Client
| undefined
1315 const logFiles
= readdirSync(resolve(dirname(fileURLToPath(import.meta
.url
)), '../'))
1316 .filter((file
) => file
.endsWith('.log'))
1317 .map((file
) => join('./', file
))
1318 const diagnosticsArchive
= `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`
1319 create({ gzip
: true }, logFiles
).pipe(createWriteStream(diagnosticsArchive
))
1320 ftpClient
= new Client()
1321 const accessResponse
= await ftpClient
.access({
1323 ...(isNotEmptyString(uri
.port
) && { port
: convertToInt(uri
.port
) }),
1324 ...(isNotEmptyString(uri
.username
) && { user
: uri
.username
}),
1325 ...(isNotEmptyString(uri
.password
) && { password
: uri
.password
})
1327 let uploadResponse
: FTPResponse
| undefined
1328 if (accessResponse
.code
=== 220) {
1329 ftpClient
.trackProgress((info
) => {
1331 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: ${
1333 } bytes transferred from diagnostics archive ${info.name}`
1335 chargingStation
.ocppRequestService
1337 OCPP16DiagnosticsStatusNotificationRequest
,
1338 OCPP16DiagnosticsStatusNotificationResponse
1339 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1340 status: OCPP16DiagnosticsStatus
.Uploading
1344 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
1345 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
1351 uploadResponse
= await ftpClient
.uploadFrom(
1352 join(resolve(dirname(fileURLToPath(import.meta
.url
)), '../'), diagnosticsArchive
),
1353 `${uri.pathname}${diagnosticsArchive}`
1355 if (uploadResponse
.code
=== 226) {
1356 await chargingStation
.ocppRequestService
.requestHandler
<
1357 OCPP16DiagnosticsStatusNotificationRequest
,
1358 OCPP16DiagnosticsStatusNotificationResponse
1359 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1360 status: OCPP16DiagnosticsStatus
.Uploaded
1362 if (ftpClient
!= null) {
1365 return { fileName
: diagnosticsArchive
}
1367 throw new OCPPError(
1368 ErrorType
.GENERIC_ERROR
,
1369 `Diagnostics transfer failed with error code ${accessResponse.code}${
1370 uploadResponse?.code != null && `|${uploadResponse?.code}
`
1372 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
1375 throw new OCPPError(
1376 ErrorType
.GENERIC_ERROR
,
1377 `Diagnostics transfer failed with error code ${accessResponse.code}${
1378 uploadResponse?.code != null && `|${uploadResponse?.code}
`
1380 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
1383 await chargingStation
.ocppRequestService
.requestHandler
<
1384 OCPP16DiagnosticsStatusNotificationRequest
,
1385 OCPP16DiagnosticsStatusNotificationResponse
1386 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1387 status: OCPP16DiagnosticsStatus
.UploadFailed
1389 if (ftpClient
!= null) {
1392 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1393 return this.handleIncomingRequestError
<GetDiagnosticsResponse
>(
1395 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
1397 { errorResponse
: OCPP16Constants
.OCPP_RESPONSE_EMPTY
}
1402 `${chargingStation.logPrefix()} Unsupported protocol ${
1404 } to transfer the diagnostic logs archive`
1406 await chargingStation
.ocppRequestService
.requestHandler
<
1407 OCPP16DiagnosticsStatusNotificationRequest
,
1408 OCPP16DiagnosticsStatusNotificationResponse
1409 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1410 status: OCPP16DiagnosticsStatus
.UploadFailed
1412 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
1416 private handleRequestTriggerMessage (
1417 chargingStation
: ChargingStation
,
1418 commandPayload
: OCPP16TriggerMessageRequest
1419 ): OCPP16TriggerMessageResponse
{
1420 const { requestedMessage
, connectorId
} = commandPayload
1422 !OCPP16ServiceUtils
.checkFeatureProfile(
1424 OCPP16SupportedFeatureProfiles
.RemoteTrigger
,
1425 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
1427 !OCPP16ServiceUtils
.isMessageTriggerSupported(chargingStation
, requestedMessage
)
1429 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED
1432 !OCPP16ServiceUtils
.isConnectorIdValid(
1434 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
1435 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1439 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED
1442 switch (requestedMessage
) {
1443 case OCPP16MessageTrigger
.BootNotification
:
1445 chargingStation
.ocppRequestService
1446 .requestHandler
<OCPP16BootNotificationRequest
, OCPP16BootNotificationResponse
>(
1448 OCPP16RequestCommand
.BOOT_NOTIFICATION
,
1449 chargingStation
.bootNotificationRequest
,
1450 { skipBufferingOnError
: true, triggerMessage
: true }
1452 .then((response
) => {
1453 chargingStation
.bootNotificationResponse
= response
1455 .catch(Constants
.EMPTY_FUNCTION
)
1456 }, OCPP16Constants
.OCPP_TRIGGER_MESSAGE_DELAY
)
1457 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED
1458 case OCPP16MessageTrigger
.Heartbeat
:
1460 chargingStation
.ocppRequestService
1461 .requestHandler
<OCPP16HeartbeatRequest
, OCPP16HeartbeatResponse
>(
1463 OCPP16RequestCommand
.HEARTBEAT
,
1466 triggerMessage
: true
1469 .catch(Constants
.EMPTY_FUNCTION
)
1470 }, OCPP16Constants
.OCPP_TRIGGER_MESSAGE_DELAY
)
1471 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED
1472 case OCPP16MessageTrigger
.StatusNotification
:
1474 if (connectorId
!= null) {
1475 chargingStation
.ocppRequestService
1476 .requestHandler
<OCPP16StatusNotificationRequest
, OCPP16StatusNotificationResponse
>(
1478 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
1481 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
1482 status: chargingStation
.getConnectorStatus(connectorId
)?.status
1485 triggerMessage
: true
1488 .catch(Constants
.EMPTY_FUNCTION
)
1490 if (chargingStation
.hasEvses
) {
1491 for (const evseStatus
of chargingStation
.evses
.values()) {
1492 for (const [id
, connectorStatus
] of evseStatus
.connectors
) {
1493 chargingStation
.ocppRequestService
1495 OCPP16StatusNotificationRequest
,
1496 OCPP16StatusNotificationResponse
1499 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
1502 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
1503 status: connectorStatus
.status
1506 triggerMessage
: true
1509 .catch(Constants
.EMPTY_FUNCTION
)
1513 for (const id
of chargingStation
.connectors
.keys()) {
1514 chargingStation
.ocppRequestService
1516 OCPP16StatusNotificationRequest
,
1517 OCPP16StatusNotificationResponse
1520 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
1523 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
1524 status: chargingStation
.getConnectorStatus(id
)?.status
1527 triggerMessage
: true
1530 .catch(Constants
.EMPTY_FUNCTION
)
1534 }, OCPP16Constants
.OCPP_TRIGGER_MESSAGE_DELAY
)
1535 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED
1537 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED
1540 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1541 return this.handleIncomingRequestError
<OCPP16TriggerMessageResponse
>(
1543 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
1545 { errorResponse
: OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED
}
1550 private handleRequestDataTransfer (
1551 chargingStation
: ChargingStation
,
1552 commandPayload
: OCPP16DataTransferRequest
1553 ): OCPP16DataTransferResponse
{
1554 const { vendorId
} = commandPayload
1556 if (Object.values(OCPP16DataTransferVendorId
).includes(vendorId
)) {
1557 return OCPP16Constants
.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED
1559 return OCPP16Constants
.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID
1561 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1562 return this.handleIncomingRequestError
<OCPP16DataTransferResponse
>(
1564 OCPP16IncomingRequestCommand
.DATA_TRANSFER
,
1566 { errorResponse
: OCPP16Constants
.OCPP_DATA_TRANSFER_RESPONSE_REJECTED
}
1571 private async handleRequestReserveNow (
1572 chargingStation
: ChargingStation
,
1573 commandPayload
: OCPP16ReserveNowRequest
1574 ): Promise
<OCPP16ReserveNowResponse
> {
1576 !OCPP16ServiceUtils
.checkFeatureProfile(
1578 OCPP16SupportedFeatureProfiles
.Reservation
,
1579 OCPP16IncomingRequestCommand
.RESERVE_NOW
1582 return OCPP16Constants
.OCPP_RESERVATION_RESPONSE_REJECTED
1584 const { reservationId
, idTag
, connectorId
} = commandPayload
1585 let response
: OCPP16ReserveNowResponse
1587 if (connectorId
> 0 && !chargingStation
.isConnectorAvailable(connectorId
)) {
1588 return OCPP16Constants
.OCPP_RESERVATION_RESPONSE_REJECTED
1590 if (connectorId
=== 0 && !chargingStation
.getReserveConnectorZeroSupported()) {
1591 return OCPP16Constants
.OCPP_RESERVATION_RESPONSE_REJECTED
1593 if (!(await OCPP16ServiceUtils
.isIdTagAuthorized(chargingStation
, connectorId
, idTag
))) {
1594 return OCPP16Constants
.OCPP_RESERVATION_RESPONSE_REJECTED
1596 await removeExpiredReservations(chargingStation
)
1597 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1598 switch (chargingStation
.getConnectorStatus(connectorId
)!.status) {
1599 case OCPP16ChargePointStatus
.Faulted
:
1600 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_FAULTED
1602 case OCPP16ChargePointStatus
.Preparing
:
1603 case OCPP16ChargePointStatus
.Charging
:
1604 case OCPP16ChargePointStatus
.SuspendedEV
:
1605 case OCPP16ChargePointStatus
.SuspendedEVSE
:
1606 case OCPP16ChargePointStatus
.Finishing
:
1607 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_OCCUPIED
1609 case OCPP16ChargePointStatus
.Unavailable
:
1610 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_UNAVAILABLE
1612 case OCPP16ChargePointStatus
.Reserved
:
1613 if (!chargingStation
.isConnectorReservable(reservationId
, idTag
, connectorId
)) {
1614 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_OCCUPIED
1617 // eslint-disable-next-line no-fallthrough
1619 if (!chargingStation
.isConnectorReservable(reservationId
, idTag
)) {
1620 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_OCCUPIED
1623 await chargingStation
.addReservation({
1624 id
: commandPayload
.reservationId
,
1627 response
= OCPP16Constants
.OCPP_RESERVATION_RESPONSE_ACCEPTED
1632 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1633 chargingStation
.getConnectorStatus(connectorId
)!.status = OCPP16ChargePointStatus
.Available
1634 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1635 return this.handleIncomingRequestError
<OCPP16ReserveNowResponse
>(
1637 OCPP16IncomingRequestCommand
.RESERVE_NOW
,
1639 { errorResponse
: OCPP16Constants
.OCPP_RESERVATION_RESPONSE_FAULTED
}
1644 private async handleRequestCancelReservation (
1645 chargingStation
: ChargingStation
,
1646 commandPayload
: OCPP16CancelReservationRequest
1647 ): Promise
<GenericResponse
> {
1649 !OCPP16ServiceUtils
.checkFeatureProfile(
1651 OCPP16SupportedFeatureProfiles
.Reservation
,
1652 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
1655 return OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
1658 const { reservationId
} = commandPayload
1659 const reservation
= chargingStation
.getReservationBy('reservationId', reservationId
)
1660 if (isUndefined(reservation
)) {
1662 `${chargingStation.logPrefix()} Reservation with id ${reservationId} does not exist on charging station`
1664 return OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
1666 await chargingStation
.removeReservation(
1667 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1669 ReservationTerminationReason
.RESERVATION_CANCELED
1671 return OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED
1673 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1674 return this.handleIncomingRequestError
<GenericResponse
>(
1676 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
,
1678 { errorResponse
: OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
}