refactor: switch eslint configuration to strict type checking
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 2.0 / OCPP20ResponseService.ts
CommitLineData
edd13439 1// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
953d6b02 2
66a7748d 3import type { JSONSchemaType } 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'
22import { logger } from '../../../utils/index.js'
23import { OCPPResponseService } from '../OCPPResponseService.js'
953d6b02 24
66a7748d 25const moduleName = 'OCPP20ResponseService'
953d6b02 26
268a74bb 27export class OCPP20ResponseService extends OCPPResponseService {
b3fc3ff5 28 public jsonIncomingRequestResponseSchemas: Map<
66a7748d
JB
29 OCPP20IncomingRequestCommand,
30 JSONSchemaType<JsonType>
31 >
b3fc3ff5 32
66a7748d
JB
33 private readonly responseHandlers: Map<OCPP20RequestCommand, ResponseHandler>
34 private readonly jsonSchemas: Map<OCPP20RequestCommand, JSONSchemaType<JsonType>>
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
JB
45 ],
46 [OCPP20RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this) as ResponseHandler],
47 [
48 OCPP20RequestCommand.STATUS_NOTIFICATION,
66a7748d
JB
49 this.emptyResponseHandler.bind(this) as ResponseHandler
50 ]
51 ])
291b5ec8 52 this.jsonSchemas = new Map<OCPP20RequestCommand, JSONSchemaType<JsonType>>([
d270cc87
JB
53 [
54 OCPP20RequestCommand.BOOT_NOTIFICATION,
130783a7 55 OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20BootNotificationResponse>(
51022aa0 56 'assets/json-schemas/ocpp/2.0/BootNotificationResponse.json',
1b271a54 57 moduleName,
66a7748d
JB
58 'constructor'
59 )
d270cc87 60 ],
81533a20
JB
61 [
62 OCPP20RequestCommand.HEARTBEAT,
130783a7 63 OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20HeartbeatResponse>(
51022aa0 64 'assets/json-schemas/ocpp/2.0/HeartbeatResponse.json',
1b271a54 65 moduleName,
66a7748d
JB
66 'constructor'
67 )
81533a20 68 ],
6e939d9e
JB
69 [
70 OCPP20RequestCommand.STATUS_NOTIFICATION,
130783a7 71 OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20StatusNotificationResponse>(
51022aa0 72 'assets/json-schemas/ocpp/2.0/StatusNotificationResponse.json',
1b271a54 73 moduleName,
66a7748d
JB
74 'constructor'
75 )
76 ]
77 ])
02887891
JB
78 this.jsonIncomingRequestResponseSchemas = new Map([
79 [
80 OCPP20IncomingRequestCommand.CLEAR_CACHE,
130783a7 81 OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20ClearCacheResponse>(
51022aa0 82 'assets/json-schemas/ocpp/2.0/ClearCacheResponse.json',
1b271a54 83 moduleName,
66a7748d
JB
84 'constructor'
85 )
86 ]
87 ])
31f59c6d
JB
88 this.validatePayload = this.validatePayload.bind(this) as (
89 chargingStation: ChargingStation,
90 commandName: OCPP20RequestCommand,
66a7748d
JB
91 payload: JsonType
92 ) => boolean
953d6b02
JB
93 }
94
9429aa42 95 public async responseHandler<ReqType extends JsonType, ResType extends JsonType>(
953d6b02
JB
96 chargingStation: ChargingStation,
97 commandName: OCPP20RequestCommand,
9429aa42 98 payload: ResType,
66a7748d 99 requestPayload: ReqType
953d6b02 100 ): Promise<void> {
66a7748d 101 if (chargingStation.isRegistered() || commandName === OCPP20RequestCommand.BOOT_NOTIFICATION) {
953d6b02 102 if (
66a7748d
JB
103 this.responseHandlers.has(commandName) &&
104 OCPP20ServiceUtils.isRequestCommandSupported(chargingStation, commandName)
953d6b02
JB
105 ) {
106 try {
66a7748d
JB
107 this.validatePayload(chargingStation, commandName, payload)
108 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
109 await this.responseHandlers.get(commandName)!(chargingStation, payload, requestPayload)
953d6b02
JB
110 } catch (error) {
111 logger.error(
112 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
66a7748d
JB
113 error
114 )
115 throw error
953d6b02
JB
116 }
117 } else {
118 // Throw exception
119 throw new OCPPError(
120 ErrorType.NOT_IMPLEMENTED,
121 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
122 payload,
4ed03b6e 123 undefined,
66a7748d 124 2
953d6b02
JB
125 )}`,
126 commandName,
66a7748d
JB
127 payload
128 )
953d6b02
JB
129 }
130 } else {
131 throw new OCPPError(
132 ErrorType.SECURITY_ERROR,
133 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
134 payload,
4ed03b6e 135 undefined,
66a7748d 136 2
439fc71b 137 )} while the charging station is not registered on the central server.`,
953d6b02 138 commandName,
66a7748d
JB
139 payload
140 )
953d6b02
JB
141 }
142 }
143
66a7748d 144 private validatePayload (
953d6b02
JB
145 chargingStation: ChargingStation,
146 commandName: OCPP20RequestCommand,
66a7748d 147 payload: JsonType
953d6b02 148 ): boolean {
66a7748d 149 if (this.jsonSchemas.has(commandName)) {
953d6b02
JB
150 return this.validateResponsePayload(
151 chargingStation,
152 commandName,
66a7748d 153 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e1d9a0f4 154 this.jsonSchemas.get(commandName)!,
66a7748d
JB
155 payload
156 )
953d6b02
JB
157 }
158 logger.warn(
66a7748d
JB
159 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
160 )
161 return false
953d6b02 162 }
d270cc87 163
66a7748d 164 private handleResponseBootNotification (
d270cc87 165 chargingStation: ChargingStation,
66a7748d 166 payload: OCPP20BootNotificationResponse
d270cc87
JB
167 ): void {
168 if (payload.status === RegistrationStatusEnumType.ACCEPTED) {
f2d5e3d9 169 addConfigurationKey(
268a74bb 170 chargingStation,
857d8dd9 171 OCPP20OptionalVariableName.HeartbeatInterval,
268a74bb 172 payload.interval.toString(),
abe9e9dd 173 {},
66a7748d
JB
174 { overwrite: true, save: true }
175 )
176 OCPP20ServiceUtils.startHeartbeatInterval(chargingStation, payload.interval)
d270cc87
JB
177 }
178 if (Object.values(RegistrationStatusEnumType).includes(payload.status)) {
179 const logMsg = `${chargingStation.logPrefix()} Charging station in '${
180 payload.status
66a7748d 181 }' state on the central server`
d270cc87
JB
182 payload.status === RegistrationStatusEnumType.REJECTED
183 ? logger.warn(logMsg)
66a7748d 184 : logger.info(logMsg)
d270cc87
JB
185 } else {
186 logger.error(
44eb6026 187 `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`,
66a7748d
JB
188 payload
189 )
d270cc87
JB
190 }
191 }
953d6b02 192}