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