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