1 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
3 import type { JSONSchemaType
} from
'ajv';
5 import { type ChargingStation
, ChargingStationConfigurationUtils
} from
'../../../charging-station';
6 import { OCPPError
} from
'../../../exception';
11 type OCPP20BootNotificationResponse
,
12 type OCPP20ClearCacheResponse
,
13 type OCPP20HeartbeatResponse
,
14 OCPP20IncomingRequestCommand
,
15 OCPP20OptionalVariableName
,
17 type OCPP20StatusNotificationResponse
,
19 RegistrationStatusEnumType
,
21 } from
'../../../types';
22 import { logger
} from
'../../../utils';
23 import { OCPP20ServiceUtils
, OCPPResponseService
} from
'../internal';
25 const moduleName
= 'OCPP20ResponseService';
27 export class OCPP20ResponseService
extends OCPPResponseService
{
28 public jsonIncomingRequestResponseSchemas
: Map
<
29 OCPP20IncomingRequestCommand
,
30 JSONSchemaType
<JsonObject
>
33 private responseHandlers
: Map
<OCPP20RequestCommand
, ResponseHandler
>;
34 private jsonSchemas
: Map
<OCPP20RequestCommand
, JSONSchemaType
<JsonObject
>>;
36 public constructor() {
37 // if (new.target?.name === moduleName) {
38 // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
40 super(OCPPVersion
.VERSION_20
);
41 this.responseHandlers
= new Map
<OCPP20RequestCommand
, ResponseHandler
>([
42 [OCPP20RequestCommand
.BOOT_NOTIFICATION
, this.handleResponseBootNotification
.bind(this)],
43 [OCPP20RequestCommand
.HEARTBEAT
, this.emptyResponseHandler
.bind(this)],
44 [OCPP20RequestCommand
.STATUS_NOTIFICATION
, this.emptyResponseHandler
.bind(this)],
46 this.jsonSchemas
= new Map
<OCPP20RequestCommand
, JSONSchemaType
<JsonObject
>>([
48 OCPP20RequestCommand
.BOOT_NOTIFICATION
,
49 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20BootNotificationResponse
>(
50 '../../../assets/json-schemas/ocpp/2.0/BootNotificationResponse.json',
56 OCPP20RequestCommand
.HEARTBEAT
,
57 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20HeartbeatResponse
>(
58 '../../../assets/json-schemas/ocpp/2.0/HeartbeatResponse.json',
64 OCPP20RequestCommand
.STATUS_NOTIFICATION
,
65 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20StatusNotificationResponse
>(
66 '../../../assets/json-schemas/ocpp/2.0/StatusNotificationResponse.json',
72 this.jsonIncomingRequestResponseSchemas
= new Map([
74 OCPP20IncomingRequestCommand
.CLEAR_CACHE
,
75 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20ClearCacheResponse
>(
76 '../../../assets/json-schemas/ocpp/2.0/ClearCacheResponse.json',
82 this.validatePayload
= this.validatePayload
.bind(this) as (
83 chargingStation
: ChargingStation
,
84 commandName
: OCPP20RequestCommand
,
89 public async responseHandler(
90 chargingStation
: ChargingStation
,
91 commandName
: OCPP20RequestCommand
,
93 requestPayload
: JsonType
96 chargingStation
.isRegistered() === true ||
97 commandName
=== OCPP20RequestCommand
.BOOT_NOTIFICATION
100 this.responseHandlers
.has(commandName
) === true &&
101 OCPP20ServiceUtils
.isRequestCommandSupported(chargingStation
, commandName
) === true
104 this.validatePayload(chargingStation
, commandName
, payload
);
105 await this.responseHandlers
.get(commandName
)(chargingStation
, payload
, requestPayload
);
108 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
116 ErrorType
.NOT_IMPLEMENTED
,
117 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
128 ErrorType
.SECURITY_ERROR
,
129 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
133 )} while the charging station is not registered on the central server.`,
140 private validatePayload(
141 chargingStation
: ChargingStation
,
142 commandName
: OCPP20RequestCommand
,
145 if (this.jsonSchemas
.has(commandName
) === true) {
146 return this.validateResponsePayload(
149 this.jsonSchemas
.get(commandName
),
154 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
159 private handleResponseBootNotification(
160 chargingStation
: ChargingStation
,
161 payload
: OCPP20BootNotificationResponse
163 if (payload
.status === RegistrationStatusEnumType
.ACCEPTED
) {
164 ChargingStationConfigurationUtils
.addConfigurationKey(
166 OCPP20OptionalVariableName
.HeartbeatInterval
,
167 payload
.interval
.toString(),
169 { overwrite
: true, save
: true }
171 OCPP20ServiceUtils
.startHeartbeatInterval(chargingStation
, payload
.interval
);
173 if (Object.values(RegistrationStatusEnumType
).includes(payload
.status)) {
174 const logMsg
= `${chargingStation.logPrefix()} Charging station in '${
176 }' state on the central server`;
177 payload
.status === RegistrationStatusEnumType
.REJECTED
178 ? logger
.warn(logMsg
)
179 : logger
.info(logMsg
);
182 `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`,