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