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 public incomingRequestResponsePayloadValidateFunctions
: Map
<
30 OCPP20IncomingRequestCommand
,
31 ValidateFunction
<JsonType
>
34 protected payloadValidateFunctions
: Map
<OCPP20RequestCommand
, ValidateFunction
<JsonType
>>
35 private readonly responseHandlers
: Map
<OCPP20RequestCommand
, ResponseHandler
>
37 public constructor () {
38 // if (new.target.name === moduleName) {
39 // throw new TypeError(`Cannot construct ${new.target.name} instances directly`)
41 super(OCPPVersion
.VERSION_201
)
42 this.responseHandlers
= new Map
<OCPP20RequestCommand
, ResponseHandler
>([
44 OCPP20RequestCommand
.BOOT_NOTIFICATION
,
45 this.handleResponseBootNotification
.bind(this) as ResponseHandler
47 [OCPP20RequestCommand
.HEARTBEAT
, this.emptyResponseHandler
],
48 [OCPP20RequestCommand
.STATUS_NOTIFICATION
, this.emptyResponseHandler
]
50 this.payloadValidateFunctions
= new Map
<OCPP20RequestCommand
, ValidateFunction
<JsonType
>>([
52 OCPP20RequestCommand
.BOOT_NOTIFICATION
,
55 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20BootNotificationResponse
>(
56 'assets/json-schemas/ocpp/2.0/BootNotificationResponse.json',
64 OCPP20RequestCommand
.HEARTBEAT
,
67 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20HeartbeatResponse
>(
68 'assets/json-schemas/ocpp/2.0/HeartbeatResponse.json',
76 OCPP20RequestCommand
.STATUS_NOTIFICATION
,
79 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20StatusNotificationResponse
>(
80 'assets/json-schemas/ocpp/2.0/StatusNotificationResponse.json',
88 this.incomingRequestResponsePayloadValidateFunctions
= new Map
<
89 OCPP20IncomingRequestCommand
,
90 ValidateFunction
<JsonType
>
93 OCPP20IncomingRequestCommand
.CLEAR_CACHE
,
94 this.ajvIncomingRequest
96 OCPP20ServiceUtils
.parseJsonSchemaFile
<OCPP20ClearCacheResponse
>(
97 'assets/json-schemas/ocpp/2.0/ClearCacheResponse.json',
105 this.validatePayload
= this.validatePayload
.bind(this)
108 public async responseHandler
<ReqType
extends JsonType
, ResType
extends JsonType
>(
109 chargingStation
: ChargingStation
,
110 commandName
: OCPP20RequestCommand
,
112 requestPayload
: ReqType
114 if (chargingStation
.isRegistered() || commandName
=== OCPP20RequestCommand
.BOOT_NOTIFICATION
) {
116 this.responseHandlers
.has(commandName
) &&
117 OCPP20ServiceUtils
.isRequestCommandSupported(chargingStation
, commandName
)
120 this.validatePayload(chargingStation
, commandName
, payload
)
121 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
122 const responseHandler
= this.responseHandlers
.get(commandName
)!
123 if (isAsyncFunction(responseHandler
)) {
124 await responseHandler(chargingStation
, payload
, requestPayload
)
128 chargingStation
: ChargingStation
,
130 requestPayload
?: JsonType
132 )(chargingStation
, payload
, requestPayload
)
136 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
144 ErrorType
.NOT_IMPLEMENTED
,
145 `'${commandName}' is not implemented to handle response PDU ${JSON.stringify(
156 ErrorType
.SECURITY_ERROR
,
157 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
161 )} while the charging station is not registered on the central server`,
168 private validatePayload (
169 chargingStation
: ChargingStation
,
170 commandName
: OCPP20RequestCommand
,
173 if (this.payloadValidateFunctions
.has(commandName
)) {
174 return this.validateResponsePayload(chargingStation
, commandName
, payload
)
177 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema validation function found for command '${commandName}' PDU validation`
182 private handleResponseBootNotification (
183 chargingStation
: ChargingStation
,
184 payload
: OCPP20BootNotificationResponse
186 if (Object.values(RegistrationStatusEnumType
).includes(payload
.status)) {
187 chargingStation
.bootNotificationResponse
= payload
188 if (chargingStation
.isRegistered()) {
189 chargingStation
.emit(ChargingStationEvents
.registered
)
190 if (chargingStation
.inAcceptedState()) {
193 OCPP20OptionalVariableName
.HeartbeatInterval
,
194 payload
.interval
.toString(),
196 { overwrite
: true, save
: true }
198 chargingStation
.emit(ChargingStationEvents
.accepted
)
200 } else if (chargingStation
.inRejectedState()) {
201 chargingStation
.emit(ChargingStationEvents
.rejected
)
203 const logMsg
= `${chargingStation.logPrefix()} Charging station in '${
205 }' state on the central server`
206 payload
.status === RegistrationStatusEnumType
.REJECTED
207 ? logger
.warn(logMsg
)
208 : logger
.info(logMsg
)
210 delete chargingStation
.bootNotificationResponse
212 `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`,