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