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