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