build(simulator): don't preserve modules
[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,
12 MessageChannelUtils,
13} from '../../../charging-station';
268a74bb 14import { OCPPError } from '../../../exception';
ef6fa3fb 15import {
268a74bb
JB
16 type ChangeAvailabilityResponse,
17 type ChangeConfigurationResponse,
18 type ClearChargingProfileResponse,
19 ErrorType,
20 type GenericResponse,
21 type GetConfigurationResponse,
22 type GetDiagnosticsResponse,
23 type JsonObject,
24 type JsonType,
8114d10e 25 OCPP16AuthorizationStatus,
27782dbc
JB
26 type OCPP16AuthorizeRequest,
27 type OCPP16AuthorizeResponse,
268a74bb 28 type OCPP16BootNotificationResponse,
268a74bb
JB
29 OCPP16ChargePointStatus,
30 type OCPP16DataTransferResponse,
31 type OCPP16DiagnosticsStatusNotificationResponse,
32 type OCPP16FirmwareStatusNotificationResponse,
41189456 33 type OCPP16GetCompositeScheduleResponse,
268a74bb
JB
34 type OCPP16HeartbeatResponse,
35 OCPP16IncomingRequestCommand,
36 type OCPP16MeterValuesRequest,
37 type OCPP16MeterValuesResponse,
38 OCPP16RequestCommand,
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';
60a74391 53import { Constants, Utils, 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
JB
286 ],
287 ]);
31f59c6d
JB
288 this.validatePayload = this.validatePayload.bind(this) as (
289 chargingStation: ChargingStation,
290 commandName: OCPP16RequestCommand,
291 payload: JsonType
292 ) => boolean;
58144adb
JB
293 }
294
f7f98c68 295 public async responseHandler(
08f130a0 296 chargingStation: ChargingStation,
e7aeea18 297 commandName: OCPP16RequestCommand,
5cc4b63b
JB
298 payload: JsonType,
299 requestPayload: JsonType
e7aeea18 300 ): Promise<void> {
ed6cfcff
JB
301 if (
302 chargingStation.isRegistered() === true ||
303 commandName === OCPP16RequestCommand.BOOT_NOTIFICATION
304 ) {
65554cc3 305 if (
ed6cfcff
JB
306 this.responseHandlers.has(commandName) === true &&
307 OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true
65554cc3 308 ) {
124f3553 309 try {
9c5c4195 310 this.validatePayload(chargingStation, commandName, payload);
08f130a0 311 await this.responseHandlers.get(commandName)(chargingStation, payload, requestPayload);
124f3553 312 } catch (error) {
6c8f5d90
JB
313 logger.error(
314 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
315 error
316 );
124f3553
JB
317 throw error;
318 }
319 } else {
320 // Throw exception
e7aeea18
JB
321 throw new OCPPError(
322 ErrorType.NOT_IMPLEMENTED,
6c8f5d90 323 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
e7aeea18
JB
324 payload,
325 null,
326 2
327 )}`,
7369e417
JB
328 commandName,
329 payload
e7aeea18 330 );
887fef76 331 }
c0560973 332 } else {
e7aeea18
JB
333 throw new OCPPError(
334 ErrorType.SECURITY_ERROR,
6c8f5d90 335 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
e7aeea18
JB
336 payload,
337 null,
338 2
439fc71b 339 )} while the charging station is not registered on the central server.`,
7369e417
JB
340 commandName,
341 payload
e7aeea18 342 );
c0560973
JB
343 }
344 }
345
9c5c4195
JB
346 private validatePayload(
347 chargingStation: ChargingStation,
348 commandName: OCPP16RequestCommand,
349 payload: JsonType
350 ): boolean {
45988780 351 if (this.jsonSchemas.has(commandName) === true) {
9c5c4195
JB
352 return this.validateResponsePayload(
353 chargingStation,
354 commandName,
355 this.jsonSchemas.get(commandName),
356 payload
357 );
358 }
359 logger.warn(
b3fc3ff5 360 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
9c5c4195
JB
361 );
362 return false;
363 }
364
08f130a0
JB
365 private handleResponseBootNotification(
366 chargingStation: ChargingStation,
367 payload: OCPP16BootNotificationResponse
368 ): void {
d270cc87 369 if (payload.status === RegistrationStatusEnumType.ACCEPTED) {
17ac262c
JB
370 ChargingStationConfigurationUtils.addConfigurationKey(
371 chargingStation,
f0f65a62 372 OCPP16StandardParametersKey.HeartbeatInterval,
a95873d8 373 payload.interval.toString(),
abe9e9dd 374 {},
a95873d8 375 { overwrite: true, save: true }
e7aeea18 376 );
17ac262c
JB
377 ChargingStationConfigurationUtils.addConfigurationKey(
378 chargingStation,
f0f65a62 379 OCPP16StandardParametersKey.HeartBeatInterval,
e7aeea18 380 payload.interval.toString(),
00db15b8 381 { visible: false },
a95873d8 382 { overwrite: true, save: true }
e7aeea18 383 );
8f953431 384 OCPP16ServiceUtils.startHeartbeatInterval(chargingStation, payload.interval);
672fed6e 385 }
d270cc87 386 if (Object.values(RegistrationStatusEnumType).includes(payload.status)) {
08f130a0 387 const logMsg = `${chargingStation.logPrefix()} Charging station in '${
e7aeea18
JB
388 payload.status
389 }' state on the central server`;
d270cc87 390 payload.status === RegistrationStatusEnumType.REJECTED
e7aeea18
JB
391 ? logger.warn(logMsg)
392 : logger.info(logMsg);
c0560973 393 } else {
e7aeea18 394 logger.error(
44eb6026 395 `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`,
e7aeea18
JB
396 payload
397 );
c0560973
JB
398 }
399 }
400
e7aeea18 401 private handleResponseAuthorize(
08f130a0 402 chargingStation: ChargingStation,
e7aeea18 403 payload: OCPP16AuthorizeResponse,
ef6fa3fb 404 requestPayload: OCPP16AuthorizeRequest
e7aeea18 405 ): void {
58144adb 406 let authorizeConnectorId: number;
ded57f02
JB
407 if (chargingStation.hasEvses) {
408 for (const [evseId, evseStatus] of chargingStation.evses) {
409 if (evseId > 0) {
410 for (const [connectorId, connectorStatus] of evseStatus.connectors) {
411 if (connectorStatus?.authorizeIdTag === requestPayload.idTag) {
412 authorizeConnectorId = connectorId;
413 break;
414 }
415 }
416 }
417 }
418 } else {
419 for (const connectorId of chargingStation.connectors.keys()) {
420 if (
421 connectorId > 0 &&
422 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag === requestPayload.idTag
423 ) {
424 authorizeConnectorId = connectorId;
425 break;
426 }
58144adb
JB
427 }
428 }
9bb1159e 429 const authorizeConnectorIdDefined = !Utils.isNullOrUndefined(authorizeConnectorId);
58144adb 430 if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
44eb6026 431 authorizeConnectorIdDefined &&
1984f194 432 (chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true);
e7aeea18 433 logger.debug(
2585c6e9 434 `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' accepted${
54ebb82c 435 authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : ''
1984f194 436 }`
e7aeea18 437 );
58144adb 438 } else {
44eb6026 439 if (authorizeConnectorIdDefined) {
1984f194 440 chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false;
d812bdcb 441 delete chargingStation.getConnectorStatus(authorizeConnectorId)?.authorizeIdTag;
1984f194 442 }
e7aeea18 443 logger.debug(
2585c6e9 444 `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' rejected with status '${
e7aeea18 445 payload.idTagInfo.status
54ebb82c 446 }'${authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : ''}`
e7aeea18 447 );
58144adb
JB
448 }
449 }
450
e7aeea18 451 private async handleResponseStartTransaction(
08f130a0 452 chargingStation: ChargingStation,
e7aeea18 453 payload: OCPP16StartTransactionResponse,
ef6fa3fb 454 requestPayload: OCPP16StartTransactionRequest
e7aeea18 455 ): Promise<void> {
649287f8
JB
456 const transactionConnectorId = requestPayload.connectorId;
457 if (
458 transactionConnectorId === 0 ||
459 chargingStation.hasConnector(transactionConnectorId) === false
460 ) {
e7aeea18 461 logger.error(
649287f8 462 `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector id ${transactionConnectorId.toString()}`
e7aeea18 463 );
c0560973
JB
464 return;
465 }
e7aeea18 466 if (
649287f8
JB
467 chargingStation.getConnectorStatus(transactionConnectorId)?.transactionRemoteStarted ===
468 true &&
3a13fc92
JB
469 chargingStation.getAuthorizeRemoteTxRequests() === true &&
470 chargingStation.getLocalAuthListEnabled() === true &&
f911a4af 471 chargingStation.hasIdTags() &&
649287f8 472 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized === false
e7aeea18
JB
473 ) {
474 logger.error(
44eb6026 475 `${chargingStation.logPrefix()} Trying to start a transaction with a not local authorized idTag ${
649287f8
JB
476 chargingStation.getConnectorStatus(transactionConnectorId)?.localAuthorizeIdTag
477 } on connector id ${transactionConnectorId.toString()}`
e7aeea18 478 );
649287f8 479 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
a2653482
JB
480 return;
481 }
e7aeea18 482 if (
649287f8
JB
483 chargingStation.getConnectorStatus(transactionConnectorId)?.transactionRemoteStarted ===
484 true &&
3a13fc92
JB
485 chargingStation.getAuthorizeRemoteTxRequests() === true &&
486 chargingStation.getMustAuthorizeAtRemoteStart() === true &&
649287f8
JB
487 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized === false &&
488 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagAuthorized === false
e7aeea18
JB
489 ) {
490 logger.error(
44eb6026 491 `${chargingStation.logPrefix()} Trying to start a transaction with a not authorized idTag ${
649287f8
JB
492 chargingStation.getConnectorStatus(transactionConnectorId)?.authorizeIdTag
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)?.idTagAuthorized &&
500 chargingStation.getConnectorStatus(transactionConnectorId)?.authorizeIdTag !==
501 requestPayload.idTag
e7aeea18
JB
502 ) {
503 logger.error(
44eb6026
JB
504 `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
505 requestPayload.idTag
506 } different from the authorize request one ${
649287f8
JB
507 chargingStation.getConnectorStatus(transactionConnectorId)?.authorizeIdTag
508 } on connector id ${transactionConnectorId.toString()}`
e7aeea18 509 );
649287f8 510 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
a2653482
JB
511 return;
512 }
e7aeea18 513 if (
649287f8
JB
514 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized &&
515 chargingStation.getConnectorStatus(transactionConnectorId)?.localAuthorizeIdTag !==
516 requestPayload.idTag
e7aeea18
JB
517 ) {
518 logger.error(
44eb6026
JB
519 `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
520 requestPayload.idTag
521 } different from the local authorized one ${
649287f8
JB
522 chargingStation.getConnectorStatus(transactionConnectorId)?.localAuthorizeIdTag
523 } on connector id ${transactionConnectorId.toString()}`
e7aeea18 524 );
649287f8 525 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
163547b1
JB
526 return;
527 }
649287f8
JB
528 if (chargingStation.getConnectorStatus(transactionConnectorId)?.transactionStarted === true) {
529 logger.error(
530 `${chargingStation.logPrefix()} Trying to start a transaction on an already used connector id ${transactionConnectorId.toString()}:`,
531 chargingStation.getConnectorStatus(transactionConnectorId)
e7aeea18 532 );
c0560973
JB
533 return;
534 }
649287f8
JB
535 if (chargingStation.hasEvses) {
536 for (const [evseId, evseStatus] of chargingStation.evses) {
537 if (evseStatus.connectors.size > 1) {
538 for (const [connectorId, connectorStatus] of evseStatus.connectors) {
539 if (
540 transactionConnectorId !== connectorId &&
541 connectorStatus?.transactionStarted === true
542 ) {
543 logger.error(
544 `${chargingStation.logPrefix()} Trying to start a transaction on an already used evse id ${evseId.toString()}:`,
545 evseStatus
546 );
547 await this.resetConnectorOnStartTransactionError(
548 chargingStation,
549 transactionConnectorId
550 );
551 return;
552 }
553 }
554 }
555 }
556 }
e7aeea18 557 if (
649287f8 558 chargingStation.getConnectorStatus(transactionConnectorId)?.status !==
721646e9 559 OCPP16ChargePointStatus.Available &&
649287f8
JB
560 chargingStation.getConnectorStatus(transactionConnectorId)?.status !==
561 OCPP16ChargePointStatus.Preparing
e7aeea18
JB
562 ) {
563 logger.error(
649287f8
JB
564 `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${transactionConnectorId.toString()} with status ${
565 chargingStation.getConnectorStatus(transactionConnectorId)?.status
e7aeea18
JB
566 }`
567 );
290d006c
JB
568 return;
569 }
54ebb82c
JB
570 if (!Number.isInteger(payload.transactionId)) {
571 logger.warn(
649287f8 572 `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${transactionConnectorId.toString()} with a non integer transaction id ${
54ebb82c
JB
573 payload.transactionId
574 }, converting to integer`
575 );
576 payload.transactionId = Utils.convertToInt(payload.transactionId);
577 }
c0560973 578
f0c6ed89 579 if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
649287f8
JB
580 chargingStation.getConnectorStatus(transactionConnectorId).transactionStarted = true;
581 chargingStation.getConnectorStatus(transactionConnectorId).transactionId =
582 payload.transactionId;
583 chargingStation.getConnectorStatus(transactionConnectorId).transactionIdTag =
584 requestPayload.idTag;
08f130a0 585 chargingStation.getConnectorStatus(
649287f8 586 transactionConnectorId
e7aeea18 587 ).transactionEnergyActiveImportRegisterValue = 0;
649287f8 588 chargingStation.getConnectorStatus(transactionConnectorId).transactionBeginMeterValue =
e7aeea18 589 OCPP16ServiceUtils.buildTransactionBeginMeterValue(
08f130a0 590 chargingStation,
649287f8 591 transactionConnectorId,
e7aeea18
JB
592 requestPayload.meterStart
593 );
08f130a0
JB
594 chargingStation.getBeginEndMeterValues() &&
595 (await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
596 OCPP16MeterValuesRequest,
597 OCPP16MeterValuesResponse
08f130a0 598 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
649287f8 599 connectorId: transactionConnectorId,
ef6fa3fb 600 transactionId: payload.transactionId,
649287f8
JB
601 meterValue: [
602 chargingStation.getConnectorStatus(transactionConnectorId).transactionBeginMeterValue,
603 ],
ef6fa3fb 604 }));
4ecff7ce
JB
605 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
606 chargingStation,
649287f8 607 transactionConnectorId,
4ecff7ce
JB
608 OCPP16ChargePointStatus.Charging
609 );
e7aeea18 610 logger.info(
54ebb82c 611 `${chargingStation.logPrefix()} Transaction with id ${payload.transactionId.toString()} STARTED on ${
44eb6026 612 chargingStation.stationInfo.chargingStationId
649287f8 613 }#${transactionConnectorId.toString()} for idTag '${requestPayload.idTag}'`
e7aeea18 614 );
08f130a0 615 if (chargingStation.stationInfo.powerSharedByConnectors) {
fa7bccf4 616 chargingStation.powerDivider++;
c0560973 617 }
17ac262c
JB
618 const configuredMeterValueSampleInterval =
619 ChargingStationConfigurationUtils.getConfigurationKey(
620 chargingStation,
621 OCPP16StandardParametersKey.MeterValueSampleInterval
622 );
08f130a0 623 chargingStation.startMeterValues(
649287f8 624 transactionConnectorId,
e7aeea18
JB
625 configuredMeterValueSampleInterval
626 ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000
d3195f0a 627 : Constants.DEFAULT_METER_VALUES_INTERVAL
e7aeea18 628 );
c0560973 629 } else {
e7aeea18 630 logger.warn(
54ebb82c 631 `${chargingStation.logPrefix()} Starting transaction with id ${payload.transactionId.toString()} REJECTED with status '${
44eb6026
JB
632 payload.idTagInfo?.status
633 }', idTag '${requestPayload.idTag}'`
e7aeea18 634 );
649287f8 635 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
a2653482
JB
636 }
637 }
638
08f130a0
JB
639 private async resetConnectorOnStartTransactionError(
640 chargingStation: ChargingStation,
641 connectorId: number
642 ): Promise<void> {
04b1261c
JB
643 ChargingStationUtils.resetConnectorStatus(chargingStation.getConnectorStatus(connectorId));
644 chargingStation.stopMeterValues(connectorId);
645 parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(chargingStation));
e7aeea18 646 if (
721646e9 647 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
e7aeea18 648 ) {
4ecff7ce
JB
649 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
650 chargingStation,
ef6fa3fb 651 connectorId,
4ecff7ce
JB
652 OCPP16ChargePointStatus.Available
653 );
c0560973
JB
654 }
655 }
656
e7aeea18 657 private async handleResponseStopTransaction(
08f130a0 658 chargingStation: ChargingStation,
e7aeea18 659 payload: OCPP16StopTransactionResponse,
ef6fa3fb 660 requestPayload: OCPP16StopTransactionRequest
e7aeea18 661 ): Promise<void> {
08f130a0 662 const transactionConnectorId = chargingStation.getConnectorIdByTransactionId(
f479a792
JB
663 requestPayload.transactionId
664 );
d812bdcb 665 if (Utils.isNullOrUndefined(transactionConnectorId)) {
e7aeea18 666 logger.error(
54ebb82c 667 `${chargingStation.logPrefix()} Trying to stop a non existing transaction with id ${requestPayload.transactionId.toString()}`
e7aeea18 668 );
c0560973
JB
669 return;
670 }
2cace1a5
JB
671 chargingStation.getBeginEndMeterValues() === true &&
672 chargingStation.getOcppStrictCompliance() === false &&
673 chargingStation.getOutOfOrderEndMeterValues() === true &&
674 (await chargingStation.ocppRequestService.requestHandler<
675 OCPP16MeterValuesRequest,
676 OCPP16MeterValuesResponse
677 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
678 connectorId: transactionConnectorId,
679 transactionId: requestPayload.transactionId,
680 meterValue: [
681 OCPP16ServiceUtils.buildTransactionEndMeterValue(
682 chargingStation,
683 transactionConnectorId,
684 requestPayload.meterStop
685 ),
686 ],
687 }));
688 if (
689 chargingStation.isChargingStationAvailable() === false ||
690 chargingStation.isConnectorAvailable(transactionConnectorId) === false
691 ) {
4ecff7ce
JB
692 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
693 chargingStation,
694 transactionConnectorId,
695 OCPP16ChargePointStatus.Unavailable
696 );
c0560973 697 } else {
4ecff7ce
JB
698 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
699 chargingStation,
700 transactionConnectorId,
701 OCPP16ChargePointStatus.Available
702 );
2cace1a5
JB
703 }
704 if (chargingStation.stationInfo.powerSharedByConnectors) {
705 chargingStation.powerDivider--;
706 }
04b1261c
JB
707 ChargingStationUtils.resetConnectorStatus(
708 chargingStation.getConnectorStatus(transactionConnectorId)
709 );
710 chargingStation.stopMeterValues(transactionConnectorId);
711 parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(chargingStation));
54ebb82c 712 const logMsg = `${chargingStation.logPrefix()} Transaction with id ${requestPayload.transactionId.toString()} STOPPED on ${
8ca6874c 713 chargingStation.stationInfo.chargingStationId
d812bdcb 714 }#${transactionConnectorId?.toString()} with status '${
8ca6874c
JB
715 payload.idTagInfo?.status ?? 'undefined'
716 }'`;
2cace1a5
JB
717 if (
718 Utils.isNullOrUndefined(payload.idTagInfo) ||
719 payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED
720 ) {
721 logger.info(logMsg);
722 } else {
723 logger.warn(logMsg);
c0560973
JB
724 }
725 }
c0560973 726}