refactor: cleanup async lock API
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16ResponseService.ts
CommitLineData
edd13439 1// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
c8eeb62b 2
04b1261c
JB
3import { parentPort } from 'node:worker_threads';
4
6c1761d4 5import type { JSONSchemaType } from 'ajv';
844e496b 6
04b1261c
JB
7import {
8 type ChargingStation,
9 ChargingStationConfigurationUtils,
10 ChargingStationUtils,
11 MessageChannelUtils,
12} from '../../../charging-station';
268a74bb 13import { OCPPError } from '../../../exception';
ef6fa3fb 14import {
268a74bb
JB
15 type ChangeAvailabilityResponse,
16 type ChangeConfigurationResponse,
17 type ClearChargingProfileResponse,
18 ErrorType,
19 type GenericResponse,
20 type GetConfigurationResponse,
21 type GetDiagnosticsResponse,
22 type JsonObject,
23 type JsonType,
8114d10e 24 OCPP16AuthorizationStatus,
27782dbc
JB
25 type OCPP16AuthorizeRequest,
26 type OCPP16AuthorizeResponse,
268a74bb 27 type OCPP16BootNotificationResponse,
268a74bb
JB
28 OCPP16ChargePointStatus,
29 type OCPP16DataTransferResponse,
30 type OCPP16DiagnosticsStatusNotificationResponse,
31 type OCPP16FirmwareStatusNotificationResponse,
41189456 32 type OCPP16GetCompositeScheduleResponse,
268a74bb
JB
33 type OCPP16HeartbeatResponse,
34 OCPP16IncomingRequestCommand,
35 type OCPP16MeterValuesRequest,
36 type OCPP16MeterValuesResponse,
37 OCPP16RequestCommand,
38 OCPP16StandardParametersKey,
27782dbc
JB
39 type OCPP16StartTransactionRequest,
40 type OCPP16StartTransactionResponse,
268a74bb 41 type OCPP16StatusNotificationResponse,
27782dbc
JB
42 type OCPP16StopTransactionRequest,
43 type OCPP16StopTransactionResponse,
268a74bb
JB
44 type OCPP16TriggerMessageResponse,
45 type OCPP16UpdateFirmwareResponse,
46 OCPPVersion,
02887891
JB
47 RegistrationStatusEnumType,
48 type ResponseHandler,
268a74bb
JB
49 type SetChargingProfileResponse,
50 type UnlockConnectorResponse,
51} from '../../../types';
60a74391 52import { Constants, Utils, logger } from '../../../utils';
2896e06d 53import { OCPP16ServiceUtils, OCPPResponseService } from '../internal';
c0560973 54
909dcf2d
JB
55const moduleName = 'OCPP16ResponseService';
56
268a74bb 57export class OCPP16ResponseService extends OCPPResponseService {
b3fc3ff5
JB
58 public jsonIncomingRequestResponseSchemas: Map<
59 OCPP16IncomingRequestCommand,
60 JSONSchemaType<JsonObject>
61 >;
62
58144adb 63 private responseHandlers: Map<OCPP16RequestCommand, ResponseHandler>;
b52c969d 64 private jsonSchemas: Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>;
58144adb 65
08f130a0 66 public constructor() {
b768993d
JB
67 // if (new.target?.name === moduleName) {
68 // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
69 // }
d270cc87 70 super(OCPPVersion.VERSION_16);
58144adb
JB
71 this.responseHandlers = new Map<OCPP16RequestCommand, ResponseHandler>([
72 [OCPP16RequestCommand.BOOT_NOTIFICATION, this.handleResponseBootNotification.bind(this)],
b52c969d 73 [OCPP16RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this)],
58144adb
JB
74 [OCPP16RequestCommand.AUTHORIZE, this.handleResponseAuthorize.bind(this)],
75 [OCPP16RequestCommand.START_TRANSACTION, this.handleResponseStartTransaction.bind(this)],
76 [OCPP16RequestCommand.STOP_TRANSACTION, this.handleResponseStopTransaction.bind(this)],
b52c969d
JB
77 [OCPP16RequestCommand.STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
78 [OCPP16RequestCommand.METER_VALUES, this.emptyResponseHandler.bind(this)],
79 [OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
91a7d3ea 80 [OCPP16RequestCommand.DATA_TRANSFER, this.emptyResponseHandler.bind(this)],
c9a4f9ea 81 [OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
b52c969d
JB
82 ]);
83 this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>([
84 [
85 OCPP16RequestCommand.BOOT_NOTIFICATION,
130783a7 86 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16BootNotificationResponse>(
1b271a54
JB
87 '../../../assets/json-schemas/ocpp/1.6/BootNotificationResponse.json',
88 moduleName,
89 'constructor'
e9a4164c 90 ),
b52c969d
JB
91 ],
92 [
93 OCPP16RequestCommand.HEARTBEAT,
130783a7 94 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16HeartbeatResponse>(
1b271a54
JB
95 '../../../assets/json-schemas/ocpp/1.6/HeartbeatResponse.json',
96 moduleName,
97 'constructor'
e9a4164c 98 ),
b52c969d
JB
99 ],
100 [
101 OCPP16RequestCommand.AUTHORIZE,
130783a7 102 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16AuthorizeResponse>(
1b271a54
JB
103 '../../../assets/json-schemas/ocpp/1.6/AuthorizeResponse.json',
104 moduleName,
105 'constructor'
e9a4164c 106 ),
b52c969d
JB
107 ],
108 [
109 OCPP16RequestCommand.START_TRANSACTION,
130783a7 110 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StartTransactionResponse>(
1b271a54
JB
111 '../../../assets/json-schemas/ocpp/1.6/StartTransactionResponse.json',
112 moduleName,
113 'constructor'
e9a4164c 114 ),
b52c969d
JB
115 ],
116 [
117 OCPP16RequestCommand.STOP_TRANSACTION,
130783a7 118 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StopTransactionResponse>(
1b271a54
JB
119 '../../../assets/json-schemas/ocpp/1.6/StopTransactionResponse.json',
120 moduleName,
121 'constructor'
e9a4164c 122 ),
b52c969d
JB
123 ],
124 [
125 OCPP16RequestCommand.STATUS_NOTIFICATION,
130783a7 126 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StatusNotificationResponse>(
1b271a54
JB
127 '../../../assets/json-schemas/ocpp/1.6/StatusNotificationResponse.json',
128 moduleName,
129 'constructor'
e9a4164c 130 ),
b52c969d
JB
131 ],
132 [
133 OCPP16RequestCommand.METER_VALUES,
130783a7 134 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16MeterValuesResponse>(
1b271a54
JB
135 '../../../assets/json-schemas/ocpp/1.6/MeterValuesResponse.json',
136 moduleName,
137 'constructor'
e9a4164c 138 ),
b52c969d
JB
139 ],
140 [
141 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
130783a7 142 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DiagnosticsStatusNotificationResponse>(
1b271a54
JB
143 '../../../assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotificationResponse.json',
144 moduleName,
145 'constructor'
e9a4164c 146 ),
b52c969d 147 ],
91a7d3ea
JB
148 [
149 OCPP16RequestCommand.DATA_TRANSFER,
130783a7 150 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferResponse>(
1b271a54
JB
151 '../../../assets/json-schemas/ocpp/1.6/DataTransferResponse.json',
152 moduleName,
153 'constructor'
e9a4164c 154 ),
91a7d3ea 155 ],
c9a4f9ea
JB
156 [
157 OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION,
130783a7 158 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16FirmwareStatusNotificationResponse>(
1b271a54
JB
159 '../../../assets/json-schemas/ocpp/1.6/FirmwareStatusNotificationResponse.json',
160 moduleName,
161 'constructor'
e9a4164c 162 ),
c9a4f9ea 163 ],
58144adb 164 ]);
02887891
JB
165 this.jsonIncomingRequestResponseSchemas = new Map([
166 [
167 OCPP16IncomingRequestCommand.RESET,
130783a7 168 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
1b271a54
JB
169 '../../../assets/json-schemas/ocpp/1.6/ResetResponse.json',
170 moduleName,
171 'constructor'
e9a4164c 172 ),
02887891
JB
173 ],
174 [
175 OCPP16IncomingRequestCommand.CLEAR_CACHE,
130783a7 176 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
1b271a54
JB
177 '../../../assets/json-schemas/ocpp/1.6/ClearCacheResponse.json',
178 moduleName,
179 'constructor'
e9a4164c 180 ),
02887891
JB
181 ],
182 [
183 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
130783a7 184 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeAvailabilityResponse>(
1b271a54
JB
185 '../../../assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json',
186 moduleName,
187 'constructor'
e9a4164c 188 ),
02887891
JB
189 ],
190 [
191 OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
130783a7 192 OCPP16ServiceUtils.parseJsonSchemaFile<UnlockConnectorResponse>(
1b271a54
JB
193 '../../../assets/json-schemas/ocpp/1.6/UnlockConnectorResponse.json',
194 moduleName,
195 'constructor'
e9a4164c 196 ),
02887891
JB
197 ],
198 [
199 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
130783a7 200 OCPP16ServiceUtils.parseJsonSchemaFile<GetConfigurationResponse>(
1b271a54
JB
201 '../../../assets/json-schemas/ocpp/1.6/GetConfigurationResponse.json',
202 moduleName,
203 'constructor'
e9a4164c 204 ),
02887891
JB
205 ],
206 [
207 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
130783a7 208 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeConfigurationResponse>(
1b271a54
JB
209 '../../../assets/json-schemas/ocpp/1.6/ChangeConfigurationResponse.json',
210 moduleName,
211 'constructor'
e9a4164c 212 ),
02887891 213 ],
41189456
JB
214 [
215 OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
216 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16GetCompositeScheduleResponse>(
217 '../../../assets/json-schemas/ocpp/1.6/GetCompositeScheduleResponse.json',
218 moduleName,
219 'constructor'
220 ),
221 ],
02887891
JB
222 [
223 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
130783a7 224 OCPP16ServiceUtils.parseJsonSchemaFile<SetChargingProfileResponse>(
1b271a54
JB
225 '../../../assets/json-schemas/ocpp/1.6/SetChargingProfileResponse.json',
226 moduleName,
227 'constructor'
e9a4164c 228 ),
02887891
JB
229 ],
230 [
231 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
130783a7 232 OCPP16ServiceUtils.parseJsonSchemaFile<ClearChargingProfileResponse>(
1b271a54
JB
233 '../../../assets/json-schemas/ocpp/1.6/ClearChargingProfileResponse.json',
234 moduleName,
235 'constructor'
e9a4164c 236 ),
02887891
JB
237 ],
238 [
239 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
130783a7 240 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
1b271a54
JB
241 '../../../assets/json-schemas/ocpp/1.6/RemoteStartTransactionResponse.json',
242 moduleName,
243 'constructor'
e9a4164c 244 ),
02887891
JB
245 ],
246 [
247 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
130783a7 248 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
1b271a54
JB
249 '../../../assets/json-schemas/ocpp/1.6/RemoteStopTransactionResponse.json',
250 moduleName,
251 'constructor'
e9a4164c 252 ),
02887891
JB
253 ],
254 [
255 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
130783a7 256 OCPP16ServiceUtils.parseJsonSchemaFile<GetDiagnosticsResponse>(
1b271a54
JB
257 '../../../assets/json-schemas/ocpp/1.6/GetDiagnosticsResponse.json',
258 moduleName,
259 'constructor'
e9a4164c 260 ),
02887891
JB
261 ],
262 [
263 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
130783a7 264 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16TriggerMessageResponse>(
1b271a54
JB
265 '../../../assets/json-schemas/ocpp/1.6/TriggerMessageResponse.json',
266 moduleName,
267 'constructor'
e9a4164c 268 ),
02887891
JB
269 ],
270 [
271 OCPP16IncomingRequestCommand.DATA_TRANSFER,
130783a7 272 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferResponse>(
1b271a54
JB
273 '../../../assets/json-schemas/ocpp/1.6/DataTransferResponse.json',
274 moduleName,
275 'constructor'
e9a4164c 276 ),
02887891
JB
277 ],
278 [
279 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE,
130783a7 280 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16UpdateFirmwareResponse>(
1b271a54
JB
281 '../../../assets/json-schemas/ocpp/1.6/UpdateFirmwareResponse.json',
282 moduleName,
283 'constructor'
e9a4164c 284 ),
02887891
JB
285 ],
286 ]);
31f59c6d
JB
287 this.validatePayload = this.validatePayload.bind(this) as (
288 chargingStation: ChargingStation,
289 commandName: OCPP16RequestCommand,
290 payload: JsonType
291 ) => boolean;
58144adb
JB
292 }
293
f7f98c68 294 public async responseHandler(
08f130a0 295 chargingStation: ChargingStation,
e7aeea18 296 commandName: OCPP16RequestCommand,
5cc4b63b
JB
297 payload: JsonType,
298 requestPayload: JsonType
e7aeea18 299 ): Promise<void> {
ed6cfcff
JB
300 if (
301 chargingStation.isRegistered() === true ||
302 commandName === OCPP16RequestCommand.BOOT_NOTIFICATION
303 ) {
65554cc3 304 if (
ed6cfcff
JB
305 this.responseHandlers.has(commandName) === true &&
306 OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true
65554cc3 307 ) {
124f3553 308 try {
9c5c4195 309 this.validatePayload(chargingStation, commandName, payload);
08f130a0 310 await this.responseHandlers.get(commandName)(chargingStation, payload, requestPayload);
124f3553 311 } catch (error) {
6c8f5d90
JB
312 logger.error(
313 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
314 error
315 );
124f3553
JB
316 throw error;
317 }
318 } else {
319 // Throw exception
e7aeea18
JB
320 throw new OCPPError(
321 ErrorType.NOT_IMPLEMENTED,
6c8f5d90 322 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
e7aeea18
JB
323 payload,
324 null,
325 2
326 )}`,
7369e417
JB
327 commandName,
328 payload
e7aeea18 329 );
887fef76 330 }
c0560973 331 } else {
e7aeea18
JB
332 throw new OCPPError(
333 ErrorType.SECURITY_ERROR,
6c8f5d90 334 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
e7aeea18
JB
335 payload,
336 null,
337 2
439fc71b 338 )} while the charging station is not registered on the central server.`,
7369e417
JB
339 commandName,
340 payload
e7aeea18 341 );
c0560973
JB
342 }
343 }
344
9c5c4195
JB
345 private validatePayload(
346 chargingStation: ChargingStation,
347 commandName: OCPP16RequestCommand,
348 payload: JsonType
349 ): boolean {
45988780 350 if (this.jsonSchemas.has(commandName) === true) {
9c5c4195
JB
351 return this.validateResponsePayload(
352 chargingStation,
353 commandName,
354 this.jsonSchemas.get(commandName),
355 payload
356 );
357 }
358 logger.warn(
b3fc3ff5 359 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
9c5c4195
JB
360 );
361 return false;
362 }
363
08f130a0
JB
364 private handleResponseBootNotification(
365 chargingStation: ChargingStation,
366 payload: OCPP16BootNotificationResponse
367 ): void {
d270cc87 368 if (payload.status === RegistrationStatusEnumType.ACCEPTED) {
17ac262c
JB
369 ChargingStationConfigurationUtils.addConfigurationKey(
370 chargingStation,
f0f65a62 371 OCPP16StandardParametersKey.HeartbeatInterval,
a95873d8 372 payload.interval.toString(),
abe9e9dd 373 {},
a95873d8 374 { overwrite: true, save: true }
e7aeea18 375 );
17ac262c
JB
376 ChargingStationConfigurationUtils.addConfigurationKey(
377 chargingStation,
f0f65a62 378 OCPP16StandardParametersKey.HeartBeatInterval,
e7aeea18 379 payload.interval.toString(),
00db15b8 380 { visible: false },
a95873d8 381 { overwrite: true, save: true }
e7aeea18 382 );
8f953431 383 OCPP16ServiceUtils.startHeartbeatInterval(chargingStation, payload.interval);
672fed6e 384 }
d270cc87 385 if (Object.values(RegistrationStatusEnumType).includes(payload.status)) {
08f130a0 386 const logMsg = `${chargingStation.logPrefix()} Charging station in '${
e7aeea18
JB
387 payload.status
388 }' state on the central server`;
d270cc87 389 payload.status === RegistrationStatusEnumType.REJECTED
e7aeea18
JB
390 ? logger.warn(logMsg)
391 : logger.info(logMsg);
c0560973 392 } else {
e7aeea18 393 logger.error(
44eb6026 394 `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`,
e7aeea18
JB
395 payload
396 );
c0560973
JB
397 }
398 }
399
e7aeea18 400 private handleResponseAuthorize(
08f130a0 401 chargingStation: ChargingStation,
e7aeea18 402 payload: OCPP16AuthorizeResponse,
ef6fa3fb 403 requestPayload: OCPP16AuthorizeRequest
e7aeea18 404 ): void {
58144adb 405 let authorizeConnectorId: number;
ded57f02
JB
406 if (chargingStation.hasEvses) {
407 for (const [evseId, evseStatus] of chargingStation.evses) {
408 if (evseId > 0) {
409 for (const [connectorId, connectorStatus] of evseStatus.connectors) {
410 if (connectorStatus?.authorizeIdTag === requestPayload.idTag) {
411 authorizeConnectorId = connectorId;
412 break;
413 }
414 }
415 }
416 }
417 } else {
418 for (const connectorId of chargingStation.connectors.keys()) {
419 if (
420 connectorId > 0 &&
421 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag === requestPayload.idTag
422 ) {
423 authorizeConnectorId = connectorId;
424 break;
425 }
58144adb
JB
426 }
427 }
9bb1159e 428 const authorizeConnectorIdDefined = !Utils.isNullOrUndefined(authorizeConnectorId);
58144adb 429 if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
44eb6026 430 authorizeConnectorIdDefined &&
1984f194 431 (chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true);
e7aeea18 432 logger.debug(
2585c6e9 433 `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' accepted${
54ebb82c 434 authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : ''
1984f194 435 }`
e7aeea18 436 );
58144adb 437 } else {
44eb6026 438 if (authorizeConnectorIdDefined) {
1984f194 439 chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false;
d812bdcb 440 delete chargingStation.getConnectorStatus(authorizeConnectorId)?.authorizeIdTag;
1984f194 441 }
e7aeea18 442 logger.debug(
2585c6e9 443 `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' rejected with status '${
e7aeea18 444 payload.idTagInfo.status
54ebb82c 445 }'${authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : ''}`
e7aeea18 446 );
58144adb
JB
447 }
448 }
449
e7aeea18 450 private async handleResponseStartTransaction(
08f130a0 451 chargingStation: ChargingStation,
e7aeea18 452 payload: OCPP16StartTransactionResponse,
ef6fa3fb 453 requestPayload: OCPP16StartTransactionRequest
e7aeea18 454 ): Promise<void> {
c0560973 455 const connectorId = requestPayload.connectorId;
a14022a2 456 if (connectorId === 0 || chargingStation.hasConnector(connectorId) === false) {
e7aeea18 457 logger.error(
2585c6e9 458 `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector id ${connectorId.toString()}`
e7aeea18 459 );
c0560973
JB
460 return;
461 }
e7aeea18 462 if (
72092cfc 463 chargingStation.getConnectorStatus(connectorId)?.transactionRemoteStarted === true &&
3a13fc92
JB
464 chargingStation.getAuthorizeRemoteTxRequests() === true &&
465 chargingStation.getLocalAuthListEnabled() === true &&
f911a4af 466 chargingStation.hasIdTags() &&
72092cfc 467 chargingStation.getConnectorStatus(connectorId)?.idTagLocalAuthorized === false
e7aeea18
JB
468 ) {
469 logger.error(
44eb6026 470 `${chargingStation.logPrefix()} Trying to start a transaction with a not local authorized idTag ${
72092cfc 471 chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag
2585c6e9 472 } on connector id ${connectorId.toString()}`
e7aeea18 473 );
08f130a0 474 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
475 return;
476 }
e7aeea18 477 if (
72092cfc 478 chargingStation.getConnectorStatus(connectorId)?.transactionRemoteStarted === true &&
3a13fc92
JB
479 chargingStation.getAuthorizeRemoteTxRequests() === true &&
480 chargingStation.getMustAuthorizeAtRemoteStart() === true &&
72092cfc
JB
481 chargingStation.getConnectorStatus(connectorId)?.idTagLocalAuthorized === false &&
482 chargingStation.getConnectorStatus(connectorId)?.idTagAuthorized === false
e7aeea18
JB
483 ) {
484 logger.error(
44eb6026 485 `${chargingStation.logPrefix()} Trying to start a transaction with a not authorized idTag ${
72092cfc 486 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag
2585c6e9 487 } on connector id ${connectorId.toString()}`
e7aeea18 488 );
08f130a0 489 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
490 return;
491 }
e7aeea18 492 if (
72092cfc
JB
493 chargingStation.getConnectorStatus(connectorId)?.idTagAuthorized &&
494 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag !== requestPayload.idTag
e7aeea18
JB
495 ) {
496 logger.error(
44eb6026
JB
497 `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
498 requestPayload.idTag
499 } different from the authorize request one ${
72092cfc 500 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag
2585c6e9 501 } on connector id ${connectorId.toString()}`
e7aeea18 502 );
08f130a0 503 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
504 return;
505 }
e7aeea18 506 if (
72092cfc
JB
507 chargingStation.getConnectorStatus(connectorId)?.idTagLocalAuthorized &&
508 chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag !== requestPayload.idTag
e7aeea18
JB
509 ) {
510 logger.error(
44eb6026
JB
511 `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
512 requestPayload.idTag
513 } different from the local authorized one ${
72092cfc 514 chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag
2585c6e9 515 } on connector id ${connectorId.toString()}`
e7aeea18 516 );
08f130a0 517 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
163547b1
JB
518 return;
519 }
5e3cb728 520 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
e7aeea18 521 logger.debug(
54ebb82c 522 `${chargingStation.logPrefix()} Trying to start a transaction on an already used connector id ${connectorId.toString()}: %j`,
08f130a0 523 chargingStation.getConnectorStatus(connectorId)
e7aeea18 524 );
c0560973
JB
525 return;
526 }
e7aeea18 527 if (
08f130a0 528 chargingStation.getConnectorStatus(connectorId)?.status !==
721646e9
JB
529 OCPP16ChargePointStatus.Available &&
530 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Preparing
e7aeea18
JB
531 ) {
532 logger.error(
54ebb82c 533 `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${connectorId.toString()} with status ${
08f130a0 534 chargingStation.getConnectorStatus(connectorId)?.status
e7aeea18
JB
535 }`
536 );
290d006c
JB
537 return;
538 }
54ebb82c
JB
539 if (!Number.isInteger(payload.transactionId)) {
540 logger.warn(
541 `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${connectorId.toString()} with a non integer transaction id ${
542 payload.transactionId
543 }, converting to integer`
544 );
545 payload.transactionId = Utils.convertToInt(payload.transactionId);
546 }
c0560973 547
f0c6ed89 548 if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
08f130a0
JB
549 chargingStation.getConnectorStatus(connectorId).transactionStarted = true;
550 chargingStation.getConnectorStatus(connectorId).transactionId = payload.transactionId;
551 chargingStation.getConnectorStatus(connectorId).transactionIdTag = requestPayload.idTag;
552 chargingStation.getConnectorStatus(
e7aeea18
JB
553 connectorId
554 ).transactionEnergyActiveImportRegisterValue = 0;
08f130a0 555 chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue =
e7aeea18 556 OCPP16ServiceUtils.buildTransactionBeginMeterValue(
08f130a0 557 chargingStation,
e7aeea18
JB
558 connectorId,
559 requestPayload.meterStart
560 );
08f130a0
JB
561 chargingStation.getBeginEndMeterValues() &&
562 (await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
563 OCPP16MeterValuesRequest,
564 OCPP16MeterValuesResponse
08f130a0 565 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
93b4a429 566 connectorId,
ef6fa3fb 567 transactionId: payload.transactionId,
7369e417 568 meterValue: [chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue],
ef6fa3fb 569 }));
4ecff7ce
JB
570 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
571 chargingStation,
ef6fa3fb 572 connectorId,
4ecff7ce
JB
573 OCPP16ChargePointStatus.Charging
574 );
e7aeea18 575 logger.info(
54ebb82c 576 `${chargingStation.logPrefix()} Transaction with id ${payload.transactionId.toString()} STARTED on ${
44eb6026
JB
577 chargingStation.stationInfo.chargingStationId
578 }#${connectorId.toString()} for idTag '${requestPayload.idTag}'`
e7aeea18 579 );
08f130a0 580 if (chargingStation.stationInfo.powerSharedByConnectors) {
fa7bccf4 581 chargingStation.powerDivider++;
c0560973 582 }
17ac262c
JB
583 const configuredMeterValueSampleInterval =
584 ChargingStationConfigurationUtils.getConfigurationKey(
585 chargingStation,
586 OCPP16StandardParametersKey.MeterValueSampleInterval
587 );
08f130a0 588 chargingStation.startMeterValues(
e7aeea18
JB
589 connectorId,
590 configuredMeterValueSampleInterval
591 ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000
d3195f0a 592 : Constants.DEFAULT_METER_VALUES_INTERVAL
e7aeea18 593 );
c0560973 594 } else {
e7aeea18 595 logger.warn(
54ebb82c 596 `${chargingStation.logPrefix()} Starting transaction with id ${payload.transactionId.toString()} REJECTED with status '${
44eb6026
JB
597 payload.idTagInfo?.status
598 }', idTag '${requestPayload.idTag}'`
e7aeea18 599 );
08f130a0 600 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
601 }
602 }
603
08f130a0
JB
604 private async resetConnectorOnStartTransactionError(
605 chargingStation: ChargingStation,
606 connectorId: number
607 ): Promise<void> {
04b1261c
JB
608 ChargingStationUtils.resetConnectorStatus(chargingStation.getConnectorStatus(connectorId));
609 chargingStation.stopMeterValues(connectorId);
610 parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(chargingStation));
e7aeea18 611 if (
721646e9 612 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
e7aeea18 613 ) {
4ecff7ce
JB
614 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
615 chargingStation,
ef6fa3fb 616 connectorId,
4ecff7ce
JB
617 OCPP16ChargePointStatus.Available
618 );
c0560973
JB
619 }
620 }
621
e7aeea18 622 private async handleResponseStopTransaction(
08f130a0 623 chargingStation: ChargingStation,
e7aeea18 624 payload: OCPP16StopTransactionResponse,
ef6fa3fb 625 requestPayload: OCPP16StopTransactionRequest
e7aeea18 626 ): Promise<void> {
08f130a0 627 const transactionConnectorId = chargingStation.getConnectorIdByTransactionId(
f479a792
JB
628 requestPayload.transactionId
629 );
d812bdcb 630 if (Utils.isNullOrUndefined(transactionConnectorId)) {
e7aeea18 631 logger.error(
54ebb82c 632 `${chargingStation.logPrefix()} Trying to stop a non existing transaction with id ${requestPayload.transactionId.toString()}`
e7aeea18 633 );
c0560973
JB
634 return;
635 }
2cace1a5
JB
636 chargingStation.getBeginEndMeterValues() === true &&
637 chargingStation.getOcppStrictCompliance() === false &&
638 chargingStation.getOutOfOrderEndMeterValues() === true &&
639 (await chargingStation.ocppRequestService.requestHandler<
640 OCPP16MeterValuesRequest,
641 OCPP16MeterValuesResponse
642 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
643 connectorId: transactionConnectorId,
644 transactionId: requestPayload.transactionId,
645 meterValue: [
646 OCPP16ServiceUtils.buildTransactionEndMeterValue(
647 chargingStation,
648 transactionConnectorId,
649 requestPayload.meterStop
650 ),
651 ],
652 }));
653 if (
654 chargingStation.isChargingStationAvailable() === false ||
655 chargingStation.isConnectorAvailable(transactionConnectorId) === false
656 ) {
4ecff7ce
JB
657 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
658 chargingStation,
659 transactionConnectorId,
660 OCPP16ChargePointStatus.Unavailable
661 );
c0560973 662 } else {
4ecff7ce
JB
663 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
664 chargingStation,
665 transactionConnectorId,
666 OCPP16ChargePointStatus.Available
667 );
2cace1a5
JB
668 }
669 if (chargingStation.stationInfo.powerSharedByConnectors) {
670 chargingStation.powerDivider--;
671 }
04b1261c
JB
672 ChargingStationUtils.resetConnectorStatus(
673 chargingStation.getConnectorStatus(transactionConnectorId)
674 );
675 chargingStation.stopMeterValues(transactionConnectorId);
676 parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(chargingStation));
54ebb82c 677 const logMsg = `${chargingStation.logPrefix()} Transaction with id ${requestPayload.transactionId.toString()} STOPPED on ${
8ca6874c 678 chargingStation.stationInfo.chargingStationId
d812bdcb 679 }#${transactionConnectorId?.toString()} with status '${
8ca6874c
JB
680 payload.idTagInfo?.status ?? 'undefined'
681 }'`;
2cace1a5
JB
682 if (
683 Utils.isNullOrUndefined(payload.idTagInfo) ||
684 payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED
685 ) {
686 logger.info(logMsg);
687 } else {
688 logger.warn(logMsg);
c0560973
JB
689 }
690 }
c0560973 691}