d673e8a31597cf893691991a50f901b874468677
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16RequestService.ts
1 // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
2
3 import type { ValidateFunction } from 'ajv'
4
5 import type { ChargingStation } from '../../../charging-station/index.js'
6 import { OCPPError } from '../../../exception/index.js'
7 import {
8 ErrorType,
9 type JsonObject,
10 type JsonType,
11 type OCPP16AuthorizeRequest,
12 type OCPP16BootNotificationRequest,
13 OCPP16ChargePointStatus,
14 type OCPP16DataTransferRequest,
15 type OCPP16DiagnosticsStatusNotificationRequest,
16 type OCPP16FirmwareStatusNotificationRequest,
17 type OCPP16HeartbeatRequest,
18 type OCPP16MeterValuesRequest,
19 OCPP16RequestCommand,
20 type OCPP16StartTransactionRequest,
21 type OCPP16StatusNotificationRequest,
22 type OCPP16StopTransactionRequest,
23 OCPPVersion,
24 type RequestParams
25 } from '../../../types/index.js'
26 import { Constants, generateUUID } from '../../../utils/index.js'
27 import { OCPPRequestService } from '../OCPPRequestService.js'
28 import type { OCPPResponseService } from '../OCPPResponseService.js'
29 import { OCPP16Constants } from './OCPP16Constants.js'
30 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
31
32 const moduleName = 'OCPP16RequestService'
33
34 export class OCPP16RequestService extends OCPPRequestService {
35 protected payloadValidateFunctions: Map<OCPP16RequestCommand, ValidateFunction<JsonType>>
36
37 public constructor (ocppResponseService: OCPPResponseService) {
38 // if (new.target.name === moduleName) {
39 // throw new TypeError(`Cannot construct ${new.target.name} instances directly`)
40 // }
41 super(OCPPVersion.VERSION_16, ocppResponseService)
42 this.payloadValidateFunctions = new Map<OCPP16RequestCommand, ValidateFunction<JsonType>>([
43 [
44 OCPP16RequestCommand.AUTHORIZE,
45 this.ajv
46 .compile(
47 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16AuthorizeRequest>(
48 'assets/json-schemas/ocpp/1.6/Authorize.json',
49 moduleName,
50 'constructor'
51 )
52 )
53 .bind(this)
54 ],
55 [
56 OCPP16RequestCommand.BOOT_NOTIFICATION,
57 this.ajv
58 .compile(
59 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16BootNotificationRequest>(
60 'assets/json-schemas/ocpp/1.6/BootNotification.json',
61 moduleName,
62 'constructor'
63 )
64 )
65 .bind(this)
66 ],
67 [
68 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
69 this.ajv
70 .compile(
71 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DiagnosticsStatusNotificationRequest>(
72 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotification.json',
73 moduleName,
74 'constructor'
75 )
76 )
77 .bind(this)
78 ],
79 [
80 OCPP16RequestCommand.HEARTBEAT,
81 this.ajv
82 .compile(
83 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16HeartbeatRequest>(
84 'assets/json-schemas/ocpp/1.6/Heartbeat.json',
85 moduleName,
86 'constructor'
87 )
88 )
89 .bind(this)
90 ],
91 [
92 OCPP16RequestCommand.METER_VALUES,
93 this.ajv
94 .compile(
95 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16MeterValuesRequest>(
96 'assets/json-schemas/ocpp/1.6/MeterValues.json',
97 moduleName,
98 'constructor'
99 )
100 )
101 .bind(this)
102 ],
103 [
104 OCPP16RequestCommand.STATUS_NOTIFICATION,
105 this.ajv
106 .compile(
107 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StatusNotificationRequest>(
108 'assets/json-schemas/ocpp/1.6/StatusNotification.json',
109 moduleName,
110 'constructor'
111 )
112 )
113 .bind(this)
114 ],
115 [
116 OCPP16RequestCommand.START_TRANSACTION,
117 this.ajv
118 .compile(
119 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StartTransactionRequest>(
120 'assets/json-schemas/ocpp/1.6/StartTransaction.json',
121 moduleName,
122 'constructor'
123 )
124 )
125 .bind(this)
126 ],
127 [
128 OCPP16RequestCommand.STOP_TRANSACTION,
129 this.ajv
130 .compile(
131 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StopTransactionRequest>(
132 'assets/json-schemas/ocpp/1.6/StopTransaction.json',
133 moduleName,
134 'constructor'
135 )
136 )
137 .bind(this)
138 ],
139 [
140 OCPP16RequestCommand.DATA_TRANSFER,
141 this.ajv
142 .compile(
143 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferRequest>(
144 'assets/json-schemas/ocpp/1.6/DataTransfer.json',
145 moduleName,
146 'constructor'
147 )
148 )
149 .bind(this)
150 ],
151 [
152 OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION,
153 this.ajv
154 .compile(
155 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16FirmwareStatusNotificationRequest>(
156 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotification.json',
157 moduleName,
158 'constructor'
159 )
160 )
161 .bind(this)
162 ]
163 ])
164 this.buildRequestPayload = this.buildRequestPayload.bind(this)
165 }
166
167 public async requestHandler<RequestType extends JsonType, ResponseType extends JsonType>(
168 chargingStation: ChargingStation,
169 commandName: OCPP16RequestCommand,
170 commandParams?: JsonType,
171 params?: RequestParams
172 ): Promise<ResponseType> {
173 // FIXME?: add sanity checks on charging station availability, connector availability, connector status, etc.
174 if (OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName)) {
175 return (await this.sendMessage(
176 chargingStation,
177 generateUUID(),
178 this.buildRequestPayload<RequestType>(chargingStation, commandName, commandParams),
179 commandName,
180 params
181 )) as ResponseType
182 }
183 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
184 throw new OCPPError(
185 ErrorType.NOT_SUPPORTED,
186 `Unsupported OCPP command '${commandName}'`,
187 commandName,
188 commandParams
189 )
190 }
191
192 private buildRequestPayload<Request extends JsonType>(
193 chargingStation: ChargingStation,
194 commandName: OCPP16RequestCommand,
195 commandParams?: JsonType
196 ): Request {
197 let connectorId: number | undefined
198 let energyActiveImportRegister: number
199 commandParams = commandParams as JsonObject
200 switch (commandName) {
201 case OCPP16RequestCommand.BOOT_NOTIFICATION:
202 case OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION:
203 case OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION:
204 case OCPP16RequestCommand.METER_VALUES:
205 case OCPP16RequestCommand.STATUS_NOTIFICATION:
206 case OCPP16RequestCommand.DATA_TRANSFER:
207 return commandParams as unknown as Request
208 case OCPP16RequestCommand.AUTHORIZE:
209 return {
210 idTag: Constants.DEFAULT_IDTAG,
211 ...commandParams
212 } as unknown as Request
213 case OCPP16RequestCommand.HEARTBEAT:
214 return OCPP16Constants.OCPP_REQUEST_EMPTY as unknown as Request
215 case OCPP16RequestCommand.START_TRANSACTION:
216 return {
217 idTag: Constants.DEFAULT_IDTAG,
218 meterStart: chargingStation.getEnergyActiveImportRegisterByConnectorId(
219 commandParams.connectorId as number,
220 true
221 ),
222 timestamp: new Date(),
223 ...(OCPP16ServiceUtils.hasReservation(
224 chargingStation,
225 commandParams.connectorId as number,
226 commandParams.idTag as string
227 ) && {
228 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
229 reservationId: chargingStation.getReservationBy(
230 'connectorId',
231 chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved
232 ? 0
233 : (commandParams.connectorId as number)
234 )!.reservationId
235 }),
236 ...commandParams
237 } as unknown as Request
238 case OCPP16RequestCommand.STOP_TRANSACTION:
239 chargingStation.stationInfo?.transactionDataMeterValues === true &&
240 (connectorId = chargingStation.getConnectorIdByTransactionId(
241 commandParams.transactionId as number
242 ))
243 energyActiveImportRegister = chargingStation.getEnergyActiveImportRegisterByTransactionId(
244 commandParams.transactionId as number,
245 true
246 )
247 return {
248 idTag: chargingStation.getTransactionIdTag(commandParams.transactionId as number),
249 meterStop: energyActiveImportRegister,
250 timestamp: new Date(),
251 ...(chargingStation.stationInfo?.transactionDataMeterValues === true && {
252 transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues(
253 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
254 chargingStation.getConnectorStatus(connectorId!)!.transactionBeginMeterValue!,
255 OCPP16ServiceUtils.buildTransactionEndMeterValue(
256 chargingStation,
257 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
258 connectorId!,
259 energyActiveImportRegister
260 )
261 )
262 }),
263 ...commandParams
264 } as unknown as Request
265 default:
266 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
267 throw new OCPPError(
268 ErrorType.NOT_SUPPORTED,
269 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
270 `Unsupported OCPP command '${commandName}'`,
271 commandName,
272 commandParams
273 )
274 }
275 }
276 }