refactor: remove unneeded connector 0 handling at connectors init
[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
28e78158 444 if (connectorId === 0 || !chargingStation.connectors.has(connectorId)) {
e7aeea18 445 logger.error(
2585c6e9 446 `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector id ${connectorId.toString()}`
e7aeea18 447 );
c0560973
JB
448 return;
449 }
e7aeea18 450 if (
72092cfc 451 chargingStation.getConnectorStatus(connectorId)?.transactionRemoteStarted === true &&
3a13fc92
JB
452 chargingStation.getAuthorizeRemoteTxRequests() === true &&
453 chargingStation.getLocalAuthListEnabled() === true &&
f911a4af 454 chargingStation.hasIdTags() &&
72092cfc 455 chargingStation.getConnectorStatus(connectorId)?.idTagLocalAuthorized === false
e7aeea18
JB
456 ) {
457 logger.error(
44eb6026 458 `${chargingStation.logPrefix()} Trying to start a transaction with a not local authorized idTag ${
72092cfc 459 chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag
2585c6e9 460 } on connector id ${connectorId.toString()}`
e7aeea18 461 );
08f130a0 462 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
463 return;
464 }
e7aeea18 465 if (
72092cfc 466 chargingStation.getConnectorStatus(connectorId)?.transactionRemoteStarted === true &&
3a13fc92
JB
467 chargingStation.getAuthorizeRemoteTxRequests() === true &&
468 chargingStation.getMustAuthorizeAtRemoteStart() === true &&
72092cfc
JB
469 chargingStation.getConnectorStatus(connectorId)?.idTagLocalAuthorized === false &&
470 chargingStation.getConnectorStatus(connectorId)?.idTagAuthorized === false
e7aeea18
JB
471 ) {
472 logger.error(
44eb6026 473 `${chargingStation.logPrefix()} Trying to start a transaction with a not authorized idTag ${
72092cfc 474 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag
2585c6e9 475 } on connector id ${connectorId.toString()}`
e7aeea18 476 );
08f130a0 477 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
478 return;
479 }
e7aeea18 480 if (
72092cfc
JB
481 chargingStation.getConnectorStatus(connectorId)?.idTagAuthorized &&
482 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag !== requestPayload.idTag
e7aeea18
JB
483 ) {
484 logger.error(
44eb6026
JB
485 `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
486 requestPayload.idTag
487 } different from the authorize request one ${
72092cfc 488 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag
2585c6e9 489 } on connector id ${connectorId.toString()}`
e7aeea18 490 );
08f130a0 491 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
492 return;
493 }
e7aeea18 494 if (
72092cfc
JB
495 chargingStation.getConnectorStatus(connectorId)?.idTagLocalAuthorized &&
496 chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag !== requestPayload.idTag
e7aeea18
JB
497 ) {
498 logger.error(
44eb6026
JB
499 `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
500 requestPayload.idTag
501 } different from the local authorized one ${
72092cfc 502 chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag
2585c6e9 503 } on connector id ${connectorId.toString()}`
e7aeea18 504 );
08f130a0 505 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
163547b1
JB
506 return;
507 }
5e3cb728 508 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
e7aeea18 509 logger.debug(
44eb6026 510 `${chargingStation.logPrefix()} Trying to start a transaction on an already used connector ${connectorId.toString()}: %j`,
08f130a0 511 chargingStation.getConnectorStatus(connectorId)
e7aeea18 512 );
c0560973
JB
513 return;
514 }
e7aeea18 515 if (
08f130a0 516 chargingStation.getConnectorStatus(connectorId)?.status !==
721646e9
JB
517 OCPP16ChargePointStatus.Available &&
518 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Preparing
e7aeea18
JB
519 ) {
520 logger.error(
08f130a0
JB
521 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
522 chargingStation.getConnectorStatus(connectorId)?.status
e7aeea18
JB
523 }`
524 );
290d006c
JB
525 return;
526 }
fc040c43
JB
527 // if (!Number.isInteger(payload.transactionId)) {
528 // logger.warn(
529 // `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${
530 // payload.transactionId
531 // }, converting to integer`
532 // );
533 // payload.transactionId = Utils.convertToInt(payload.transactionId);
534 // }
c0560973 535
f0c6ed89 536 if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
08f130a0
JB
537 chargingStation.getConnectorStatus(connectorId).transactionStarted = true;
538 chargingStation.getConnectorStatus(connectorId).transactionId = payload.transactionId;
539 chargingStation.getConnectorStatus(connectorId).transactionIdTag = requestPayload.idTag;
540 chargingStation.getConnectorStatus(
e7aeea18
JB
541 connectorId
542 ).transactionEnergyActiveImportRegisterValue = 0;
08f130a0 543 chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue =
e7aeea18 544 OCPP16ServiceUtils.buildTransactionBeginMeterValue(
08f130a0 545 chargingStation,
e7aeea18
JB
546 connectorId,
547 requestPayload.meterStart
548 );
08f130a0
JB
549 chargingStation.getBeginEndMeterValues() &&
550 (await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
551 OCPP16MeterValuesRequest,
552 OCPP16MeterValuesResponse
08f130a0 553 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
93b4a429 554 connectorId,
ef6fa3fb 555 transactionId: payload.transactionId,
7369e417 556 meterValue: [chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue],
ef6fa3fb 557 }));
4ecff7ce
JB
558 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
559 chargingStation,
ef6fa3fb 560 connectorId,
4ecff7ce
JB
561 OCPP16ChargePointStatus.Charging
562 );
e7aeea18 563 logger.info(
44eb6026
JB
564 `${chargingStation.logPrefix()} Transaction ${payload.transactionId.toString()} STARTED on ${
565 chargingStation.stationInfo.chargingStationId
566 }#${connectorId.toString()} for idTag '${requestPayload.idTag}'`
e7aeea18 567 );
08f130a0 568 if (chargingStation.stationInfo.powerSharedByConnectors) {
fa7bccf4 569 chargingStation.powerDivider++;
c0560973 570 }
17ac262c
JB
571 const configuredMeterValueSampleInterval =
572 ChargingStationConfigurationUtils.getConfigurationKey(
573 chargingStation,
574 OCPP16StandardParametersKey.MeterValueSampleInterval
575 );
08f130a0 576 chargingStation.startMeterValues(
e7aeea18
JB
577 connectorId,
578 configuredMeterValueSampleInterval
579 ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000
d3195f0a 580 : Constants.DEFAULT_METER_VALUES_INTERVAL
e7aeea18 581 );
c0560973 582 } else {
e7aeea18 583 logger.warn(
44eb6026
JB
584 `${chargingStation.logPrefix()} Starting transaction id ${payload.transactionId.toString()} REJECTED with status '${
585 payload.idTagInfo?.status
586 }', idTag '${requestPayload.idTag}'`
e7aeea18 587 );
08f130a0 588 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
589 }
590 }
591
08f130a0
JB
592 private async resetConnectorOnStartTransactionError(
593 chargingStation: ChargingStation,
594 connectorId: number
595 ): Promise<void> {
04b1261c
JB
596 ChargingStationUtils.resetConnectorStatus(chargingStation.getConnectorStatus(connectorId));
597 chargingStation.stopMeterValues(connectorId);
598 parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(chargingStation));
e7aeea18 599 if (
721646e9 600 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
e7aeea18 601 ) {
4ecff7ce
JB
602 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
603 chargingStation,
ef6fa3fb 604 connectorId,
4ecff7ce
JB
605 OCPP16ChargePointStatus.Available
606 );
c0560973
JB
607 }
608 }
609
e7aeea18 610 private async handleResponseStopTransaction(
08f130a0 611 chargingStation: ChargingStation,
e7aeea18 612 payload: OCPP16StopTransactionResponse,
ef6fa3fb 613 requestPayload: OCPP16StopTransactionRequest
e7aeea18 614 ): Promise<void> {
08f130a0 615 const transactionConnectorId = chargingStation.getConnectorIdByTransactionId(
f479a792
JB
616 requestPayload.transactionId
617 );
d812bdcb 618 if (Utils.isNullOrUndefined(transactionConnectorId)) {
e7aeea18 619 logger.error(
44eb6026 620 `${chargingStation.logPrefix()} Trying to stop a non existing transaction ${requestPayload.transactionId.toString()}`
e7aeea18 621 );
c0560973
JB
622 return;
623 }
2cace1a5
JB
624 chargingStation.getBeginEndMeterValues() === true &&
625 chargingStation.getOcppStrictCompliance() === false &&
626 chargingStation.getOutOfOrderEndMeterValues() === true &&
627 (await chargingStation.ocppRequestService.requestHandler<
628 OCPP16MeterValuesRequest,
629 OCPP16MeterValuesResponse
630 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
631 connectorId: transactionConnectorId,
632 transactionId: requestPayload.transactionId,
633 meterValue: [
634 OCPP16ServiceUtils.buildTransactionEndMeterValue(
635 chargingStation,
636 transactionConnectorId,
637 requestPayload.meterStop
638 ),
639 ],
640 }));
641 if (
642 chargingStation.isChargingStationAvailable() === false ||
643 chargingStation.isConnectorAvailable(transactionConnectorId) === false
644 ) {
4ecff7ce
JB
645 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
646 chargingStation,
647 transactionConnectorId,
648 OCPP16ChargePointStatus.Unavailable
649 );
c0560973 650 } else {
4ecff7ce
JB
651 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
652 chargingStation,
653 transactionConnectorId,
654 OCPP16ChargePointStatus.Available
655 );
2cace1a5
JB
656 }
657 if (chargingStation.stationInfo.powerSharedByConnectors) {
658 chargingStation.powerDivider--;
659 }
04b1261c
JB
660 ChargingStationUtils.resetConnectorStatus(
661 chargingStation.getConnectorStatus(transactionConnectorId)
662 );
663 chargingStation.stopMeterValues(transactionConnectorId);
664 parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(chargingStation));
8ca6874c
JB
665 const logMsg = `${chargingStation.logPrefix()} Transaction ${requestPayload.transactionId.toString()} STOPPED on ${
666 chargingStation.stationInfo.chargingStationId
d812bdcb 667 }#${transactionConnectorId?.toString()} with status '${
8ca6874c
JB
668 payload.idTagInfo?.status ?? 'undefined'
669 }'`;
2cace1a5
JB
670 if (
671 Utils.isNullOrUndefined(payload.idTagInfo) ||
672 payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED
673 ) {
674 logger.info(logMsg);
675 } else {
676 logger.warn(logMsg);
c0560973
JB
677 }
678 }
c0560973 679}