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