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