refactor: cleanup log messages
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 2.0 / OCPP20ResponseService.ts
CommitLineData
a19b897d 1// Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
953d6b02 2
24d15716 3import type { ValidateFunction } from 'ajv'
953d6b02 4
66a7748d
JB
5import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
6import { type ChargingStation, addConfigurationKey } from '../../../charging-station/index.js'
7import { OCPPError } from '../../../exception/index.js'
b3fc3ff5 8import {
268a74bb 9 ErrorType,
268a74bb 10 type JsonType,
268a74bb
JB
11 type OCPP20BootNotificationResponse,
12 type OCPP20ClearCacheResponse,
13 type OCPP20HeartbeatResponse,
b3fc3ff5 14 OCPP20IncomingRequestCommand,
857d8dd9 15 OCPP20OptionalVariableName,
b3fc3ff5 16 OCPP20RequestCommand,
268a74bb
JB
17 type OCPP20StatusNotificationResponse,
18 OCPPVersion,
19 RegistrationStatusEnumType,
66a7748d
JB
20 type ResponseHandler
21} from '../../../types/index.js'
bcf95df1 22import { isAsyncFunction, logger } from '../../../utils/index.js'
66a7748d 23import { OCPPResponseService } from '../OCPPResponseService.js'
953d6b02 24
66a7748d 25const moduleName = 'OCPP20ResponseService'
953d6b02 26
268a74bb 27export class OCPP20ResponseService extends OCPPResponseService {
d5490a13 28 public incomingRequestResponsePayloadValidateFunctions: Map<
66a7748d 29 OCPP20IncomingRequestCommand,
24d15716 30 ValidateFunction<JsonType>
66a7748d 31 >
b3fc3ff5 32
d5490a13 33 protected payloadValidateFunctions: Map<OCPP20RequestCommand, ValidateFunction<JsonType>>
66a7748d 34 private readonly responseHandlers: Map<OCPP20RequestCommand, ResponseHandler>
953d6b02 35
66a7748d 36 public constructor () {
5199f9fd
JB
37 // if (new.target.name === moduleName) {
38 // throw new TypeError(`Cannot construct ${new.target.name} instances directly`)
b768993d 39 // }
66a7748d 40 super(OCPPVersion.VERSION_20)
d270cc87 41 this.responseHandlers = new Map<OCPP20RequestCommand, ResponseHandler>([
a37fc6dc
JB
42 [
43 OCPP20RequestCommand.BOOT_NOTIFICATION,
66a7748d 44 this.handleResponseBootNotification.bind(this) as ResponseHandler
a37fc6dc 45 ],
65b5177e
JB
46 [OCPP20RequestCommand.HEARTBEAT, this.emptyResponseHandler],
47 [OCPP20RequestCommand.STATUS_NOTIFICATION, this.emptyResponseHandler]
66a7748d 48 ])
d5490a13 49 this.payloadValidateFunctions = new Map<OCPP20RequestCommand, ValidateFunction<JsonType>>([
d270cc87
JB
50 [
51 OCPP20RequestCommand.BOOT_NOTIFICATION,
24d15716
JB
52 this.ajv
53 .compile(
54 OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20BootNotificationResponse>(
55 'assets/json-schemas/ocpp/2.0/BootNotificationResponse.json',
56 moduleName,
57 'constructor'
58 )
59 )
60 .bind(this)
d270cc87 61 ],
81533a20
JB
62 [
63 OCPP20RequestCommand.HEARTBEAT,
24d15716
JB
64 this.ajv
65 .compile(
66 OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20HeartbeatResponse>(
67 'assets/json-schemas/ocpp/2.0/HeartbeatResponse.json',
68 moduleName,
69 'constructor'
70 )
71 )
72 .bind(this)
81533a20 73 ],
6e939d9e
JB
74 [
75 OCPP20RequestCommand.STATUS_NOTIFICATION,
24d15716
JB
76 this.ajv
77 .compile(
78 OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20StatusNotificationResponse>(
79 'assets/json-schemas/ocpp/2.0/StatusNotificationResponse.json',
80 moduleName,
81 'constructor'
82 )
83 )
84 .bind(this)
66a7748d
JB
85 ]
86 ])
d5490a13 87 this.incomingRequestResponsePayloadValidateFunctions = new Map<
24d15716
JB
88 OCPP20IncomingRequestCommand,
89 ValidateFunction<JsonType>
90 >([
02887891
JB
91 [
92 OCPP20IncomingRequestCommand.CLEAR_CACHE,
298be10c 93 this.ajvIncomingRequest
24d15716
JB
94 .compile(
95 OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20ClearCacheResponse>(
96 'assets/json-schemas/ocpp/2.0/ClearCacheResponse.json',
97 moduleName,
98 'constructor'
99 )
100 )
101 .bind(this)
66a7748d
JB
102 ]
103 ])
ba9a56a6 104 this.validatePayload = this.validatePayload.bind(this)
953d6b02
JB
105 }
106
9429aa42 107 public async responseHandler<ReqType extends JsonType, ResType extends JsonType>(
953d6b02
JB
108 chargingStation: ChargingStation,
109 commandName: OCPP20RequestCommand,
9429aa42 110 payload: ResType,
66a7748d 111 requestPayload: ReqType
953d6b02 112 ): Promise<void> {
66a7748d 113 if (chargingStation.isRegistered() || commandName === OCPP20RequestCommand.BOOT_NOTIFICATION) {
953d6b02 114 if (
66a7748d
JB
115 this.responseHandlers.has(commandName) &&
116 OCPP20ServiceUtils.isRequestCommandSupported(chargingStation, commandName)
953d6b02
JB
117 ) {
118 try {
66a7748d
JB
119 this.validatePayload(chargingStation, commandName, payload)
120 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
bcf95df1
JB
121 const responseHandler = this.responseHandlers.get(commandName)!
122 if (isAsyncFunction(responseHandler)) {
123 await responseHandler(chargingStation, payload, requestPayload)
124 } else {
125 (
126 responseHandler as (
127 chargingStation: ChargingStation,
128 payload: JsonType,
129 requestPayload?: JsonType
130 ) => void
131 )(chargingStation, payload, requestPayload)
132 }
953d6b02
JB
133 } catch (error) {
134 logger.error(
135 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
66a7748d
JB
136 error
137 )
138 throw error
953d6b02
JB
139 }
140 } else {
141 // Throw exception
142 throw new OCPPError(
143 ErrorType.NOT_IMPLEMENTED,
144 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
145 payload,
4ed03b6e 146 undefined,
66a7748d 147 2
953d6b02
JB
148 )}`,
149 commandName,
66a7748d
JB
150 payload
151 )
953d6b02
JB
152 }
153 } else {
154 throw new OCPPError(
155 ErrorType.SECURITY_ERROR,
156 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
157 payload,
4ed03b6e 158 undefined,
66a7748d 159 2
412cece8 160 )} while the charging station is not registered on the central server`,
953d6b02 161 commandName,
66a7748d
JB
162 payload
163 )
953d6b02
JB
164 }
165 }
166
66a7748d 167 private validatePayload (
953d6b02
JB
168 chargingStation: ChargingStation,
169 commandName: OCPP20RequestCommand,
66a7748d 170 payload: JsonType
953d6b02 171 ): boolean {
d5490a13 172 if (this.payloadValidateFunctions.has(commandName)) {
24d15716 173 return this.validateResponsePayload(chargingStation, commandName, payload)
953d6b02
JB
174 }
175 logger.warn(
24d15716 176 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema validation function found for command '${commandName}' PDU validation`
66a7748d
JB
177 )
178 return false
953d6b02 179 }
d270cc87 180
66a7748d 181 private handleResponseBootNotification (
d270cc87 182 chargingStation: ChargingStation,
66a7748d 183 payload: OCPP20BootNotificationResponse
d270cc87
JB
184 ): void {
185 if (payload.status === RegistrationStatusEnumType.ACCEPTED) {
f2d5e3d9 186 addConfigurationKey(
268a74bb 187 chargingStation,
857d8dd9 188 OCPP20OptionalVariableName.HeartbeatInterval,
268a74bb 189 payload.interval.toString(),
abe9e9dd 190 {},
66a7748d
JB
191 { overwrite: true, save: true }
192 )
193 OCPP20ServiceUtils.startHeartbeatInterval(chargingStation, payload.interval)
d270cc87
JB
194 }
195 if (Object.values(RegistrationStatusEnumType).includes(payload.status)) {
196 const logMsg = `${chargingStation.logPrefix()} Charging station in '${
197 payload.status
66a7748d 198 }' state on the central server`
d270cc87
JB
199 payload.status === RegistrationStatusEnumType.REJECTED
200 ? logger.warn(logMsg)
66a7748d 201 : logger.info(logMsg)
d270cc87
JB
202 } else {
203 logger.error(
44eb6026 204 `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`,
66a7748d
JB
205 payload
206 )
d270cc87
JB
207 }
208 }
953d6b02 209}