Merge branch 'main' into reservation-feature
[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,
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';
7671fa0b 52import { Constants, MessageChannelUtils, Utils, logger } from '../../../utils';
4c3c0d59 53import { OCPPResponseService } from '../OCPPResponseService';
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>(
51022aa0 87 'assets/json-schemas/ocpp/1.6/BootNotificationResponse.json',
1b271a54
JB
88 moduleName,
89 'constructor'
e9a4164c 90 ),
b52c969d
JB
91 ],
92 [
93 OCPP16RequestCommand.HEARTBEAT,
130783a7 94 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16HeartbeatResponse>(
51022aa0 95 'assets/json-schemas/ocpp/1.6/HeartbeatResponse.json',
1b271a54
JB
96 moduleName,
97 'constructor'
e9a4164c 98 ),
b52c969d
JB
99 ],
100 [
101 OCPP16RequestCommand.AUTHORIZE,
130783a7 102 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16AuthorizeResponse>(
51022aa0 103 'assets/json-schemas/ocpp/1.6/AuthorizeResponse.json',
1b271a54
JB
104 moduleName,
105 'constructor'
e9a4164c 106 ),
b52c969d
JB
107 ],
108 [
109 OCPP16RequestCommand.START_TRANSACTION,
130783a7 110 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StartTransactionResponse>(
51022aa0 111 'assets/json-schemas/ocpp/1.6/StartTransactionResponse.json',
1b271a54
JB
112 moduleName,
113 'constructor'
e9a4164c 114 ),
b52c969d
JB
115 ],
116 [
117 OCPP16RequestCommand.STOP_TRANSACTION,
130783a7 118 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StopTransactionResponse>(
51022aa0 119 'assets/json-schemas/ocpp/1.6/StopTransactionResponse.json',
1b271a54
JB
120 moduleName,
121 'constructor'
e9a4164c 122 ),
b52c969d
JB
123 ],
124 [
125 OCPP16RequestCommand.STATUS_NOTIFICATION,
130783a7 126 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StatusNotificationResponse>(
51022aa0 127 'assets/json-schemas/ocpp/1.6/StatusNotificationResponse.json',
1b271a54
JB
128 moduleName,
129 'constructor'
e9a4164c 130 ),
b52c969d
JB
131 ],
132 [
133 OCPP16RequestCommand.METER_VALUES,
130783a7 134 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16MeterValuesResponse>(
51022aa0 135 'assets/json-schemas/ocpp/1.6/MeterValuesResponse.json',
1b271a54
JB
136 moduleName,
137 'constructor'
e9a4164c 138 ),
b52c969d
JB
139 ],
140 [
141 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
130783a7 142 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DiagnosticsStatusNotificationResponse>(
51022aa0 143 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotificationResponse.json',
1b271a54
JB
144 moduleName,
145 'constructor'
e9a4164c 146 ),
b52c969d 147 ],
91a7d3ea
JB
148 [
149 OCPP16RequestCommand.DATA_TRANSFER,
130783a7 150 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferResponse>(
51022aa0 151 'assets/json-schemas/ocpp/1.6/DataTransferResponse.json',
1b271a54
JB
152 moduleName,
153 'constructor'
e9a4164c 154 ),
91a7d3ea 155 ],
c9a4f9ea
JB
156 [
157 OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION,
130783a7 158 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16FirmwareStatusNotificationResponse>(
51022aa0 159 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotificationResponse.json',
1b271a54
JB
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>(
51022aa0 169 'assets/json-schemas/ocpp/1.6/ResetResponse.json',
1b271a54
JB
170 moduleName,
171 'constructor'
e9a4164c 172 ),
02887891
JB
173 ],
174 [
175 OCPP16IncomingRequestCommand.CLEAR_CACHE,
130783a7 176 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
51022aa0 177 'assets/json-schemas/ocpp/1.6/ClearCacheResponse.json',
1b271a54
JB
178 moduleName,
179 'constructor'
e9a4164c 180 ),
02887891
JB
181 ],
182 [
183 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
130783a7 184 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeAvailabilityResponse>(
51022aa0 185 'assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json',
1b271a54
JB
186 moduleName,
187 'constructor'
e9a4164c 188 ),
02887891
JB
189 ],
190 [
191 OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
130783a7 192 OCPP16ServiceUtils.parseJsonSchemaFile<UnlockConnectorResponse>(
51022aa0 193 'assets/json-schemas/ocpp/1.6/UnlockConnectorResponse.json',
1b271a54
JB
194 moduleName,
195 'constructor'
e9a4164c 196 ),
02887891
JB
197 ],
198 [
199 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
130783a7 200 OCPP16ServiceUtils.parseJsonSchemaFile<GetConfigurationResponse>(
51022aa0 201 'assets/json-schemas/ocpp/1.6/GetConfigurationResponse.json',
1b271a54
JB
202 moduleName,
203 'constructor'
e9a4164c 204 ),
02887891
JB
205 ],
206 [
207 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
130783a7 208 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeConfigurationResponse>(
51022aa0 209 'assets/json-schemas/ocpp/1.6/ChangeConfigurationResponse.json',
1b271a54
JB
210 moduleName,
211 'constructor'
e9a4164c 212 ),
02887891 213 ],
41189456
JB
214 [
215 OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
216 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16GetCompositeScheduleResponse>(
51022aa0 217 'assets/json-schemas/ocpp/1.6/GetCompositeScheduleResponse.json',
41189456
JB
218 moduleName,
219 'constructor'
220 ),
221 ],
02887891
JB
222 [
223 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
130783a7 224 OCPP16ServiceUtils.parseJsonSchemaFile<SetChargingProfileResponse>(
51022aa0 225 'assets/json-schemas/ocpp/1.6/SetChargingProfileResponse.json',
1b271a54
JB
226 moduleName,
227 'constructor'
e9a4164c 228 ),
02887891
JB
229 ],
230 [
231 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
130783a7 232 OCPP16ServiceUtils.parseJsonSchemaFile<ClearChargingProfileResponse>(
51022aa0 233 'assets/json-schemas/ocpp/1.6/ClearChargingProfileResponse.json',
1b271a54
JB
234 moduleName,
235 'constructor'
e9a4164c 236 ),
02887891
JB
237 ],
238 [
239 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
130783a7 240 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
51022aa0 241 'assets/json-schemas/ocpp/1.6/RemoteStartTransactionResponse.json',
1b271a54
JB
242 moduleName,
243 'constructor'
e9a4164c 244 ),
02887891
JB
245 ],
246 [
247 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
130783a7 248 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
51022aa0 249 'assets/json-schemas/ocpp/1.6/RemoteStopTransactionResponse.json',
1b271a54
JB
250 moduleName,
251 'constructor'
e9a4164c 252 ),
02887891
JB
253 ],
254 [
255 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
130783a7 256 OCPP16ServiceUtils.parseJsonSchemaFile<GetDiagnosticsResponse>(
51022aa0 257 'assets/json-schemas/ocpp/1.6/GetDiagnosticsResponse.json',
1b271a54
JB
258 moduleName,
259 'constructor'
e9a4164c 260 ),
02887891
JB
261 ],
262 [
263 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
130783a7 264 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16TriggerMessageResponse>(
51022aa0 265 'assets/json-schemas/ocpp/1.6/TriggerMessageResponse.json',
1b271a54
JB
266 moduleName,
267 'constructor'
e9a4164c 268 ),
02887891
JB
269 ],
270 [
271 OCPP16IncomingRequestCommand.DATA_TRANSFER,
130783a7 272 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferResponse>(
51022aa0 273 'assets/json-schemas/ocpp/1.6/DataTransferResponse.json',
1b271a54
JB
274 moduleName,
275 'constructor'
e9a4164c 276 ),
02887891
JB
277 ],
278 [
279 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE,
130783a7 280 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16UpdateFirmwareResponse>(
51022aa0 281 'assets/json-schemas/ocpp/1.6/UpdateFirmwareResponse.json',
1b271a54
JB
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> {
649287f8
JB
455 const transactionConnectorId = requestPayload.connectorId;
456 if (
457 transactionConnectorId === 0 ||
458 chargingStation.hasConnector(transactionConnectorId) === false
459 ) {
e7aeea18 460 logger.error(
649287f8 461 `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector id ${transactionConnectorId.toString()}`
e7aeea18 462 );
c0560973
JB
463 return;
464 }
e7aeea18 465 if (
649287f8
JB
466 chargingStation.getConnectorStatus(transactionConnectorId)?.transactionRemoteStarted ===
467 true &&
3a13fc92
JB
468 chargingStation.getAuthorizeRemoteTxRequests() === true &&
469 chargingStation.getLocalAuthListEnabled() === true &&
f911a4af 470 chargingStation.hasIdTags() &&
649287f8 471 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized === false
e7aeea18
JB
472 ) {
473 logger.error(
44eb6026 474 `${chargingStation.logPrefix()} Trying to start a transaction with a not local authorized idTag ${
649287f8
JB
475 chargingStation.getConnectorStatus(transactionConnectorId)?.localAuthorizeIdTag
476 } on connector id ${transactionConnectorId.toString()}`
e7aeea18 477 );
649287f8 478 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
a2653482
JB
479 return;
480 }
e7aeea18 481 if (
649287f8
JB
482 chargingStation.getConnectorStatus(transactionConnectorId)?.transactionRemoteStarted ===
483 true &&
3a13fc92
JB
484 chargingStation.getAuthorizeRemoteTxRequests() === true &&
485 chargingStation.getMustAuthorizeAtRemoteStart() === true &&
649287f8
JB
486 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized === false &&
487 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagAuthorized === false
e7aeea18
JB
488 ) {
489 logger.error(
44eb6026 490 `${chargingStation.logPrefix()} Trying to start a transaction with a not authorized idTag ${
649287f8
JB
491 chargingStation.getConnectorStatus(transactionConnectorId)?.authorizeIdTag
492 } on connector id ${transactionConnectorId.toString()}`
e7aeea18 493 );
649287f8 494 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
a2653482
JB
495 return;
496 }
e7aeea18 497 if (
649287f8
JB
498 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagAuthorized &&
499 chargingStation.getConnectorStatus(transactionConnectorId)?.authorizeIdTag !==
500 requestPayload.idTag
e7aeea18
JB
501 ) {
502 logger.error(
44eb6026
JB
503 `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
504 requestPayload.idTag
505 } different from the authorize request one ${
649287f8
JB
506 chargingStation.getConnectorStatus(transactionConnectorId)?.authorizeIdTag
507 } on connector id ${transactionConnectorId.toString()}`
e7aeea18 508 );
649287f8 509 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
a2653482
JB
510 return;
511 }
e7aeea18 512 if (
649287f8
JB
513 chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized &&
514 chargingStation.getConnectorStatus(transactionConnectorId)?.localAuthorizeIdTag !==
515 requestPayload.idTag
e7aeea18
JB
516 ) {
517 logger.error(
44eb6026
JB
518 `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
519 requestPayload.idTag
520 } different from the local authorized one ${
649287f8
JB
521 chargingStation.getConnectorStatus(transactionConnectorId)?.localAuthorizeIdTag
522 } on connector id ${transactionConnectorId.toString()}`
e7aeea18 523 );
649287f8 524 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
163547b1
JB
525 return;
526 }
649287f8
JB
527 if (chargingStation.getConnectorStatus(transactionConnectorId)?.transactionStarted === true) {
528 logger.error(
529 `${chargingStation.logPrefix()} Trying to start a transaction on an already used connector id ${transactionConnectorId.toString()}:`,
530 chargingStation.getConnectorStatus(transactionConnectorId)
e7aeea18 531 );
c0560973
JB
532 return;
533 }
649287f8
JB
534 if (chargingStation.hasEvses) {
535 for (const [evseId, evseStatus] of chargingStation.evses) {
536 if (evseStatus.connectors.size > 1) {
537 for (const [connectorId, connectorStatus] of evseStatus.connectors) {
538 if (
539 transactionConnectorId !== connectorId &&
540 connectorStatus?.transactionStarted === true
541 ) {
542 logger.error(
543 `${chargingStation.logPrefix()} Trying to start a transaction on an already used evse id ${evseId.toString()}:`,
544 evseStatus
545 );
546 await this.resetConnectorOnStartTransactionError(
547 chargingStation,
548 transactionConnectorId
549 );
550 return;
551 }
552 }
553 }
554 }
555 }
e7aeea18 556 if (
649287f8 557 chargingStation.getConnectorStatus(transactionConnectorId)?.status !==
721646e9 558 OCPP16ChargePointStatus.Available &&
649287f8
JB
559 chargingStation.getConnectorStatus(transactionConnectorId)?.status !==
560 OCPP16ChargePointStatus.Preparing
e7aeea18
JB
561 ) {
562 logger.error(
649287f8
JB
563 `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${transactionConnectorId.toString()} with status ${
564 chargingStation.getConnectorStatus(transactionConnectorId)?.status
e7aeea18
JB
565 }`
566 );
290d006c
JB
567 return;
568 }
54ebb82c
JB
569 if (!Number.isInteger(payload.transactionId)) {
570 logger.warn(
649287f8 571 `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${transactionConnectorId.toString()} with a non integer transaction id ${
54ebb82c
JB
572 payload.transactionId
573 }, converting to integer`
574 );
575 payload.transactionId = Utils.convertToInt(payload.transactionId);
576 }
c0560973 577
f0c6ed89 578 if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
649287f8
JB
579 chargingStation.getConnectorStatus(transactionConnectorId).transactionStarted = true;
580 chargingStation.getConnectorStatus(transactionConnectorId).transactionId =
581 payload.transactionId;
582 chargingStation.getConnectorStatus(transactionConnectorId).transactionIdTag =
583 requestPayload.idTag;
08f130a0 584 chargingStation.getConnectorStatus(
649287f8 585 transactionConnectorId
e7aeea18 586 ).transactionEnergyActiveImportRegisterValue = 0;
649287f8 587 chargingStation.getConnectorStatus(transactionConnectorId).transactionBeginMeterValue =
e7aeea18 588 OCPP16ServiceUtils.buildTransactionBeginMeterValue(
08f130a0 589 chargingStation,
649287f8 590 transactionConnectorId,
e7aeea18
JB
591 requestPayload.meterStart
592 );
08f130a0
JB
593 chargingStation.getBeginEndMeterValues() &&
594 (await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
595 OCPP16MeterValuesRequest,
596 OCPP16MeterValuesResponse
08f130a0 597 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
649287f8 598 connectorId: transactionConnectorId,
ef6fa3fb 599 transactionId: payload.transactionId,
649287f8
JB
600 meterValue: [
601 chargingStation.getConnectorStatus(transactionConnectorId).transactionBeginMeterValue,
602 ],
ef6fa3fb 603 }));
4ecff7ce
JB
604 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
605 chargingStation,
649287f8 606 transactionConnectorId,
4ecff7ce
JB
607 OCPP16ChargePointStatus.Charging
608 );
e7aeea18 609 logger.info(
54ebb82c 610 `${chargingStation.logPrefix()} Transaction with id ${payload.transactionId.toString()} STARTED on ${
44eb6026 611 chargingStation.stationInfo.chargingStationId
649287f8 612 }#${transactionConnectorId.toString()} for idTag '${requestPayload.idTag}'`
e7aeea18 613 );
08f130a0 614 if (chargingStation.stationInfo.powerSharedByConnectors) {
1fe0632a 615 ++chargingStation.powerDivider;
c0560973 616 }
17ac262c
JB
617 const configuredMeterValueSampleInterval =
618 ChargingStationConfigurationUtils.getConfigurationKey(
619 chargingStation,
620 OCPP16StandardParametersKey.MeterValueSampleInterval
621 );
08f130a0 622 chargingStation.startMeterValues(
649287f8 623 transactionConnectorId,
e7aeea18
JB
624 configuredMeterValueSampleInterval
625 ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000
d3195f0a 626 : Constants.DEFAULT_METER_VALUES_INTERVAL
e7aeea18 627 );
c0560973 628 } else {
e7aeea18 629 logger.warn(
54ebb82c 630 `${chargingStation.logPrefix()} Starting transaction with id ${payload.transactionId.toString()} REJECTED with status '${
44eb6026
JB
631 payload.idTagInfo?.status
632 }', idTag '${requestPayload.idTag}'`
e7aeea18 633 );
649287f8 634 await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
a2653482
JB
635 }
636 }
637
08f130a0
JB
638 private async resetConnectorOnStartTransactionError(
639 chargingStation: ChargingStation,
640 connectorId: number
641 ): Promise<void> {
04b1261c
JB
642 ChargingStationUtils.resetConnectorStatus(chargingStation.getConnectorStatus(connectorId));
643 chargingStation.stopMeterValues(connectorId);
644 parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(chargingStation));
e7aeea18 645 if (
721646e9 646 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
e7aeea18 647 ) {
4ecff7ce
JB
648 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
649 chargingStation,
ef6fa3fb 650 connectorId,
4ecff7ce
JB
651 OCPP16ChargePointStatus.Available
652 );
c0560973
JB
653 }
654 }
655
e7aeea18 656 private async handleResponseStopTransaction(
08f130a0 657 chargingStation: ChargingStation,
e7aeea18 658 payload: OCPP16StopTransactionResponse,
ef6fa3fb 659 requestPayload: OCPP16StopTransactionRequest
e7aeea18 660 ): Promise<void> {
08f130a0 661 const transactionConnectorId = chargingStation.getConnectorIdByTransactionId(
f479a792
JB
662 requestPayload.transactionId
663 );
d812bdcb 664 if (Utils.isNullOrUndefined(transactionConnectorId)) {
e7aeea18 665 logger.error(
54ebb82c 666 `${chargingStation.logPrefix()} Trying to stop a non existing transaction with id ${requestPayload.transactionId.toString()}`
e7aeea18 667 );
c0560973
JB
668 return;
669 }
2cace1a5
JB
670 chargingStation.getBeginEndMeterValues() === true &&
671 chargingStation.getOcppStrictCompliance() === false &&
672 chargingStation.getOutOfOrderEndMeterValues() === true &&
673 (await chargingStation.ocppRequestService.requestHandler<
674 OCPP16MeterValuesRequest,
675 OCPP16MeterValuesResponse
676 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
677 connectorId: transactionConnectorId,
678 transactionId: requestPayload.transactionId,
679 meterValue: [
680 OCPP16ServiceUtils.buildTransactionEndMeterValue(
681 chargingStation,
682 transactionConnectorId,
683 requestPayload.meterStop
684 ),
685 ],
686 }));
687 if (
688 chargingStation.isChargingStationAvailable() === false ||
689 chargingStation.isConnectorAvailable(transactionConnectorId) === false
690 ) {
4ecff7ce
JB
691 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
692 chargingStation,
693 transactionConnectorId,
694 OCPP16ChargePointStatus.Unavailable
695 );
c0560973 696 } else {
4ecff7ce
JB
697 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
698 chargingStation,
699 transactionConnectorId,
700 OCPP16ChargePointStatus.Available
701 );
2cace1a5
JB
702 }
703 if (chargingStation.stationInfo.powerSharedByConnectors) {
704 chargingStation.powerDivider--;
705 }
04b1261c
JB
706 ChargingStationUtils.resetConnectorStatus(
707 chargingStation.getConnectorStatus(transactionConnectorId)
708 );
709 chargingStation.stopMeterValues(transactionConnectorId);
710 parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(chargingStation));
54ebb82c 711 const logMsg = `${chargingStation.logPrefix()} Transaction with id ${requestPayload.transactionId.toString()} STOPPED on ${
8ca6874c 712 chargingStation.stationInfo.chargingStationId
d812bdcb 713 }#${transactionConnectorId?.toString()} with status '${
8ca6874c
JB
714 payload.idTagInfo?.status ?? 'undefined'
715 }'`;
2cace1a5
JB
716 if (
717 Utils.isNullOrUndefined(payload.idTagInfo) ||
718 payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED
719 ) {
720 logger.info(logMsg);
721 } else {
722 logger.warn(logMsg);
c0560973
JB
723 }
724 }
c0560973 725}