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