1 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
3 import type { JSONSchemaType
} from
'ajv';
5 import { OCPP20ServiceUtils
} from
'./OCPP20ServiceUtils';
6 import { OCPPError
} from
'../../../exception';
11 OCPP16StandardParametersKey
,
12 type OCPP20BootNotificationResponse
,
13 type OCPP20ClearCacheResponse
,
14 type OCPP20HeartbeatResponse
,
15 OCPP20IncomingRequestCommand
,
17 type OCPP20StatusNotificationResponse
,
19 RegistrationStatusEnumType
,
21 } from
'../../../types';
22 import { logger
} from
'../../../utils/Logger';
23 import type { ChargingStation
} from
'../../ChargingStation';
24 import { ChargingStationConfigurationUtils
} from
'../../ChargingStationConfigurationUtils';
25 import { OCPPResponseService
} from
'../OCPPResponseService';
27 const moduleName
= 'OCPP20ResponseService';
29 export class OCPP20ResponseService
extends OCPPResponseService
{
30 public jsonIncomingRequestResponseSchemas
: Map
<
31 OCPP20IncomingRequestCommand
,
32 JSONSchemaType
<JsonObject
>
35 private responseHandlers
: Map
<OCPP20RequestCommand
, ResponseHandler
>;
36 private jsonSchemas
: Map
<OCPP20RequestCommand
, JSONSchemaType
<JsonObject
>>;
38 public constructor() {
39 if (new.target
?.name
=== moduleName
) {
40 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
42 super(OCPPVersion
.VERSION_20
);
43 this.responseHandlers
= new Map
<OCPP20RequestCommand
, ResponseHandler
>([
44 [OCPP20RequestCommand
.BOOT_NOTIFICATION
, this.handleResponseBootNotification
.bind(this)],
45 [OCPP20RequestCommand
.HEARTBEAT
, this.emptyResponseHandler
.bind(this)],
46 [OCPP20RequestCommand
.STATUS_NOTIFICATION
, this.emptyResponseHandler
.bind(this)],
48 this.jsonSchemas
= new Map
<OCPP20RequestCommand
, JSONSchemaType
<JsonObject
>>([
50 OCPP20RequestCommand
.BOOT_NOTIFICATION
,
51 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20BootNotificationResponse
>(
52 '../../../assets/json-schemas/ocpp/2.0/BootNotificationResponse.json',
58 OCPP20RequestCommand
.HEARTBEAT
,
59 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20HeartbeatResponse
>(
60 '../../../assets/json-schemas/ocpp/2.0/HeartbeatResponse.json',
66 OCPP20RequestCommand
.STATUS_NOTIFICATION
,
67 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20StatusNotificationResponse
>(
68 '../../../assets/json-schemas/ocpp/2.0/StatusNotificationResponse.json',
74 this.jsonIncomingRequestResponseSchemas
= new Map([
76 OCPP20IncomingRequestCommand
.CLEAR_CACHE
,
77 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20ClearCacheResponse
>(
78 '../../../assets/json-schemas/ocpp/2.0/ClearCacheResponse.json',
84 this.validatePayload
.bind(this);
87 public async responseHandler(
88 chargingStation
: ChargingStation
,
89 commandName
: OCPP20RequestCommand
,
91 requestPayload
: JsonType
94 chargingStation
.isRegistered() === true ||
95 commandName
=== OCPP20RequestCommand
.BOOT_NOTIFICATION
98 this.responseHandlers
.has(commandName
) === true &&
99 OCPP20ServiceUtils
.isRequestCommandSupported(chargingStation
, commandName
) === true
102 this.validatePayload(chargingStation
, commandName
, payload
);
103 await this.responseHandlers
.get(commandName
)(chargingStation
, payload
, requestPayload
);
106 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
114 ErrorType
.NOT_IMPLEMENTED
,
115 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
126 ErrorType
.SECURITY_ERROR
,
127 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
131 )} while the charging station is not registered on the central server.`,
138 private validatePayload(
139 chargingStation
: ChargingStation
,
140 commandName
: OCPP20RequestCommand
,
143 if (this.jsonSchemas
.has(commandName
) === true) {
144 return this.validateResponsePayload(
147 this.jsonSchemas
.get(commandName
),
152 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
157 private handleResponseBootNotification(
158 chargingStation
: ChargingStation
,
159 payload
: OCPP20BootNotificationResponse
161 if (payload
.status === RegistrationStatusEnumType
.ACCEPTED
) {
162 ChargingStationConfigurationUtils
.addConfigurationKey(
164 OCPP16StandardParametersKey
.HeartbeatInterval
,
165 payload
.interval
.toString(),
167 { overwrite
: true, save
: true }
169 ChargingStationConfigurationUtils
.addConfigurationKey(
171 OCPP16StandardParametersKey
.HeartBeatInterval
,
172 payload
.interval
.toString(),
174 { overwrite
: true, save
: true }
176 chargingStation
.heartbeatSetInterval
177 ? chargingStation
.restartHeartbeat()
178 : chargingStation
.startHeartbeat();
180 if (Object.values(RegistrationStatusEnumType
).includes(payload
.status)) {
181 const logMsg
= `${chargingStation.logPrefix()} Charging station in '${
183 }' state on the central server`;
184 payload
.status === RegistrationStatusEnumType
.REJECTED
185 ? logger
.warn(logMsg
)
186 : logger
.info(logMsg
);
189 `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`,