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