fix: send preparing connector status before `StartTransaction`
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16RequestService.ts
CommitLineData
a19b897d 1// Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
c8eeb62b 2
24d15716 3import type { ValidateFunction } from 'ajv'
b52c969d 4
66a7748d
JB
5import type { ChargingStation } from '../../../charging-station/index.js'
6import { OCPPError } from '../../../exception/index.js'
b52c969d 7import {
268a74bb
JB
8 ErrorType,
9 type JsonObject,
10 type JsonType,
11 type OCPP16AuthorizeRequest,
27782dbc 12 type OCPP16BootNotificationRequest,
90aceaf6 13 OCPP16ChargePointStatus,
27782dbc 14 type OCPP16DataTransferRequest,
c9a4f9ea 15 type OCPP16DiagnosticsStatusNotificationRequest,
e9a4164c 16 type OCPP16FirmwareStatusNotificationRequest,
27782dbc 17 type OCPP16HeartbeatRequest,
268a74bb 18 type OCPP16MeterValuesRequest,
b52c969d 19 OCPP16RequestCommand,
268a74bb 20 type OCPP16StartTransactionRequest,
27782dbc 21 type OCPP16StatusNotificationRequest,
268a74bb
JB
22 type OCPP16StopTransactionRequest,
23 OCPPVersion,
66a7748d
JB
24 type RequestParams
25} from '../../../types/index.js'
26import { Constants, generateUUID } from '../../../utils/index.js'
27import { OCPPRequestService } from '../OCPPRequestService.js'
28import type { OCPPResponseService } from '../OCPPResponseService.js'
4c3f6c20
JB
29import { OCPP16Constants } from './OCPP16Constants.js'
30import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
c0560973 31
66a7748d 32const moduleName = 'OCPP16RequestService'
909dcf2d 33
268a74bb 34export class OCPP16RequestService extends OCPPRequestService {
d5490a13 35 protected payloadValidateFunctions: Map<OCPP16RequestCommand, ValidateFunction<JsonType>>
b52c969d 36
66a7748d 37 public constructor (ocppResponseService: OCPPResponseService) {
5199f9fd
JB
38 // if (new.target.name === moduleName) {
39 // throw new TypeError(`Cannot construct ${new.target.name} instances directly`)
b768993d 40 // }
66a7748d 41 super(OCPPVersion.VERSION_16, ocppResponseService)
d5490a13 42 this.payloadValidateFunctions = new Map<OCPP16RequestCommand, ValidateFunction<JsonType>>([
b52c969d
JB
43 [
44 OCPP16RequestCommand.AUTHORIZE,
24d15716
JB
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)
b52c969d
JB
54 ],
55 [
56 OCPP16RequestCommand.BOOT_NOTIFICATION,
24d15716
JB
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)
b52c969d
JB
66 ],
67 [
68 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
24d15716
JB
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)
b52c969d
JB
78 ],
79 [
80 OCPP16RequestCommand.HEARTBEAT,
24d15716
JB
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)
b52c969d
JB
90 ],
91 [
92 OCPP16RequestCommand.METER_VALUES,
24d15716
JB
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)
b52c969d
JB
102 ],
103 [
104 OCPP16RequestCommand.STATUS_NOTIFICATION,
24d15716
JB
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)
b52c969d
JB
114 ],
115 [
116 OCPP16RequestCommand.START_TRANSACTION,
24d15716
JB
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)
b52c969d
JB
126 ],
127 [
128 OCPP16RequestCommand.STOP_TRANSACTION,
24d15716
JB
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)
b52c969d 138 ],
91a7d3ea
JB
139 [
140 OCPP16RequestCommand.DATA_TRANSFER,
24d15716
JB
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)
91a7d3ea 150 ],
c9a4f9ea
JB
151 [
152 OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION,
24d15716
JB
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)
66a7748d
JB
162 ]
163 ])
ba9a56a6 164 this.buildRequestPayload = this.buildRequestPayload.bind(this)
9f2e3130
JB
165 }
166
6c1761d4 167 public async requestHandler<RequestType extends JsonType, ResponseType extends JsonType>(
08f130a0 168 chargingStation: ChargingStation,
94a464f9 169 commandName: OCPP16RequestCommand,
a9671b9e 170 commandParams?: RequestType,
66a7748d 171 params?: RequestParams
6c1761d4 172 ): Promise<ResponseType> {
62340a29 173 // FIXME?: add sanity checks on charging station availability, connector availability, connector status, etc.
66a7748d 174 if (OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName)) {
a9671b9e
JB
175 // Post request actions hook
176 switch (commandName) {
177 case OCPP16RequestCommand.START_TRANSACTION:
178 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
179 chargingStation,
180 (commandParams as OCPP16StartTransactionRequest).connectorId,
181 OCPP16ChargePointStatus.Preparing
182 )
183 break
184 }
f22266fd 185 return (await this.sendMessage(
08f130a0 186 chargingStation,
9bf0ef23 187 generateUUID(),
18bf8274 188 this.buildRequestPayload<RequestType>(chargingStation, commandName, commandParams),
94a464f9 189 commandName,
66a7748d
JB
190 params
191 )) as ResponseType
94a464f9 192 }
e909d2a7 193 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
94a464f9
JB
194 throw new OCPPError(
195 ErrorType.NOT_SUPPORTED,
6c8f5d90 196 `Unsupported OCPP command '${commandName}'`,
94a464f9 197 commandName,
66a7748d
JB
198 commandParams
199 )
c0560973
JB
200 }
201
5cc4b63b 202 private buildRequestPayload<Request extends JsonType>(
08f130a0 203 chargingStation: ChargingStation,
78085c42 204 commandName: OCPP16RequestCommand,
66a7748d 205 commandParams?: JsonType
f22266fd 206 ): Request {
66a7748d
JB
207 let connectorId: number | undefined
208 let energyActiveImportRegister: number
209 commandParams = commandParams as JsonObject
78085c42 210 switch (commandName) {
78085c42 211 case OCPP16RequestCommand.BOOT_NOTIFICATION:
78085c42 212 case OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION:
22e0d48e 213 case OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION:
78085c42 214 case OCPP16RequestCommand.METER_VALUES:
78085c42 215 case OCPP16RequestCommand.STATUS_NOTIFICATION:
36c462a4 216 case OCPP16RequestCommand.DATA_TRANSFER:
66a7748d 217 return commandParams as unknown as Request
36c462a4 218 case OCPP16RequestCommand.AUTHORIZE:
78085c42 219 return {
36c462a4 220 idTag: Constants.DEFAULT_IDTAG,
66a7748d
JB
221 ...commandParams
222 } as unknown as Request
36c462a4 223 case OCPP16RequestCommand.HEARTBEAT:
66a7748d 224 return OCPP16Constants.OCPP_REQUEST_EMPTY as unknown as Request
78085c42
JB
225 case OCPP16RequestCommand.START_TRANSACTION:
226 return {
36c462a4
JB
227 idTag: Constants.DEFAULT_IDTAG,
228 meterStart: chargingStation.getEnergyActiveImportRegisterByConnectorId(
5199f9fd 229 commandParams.connectorId as number,
66a7748d 230 true
36c462a4
JB
231 ),
232 timestamp: new Date(),
90aceaf6
JB
233 ...(OCPP16ServiceUtils.hasReservation(
234 chargingStation,
5199f9fd
JB
235 commandParams.connectorId as number,
236 commandParams.idTag as string
90aceaf6 237 ) && {
66a7748d 238 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
90aceaf6
JB
239 reservationId: chargingStation.getReservationBy(
240 'connectorId',
241 chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved
242 ? 0
5199f9fd 243 : (commandParams.connectorId as number)
66a7748d 244 )!.reservationId
90aceaf6 245 }),
66a7748d
JB
246 ...commandParams
247 } as unknown as Request
78085c42 248 case OCPP16RequestCommand.STOP_TRANSACTION:
66a7748d 249 chargingStation.stationInfo?.transactionDataMeterValues === true &&
f1e731bd 250 (connectorId = chargingStation.getConnectorIdByTransactionId(
5199f9fd 251 commandParams.transactionId as number
f938317f 252 ))
36c462a4 253 energyActiveImportRegister = chargingStation.getEnergyActiveImportRegisterByTransactionId(
5199f9fd 254 commandParams.transactionId as number,
66a7748d
JB
255 true
256 )
78085c42 257 return {
5199f9fd 258 idTag: chargingStation.getTransactionIdTag(commandParams.transactionId as number),
36c462a4
JB
259 meterStop: energyActiveImportRegister,
260 timestamp: new Date(),
66a7748d 261 ...(chargingStation.stationInfo?.transactionDataMeterValues === true && {
36c462a4 262 transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues(
66a7748d 263 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e1d9a0f4 264 chargingStation.getConnectorStatus(connectorId!)!.transactionBeginMeterValue!,
36c462a4
JB
265 OCPP16ServiceUtils.buildTransactionEndMeterValue(
266 chargingStation,
66a7748d 267 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e1d9a0f4 268 connectorId!,
66a7748d
JB
269 energyActiveImportRegister
270 )
271 )
78085c42 272 }),
66a7748d
JB
273 ...commandParams
274 } as unknown as Request
78085c42 275 default:
e909d2a7 276 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
78085c42
JB
277 throw new OCPPError(
278 ErrorType.NOT_SUPPORTED,
279 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
6c8f5d90 280 `Unsupported OCPP command '${commandName}'`,
78085c42 281 commandName,
66a7748d
JB
282 commandParams
283 )
78085c42
JB
284 }
285 }
c0560973 286}