feat: add initial support for evse definition in template
[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 20 type OCPP16BootNotificationResponse,
268a74bb
JB
21 OCPP16ChargePointStatus,
22 type OCPP16DataTransferResponse,
23 type OCPP16DiagnosticsStatusNotificationResponse,
24 type OCPP16FirmwareStatusNotificationResponse,
41189456 25 type OCPP16GetCompositeScheduleResponse,
268a74bb
JB
26 type OCPP16HeartbeatResponse,
27 OCPP16IncomingRequestCommand,
28 type OCPP16MeterValuesRequest,
29 type OCPP16MeterValuesResponse,
30 OCPP16RequestCommand,
31 OCPP16StandardParametersKey,
27782dbc
JB
32 type OCPP16StartTransactionRequest,
33 type OCPP16StartTransactionResponse,
268a74bb 34 type OCPP16StatusNotificationResponse,
27782dbc
JB
35 type OCPP16StopTransactionRequest,
36 type OCPP16StopTransactionResponse,
268a74bb
JB
37 type OCPP16TriggerMessageResponse,
38 type OCPP16UpdateFirmwareResponse,
39 OCPPVersion,
02887891
JB
40 RegistrationStatusEnumType,
41 type ResponseHandler,
268a74bb
JB
42 type SetChargingProfileResponse,
43 type UnlockConnectorResponse,
44} from '../../../types';
60a74391 45import { Constants, Utils, logger } from '../../../utils';
2896e06d 46import { OCPP16ServiceUtils, OCPPResponseService } from '../internal';
c0560973 47
909dcf2d
JB
48const moduleName = 'OCPP16ResponseService';
49
268a74bb 50export class OCPP16ResponseService extends OCPPResponseService {
b3fc3ff5
JB
51 public jsonIncomingRequestResponseSchemas: Map<
52 OCPP16IncomingRequestCommand,
53 JSONSchemaType<JsonObject>
54 >;
55
58144adb 56 private responseHandlers: Map<OCPP16RequestCommand, ResponseHandler>;
b52c969d 57 private jsonSchemas: Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>;
58144adb 58
08f130a0 59 public constructor() {
b768993d
JB
60 // if (new.target?.name === moduleName) {
61 // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
62 // }
d270cc87 63 super(OCPPVersion.VERSION_16);
58144adb
JB
64 this.responseHandlers = new Map<OCPP16RequestCommand, ResponseHandler>([
65 [OCPP16RequestCommand.BOOT_NOTIFICATION, this.handleResponseBootNotification.bind(this)],
b52c969d 66 [OCPP16RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this)],
58144adb
JB
67 [OCPP16RequestCommand.AUTHORIZE, this.handleResponseAuthorize.bind(this)],
68 [OCPP16RequestCommand.START_TRANSACTION, this.handleResponseStartTransaction.bind(this)],
69 [OCPP16RequestCommand.STOP_TRANSACTION, this.handleResponseStopTransaction.bind(this)],
b52c969d
JB
70 [OCPP16RequestCommand.STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
71 [OCPP16RequestCommand.METER_VALUES, this.emptyResponseHandler.bind(this)],
72 [OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
91a7d3ea 73 [OCPP16RequestCommand.DATA_TRANSFER, this.emptyResponseHandler.bind(this)],
c9a4f9ea 74 [OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
b52c969d
JB
75 ]);
76 this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>([
77 [
78 OCPP16RequestCommand.BOOT_NOTIFICATION,
130783a7 79 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16BootNotificationResponse>(
1b271a54
JB
80 '../../../assets/json-schemas/ocpp/1.6/BootNotificationResponse.json',
81 moduleName,
82 'constructor'
e9a4164c 83 ),
b52c969d
JB
84 ],
85 [
86 OCPP16RequestCommand.HEARTBEAT,
130783a7 87 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16HeartbeatResponse>(
1b271a54
JB
88 '../../../assets/json-schemas/ocpp/1.6/HeartbeatResponse.json',
89 moduleName,
90 'constructor'
e9a4164c 91 ),
b52c969d
JB
92 ],
93 [
94 OCPP16RequestCommand.AUTHORIZE,
130783a7 95 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16AuthorizeResponse>(
1b271a54
JB
96 '../../../assets/json-schemas/ocpp/1.6/AuthorizeResponse.json',
97 moduleName,
98 'constructor'
e9a4164c 99 ),
b52c969d
JB
100 ],
101 [
102 OCPP16RequestCommand.START_TRANSACTION,
130783a7 103 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StartTransactionResponse>(
1b271a54
JB
104 '../../../assets/json-schemas/ocpp/1.6/StartTransactionResponse.json',
105 moduleName,
106 'constructor'
e9a4164c 107 ),
b52c969d
JB
108 ],
109 [
110 OCPP16RequestCommand.STOP_TRANSACTION,
130783a7 111 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StopTransactionResponse>(
1b271a54
JB
112 '../../../assets/json-schemas/ocpp/1.6/StopTransactionResponse.json',
113 moduleName,
114 'constructor'
e9a4164c 115 ),
b52c969d
JB
116 ],
117 [
118 OCPP16RequestCommand.STATUS_NOTIFICATION,
130783a7 119 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StatusNotificationResponse>(
1b271a54
JB
120 '../../../assets/json-schemas/ocpp/1.6/StatusNotificationResponse.json',
121 moduleName,
122 'constructor'
e9a4164c 123 ),
b52c969d
JB
124 ],
125 [
126 OCPP16RequestCommand.METER_VALUES,
130783a7 127 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16MeterValuesResponse>(
1b271a54
JB
128 '../../../assets/json-schemas/ocpp/1.6/MeterValuesResponse.json',
129 moduleName,
130 'constructor'
e9a4164c 131 ),
b52c969d
JB
132 ],
133 [
134 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
130783a7 135 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DiagnosticsStatusNotificationResponse>(
1b271a54
JB
136 '../../../assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotificationResponse.json',
137 moduleName,
138 'constructor'
e9a4164c 139 ),
b52c969d 140 ],
91a7d3ea
JB
141 [
142 OCPP16RequestCommand.DATA_TRANSFER,
130783a7 143 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferResponse>(
1b271a54
JB
144 '../../../assets/json-schemas/ocpp/1.6/DataTransferResponse.json',
145 moduleName,
146 'constructor'
e9a4164c 147 ),
91a7d3ea 148 ],
c9a4f9ea
JB
149 [
150 OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION,
130783a7 151 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16FirmwareStatusNotificationResponse>(
1b271a54
JB
152 '../../../assets/json-schemas/ocpp/1.6/FirmwareStatusNotificationResponse.json',
153 moduleName,
154 'constructor'
e9a4164c 155 ),
c9a4f9ea 156 ],
58144adb 157 ]);
02887891
JB
158 this.jsonIncomingRequestResponseSchemas = new Map([
159 [
160 OCPP16IncomingRequestCommand.RESET,
130783a7 161 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
1b271a54
JB
162 '../../../assets/json-schemas/ocpp/1.6/ResetResponse.json',
163 moduleName,
164 'constructor'
e9a4164c 165 ),
02887891
JB
166 ],
167 [
168 OCPP16IncomingRequestCommand.CLEAR_CACHE,
130783a7 169 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
1b271a54
JB
170 '../../../assets/json-schemas/ocpp/1.6/ClearCacheResponse.json',
171 moduleName,
172 'constructor'
e9a4164c 173 ),
02887891
JB
174 ],
175 [
176 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
130783a7 177 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeAvailabilityResponse>(
1b271a54
JB
178 '../../../assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json',
179 moduleName,
180 'constructor'
e9a4164c 181 ),
02887891
JB
182 ],
183 [
184 OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
130783a7 185 OCPP16ServiceUtils.parseJsonSchemaFile<UnlockConnectorResponse>(
1b271a54
JB
186 '../../../assets/json-schemas/ocpp/1.6/UnlockConnectorResponse.json',
187 moduleName,
188 'constructor'
e9a4164c 189 ),
02887891
JB
190 ],
191 [
192 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
130783a7 193 OCPP16ServiceUtils.parseJsonSchemaFile<GetConfigurationResponse>(
1b271a54
JB
194 '../../../assets/json-schemas/ocpp/1.6/GetConfigurationResponse.json',
195 moduleName,
196 'constructor'
e9a4164c 197 ),
02887891
JB
198 ],
199 [
200 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
130783a7 201 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeConfigurationResponse>(
1b271a54
JB
202 '../../../assets/json-schemas/ocpp/1.6/ChangeConfigurationResponse.json',
203 moduleName,
204 'constructor'
e9a4164c 205 ),
02887891 206 ],
41189456
JB
207 [
208 OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
209 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16GetCompositeScheduleResponse>(
210 '../../../assets/json-schemas/ocpp/1.6/GetCompositeScheduleResponse.json',
211 moduleName,
212 'constructor'
213 ),
214 ],
02887891
JB
215 [
216 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
130783a7 217 OCPP16ServiceUtils.parseJsonSchemaFile<SetChargingProfileResponse>(
1b271a54
JB
218 '../../../assets/json-schemas/ocpp/1.6/SetChargingProfileResponse.json',
219 moduleName,
220 'constructor'
e9a4164c 221 ),
02887891
JB
222 ],
223 [
224 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
130783a7 225 OCPP16ServiceUtils.parseJsonSchemaFile<ClearChargingProfileResponse>(
1b271a54
JB
226 '../../../assets/json-schemas/ocpp/1.6/ClearChargingProfileResponse.json',
227 moduleName,
228 'constructor'
e9a4164c 229 ),
02887891
JB
230 ],
231 [
232 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
130783a7 233 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
1b271a54
JB
234 '../../../assets/json-schemas/ocpp/1.6/RemoteStartTransactionResponse.json',
235 moduleName,
236 'constructor'
e9a4164c 237 ),
02887891
JB
238 ],
239 [
240 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
130783a7 241 OCPP16ServiceUtils.parseJsonSchemaFile<GenericResponse>(
1b271a54
JB
242 '../../../assets/json-schemas/ocpp/1.6/RemoteStopTransactionResponse.json',
243 moduleName,
244 'constructor'
e9a4164c 245 ),
02887891
JB
246 ],
247 [
248 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
130783a7 249 OCPP16ServiceUtils.parseJsonSchemaFile<GetDiagnosticsResponse>(
1b271a54
JB
250 '../../../assets/json-schemas/ocpp/1.6/GetDiagnosticsResponse.json',
251 moduleName,
252 'constructor'
e9a4164c 253 ),
02887891
JB
254 ],
255 [
256 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
130783a7 257 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16TriggerMessageResponse>(
1b271a54
JB
258 '../../../assets/json-schemas/ocpp/1.6/TriggerMessageResponse.json',
259 moduleName,
260 'constructor'
e9a4164c 261 ),
02887891
JB
262 ],
263 [
264 OCPP16IncomingRequestCommand.DATA_TRANSFER,
130783a7 265 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferResponse>(
1b271a54
JB
266 '../../../assets/json-schemas/ocpp/1.6/DataTransferResponse.json',
267 moduleName,
268 'constructor'
e9a4164c 269 ),
02887891
JB
270 ],
271 [
272 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE,
130783a7 273 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16UpdateFirmwareResponse>(
1b271a54
JB
274 '../../../assets/json-schemas/ocpp/1.6/UpdateFirmwareResponse.json',
275 moduleName,
276 'constructor'
e9a4164c 277 ),
02887891
JB
278 ],
279 ]);
31f59c6d
JB
280 this.validatePayload = this.validatePayload.bind(this) as (
281 chargingStation: ChargingStation,
282 commandName: OCPP16RequestCommand,
283 payload: JsonType
284 ) => boolean;
58144adb
JB
285 }
286
f7f98c68 287 public async responseHandler(
08f130a0 288 chargingStation: ChargingStation,
e7aeea18 289 commandName: OCPP16RequestCommand,
5cc4b63b
JB
290 payload: JsonType,
291 requestPayload: JsonType
e7aeea18 292 ): Promise<void> {
ed6cfcff
JB
293 if (
294 chargingStation.isRegistered() === true ||
295 commandName === OCPP16RequestCommand.BOOT_NOTIFICATION
296 ) {
65554cc3 297 if (
ed6cfcff
JB
298 this.responseHandlers.has(commandName) === true &&
299 OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true
65554cc3 300 ) {
124f3553 301 try {
9c5c4195 302 this.validatePayload(chargingStation, commandName, payload);
08f130a0 303 await this.responseHandlers.get(commandName)(chargingStation, payload, requestPayload);
124f3553 304 } catch (error) {
6c8f5d90
JB
305 logger.error(
306 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
307 error
308 );
124f3553
JB
309 throw error;
310 }
311 } else {
312 // Throw exception
e7aeea18
JB
313 throw new OCPPError(
314 ErrorType.NOT_IMPLEMENTED,
6c8f5d90 315 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
e7aeea18
JB
316 payload,
317 null,
318 2
319 )}`,
7369e417
JB
320 commandName,
321 payload
e7aeea18 322 );
887fef76 323 }
c0560973 324 } else {
e7aeea18
JB
325 throw new OCPPError(
326 ErrorType.SECURITY_ERROR,
6c8f5d90 327 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
e7aeea18
JB
328 payload,
329 null,
330 2
439fc71b 331 )} while the charging station is not registered on the central server.`,
7369e417
JB
332 commandName,
333 payload
e7aeea18 334 );
c0560973
JB
335 }
336 }
337
9c5c4195
JB
338 private validatePayload(
339 chargingStation: ChargingStation,
340 commandName: OCPP16RequestCommand,
341 payload: JsonType
342 ): boolean {
45988780 343 if (this.jsonSchemas.has(commandName) === true) {
9c5c4195
JB
344 return this.validateResponsePayload(
345 chargingStation,
346 commandName,
347 this.jsonSchemas.get(commandName),
348 payload
349 );
350 }
351 logger.warn(
b3fc3ff5 352 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
9c5c4195
JB
353 );
354 return false;
355 }
356
08f130a0
JB
357 private handleResponseBootNotification(
358 chargingStation: ChargingStation,
359 payload: OCPP16BootNotificationResponse
360 ): void {
d270cc87 361 if (payload.status === RegistrationStatusEnumType.ACCEPTED) {
17ac262c
JB
362 ChargingStationConfigurationUtils.addConfigurationKey(
363 chargingStation,
f0f65a62 364 OCPP16StandardParametersKey.HeartbeatInterval,
a95873d8 365 payload.interval.toString(),
abe9e9dd 366 {},
a95873d8 367 { overwrite: true, save: true }
e7aeea18 368 );
17ac262c
JB
369 ChargingStationConfigurationUtils.addConfigurationKey(
370 chargingStation,
f0f65a62 371 OCPP16StandardParametersKey.HeartBeatInterval,
e7aeea18 372 payload.interval.toString(),
00db15b8 373 { visible: false },
a95873d8 374 { overwrite: true, save: true }
e7aeea18 375 );
8f953431 376 OCPP16ServiceUtils.startHeartbeatInterval(chargingStation, payload.interval);
672fed6e 377 }
d270cc87 378 if (Object.values(RegistrationStatusEnumType).includes(payload.status)) {
08f130a0 379 const logMsg = `${chargingStation.logPrefix()} Charging station in '${
e7aeea18
JB
380 payload.status
381 }' state on the central server`;
d270cc87 382 payload.status === RegistrationStatusEnumType.REJECTED
e7aeea18
JB
383 ? logger.warn(logMsg)
384 : logger.info(logMsg);
c0560973 385 } else {
e7aeea18 386 logger.error(
44eb6026 387 `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`,
e7aeea18
JB
388 payload
389 );
c0560973
JB
390 }
391 }
392
e7aeea18 393 private handleResponseAuthorize(
08f130a0 394 chargingStation: ChargingStation,
e7aeea18 395 payload: OCPP16AuthorizeResponse,
ef6fa3fb 396 requestPayload: OCPP16AuthorizeRequest
e7aeea18 397 ): void {
58144adb 398 let authorizeConnectorId: number;
08f130a0 399 for (const connectorId of chargingStation.connectors.keys()) {
e7aeea18
JB
400 if (
401 connectorId > 0 &&
08f130a0 402 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag === requestPayload.idTag
e7aeea18 403 ) {
734d790d 404 authorizeConnectorId = connectorId;
58144adb
JB
405 break;
406 }
407 }
9bb1159e 408 const authorizeConnectorIdDefined = !Utils.isNullOrUndefined(authorizeConnectorId);
58144adb 409 if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
44eb6026 410 authorizeConnectorIdDefined &&
1984f194 411 (chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true);
e7aeea18 412 logger.debug(
2585c6e9 413 `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' accepted${
44eb6026 414 authorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''
1984f194 415 }`
e7aeea18 416 );
58144adb 417 } else {
44eb6026 418 if (authorizeConnectorIdDefined) {
1984f194 419 chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false;
d812bdcb 420 delete chargingStation.getConnectorStatus(authorizeConnectorId)?.authorizeIdTag;
1984f194 421 }
e7aeea18 422 logger.debug(
2585c6e9 423 `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' rejected with status '${
e7aeea18 424 payload.idTagInfo.status
44eb6026 425 }'${authorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''}`
e7aeea18 426 );
58144adb
JB
427 }
428 }
429
e7aeea18 430 private async handleResponseStartTransaction(
08f130a0 431 chargingStation: ChargingStation,
e7aeea18 432 payload: OCPP16StartTransactionResponse,
ef6fa3fb 433 requestPayload: OCPP16StartTransactionRequest
e7aeea18 434 ): Promise<void> {
c0560973
JB
435 const connectorId = requestPayload.connectorId;
436
437 let transactionConnectorId: number;
08f130a0 438 for (const id of chargingStation.connectors.keys()) {
734d790d
JB
439 if (id > 0 && id === connectorId) {
440 transactionConnectorId = id;
c0560973
JB
441 break;
442 }
443 }
72092cfc 444 if (Utils.isNullOrUndefined(transactionConnectorId)) {
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> {
596 chargingStation.resetConnectorStatus(connectorId);
e7aeea18 597 if (
721646e9 598 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
e7aeea18 599 ) {
4ecff7ce
JB
600 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
601 chargingStation,
ef6fa3fb 602 connectorId,
4ecff7ce
JB
603 OCPP16ChargePointStatus.Available
604 );
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 ) {
4ecff7ce
JB
643 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
644 chargingStation,
645 transactionConnectorId,
646 OCPP16ChargePointStatus.Unavailable
647 );
c0560973 648 } else {
4ecff7ce
JB
649 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
650 chargingStation,
651 transactionConnectorId,
652 OCPP16ChargePointStatus.Available
653 );
2cace1a5
JB
654 }
655 if (chargingStation.stationInfo.powerSharedByConnectors) {
656 chargingStation.powerDivider--;
657 }
658 chargingStation.resetConnectorStatus(transactionConnectorId);
8ca6874c
JB
659 const logMsg = `${chargingStation.logPrefix()} Transaction ${requestPayload.transactionId.toString()} STOPPED on ${
660 chargingStation.stationInfo.chargingStationId
d812bdcb 661 }#${transactionConnectorId?.toString()} with status '${
8ca6874c
JB
662 payload.idTagInfo?.status ?? 'undefined'
663 }'`;
2cace1a5
JB
664 if (
665 Utils.isNullOrUndefined(payload.idTagInfo) ||
666 payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED
667 ) {
668 logger.info(logMsg);
669 } else {
670 logger.warn(logMsg);
c0560973
JB
671 }
672 }
c0560973 673}