1 /* eslint-disable max-len */
2 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
4 import fs from
'node:fs';
5 import path from
'node:path';
6 import { URL
, fileURLToPath
} from
'node:url';
8 import type { JSONSchemaType
} from
'ajv';
9 import { Client
, type FTPResponse
} from
'basic-ftp';
10 import tar from
'tar';
12 import { OCPP16Constants
} from
'./OCPP16Constants';
13 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
16 ChargingStationConfigurationUtils
,
18 } from
'../../../charging-station';
19 import { OCPPError
} from
'../../../exception';
21 type ChangeAvailabilityRequest
,
22 type ChangeAvailabilityResponse
,
23 type ChangeConfigurationRequest
,
24 type ChangeConfigurationResponse
,
25 type ClearChargingProfileRequest
,
26 type ClearChargingProfileResponse
,
32 type GetConfigurationRequest
,
33 type GetConfigurationResponse
,
34 type GetDiagnosticsRequest
,
35 type GetDiagnosticsResponse
,
36 type IncomingRequestHandler
,
39 OCPP16AuthorizationStatus
,
40 type OCPP16AuthorizeRequest
,
41 type OCPP16AuthorizeResponse
,
42 OCPP16AvailabilityType
,
43 type OCPP16BootNotificationRequest
,
44 type OCPP16BootNotificationResponse
,
45 OCPP16ChargePointErrorCode
,
46 OCPP16ChargePointStatus
,
47 type OCPP16ChargingProfile
,
48 OCPP16ChargingProfilePurposeType
,
49 type OCPP16ChargingSchedule
,
50 type OCPP16ClearCacheRequest
,
51 type OCPP16DataTransferRequest
,
52 type OCPP16DataTransferResponse
,
53 OCPP16DataTransferStatus
,
54 OCPP16DataTransferVendorId
,
55 OCPP16DiagnosticsStatus
,
56 type OCPP16DiagnosticsStatusNotificationRequest
,
57 type OCPP16DiagnosticsStatusNotificationResponse
,
59 type OCPP16FirmwareStatusNotificationRequest
,
60 type OCPP16FirmwareStatusNotificationResponse
,
61 type OCPP16GetCompositeScheduleRequest
,
62 type OCPP16GetCompositeScheduleResponse
,
63 type OCPP16HeartbeatRequest
,
64 type OCPP16HeartbeatResponse
,
65 OCPP16IncomingRequestCommand
,
68 OCPP16StandardParametersKey
,
69 type OCPP16StartTransactionRequest
,
70 type OCPP16StartTransactionResponse
,
71 type OCPP16StatusNotificationRequest
,
72 type OCPP16StatusNotificationResponse
,
73 OCPP16StopTransactionReason
,
74 OCPP16SupportedFeatureProfiles
,
75 type OCPP16TriggerMessageRequest
,
76 type OCPP16TriggerMessageResponse
,
77 type OCPP16UpdateFirmwareRequest
,
78 type OCPP16UpdateFirmwareResponse
,
79 type OCPPConfigurationKey
,
81 type RemoteStartTransactionRequest
,
82 type RemoteStopTransactionRequest
,
84 type SetChargingProfileRequest
,
85 type SetChargingProfileResponse
,
86 type UnlockConnectorRequest
,
87 type UnlockConnectorResponse
,
88 } from
'../../../types';
90 OCPP16CancelReservationRequest
,
91 OCPP16ReserveNowRequest
,
92 } from
'../../../types/ocpp/1.6/Requests';
94 OCPP16CancelReservationResponse
,
95 OCPP16ReserveNowResponse
,
96 } from
'../../../types/ocpp/1.6/Responses';
97 import { Constants
, Utils
, logger
} from
'../../../utils';
98 import { OCPPConstants
} from
'../OCPPConstants';
99 import { OCPPIncomingRequestService
} from
'../OCPPIncomingRequestService';
101 const moduleName
= 'OCPP16IncomingRequestService';
103 export class OCPP16IncomingRequestService
extends OCPPIncomingRequestService
{
104 protected jsonSchemas
: Map
<OCPP16IncomingRequestCommand
, JSONSchemaType
<JsonObject
>>;
105 private incomingRequestHandlers
: Map
<OCPP16IncomingRequestCommand
, IncomingRequestHandler
>;
107 public constructor() {
108 // if (new.target?.name === moduleName) {
109 // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
111 super(OCPPVersion
.VERSION_16
);
112 this.incomingRequestHandlers
= new Map
<OCPP16IncomingRequestCommand
, IncomingRequestHandler
>([
113 [OCPP16IncomingRequestCommand
.RESET
, this.handleRequestReset
.bind(this)],
114 [OCPP16IncomingRequestCommand
.CLEAR_CACHE
, this.handleRequestClearCache
.bind(this)],
115 [OCPP16IncomingRequestCommand
.UNLOCK_CONNECTOR
, this.handleRequestUnlockConnector
.bind(this)],
117 OCPP16IncomingRequestCommand
.GET_CONFIGURATION
,
118 this.handleRequestGetConfiguration
.bind(this),
121 OCPP16IncomingRequestCommand
.CHANGE_CONFIGURATION
,
122 this.handleRequestChangeConfiguration
.bind(this),
125 OCPP16IncomingRequestCommand
.GET_COMPOSITE_SCHEDULE
,
126 this.handleRequestGetCompositeSchedule
.bind(this),
129 OCPP16IncomingRequestCommand
.SET_CHARGING_PROFILE
,
130 this.handleRequestSetChargingProfile
.bind(this),
133 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
,
134 this.handleRequestClearChargingProfile
.bind(this),
137 OCPP16IncomingRequestCommand
.CHANGE_AVAILABILITY
,
138 this.handleRequestChangeAvailability
.bind(this),
141 OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
,
142 this.handleRequestRemoteStartTransaction
.bind(this),
145 OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
,
146 this.handleRequestRemoteStopTransaction
.bind(this),
148 [OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
, this.handleRequestGetDiagnostics
.bind(this)],
149 [OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
, this.handleRequestTriggerMessage
.bind(this)],
150 [OCPP16IncomingRequestCommand
.DATA_TRANSFER
, this.handleRequestDataTransfer
.bind(this)],
151 [OCPP16IncomingRequestCommand
.UPDATE_FIRMWARE
, this.handleRequestUpdateFirmware
.bind(this)],
153 this.jsonSchemas
= new Map
<OCPP16IncomingRequestCommand
, JSONSchemaType
<JsonObject
>>([
155 OCPP16IncomingRequestCommand
.RESET
,
156 OCPP16ServiceUtils
.parseJsonSchemaFile
<ResetRequest
>(
157 'assets/json-schemas/ocpp/1.6/Reset.json',
163 OCPP16IncomingRequestCommand
.CLEAR_CACHE
,
164 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16ClearCacheRequest
>(
165 'assets/json-schemas/ocpp/1.6/ClearCache.json',
171 OCPP16IncomingRequestCommand
.UNLOCK_CONNECTOR
,
172 OCPP16ServiceUtils
.parseJsonSchemaFile
<UnlockConnectorRequest
>(
173 'assets/json-schemas/ocpp/1.6/UnlockConnector.json',
179 OCPP16IncomingRequestCommand
.GET_CONFIGURATION
,
180 OCPP16ServiceUtils
.parseJsonSchemaFile
<GetConfigurationRequest
>(
181 'assets/json-schemas/ocpp/1.6/GetConfiguration.json',
187 OCPP16IncomingRequestCommand
.CHANGE_CONFIGURATION
,
188 OCPP16ServiceUtils
.parseJsonSchemaFile
<ChangeConfigurationRequest
>(
189 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json',
195 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
196 OCPP16ServiceUtils
.parseJsonSchemaFile
<GetDiagnosticsRequest
>(
197 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json',
203 OCPP16IncomingRequestCommand
.GET_COMPOSITE_SCHEDULE
,
204 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16GetCompositeScheduleRequest
>(
205 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json',
211 OCPP16IncomingRequestCommand
.SET_CHARGING_PROFILE
,
212 OCPP16ServiceUtils
.parseJsonSchemaFile
<SetChargingProfileRequest
>(
213 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json',
219 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
,
220 OCPP16ServiceUtils
.parseJsonSchemaFile
<ClearChargingProfileRequest
>(
221 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json',
227 OCPP16IncomingRequestCommand
.CHANGE_AVAILABILITY
,
228 OCPP16ServiceUtils
.parseJsonSchemaFile
<ChangeAvailabilityRequest
>(
229 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json',
235 OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
,
236 OCPP16ServiceUtils
.parseJsonSchemaFile
<RemoteStartTransactionRequest
>(
237 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json',
243 OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
,
244 OCPP16ServiceUtils
.parseJsonSchemaFile
<RemoteStopTransactionRequest
>(
245 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json',
251 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
252 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16TriggerMessageRequest
>(
253 'assets/json-schemas/ocpp/1.6/TriggerMessage.json',
259 OCPP16IncomingRequestCommand
.DATA_TRANSFER
,
260 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16DataTransferRequest
>(
261 'assets/json-schemas/ocpp/1.6/DataTransfer.json',
267 OCPP16IncomingRequestCommand
.UPDATE_FIRMWARE
,
268 OCPP16ServiceUtils
.parseJsonSchemaFile
<OCPP16UpdateFirmwareRequest
>(
269 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json',
275 this.validatePayload
= this.validatePayload
.bind(this) as (
276 chargingStation
: ChargingStation
,
277 commandName
: OCPP16IncomingRequestCommand
,
278 commandPayload
: JsonType
282 public async incomingRequestHandler(
283 chargingStation
: ChargingStation
,
285 commandName
: OCPP16IncomingRequestCommand
,
286 commandPayload
: JsonType
288 let response
: JsonType
;
290 chargingStation
.getOcppStrictCompliance() === true &&
291 chargingStation
.inPendingState() === true &&
292 (commandName
=== OCPP16IncomingRequestCommand
.REMOTE_START_TRANSACTION
||
293 commandName
=== OCPP16IncomingRequestCommand
.REMOTE_STOP_TRANSACTION
)
296 ErrorType
.SECURITY_ERROR
,
297 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
301 )} while the charging station is in pending state on the central server`,
307 chargingStation
.isRegistered() === true ||
308 (chargingStation
.getOcppStrictCompliance() === false &&
309 chargingStation
.inUnknownState() === true)
312 this.incomingRequestHandlers
.has(commandName
) === true &&
313 OCPP16ServiceUtils
.isIncomingRequestCommandSupported(chargingStation
, commandName
) === true
316 this.validatePayload(chargingStation
, commandName
, commandPayload
);
317 // Call the method to build the response
318 response
= await this.incomingRequestHandlers
.get(commandName
)(
325 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
333 ErrorType
.NOT_IMPLEMENTED
,
334 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
345 ErrorType
.SECURITY_ERROR
,
346 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
350 )} while the charging station is not registered on the central server.`,
355 // Send the built response
356 await chargingStation
.ocppRequestService
.sendResponse(
364 private validatePayload(
365 chargingStation
: ChargingStation
,
366 commandName
: OCPP16IncomingRequestCommand
,
367 commandPayload
: JsonType
369 if (this.jsonSchemas
.has(commandName
) === true) {
370 return this.validateIncomingRequestPayload(
373 this.jsonSchemas
.get(commandName
),
378 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
383 // Simulate charging station restart
384 private handleRequestReset(
385 chargingStation
: ChargingStation
,
386 commandPayload
: ResetRequest
388 this.runInAsyncScope(
389 chargingStation
.reset
.bind(chargingStation
) as (
390 this: ChargingStation
,
394 `${commandPayload.type}Reset` as OCPP16StopTransactionReason
395 ).catch(Constants
.EMPTY_FUNCTION
);
397 `${chargingStation.logPrefix()} ${
399 } reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds(
400 chargingStation.stationInfo.resetTime
403 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
;
406 private async handleRequestUnlockConnector(
407 chargingStation
: ChargingStation
,
408 commandPayload
: UnlockConnectorRequest
409 ): Promise
<UnlockConnectorResponse
> {
410 const connectorId
= commandPayload
.connectorId
;
411 if (chargingStation
.hasConnector(connectorId
) === false) {
413 `${chargingStation.logPrefix()} Trying to unlock a non existing connector id ${connectorId.toString()}`
415 return OCPP16Constants
.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED
;
417 if (connectorId
=== 0) {
419 `${chargingStation.logPrefix()} Trying to unlock connector id ${connectorId.toString()}`
421 return OCPP16Constants
.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED
;
423 if (chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== true) {
424 const stopResponse
= await chargingStation
.stopTransactionOnConnector(
426 OCPP16StopTransactionReason
.UNLOCK_COMMAND
428 if (stopResponse
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
429 return OCPP16Constants
.OCPP_RESPONSE_UNLOCKED
;
431 return OCPP16Constants
.OCPP_RESPONSE_UNLOCK_FAILED
;
433 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
436 OCPP16ChargePointStatus
.Available
438 return OCPP16Constants
.OCPP_RESPONSE_UNLOCKED
;
441 private handleRequestGetConfiguration(
442 chargingStation
: ChargingStation
,
443 commandPayload
: GetConfigurationRequest
444 ): GetConfigurationResponse
{
445 const configurationKey
: OCPPConfigurationKey
[] = [];
446 const unknownKey
: string[] = [];
447 if (Utils
.isUndefined(commandPayload
.key
) === true) {
448 for (const configuration
of chargingStation
.ocppConfiguration
.configurationKey
) {
449 if (Utils
.isUndefined(configuration
.visible
) === true) {
450 configuration
.visible
= true;
452 if (configuration
.visible
=== false) {
455 configurationKey
.push({
456 key
: configuration
.key
,
457 readonly: configuration
.readonly,
458 value
: configuration
.value
,
461 } else if (Utils
.isNotEmptyArray(commandPayload
.key
) === true) {
462 for (const key
of commandPayload
.key
) {
463 const keyFound
= ChargingStationConfigurationUtils
.getConfigurationKey(
469 if (Utils
.isUndefined(keyFound
.visible
) === true) {
470 keyFound
.visible
= true;
472 if (keyFound
.visible
=== false) {
475 configurationKey
.push({
477 readonly: keyFound
.readonly,
478 value
: keyFound
.value
,
481 unknownKey
.push(key
);
491 private handleRequestChangeConfiguration(
492 chargingStation
: ChargingStation
,
493 commandPayload
: ChangeConfigurationRequest
494 ): ChangeConfigurationResponse
{
495 const keyToChange
= ChargingStationConfigurationUtils
.getConfigurationKey(
501 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED
;
502 } else if (keyToChange
?.readonly === true) {
503 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_REJECTED
;
504 } else if (keyToChange
?.readonly === false) {
505 let valueChanged
= false;
506 if (keyToChange
.value
!== commandPayload
.value
) {
507 ChargingStationConfigurationUtils
.setConfigurationKeyValue(
510 commandPayload
.value
,
515 let triggerHeartbeatRestart
= false;
516 if (keyToChange
.key
=== OCPP16StandardParametersKey
.HeartBeatInterval
&& valueChanged
) {
517 ChargingStationConfigurationUtils
.setConfigurationKeyValue(
519 OCPP16StandardParametersKey
.HeartbeatInterval
,
522 triggerHeartbeatRestart
= true;
524 if (keyToChange
.key
=== OCPP16StandardParametersKey
.HeartbeatInterval
&& valueChanged
) {
525 ChargingStationConfigurationUtils
.setConfigurationKeyValue(
527 OCPP16StandardParametersKey
.HeartBeatInterval
,
530 triggerHeartbeatRestart
= true;
532 if (triggerHeartbeatRestart
) {
533 chargingStation
.restartHeartbeat();
535 if (keyToChange
.key
=== OCPP16StandardParametersKey
.WebSocketPingInterval
&& valueChanged
) {
536 chargingStation
.restartWebSocketPing();
538 if (keyToChange
.reboot
) {
539 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED
;
541 return OCPP16Constants
.OCPP_CONFIGURATION_RESPONSE_ACCEPTED
;
545 private handleRequestSetChargingProfile(
546 chargingStation
: ChargingStation
,
547 commandPayload
: SetChargingProfileRequest
548 ): SetChargingProfileResponse
{
550 OCPP16ServiceUtils
.checkFeatureProfile(
552 OCPP16SupportedFeatureProfiles
.SmartCharging
,
553 OCPP16IncomingRequestCommand
.SET_CHARGING_PROFILE
556 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED
;
558 if (chargingStation
.hasConnector(commandPayload
.connectorId
) === false) {
560 `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector id ${
561 commandPayload.connectorId
564 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
;
567 commandPayload
.csChargingProfiles
.chargingProfilePurpose
===
568 OCPP16ChargingProfilePurposeType
.CHARGE_POINT_MAX_PROFILE
&&
569 commandPayload
.connectorId
!== 0
571 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
;
574 commandPayload
.csChargingProfiles
.chargingProfilePurpose
===
575 OCPP16ChargingProfilePurposeType
.TX_PROFILE
&&
576 (commandPayload
.connectorId
=== 0 ||
577 chargingStation
.getConnectorStatus(commandPayload
.connectorId
)?.transactionStarted
===
581 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${
582 commandPayload.connectorId
583 } without a started transaction`
585 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
;
587 OCPP16ServiceUtils
.setChargingProfile(
589 commandPayload
.connectorId
,
590 commandPayload
.csChargingProfiles
593 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${
594 commandPayload.connectorId
596 commandPayload
.csChargingProfiles
598 return OCPP16Constants
.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED
;
601 private handleRequestGetCompositeSchedule(
602 chargingStation
: ChargingStation
,
603 commandPayload
: OCPP16GetCompositeScheduleRequest
604 ): OCPP16GetCompositeScheduleResponse
{
606 OCPP16ServiceUtils
.checkFeatureProfile(
608 OCPP16SupportedFeatureProfiles
.SmartCharging
,
609 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
612 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
;
614 if (chargingStation
.hasConnector(commandPayload
.connectorId
) === false) {
616 `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector id ${
617 commandPayload.connectorId
620 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
;
624 chargingStation
.getConnectorStatus(commandPayload
.connectorId
)?.chargingProfiles
627 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
;
629 const startDate
= new Date();
630 const endDate
= new Date(startDate
.getTime() + commandPayload
.duration
* 1000);
631 let compositeSchedule
: OCPP16ChargingSchedule
;
632 for (const chargingProfile
of chargingStation
.getConnectorStatus(commandPayload
.connectorId
)
634 // FIXME: build the composite schedule including the local power limit, the stack level, the charging rate unit, etc.
636 chargingProfile
.chargingSchedule
?.startSchedule
>= startDate
&&
637 chargingProfile
.chargingSchedule
?.startSchedule
<= endDate
639 compositeSchedule
= chargingProfile
.chargingSchedule
;
644 status: GenericStatus
.Accepted
,
645 scheduleStart
: compositeSchedule
?.startSchedule
,
646 connectorId
: commandPayload
.connectorId
,
647 chargingSchedule
: compositeSchedule
,
651 private handleRequestClearChargingProfile(
652 chargingStation
: ChargingStation
,
653 commandPayload
: ClearChargingProfileRequest
654 ): ClearChargingProfileResponse
{
656 OCPP16ServiceUtils
.checkFeatureProfile(
658 OCPP16SupportedFeatureProfiles
.SmartCharging
,
659 OCPP16IncomingRequestCommand
.CLEAR_CHARGING_PROFILE
662 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
;
664 if (chargingStation
.hasConnector(commandPayload
.connectorId
) === false) {
666 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector id ${
667 commandPayload.connectorId
670 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
;
673 !Utils
.isNullOrUndefined(commandPayload
.connectorId
) &&
674 Utils
.isNotEmptyArray(
675 chargingStation
.getConnectorStatus(commandPayload
.connectorId
)?.chargingProfiles
678 chargingStation
.getConnectorStatus(commandPayload
.connectorId
).chargingProfiles
= [];
680 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${
681 commandPayload.connectorId
684 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED
;
686 if (Utils
.isNullOrUndefined(commandPayload
.connectorId
)) {
687 let clearedCP
= false;
688 const clearChargingProfiles
= (connectorStatus
: ConnectorStatus
) => {
689 if (Utils
.isNotEmptyArray(connectorStatus
?.chargingProfiles
)) {
690 connectorStatus
?.chargingProfiles
?.forEach(
691 (chargingProfile
: OCPP16ChargingProfile
, index
: number) => {
692 let clearCurrentCP
= false;
693 if (chargingProfile
.chargingProfileId
=== commandPayload
.id
) {
694 clearCurrentCP
= true;
697 !commandPayload
.chargingProfilePurpose
&&
698 chargingProfile
.stackLevel
=== commandPayload
.stackLevel
700 clearCurrentCP
= true;
703 !chargingProfile
.stackLevel
&&
704 chargingProfile
.chargingProfilePurpose
=== commandPayload
.chargingProfilePurpose
706 clearCurrentCP
= true;
709 chargingProfile
.stackLevel
=== commandPayload
.stackLevel
&&
710 chargingProfile
.chargingProfilePurpose
=== commandPayload
.chargingProfilePurpose
712 clearCurrentCP
= true;
714 if (clearCurrentCP
) {
715 connectorStatus
?.chargingProfiles
?.splice(index
, 1);
717 `${chargingStation.logPrefix()} Matching charging profile(s) cleared: %j`,
726 if (chargingStation
.hasEvses
) {
727 for (const evseStatus
of chargingStation
.evses
.values()) {
728 for (const connectorStatus
of evseStatus
.connectors
.values()) {
729 clearChargingProfiles(connectorStatus
);
733 for (const connectorId
of chargingStation
.connectors
.keys()) {
734 clearChargingProfiles(chargingStation
.getConnectorStatus(connectorId
));
738 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED
;
741 return OCPP16Constants
.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
;
744 private async handleRequestChangeAvailability(
745 chargingStation
: ChargingStation
,
746 commandPayload
: ChangeAvailabilityRequest
747 ): Promise
<ChangeAvailabilityResponse
> {
748 const connectorId
: number = commandPayload
.connectorId
;
749 if (chargingStation
.hasConnector(connectorId
) === false) {
751 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector id ${connectorId.toString()}`
753 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_REJECTED
;
755 const chargePointStatus
: OCPP16ChargePointStatus
=
756 commandPayload
.type === OCPP16AvailabilityType
.Operative
757 ? OCPP16ChargePointStatus
.Available
758 : OCPP16ChargePointStatus
.Unavailable
;
759 if (connectorId
=== 0) {
760 let response
: ChangeAvailabilityResponse
=
761 OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_ACCEPTED
;
762 const changeAvailability
= async (id
: number, connectorStatus
: ConnectorStatus
) => {
763 if (connectorStatus
?.transactionStarted
=== true) {
764 response
= OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_SCHEDULED
;
766 connectorStatus
.availability
= commandPayload
.type;
767 if (response
=== OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_ACCEPTED
) {
768 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
775 if (chargingStation
.hasEvses
) {
776 for (const evseStatus
of chargingStation
.evses
.values()) {
777 for (const [id
, connectorStatus
] of evseStatus
.connectors
) {
778 await changeAvailability(id
, connectorStatus
);
782 for (const id
of chargingStation
.connectors
.keys()) {
783 await changeAvailability(id
, chargingStation
.getConnectorStatus(id
));
789 (chargingStation
.isChargingStationAvailable() === true ||
790 (chargingStation
.isChargingStationAvailable() === false &&
791 commandPayload
.type === OCPP16AvailabilityType
.Inoperative
))
793 if (chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== true) {
794 chargingStation
.getConnectorStatus(connectorId
).availability
= commandPayload
.type;
795 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_SCHEDULED
;
797 chargingStation
.getConnectorStatus(connectorId
).availability
= commandPayload
.type;
798 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
803 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_ACCEPTED
;
805 return OCPP16Constants
.OCPP_AVAILABILITY_RESPONSE_REJECTED
;
808 private async handleRequestRemoteStartTransaction(
809 chargingStation
: ChargingStation
,
810 commandPayload
: RemoteStartTransactionRequest
811 ): Promise
<GenericResponse
> {
812 const transactionConnectorId
= commandPayload
.connectorId
;
813 const reserved
: boolean =
814 chargingStation
.getConnectorStatus(transactionConnectorId
).status ===
815 OCPP16ChargePointStatus
.Reserved
;
816 if (chargingStation
.hasConnector(transactionConnectorId
) === false) {
817 return this.notifyRemoteStartTransactionRejected(
819 transactionConnectorId
,
824 chargingStation
.isChargingStationAvailable() === false ||
825 chargingStation
.isConnectorAvailable(transactionConnectorId
) === false ||
828 return this.notifyRemoteStartTransactionRejected(
830 transactionConnectorId
,
834 const remoteStartTransactionLogMsg
= `${chargingStation.logPrefix()} Transaction remotely STARTED on ${
835 chargingStation.stationInfo.chargingStationId
836 }#${transactionConnectorId.toString()} for idTag '${commandPayload.idTag}'`;
837 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
839 transactionConnectorId
,
840 OCPP16ChargePointStatus
.Preparing
842 const connectorStatus
= chargingStation
.getConnectorStatus(transactionConnectorId
);
843 // Check if authorized
844 if (chargingStation
.getAuthorizeRemoteTxRequests() === true) {
845 const authorized
= await this.isAuthorized(
847 transactionConnectorId
,
850 if (authorized
=== true) {
851 // Authorization successful, start transaction
853 this.setRemoteStartTransactionChargingProfile(
855 transactionConnectorId
,
856 commandPayload
.chargingProfile
859 connectorStatus
.transactionRemoteStarted
= true;
860 const startTransactionData
: JsonType
= {
861 connectorId
: transactionConnectorId
,
862 idTag
: commandPayload
.idTag
,
865 startTransactionData
['reservationId'] =
866 chargingStation
.getReservationByConnectorId(transactionConnectorId
).reservationId
;
870 await chargingStation
.ocppRequestService
.requestHandler
<
871 OCPP16StartTransactionRequest
,
872 OCPP16StartTransactionResponse
873 >(chargingStation
, OCPP16RequestCommand
.START_TRANSACTION
, startTransactionData
)
874 ).idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
876 logger
.debug(remoteStartTransactionLogMsg
);
877 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
;
879 return this.notifyRemoteStartTransactionRejected(
881 transactionConnectorId
,
885 return this.notifyRemoteStartTransactionRejected(
887 transactionConnectorId
,
891 return this.notifyRemoteStartTransactionRejected(
893 transactionConnectorId
,
898 await this.handleReservedRemoteStartTransaction(
900 transactionConnectorId
,
904 // No authorization check required, start transaction
906 this.setRemoteStartTransactionChargingProfile(
908 transactionConnectorId
,
909 commandPayload
.chargingProfile
912 connectorStatus
.transactionRemoteStarted
= true;
915 await chargingStation
.ocppRequestService
.requestHandler
<
916 OCPP16StartTransactionRequest
,
917 OCPP16StartTransactionResponse
918 >(chargingStation
, OCPP16RequestCommand
.START_TRANSACTION
, {
919 connectorId
: transactionConnectorId
,
920 idTag
: commandPayload
.idTag
,
922 ).idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
924 logger
.debug(remoteStartTransactionLogMsg
);
925 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
;
927 return this.notifyRemoteStartTransactionRejected(
929 transactionConnectorId
,
933 return this.notifyRemoteStartTransactionRejected(
935 transactionConnectorId
,
940 private async notifyRemoteStartTransactionRejected(
941 chargingStation
: ChargingStation
,
944 ): Promise
<GenericResponse
> {
946 chargingStation
.getConnectorStatus(connectorId
)?.status !== OCPP16ChargePointStatus
.Available
948 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
951 OCPP16ChargePointStatus
.Available
955 `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id ${connectorId.toString()}, idTag '${idTag}', availability '${
956 chargingStation.getConnectorStatus(connectorId)?.availability
957 }', status '${chargingStation.getConnectorStatus(connectorId)?.status}'`
959 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
;
962 private setRemoteStartTransactionChargingProfile(
963 chargingStation
: ChargingStation
,
965 cp
: OCPP16ChargingProfile
967 if (cp
&& cp
.chargingProfilePurpose
=== OCPP16ChargingProfilePurposeType
.TX_PROFILE
) {
968 OCPP16ServiceUtils
.setChargingProfile(chargingStation
, connectorId
, cp
);
970 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`,
974 } else if (cp
&& cp
.chargingProfilePurpose
!== OCPP16ChargingProfilePurposeType
.TX_PROFILE
) {
976 `${chargingStation.logPrefix()} Not allowed to set ${
977 cp.chargingProfilePurpose
978 } charging profile(s) at remote start transaction`
986 private async handleRequestRemoteStopTransaction(
987 chargingStation
: ChargingStation
,
988 commandPayload
: RemoteStopTransactionRequest
989 ): Promise
<GenericResponse
> {
990 const transactionId
= commandPayload
.transactionId
;
991 const remoteStopTransaction
= async (connectorId
: number): Promise
<GenericResponse
> => {
992 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
995 OCPP16ChargePointStatus
.Finishing
997 const stopResponse
= await chargingStation
.stopTransactionOnConnector(
999 OCPP16StopTransactionReason
.REMOTE
1001 if (stopResponse
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
1002 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
;
1004 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
;
1006 if (chargingStation
.hasEvses
) {
1007 for (const [evseId
, evseStatus
] of chargingStation
.evses
) {
1009 for (const [connectorId
, connectorStatus
] of evseStatus
.connectors
) {
1010 if (connectorStatus
.transactionId
=== transactionId
) {
1011 return remoteStopTransaction(connectorId
);
1017 for (const connectorId
of chargingStation
.connectors
.keys()) {
1020 chargingStation
.getConnectorStatus(connectorId
)?.transactionId
=== transactionId
1022 return remoteStopTransaction(connectorId
);
1027 `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction with id: ${transactionId.toString()}`
1029 return OCPP16Constants
.OCPP_RESPONSE_REJECTED
;
1032 private handleRequestUpdateFirmware(
1033 chargingStation
: ChargingStation
,
1034 commandPayload
: OCPP16UpdateFirmwareRequest
1035 ): OCPP16UpdateFirmwareResponse
{
1037 OCPP16ServiceUtils
.checkFeatureProfile(
1039 OCPP16SupportedFeatureProfiles
.FirmwareManagement
,
1040 OCPP16IncomingRequestCommand
.UPDATE_FIRMWARE
1044 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported`
1046 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
;
1049 !Utils
.isNullOrUndefined(chargingStation
.stationInfo
.firmwareStatus
) &&
1050 chargingStation
.stationInfo
.firmwareStatus
!== OCPP16FirmwareStatus
.Installed
1053 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`
1055 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
;
1057 const retrieveDate
= Utils
.convertToDate(commandPayload
.retrieveDate
);
1058 const now
= Date.now();
1059 if (retrieveDate
?.getTime() <= now
) {
1060 this.runInAsyncScope(
1061 this.updateFirmwareSimulation
.bind(this) as (
1062 this: OCPP16IncomingRequestService
,
1067 ).catch(Constants
.EMPTY_FUNCTION
);
1070 this.updateFirmwareSimulation(chargingStation
).catch(Constants
.EMPTY_FUNCTION
);
1071 }, retrieveDate
?.getTime() - now
);
1073 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
;
1076 private async updateFirmwareSimulation(
1077 chargingStation
: ChargingStation
,
1082 ChargingStationUtils
.checkChargingStation(chargingStation
, chargingStation
.logPrefix()) ===
1087 if (chargingStation
.hasEvses
) {
1088 for (const [evseId
, evseStatus
] of chargingStation
.evses
) {
1090 for (const [connectorId
, connectorStatus
] of evseStatus
.connectors
) {
1091 if (connectorStatus
?.transactionStarted
=== false) {
1092 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1095 OCPP16ChargePointStatus
.Unavailable
1102 for (const connectorId
of chargingStation
.connectors
.keys()) {
1105 chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
=== false
1107 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1110 OCPP16ChargePointStatus
.Unavailable
1115 await chargingStation
.ocppRequestService
.requestHandler
<
1116 OCPP16FirmwareStatusNotificationRequest
,
1117 OCPP16FirmwareStatusNotificationResponse
1118 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1119 status: OCPP16FirmwareStatus
.Downloading
,
1121 chargingStation
.stationInfo
.firmwareStatus
= OCPP16FirmwareStatus
.Downloading
;
1123 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
===
1124 OCPP16FirmwareStatus
.DownloadFailed
1126 await Utils
.sleep(Utils
.getRandomInteger(maxDelay
, minDelay
) * 1000);
1127 await chargingStation
.ocppRequestService
.requestHandler
<
1128 OCPP16FirmwareStatusNotificationRequest
,
1129 OCPP16FirmwareStatusNotificationResponse
1130 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1131 status: chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
,
1133 chargingStation
.stationInfo
.firmwareStatus
=
1134 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
;
1137 await Utils
.sleep(Utils
.getRandomInteger(maxDelay
, minDelay
) * 1000);
1138 await chargingStation
.ocppRequestService
.requestHandler
<
1139 OCPP16FirmwareStatusNotificationRequest
,
1140 OCPP16FirmwareStatusNotificationResponse
1141 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1142 status: OCPP16FirmwareStatus
.Downloaded
,
1144 chargingStation
.stationInfo
.firmwareStatus
= OCPP16FirmwareStatus
.Downloaded
;
1145 let wasTransactionsStarted
= false;
1146 let transactionsStarted
: boolean;
1148 const runningTransactions
= chargingStation
.getNumberOfRunningTransactions();
1149 if (runningTransactions
> 0) {
1150 const waitTime
= 15 * 1000;
1152 `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: ${runningTransactions} transaction(s) in progress, waiting ${
1154 } seconds before continuing firmware update simulation`
1156 await Utils
.sleep(waitTime
);
1157 transactionsStarted
= true;
1158 wasTransactionsStarted
= true;
1160 if (chargingStation
.hasEvses
) {
1161 for (const [evseId
, evseStatus
] of chargingStation
.evses
) {
1163 for (const [connectorId
, connectorStatus
] of evseStatus
.connectors
) {
1164 if (connectorStatus
?.status !== OCPP16ChargePointStatus
.Unavailable
) {
1165 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1168 OCPP16ChargePointStatus
.Unavailable
1175 for (const connectorId
of chargingStation
.connectors
.keys()) {
1178 chargingStation
.getConnectorStatus(connectorId
)?.status !==
1179 OCPP16ChargePointStatus
.Unavailable
1181 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1184 OCPP16ChargePointStatus
.Unavailable
1189 transactionsStarted
= false;
1191 } while (transactionsStarted
);
1192 !wasTransactionsStarted
&&
1193 (await Utils
.sleep(Utils
.getRandomInteger(maxDelay
, minDelay
) * 1000));
1195 ChargingStationUtils
.checkChargingStation(chargingStation
, chargingStation
.logPrefix()) ===
1200 await chargingStation
.ocppRequestService
.requestHandler
<
1201 OCPP16FirmwareStatusNotificationRequest
,
1202 OCPP16FirmwareStatusNotificationResponse
1203 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1204 status: OCPP16FirmwareStatus
.Installing
,
1206 chargingStation
.stationInfo
.firmwareStatus
= OCPP16FirmwareStatus
.Installing
;
1208 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
===
1209 OCPP16FirmwareStatus
.InstallationFailed
1211 await Utils
.sleep(Utils
.getRandomInteger(maxDelay
, minDelay
) * 1000);
1212 await chargingStation
.ocppRequestService
.requestHandler
<
1213 OCPP16FirmwareStatusNotificationRequest
,
1214 OCPP16FirmwareStatusNotificationResponse
1215 >(chargingStation
, OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
, {
1216 status: chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
,
1218 chargingStation
.stationInfo
.firmwareStatus
=
1219 chargingStation
.stationInfo
?.firmwareUpgrade
?.failureStatus
;
1222 if (chargingStation
.stationInfo
?.firmwareUpgrade
?.reset
=== true) {
1223 await Utils
.sleep(Utils
.getRandomInteger(maxDelay
, minDelay
) * 1000);
1224 await chargingStation
.reset(OCPP16StopTransactionReason
.REBOOT
);
1228 private async handleRequestGetDiagnostics(
1229 chargingStation
: ChargingStation
,
1230 commandPayload
: GetDiagnosticsRequest
1231 ): Promise
<GetDiagnosticsResponse
> {
1233 OCPP16ServiceUtils
.checkFeatureProfile(
1235 OCPP16SupportedFeatureProfiles
.FirmwareManagement
,
1236 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
1240 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`
1242 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
;
1244 const uri
= new URL(commandPayload
.location
);
1245 if (uri
.protocol
.startsWith('ftp:')) {
1246 let ftpClient
: Client
;
1249 .readdirSync(path
.resolve(path
.dirname(fileURLToPath(import.meta
.url
)), '../'))
1250 .filter((file
) => file
.endsWith('.log'))
1251 .map((file
) => path
.join('./', file
));
1252 const diagnosticsArchive
= `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`;
1253 tar
.create({ gzip
: true }, logFiles
).pipe(fs
.createWriteStream(diagnosticsArchive
));
1254 ftpClient
= new Client();
1255 const accessResponse
= await ftpClient
.access({
1257 ...(Utils
.isNotEmptyString(uri
.port
) && { port
: Utils
.convertToInt(uri
.port
) }),
1258 ...(Utils
.isNotEmptyString(uri
.username
) && { user
: uri
.username
}),
1259 ...(Utils
.isNotEmptyString(uri
.password
) && { password
: uri
.password
}),
1261 let uploadResponse
: FTPResponse
;
1262 if (accessResponse
.code
=== 220) {
1263 ftpClient
.trackProgress((info
) => {
1265 `${chargingStation.logPrefix()} ${
1267 } bytes transferred from diagnostics archive ${info.name}`
1269 chargingStation
.ocppRequestService
1271 OCPP16DiagnosticsStatusNotificationRequest
,
1272 OCPP16DiagnosticsStatusNotificationResponse
1273 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1274 status: OCPP16DiagnosticsStatus
.Uploading
,
1278 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
1279 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
1285 uploadResponse
= await ftpClient
.uploadFrom(
1287 path
.resolve(path
.dirname(fileURLToPath(import.meta
.url
)), '../'),
1290 `${uri.pathname}${diagnosticsArchive}`
1292 if (uploadResponse
.code
=== 226) {
1293 await chargingStation
.ocppRequestService
.requestHandler
<
1294 OCPP16DiagnosticsStatusNotificationRequest
,
1295 OCPP16DiagnosticsStatusNotificationResponse
1296 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1297 status: OCPP16DiagnosticsStatus
.Uploaded
,
1302 return { fileName
: diagnosticsArchive
};
1304 throw new OCPPError(
1305 ErrorType
.GENERIC_ERROR
,
1306 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1307 uploadResponse?.code && `|${uploadResponse?.code.toString()}
`
1309 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
1312 throw new OCPPError(
1313 ErrorType
.GENERIC_ERROR
,
1314 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1315 uploadResponse?.code && `|${uploadResponse?.code.toString()}
`
1317 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
1320 await chargingStation
.ocppRequestService
.requestHandler
<
1321 OCPP16DiagnosticsStatusNotificationRequest
,
1322 OCPP16DiagnosticsStatusNotificationResponse
1323 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1324 status: OCPP16DiagnosticsStatus
.UploadFailed
,
1329 return this.handleIncomingRequestError(
1331 OCPP16IncomingRequestCommand
.GET_DIAGNOSTICS
,
1333 { errorResponse
: OCPP16Constants
.OCPP_RESPONSE_EMPTY
}
1338 `${chargingStation.logPrefix()} Unsupported protocol ${
1340 } to transfer the diagnostic logs archive`
1342 await chargingStation
.ocppRequestService
.requestHandler
<
1343 OCPP16DiagnosticsStatusNotificationRequest
,
1344 OCPP16DiagnosticsStatusNotificationResponse
1345 >(chargingStation
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
, {
1346 status: OCPP16DiagnosticsStatus
.UploadFailed
,
1348 return OCPP16Constants
.OCPP_RESPONSE_EMPTY
;
1352 private handleRequestTriggerMessage(
1353 chargingStation
: ChargingStation
,
1354 commandPayload
: OCPP16TriggerMessageRequest
1355 ): OCPP16TriggerMessageResponse
{
1357 !OCPP16ServiceUtils
.checkFeatureProfile(
1359 OCPP16SupportedFeatureProfiles
.RemoteTrigger
,
1360 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
1362 !OCPP16ServiceUtils
.isMessageTriggerSupported(
1364 commandPayload
.requestedMessage
1367 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED
;
1370 !OCPP16ServiceUtils
.isConnectorIdValid(
1372 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
1373 commandPayload
.connectorId
1376 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED
;
1379 switch (commandPayload
.requestedMessage
) {
1380 case OCPP16MessageTrigger
.BootNotification
:
1382 chargingStation
.ocppRequestService
1383 .requestHandler
<OCPP16BootNotificationRequest
, OCPP16BootNotificationResponse
>(
1385 OCPP16RequestCommand
.BOOT_NOTIFICATION
,
1386 chargingStation
.bootNotificationRequest
,
1387 { skipBufferingOnError
: true, triggerMessage
: true }
1389 .then((response
) => {
1390 chargingStation
.bootNotificationResponse
= response
;
1392 .catch(Constants
.EMPTY_FUNCTION
);
1393 }, OCPP16Constants
.OCPP_TRIGGER_MESSAGE_DELAY
);
1394 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED
;
1395 case OCPP16MessageTrigger
.Heartbeat
:
1397 chargingStation
.ocppRequestService
1398 .requestHandler
<OCPP16HeartbeatRequest
, OCPP16HeartbeatResponse
>(
1400 OCPP16RequestCommand
.HEARTBEAT
,
1403 triggerMessage
: true,
1406 .catch(Constants
.EMPTY_FUNCTION
);
1407 }, OCPP16Constants
.OCPP_TRIGGER_MESSAGE_DELAY
);
1408 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED
;
1409 case OCPP16MessageTrigger
.StatusNotification
:
1411 if (!Utils
.isNullOrUndefined(commandPayload
?.connectorId
)) {
1412 chargingStation
.ocppRequestService
1413 .requestHandler
<OCPP16StatusNotificationRequest
, OCPP16StatusNotificationResponse
>(
1415 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
1417 connectorId
: commandPayload
.connectorId
,
1418 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
1419 status: chargingStation
.getConnectorStatus(commandPayload
.connectorId
)?.status,
1422 triggerMessage
: true,
1425 .catch(Constants
.EMPTY_FUNCTION
);
1427 // eslint-disable-next-line no-lonely-if
1428 if (chargingStation
.hasEvses
) {
1429 for (const evseStatus
of chargingStation
.evses
.values()) {
1430 for (const [connectorId
, connectorStatus
] of evseStatus
.connectors
) {
1431 chargingStation
.ocppRequestService
1433 OCPP16StatusNotificationRequest
,
1434 OCPP16StatusNotificationResponse
1437 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
1440 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
1441 status: connectorStatus
.status,
1444 triggerMessage
: true,
1447 .catch(Constants
.EMPTY_FUNCTION
);
1451 for (const connectorId
of chargingStation
.connectors
.keys()) {
1452 chargingStation
.ocppRequestService
1454 OCPP16StatusNotificationRequest
,
1455 OCPP16StatusNotificationResponse
1458 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
1461 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
1462 status: chargingStation
.getConnectorStatus(connectorId
)?.status,
1465 triggerMessage
: true,
1468 .catch(Constants
.EMPTY_FUNCTION
);
1472 }, OCPP16Constants
.OCPP_TRIGGER_MESSAGE_DELAY
);
1473 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED
;
1475 return OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED
;
1478 return this.handleIncomingRequestError(
1480 OCPP16IncomingRequestCommand
.TRIGGER_MESSAGE
,
1482 { errorResponse
: OCPP16Constants
.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED
}
1487 private handleRequestDataTransfer(
1488 chargingStation
: ChargingStation
,
1489 commandPayload
: OCPP16DataTransferRequest
1490 ): OCPP16DataTransferResponse
{
1492 if (Object.values(OCPP16DataTransferVendorId
).includes(commandPayload
.vendorId
)) {
1494 status: OCPP16DataTransferStatus
.ACCEPTED
,
1498 status: OCPP16DataTransferStatus
.UNKNOWN_VENDOR_ID
,
1501 return this.handleIncomingRequestError(
1503 OCPP16IncomingRequestCommand
.DATA_TRANSFER
,
1505 { errorResponse
: OCPP16Constants
.OCPP_DATA_TRANSFER_RESPONSE_REJECTED
}
1510 private async handleRequestReserveNow(
1511 chargingStation
: ChargingStation
,
1512 commandPayload
: OCPP16ReserveNowRequest
1513 ): Promise
<OCPP16ReserveNowResponse
> {
1514 const { reservationId
, idTag
, connectorId
} = commandPayload
;
1515 let connector
: Map
<number, ConnectorStatus
>;
1516 let response
: OCPP16ReserveNowResponse
;
1519 !chargingStation
.supportsReservations() &&
1520 chargingStation
.isConnectorAvailable(connectorId
)
1522 return OCPPConstants
.OCPP_RESERVATION_RESPONSE_REJECTED
;
1524 if (connectorId
=== 0 && !chargingStation
.supportsReservationsOnConnectorId0()) {
1525 return OCPPConstants
.OCPP_RESERVATION_RESPONSE_REJECTED
;
1527 if (!(await this.isAuthorized(chargingStation
, connectorId
, commandPayload
.idTag
))) {
1528 return OCPPConstants
.OCPP_RESERVATION_RESPONSE_REJECTED
;
1530 switch (chargingStation
.getConnectorStatus(connectorId
).status) {
1531 case ConnectorStatusEnum
.Faulted
:
1532 response
= OCPPConstants
.OCPP_RESERVATION_RESPONSE_FAULTED
;
1534 case ConnectorStatusEnum
.Occupied
:
1535 response
= OCPPConstants
.OCPP_RESERVATION_RESPONSE_OCCUPIED
;
1537 case ConnectorStatusEnum
.Unavailable
:
1538 response
= OCPPConstants
.OCPP_RESERVATION_RESPONSE_UNAVAILABLE
;
1540 case ConnectorStatusEnum
.Reserved
:
1541 if (Utils
.isUndefined(chargingStation
.getReservation(commandPayload
.reservationId
))) {
1542 response
= OCPPConstants
.OCPP_RESERVATION_RESPONSE_OCCUPIED
;
1545 // eslint-disable-next-line no-fallthrough
1548 `${chargingStation.logPrefix()} on connector ${connectorId} is now reserved for ${
1549 commandPayload.idTag
1552 chargingStation
.getConnectorStatus(connectorId
).status = ConnectorStatusEnum
.Reserved
;
1553 chargingStation
.addReservation({ ...commandPayload
});
1554 await chargingStation
.ocppRequestService
1555 .requestHandler
<OCPP16StatusNotificationRequest
, OCPP16StatusNotificationResponse
>(
1557 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
1560 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
1561 status: chargingStation
.getConnectorStatus(connectorId
).status,
1564 triggerMessage
: true,
1567 .catch(Constants
.EMPTY_FUNCTION
);
1568 response
= OCPPConstants
.OCPP_RESERVATION_RESPONSE_ACCEPTED
;
1573 return this.handleIncomingRequestError(
1575 OCPP16IncomingRequestCommand
.RESERVE_NOW
,
1577 { errorResponse
: OCPP16Constants
.OCPP_RESERVATION_RESPONSE_FAULTED
}
1582 private async handleRequestCancelReservation(
1583 chargingStation
: ChargingStation
,
1584 commandPayload
: OCPP16CancelReservationRequest
1585 ): Promise
<OCPP16CancelReservationResponse
> {
1587 const reservationId
= commandPayload
.reservationId
;
1588 const [exists
, reservation
] = chargingStation
.doesReservationExist(reservationId
);
1591 `${chargingStation.logPrefix()} Reservation with ID ${reservationId} does not exist on charging station`
1593 return OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
;
1595 chargingStation
.getConnectorStatus(reservation
.connectorId
).status =
1596 ConnectorStatusEnum
.Available
;
1597 chargingStation
.removeReservation(reservation
.reservationId
);
1598 await chargingStation
.ocppRequestService
1599 .requestHandler
<OCPP16StatusNotificationRequest
, OCPP16StatusNotificationResponse
>(
1601 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
1603 connectorId
: reservation
.connectorId
,
1604 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
1605 status: chargingStation
.getConnectorStatus(reservation
.connectorId
).status,
1608 triggerMessage
: true,
1611 .catch(Constants
.EMPTY_FUNCTION
);
1612 return OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED
;
1614 return this.handleIncomingRequestError(
1616 OCPP16IncomingRequestCommand
.CANCEL_RESERVATION
,
1618 { errorResponse
: OCPP16Constants
.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
}
1624 * Check for authorized access on a connector with given ConnectorId and idTag for the user
1625 * @param {ChargingStation} chargingStation - Charging Station working on incoming request
1626 * @param {number} ConnectorId - Identifier of the connector at the charging station
1627 * @param {string} idTag - Identifier of the user
1628 * @param {string} parentIdTag - Identifier for a group of idTags, which is optional
1629 * @returns {Promise<boolean>} - 'true' if user is authorized, 'false' otherwise
1631 private async isAuthorized(
1632 chargingStation
: ChargingStation
,
1633 connectorId
: number,
1635 parentIdTag
?: string
1636 ): Promise
<boolean> {
1637 let authorized
= false;
1638 const connectorStatus
= chargingStation
.getConnectorStatus(connectorId
);
1640 chargingStation
.getLocalAuthListEnabled() === true &&
1641 chargingStation
.hasIdTags() === true &&
1642 Utils
.isNotEmptyString(
1643 chargingStation
.idTagsCache
1644 .getIdTags(ChargingStationUtils
.getIdTagsFile(chargingStation
.stationInfo
))
1645 ?.find((tag
) => tag
=== idTag
)
1648 connectorStatus
.localAuthorizeIdTag
= idTag
;
1649 connectorStatus
.idTagLocalAuthorized
= true;
1651 } else if (chargingStation
.getMustAuthorizeAtRemoteStart() === true) {
1652 connectorStatus
.authorizeIdTag
= idTag
;
1653 const authorizeResponse
: OCPP16AuthorizeResponse
=
1654 await chargingStation
.ocppRequestService
.requestHandler
<
1655 OCPP16AuthorizeRequest
,
1656 OCPP16AuthorizeResponse
1657 >(chargingStation
, OCPP16RequestCommand
.AUTHORIZE
, {
1660 if (authorizeResponse
?.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
1665 `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
1671 private async handleReservedRemoteStartTransaction(
1672 chargingStation
: ChargingStation
,
1673 connectorId
: number,
1674 commandPayload
: RemoteStartTransactionRequest
1675 ): Promise
<GenericResponse
> {
1676 const reservation
= chargingStation
.getReservationByConnectorId(connectorId
);
1678 !Utils
.isUndefined(reservation
) &&
1679 (await this.isAuthorized(chargingStation
, connectorId
, commandPayload
.idTag
)) &&
1680 reservation
.idTag
=== commandPayload
.idTag
1682 const remoteStartTransactionLogMsg
= `${chargingStation.logPrefix()} Transaction remotely STARTED on ${
1683 chargingStation.stationInfo.chargingStationId
1684 }#${connectorId.toString()} for idTag '${commandPayload.idTag}'`;
1685 await OCPP16ServiceUtils
.sendAndSetConnectorStatus(
1688 OCPP16ChargePointStatus
.Preparing
1691 this.setRemoteStartTransactionChargingProfile(
1694 commandPayload
.chargingProfile
1697 chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
= true;
1700 await chargingStation
.ocppRequestService
.requestHandler
<
1701 OCPP16StartTransactionRequest
,
1702 OCPP16StartTransactionResponse
1703 >(chargingStation
, OCPP16RequestCommand
.START_TRANSACTION
, {
1704 connectorId
: connectorId
,
1705 idTag
: commandPayload
.idTag
,
1706 reservationId
: reservation
.reservationId
,
1708 ).idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
1710 logger
.debug(remoteStartTransactionLogMsg
);
1711 return OCPP16Constants
.OCPP_RESPONSE_ACCEPTED
;