1 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
3 import type { JSONSchemaType
} from
'ajv';
5 import { OCPP20ServiceUtils
} from
'./OCPP20ServiceUtils';
6 import { type ChargingStation
, addConfigurationKey
} from
'../../../charging-station';
7 import { OCPPError
} from
'../../../exception';
12 type OCPP20BootNotificationResponse
,
13 type OCPP20ClearCacheResponse
,
14 type OCPP20HeartbeatResponse
,
15 OCPP20IncomingRequestCommand
,
16 OCPP20OptionalVariableName
,
18 type OCPP20StatusNotificationResponse
,
20 RegistrationStatusEnumType
,
22 } from
'../../../types';
23 import { logger
} from
'../../../utils';
24 import { OCPPResponseService
} from
'../OCPPResponseService';
26 const moduleName
= 'OCPP20ResponseService';
28 export class OCPP20ResponseService
extends OCPPResponseService
{
29 public jsonIncomingRequestResponseSchemas
: Map
<
30 OCPP20IncomingRequestCommand
,
31 JSONSchemaType
<JsonObject
>
34 private responseHandlers
: Map
<OCPP20RequestCommand
, ResponseHandler
>;
35 private jsonSchemas
: Map
<OCPP20RequestCommand
, JSONSchemaType
<JsonObject
>>;
37 public constructor() {
38 // if (new.target?.name === moduleName) {
39 // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
41 super(OCPPVersion
.VERSION_20
);
42 this.responseHandlers
= new Map
<OCPP20RequestCommand
, ResponseHandler
>([
44 OCPP20RequestCommand
.BOOT_NOTIFICATION
,
45 this.handleResponseBootNotification
.bind(this) as ResponseHandler
,
47 [OCPP20RequestCommand
.HEARTBEAT
, this.emptyResponseHandler
.bind(this) as ResponseHandler
],
49 OCPP20RequestCommand
.STATUS_NOTIFICATION
,
50 this.emptyResponseHandler
.bind(this) as ResponseHandler
,
53 this.jsonSchemas
= new Map
<OCPP20RequestCommand
, JSONSchemaType
<JsonObject
>>([
55 OCPP20RequestCommand
.BOOT_NOTIFICATION
,
56 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20BootNotificationResponse
>(
57 'assets/json-schemas/ocpp/2.0/BootNotificationResponse.json',
63 OCPP20RequestCommand
.HEARTBEAT
,
64 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20HeartbeatResponse
>(
65 'assets/json-schemas/ocpp/2.0/HeartbeatResponse.json',
71 OCPP20RequestCommand
.STATUS_NOTIFICATION
,
72 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20StatusNotificationResponse
>(
73 'assets/json-schemas/ocpp/2.0/StatusNotificationResponse.json',
79 this.jsonIncomingRequestResponseSchemas
= new Map([
81 OCPP20IncomingRequestCommand
.CLEAR_CACHE
,
82 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20ClearCacheResponse
>(
83 'assets/json-schemas/ocpp/2.0/ClearCacheResponse.json',
89 this.validatePayload
= this.validatePayload
.bind(this) as (
90 chargingStation
: ChargingStation
,
91 commandName
: OCPP20RequestCommand
,
96 public async responseHandler
<ReqType
extends JsonType
, ResType
extends JsonType
>(
97 chargingStation
: ChargingStation
,
98 commandName
: OCPP20RequestCommand
,
100 requestPayload
: ReqType
,
103 chargingStation
.isRegistered() === true ||
104 commandName
=== OCPP20RequestCommand
.BOOT_NOTIFICATION
107 this.responseHandlers
.has(commandName
) === true &&
108 OCPP20ServiceUtils
.isRequestCommandSupported(chargingStation
, commandName
) === true
111 this.validatePayload(chargingStation
, commandName
, payload
);
112 await this.responseHandlers
.get(commandName
)!(chargingStation
, payload
, requestPayload
);
115 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
123 ErrorType
.NOT_IMPLEMENTED
,
124 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
135 ErrorType
.SECURITY_ERROR
,
136 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
140 )} while the charging station is not registered on the central server.`,
147 private validatePayload(
148 chargingStation
: ChargingStation
,
149 commandName
: OCPP20RequestCommand
,
152 if (this.jsonSchemas
.has(commandName
) === true) {
153 return this.validateResponsePayload(
156 this.jsonSchemas
.get(commandName
)!,
161 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`,
166 private handleResponseBootNotification(
167 chargingStation
: ChargingStation
,
168 payload
: OCPP20BootNotificationResponse
,
170 if (payload
.status === RegistrationStatusEnumType
.ACCEPTED
) {
173 OCPP20OptionalVariableName
.HeartbeatInterval
,
174 payload
.interval
.toString(),
176 { overwrite
: true, save
: true },
178 OCPP20ServiceUtils
.startHeartbeatInterval(chargingStation
, payload
.interval
);
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`,