1 // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
3 import type { ValidateFunction
} from
'ajv'
5 import { addConfigurationKey
, type ChargingStation
} from
'../../../charging-station/index.js'
6 import { OCPPError
} from
'../../../exception/index.js'
11 type OCPP20BootNotificationResponse
,
12 type OCPP20ClearCacheResponse
,
13 type OCPP20HeartbeatResponse
,
14 OCPP20IncomingRequestCommand
,
15 OCPP20OptionalVariableName
,
17 type OCPP20StatusNotificationResponse
,
19 RegistrationStatusEnumType
,
21 } from
'../../../types/index.js'
22 import { isAsyncFunction
, logger
} from
'../../../utils/index.js'
23 import { OCPPResponseService
} from
'../OCPPResponseService.js'
24 import { OCPP20ServiceUtils
} from
'./OCPP20ServiceUtils.js'
26 const moduleName
= 'OCPP20ResponseService'
28 export class OCPP20ResponseService
extends OCPPResponseService
{
29 protected payloadValidateFunctions
: Map
<OCPP20RequestCommand
, ValidateFunction
<JsonType
>>
30 private readonly responseHandlers
: Map
<OCPP20RequestCommand
, ResponseHandler
>
31 public incomingRequestResponsePayloadValidateFunctions
: Map
<
32 OCPP20IncomingRequestCommand
,
33 ValidateFunction
<JsonType
>
36 public constructor () {
37 // if (new.target.name === moduleName) {
38 // throw new TypeError(`Cannot construct ${new.target.name} instances directly`)
40 super(OCPPVersion
.VERSION_201
)
41 this.responseHandlers
= new Map
<OCPP20RequestCommand
, ResponseHandler
>([
43 OCPP20RequestCommand
.BOOT_NOTIFICATION
,
44 this.handleResponseBootNotification
.bind(this) as ResponseHandler
,
46 [OCPP20RequestCommand
.HEARTBEAT
, this.emptyResponseHandler
],
47 [OCPP20RequestCommand
.STATUS_NOTIFICATION
, this.emptyResponseHandler
],
49 this.payloadValidateFunctions
= new Map
<OCPP20RequestCommand
, ValidateFunction
<JsonType
>>([
51 OCPP20RequestCommand
.BOOT_NOTIFICATION
,
53 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20BootNotificationResponse
>(
54 'assets/json-schemas/ocpp/2.0/BootNotificationResponse.json',
61 OCPP20RequestCommand
.HEARTBEAT
,
63 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20HeartbeatResponse
>(
64 'assets/json-schemas/ocpp/2.0/HeartbeatResponse.json',
71 OCPP20RequestCommand
.STATUS_NOTIFICATION
,
73 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20StatusNotificationResponse
>(
74 'assets/json-schemas/ocpp/2.0/StatusNotificationResponse.json',
81 this.incomingRequestResponsePayloadValidateFunctions
= new Map
<
82 OCPP20IncomingRequestCommand
,
83 ValidateFunction
<JsonType
>
86 OCPP20IncomingRequestCommand
.CLEAR_CACHE
,
87 this.ajvIncomingRequest
.compile(
88 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20ClearCacheResponse
>(
89 'assets/json-schemas/ocpp/2.0/ClearCacheResponse.json',
96 this.validatePayload
= this.validatePayload
.bind(this)
99 private handleResponseBootNotification (
100 chargingStation
: ChargingStation
,
101 payload
: OCPP20BootNotificationResponse
103 if (Object.values(RegistrationStatusEnumType
).includes(payload
.status)) {
104 chargingStation
.bootNotificationResponse
= payload
105 if (chargingStation
.isRegistered()) {
106 chargingStation
.emit(ChargingStationEvents
.registered
)
107 if (chargingStation
.inAcceptedState()) {
110 OCPP20OptionalVariableName
.HeartbeatInterval
,
111 payload
.interval
.toString(),
113 { overwrite
: true, save
: true }
115 chargingStation
.emit(ChargingStationEvents
.accepted
)
117 } else if (chargingStation
.inRejectedState()) {
118 chargingStation
.emit(ChargingStationEvents
.rejected
)
120 const logMsg
= `${chargingStation.logPrefix()} Charging station in '${
122 }' state on the central server`
123 payload
.status === RegistrationStatusEnumType
.REJECTED
124 ? logger
.warn(logMsg
)
125 : logger
.info(logMsg
)
127 delete chargingStation
.bootNotificationResponse
129 `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`,
135 private validatePayload (
136 chargingStation
: ChargingStation
,
137 commandName
: OCPP20RequestCommand
,
140 if (this.payloadValidateFunctions
.has(commandName
)) {
141 return this.validateResponsePayload(chargingStation
, commandName
, payload
)
144 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema validation function found for command '${commandName}' PDU validation`
149 // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
150 public async responseHandler
<ReqType
extends JsonType
, ResType
extends JsonType
>(
151 chargingStation
: ChargingStation
,
152 commandName
: OCPP20RequestCommand
,
154 requestPayload
: ReqType
156 if (chargingStation
.isRegistered() || commandName
=== OCPP20RequestCommand
.BOOT_NOTIFICATION
) {
158 this.responseHandlers
.has(commandName
) &&
159 OCPP20ServiceUtils
.isRequestCommandSupported(chargingStation
, commandName
)
162 this.validatePayload(chargingStation
, commandName
, payload
)
163 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
164 const responseHandler
= this.responseHandlers
.get(commandName
)!
165 if (isAsyncFunction(responseHandler
)) {
166 await responseHandler(chargingStation
, payload
, requestPayload
)
170 chargingStation
: ChargingStation
,
172 requestPayload
?: JsonType
174 )(chargingStation
, payload
, requestPayload
)
178 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
186 ErrorType
.NOT_IMPLEMENTED
,
187 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
198 ErrorType
.SECURITY_ERROR
,
199 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
203 )} while the charging station is not registered on the central server`,