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