AjvErrorsToErrorType -> ajvErrorsToErrorType
[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
7import { JSONSchemaType } from 'ajv';
8
8114d10e 9import OCPPError from '../../../exception/OCPPError';
b52c969d 10import { 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';
e7aeea18 14import {
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';
74c6a954 40import { 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
JB
167 ]);
168 }
169
f7f98c68 170 public async responseHandler(
08f130a0 171 chargingStation: ChargingStation,
e7aeea18 172 commandName: OCPP16RequestCommand,
5cc4b63b
JB
173 payload: JsonType,
174 requestPayload: JsonType
e7aeea18 175 ): Promise<void> {
08f130a0 176 if (chargingStation.isRegistered() || commandName === OCPP16RequestCommand.BOOT_NOTIFICATION) {
65554cc3
JB
177 if (
178 this.responseHandlers.has(commandName) &&
ada189a8 179 ChargingStationUtils.isRequestCommandSupported(commandName, chargingStation)
65554cc3 180 ) {
124f3553 181 try {
9c5c4195 182 this.validatePayload(chargingStation, commandName, payload);
08f130a0 183 await this.responseHandlers.get(commandName)(chargingStation, payload, requestPayload);
124f3553 184 } catch (error) {
08f130a0 185 logger.error(chargingStation.logPrefix() + ' Handle request response error: %j', error);
124f3553
JB
186 throw error;
187 }
188 } else {
189 // Throw exception
e7aeea18
JB
190 throw new OCPPError(
191 ErrorType.NOT_IMPLEMENTED,
e3018bc4 192 `${commandName} is not implemented to handle request response PDU ${JSON.stringify(
e7aeea18
JB
193 payload,
194 null,
195 2
196 )}`,
7369e417
JB
197 commandName,
198 payload
e7aeea18 199 );
887fef76 200 }
c0560973 201 } else {
e7aeea18
JB
202 throw new OCPPError(
203 ErrorType.SECURITY_ERROR,
e3018bc4 204 `${commandName} cannot be issued to handle request response PDU ${JSON.stringify(
e7aeea18
JB
205 payload,
206 null,
207 2
208 )} while the charging station is not registered on the central server. `,
7369e417
JB
209 commandName,
210 payload
e7aeea18 211 );
c0560973
JB
212 }
213 }
214
9c5c4195
JB
215 private validatePayload(
216 chargingStation: ChargingStation,
217 commandName: OCPP16RequestCommand,
218 payload: JsonType
219 ): boolean {
220 if (this.jsonSchemas.has(commandName)) {
221 return this.validateResponsePayload(
222 chargingStation,
223 commandName,
224 this.jsonSchemas.get(commandName),
225 payload
226 );
227 }
228 logger.warn(
ee307db5 229 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command ${commandName} PDU validation`
9c5c4195
JB
230 );
231 return false;
232 }
233
08f130a0
JB
234 private handleResponseBootNotification(
235 chargingStation: ChargingStation,
236 payload: OCPP16BootNotificationResponse
237 ): void {
c0560973 238 if (payload.status === OCPP16RegistrationStatus.ACCEPTED) {
17ac262c
JB
239 ChargingStationConfigurationUtils.addConfigurationKey(
240 chargingStation,
f0f65a62 241 OCPP16StandardParametersKey.HeartbeatInterval,
a95873d8
JB
242 payload.interval.toString(),
243 {},
244 { overwrite: true, save: true }
e7aeea18 245 );
17ac262c
JB
246 ChargingStationConfigurationUtils.addConfigurationKey(
247 chargingStation,
f0f65a62 248 OCPP16StandardParametersKey.HeartBeatInterval,
e7aeea18 249 payload.interval.toString(),
00db15b8 250 { visible: false },
a95873d8 251 { overwrite: true, save: true }
e7aeea18 252 );
08f130a0
JB
253 chargingStation.heartbeatSetInterval
254 ? chargingStation.restartHeartbeat()
255 : chargingStation.startHeartbeat();
672fed6e 256 }
74c6a954 257 if (Object.values(OCPP16RegistrationStatus).includes(payload.status)) {
08f130a0 258 const logMsg = `${chargingStation.logPrefix()} Charging station in '${
e7aeea18
JB
259 payload.status
260 }' state on the central server`;
261 payload.status === OCPP16RegistrationStatus.REJECTED
262 ? logger.warn(logMsg)
263 : logger.info(logMsg);
c0560973 264 } else {
e7aeea18 265 logger.error(
08f130a0 266 chargingStation.logPrefix() +
e7aeea18
JB
267 ' Charging station boot notification response received: %j with undefined registration status',
268 payload
269 );
c0560973
JB
270 }
271 }
272
e7aeea18 273 private handleResponseAuthorize(
08f130a0 274 chargingStation: ChargingStation,
e7aeea18 275 payload: OCPP16AuthorizeResponse,
ef6fa3fb 276 requestPayload: OCPP16AuthorizeRequest
e7aeea18 277 ): void {
58144adb 278 let authorizeConnectorId: number;
08f130a0 279 for (const connectorId of chargingStation.connectors.keys()) {
e7aeea18
JB
280 if (
281 connectorId > 0 &&
08f130a0 282 chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag === requestPayload.idTag
e7aeea18 283 ) {
734d790d 284 authorizeConnectorId = connectorId;
58144adb
JB
285 break;
286 }
287 }
288 if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
08f130a0 289 chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true;
e7aeea18 290 logger.debug(
08f130a0 291 `${chargingStation.logPrefix()} IdTag ${
e7aeea18
JB
292 requestPayload.idTag
293 } authorized on connector ${authorizeConnectorId}`
294 );
58144adb 295 } else {
08f130a0
JB
296 chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false;
297 delete chargingStation.getConnectorStatus(authorizeConnectorId).authorizeIdTag;
e7aeea18 298 logger.debug(
08f130a0 299 `${chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status '${
e7aeea18 300 payload.idTagInfo.status
e8e865ea 301 }' on connector ${authorizeConnectorId}`
e7aeea18 302 );
58144adb
JB
303 }
304 }
305
e7aeea18 306 private async handleResponseStartTransaction(
08f130a0 307 chargingStation: ChargingStation,
e7aeea18 308 payload: OCPP16StartTransactionResponse,
ef6fa3fb 309 requestPayload: OCPP16StartTransactionRequest
e7aeea18 310 ): Promise<void> {
c0560973
JB
311 const connectorId = requestPayload.connectorId;
312
313 let transactionConnectorId: number;
08f130a0 314 for (const id of chargingStation.connectors.keys()) {
734d790d
JB
315 if (id > 0 && id === connectorId) {
316 transactionConnectorId = id;
c0560973
JB
317 break;
318 }
319 }
320 if (!transactionConnectorId) {
e7aeea18 321 logger.error(
08f130a0 322 chargingStation.logPrefix() +
e7aeea18
JB
323 ' Trying to start a transaction on a non existing connector Id ' +
324 connectorId.toString()
325 );
c0560973
JB
326 return;
327 }
e7aeea18 328 if (
08f130a0
JB
329 chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted &&
330 chargingStation.getAuthorizeRemoteTxRequests() &&
331 chargingStation.getLocalAuthListEnabled() &&
332 chargingStation.hasAuthorizedTags() &&
333 !chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized
e7aeea18
JB
334 ) {
335 logger.error(
08f130a0 336 chargingStation.logPrefix() +
e7aeea18 337 ' Trying to start a transaction with a not local authorized idTag ' +
08f130a0 338 chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag +
e7aeea18
JB
339 ' on connector Id ' +
340 connectorId.toString()
341 );
08f130a0 342 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
343 return;
344 }
e7aeea18 345 if (
08f130a0
JB
346 chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted &&
347 chargingStation.getAuthorizeRemoteTxRequests() &&
348 chargingStation.getMayAuthorizeAtRemoteStart() &&
349 !chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized &&
350 !chargingStation.getConnectorStatus(connectorId).idTagAuthorized
e7aeea18
JB
351 ) {
352 logger.error(
08f130a0 353 chargingStation.logPrefix() +
e7aeea18 354 ' Trying to start a transaction with a not authorized idTag ' +
08f130a0 355 chargingStation.getConnectorStatus(connectorId).authorizeIdTag +
e7aeea18
JB
356 ' on connector Id ' +
357 connectorId.toString()
358 );
08f130a0 359 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
360 return;
361 }
e7aeea18 362 if (
08f130a0
JB
363 chargingStation.getConnectorStatus(connectorId).idTagAuthorized &&
364 chargingStation.getConnectorStatus(connectorId).authorizeIdTag !== requestPayload.idTag
e7aeea18
JB
365 ) {
366 logger.error(
08f130a0 367 chargingStation.logPrefix() +
e7aeea18
JB
368 ' Trying to start a transaction with an idTag ' +
369 requestPayload.idTag +
370 ' different from the authorize request one ' +
08f130a0 371 chargingStation.getConnectorStatus(connectorId).authorizeIdTag +
e7aeea18
JB
372 ' on connector Id ' +
373 connectorId.toString()
374 );
08f130a0 375 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
376 return;
377 }
e7aeea18 378 if (
08f130a0
JB
379 chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized &&
380 chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag !== requestPayload.idTag
e7aeea18
JB
381 ) {
382 logger.error(
08f130a0 383 chargingStation.logPrefix() +
e7aeea18
JB
384 ' Trying to start a transaction with an idTag ' +
385 requestPayload.idTag +
386 ' different from the local authorized one ' +
08f130a0 387 chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag +
e7aeea18
JB
388 ' on connector Id ' +
389 connectorId.toString()
390 );
08f130a0 391 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
163547b1
JB
392 return;
393 }
08f130a0 394 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted) {
e7aeea18 395 logger.debug(
08f130a0 396 chargingStation.logPrefix() +
e7aeea18
JB
397 ' Trying to start a transaction on an already used connector ' +
398 connectorId.toString() +
399 ': %j',
08f130a0 400 chargingStation.getConnectorStatus(connectorId)
e7aeea18 401 );
c0560973
JB
402 return;
403 }
e7aeea18 404 if (
08f130a0 405 chargingStation.getConnectorStatus(connectorId)?.status !==
e7aeea18 406 OCPP16ChargePointStatus.AVAILABLE &&
08f130a0 407 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.PREPARING
e7aeea18
JB
408 ) {
409 logger.error(
08f130a0
JB
410 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
411 chargingStation.getConnectorStatus(connectorId)?.status
e7aeea18
JB
412 }`
413 );
290d006c
JB
414 return;
415 }
f3a6f63b 416 if (!Number.isInteger(payload.transactionId)) {
e7aeea18 417 logger.warn(
08f130a0 418 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${
e7aeea18
JB
419 payload.transactionId
420 }, converting to integer`
421 );
f3a6f63b
JB
422 payload.transactionId = Utils.convertToInt(payload.transactionId);
423 }
c0560973 424
f0c6ed89 425 if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
08f130a0
JB
426 chargingStation.getConnectorStatus(connectorId).transactionStarted = true;
427 chargingStation.getConnectorStatus(connectorId).transactionId = payload.transactionId;
428 chargingStation.getConnectorStatus(connectorId).transactionIdTag = requestPayload.idTag;
429 chargingStation.getConnectorStatus(
e7aeea18
JB
430 connectorId
431 ).transactionEnergyActiveImportRegisterValue = 0;
08f130a0 432 chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue =
e7aeea18 433 OCPP16ServiceUtils.buildTransactionBeginMeterValue(
08f130a0 434 chargingStation,
e7aeea18
JB
435 connectorId,
436 requestPayload.meterStart
437 );
08f130a0
JB
438 chargingStation.getBeginEndMeterValues() &&
439 (await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
440 OCPP16MeterValuesRequest,
441 OCPP16MeterValuesResponse
08f130a0 442 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
93b4a429 443 connectorId,
ef6fa3fb 444 transactionId: payload.transactionId,
7369e417 445 meterValue: [chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue],
ef6fa3fb 446 }));
08f130a0 447 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
448 OCPP16StatusNotificationRequest,
449 OCPP16StatusNotificationResponse
08f130a0 450 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
451 connectorId,
452 status: OCPP16ChargePointStatus.CHARGING,
453 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
454 });
08f130a0 455 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.CHARGING;
e7aeea18 456 logger.info(
08f130a0 457 chargingStation.logPrefix() +
e7aeea18
JB
458 ' Transaction ' +
459 payload.transactionId.toString() +
460 ' STARTED on ' +
08f130a0 461 chargingStation.stationInfo.chargingStationId +
e7aeea18
JB
462 '#' +
463 connectorId.toString() +
464 ' for idTag ' +
465 requestPayload.idTag
466 );
08f130a0 467 if (chargingStation.stationInfo.powerSharedByConnectors) {
fa7bccf4 468 chargingStation.powerDivider++;
c0560973 469 }
17ac262c
JB
470 const configuredMeterValueSampleInterval =
471 ChargingStationConfigurationUtils.getConfigurationKey(
472 chargingStation,
473 OCPP16StandardParametersKey.MeterValueSampleInterval
474 );
08f130a0 475 chargingStation.startMeterValues(
e7aeea18
JB
476 connectorId,
477 configuredMeterValueSampleInterval
478 ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000
479 : 60000
480 );
c0560973 481 } else {
e7aeea18 482 logger.warn(
08f130a0 483 chargingStation.logPrefix() +
e7aeea18
JB
484 ' Starting transaction id ' +
485 payload.transactionId.toString() +
e8e865ea 486 " REJECTED with status '" +
e7aeea18 487 payload?.idTagInfo?.status +
e8e865ea 488 "', idTag " +
e7aeea18
JB
489 requestPayload.idTag
490 );
08f130a0 491 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
492 }
493 }
494
08f130a0
JB
495 private async resetConnectorOnStartTransactionError(
496 chargingStation: ChargingStation,
497 connectorId: number
498 ): Promise<void> {
499 chargingStation.resetConnectorStatus(connectorId);
e7aeea18 500 if (
08f130a0 501 chargingStation.getConnectorStatus(connectorId).status !== OCPP16ChargePointStatus.AVAILABLE
e7aeea18 502 ) {
08f130a0 503 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
504 OCPP16StatusNotificationRequest,
505 OCPP16StatusNotificationResponse
08f130a0 506 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
507 connectorId,
508 status: OCPP16ChargePointStatus.AVAILABLE,
509 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
510 });
08f130a0 511 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
c0560973
JB
512 }
513 }
514
e7aeea18 515 private async handleResponseStopTransaction(
08f130a0 516 chargingStation: ChargingStation,
e7aeea18 517 payload: OCPP16StopTransactionResponse,
ef6fa3fb 518 requestPayload: OCPP16StopTransactionRequest
e7aeea18 519 ): Promise<void> {
08f130a0 520 const transactionConnectorId = chargingStation.getConnectorIdByTransactionId(
f479a792
JB
521 requestPayload.transactionId
522 );
c0560973 523 if (!transactionConnectorId) {
e7aeea18 524 logger.error(
08f130a0 525 chargingStation.logPrefix() +
e7aeea18
JB
526 ' Trying to stop a non existing transaction ' +
527 requestPayload.transactionId.toString()
528 );
c0560973
JB
529 return;
530 }
531 if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
08f130a0
JB
532 chargingStation.getBeginEndMeterValues() &&
533 !chargingStation.getOcppStrictCompliance() &&
534 chargingStation.getOutOfOrderEndMeterValues() &&
535 (await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
536 OCPP16MeterValuesRequest,
537 OCPP16MeterValuesResponse
08f130a0 538 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
ef6fa3fb
JB
539 connectorId: transactionConnectorId,
540 transactionId: requestPayload.transactionId,
7369e417
JB
541 meterValue: [
542 OCPP16ServiceUtils.buildTransactionEndMeterValue(
543 chargingStation,
544 transactionConnectorId,
545 requestPayload.meterStop
546 ),
547 ],
ef6fa3fb 548 }));
e7aeea18 549 if (
08f130a0
JB
550 !chargingStation.isChargingStationAvailable() ||
551 !chargingStation.isConnectorAvailable(transactionConnectorId)
e7aeea18 552 ) {
08f130a0 553 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
554 OCPP16StatusNotificationRequest,
555 OCPP16StatusNotificationResponse
08f130a0 556 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
557 connectorId: transactionConnectorId,
558 status: OCPP16ChargePointStatus.UNAVAILABLE,
559 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
560 });
08f130a0 561 chargingStation.getConnectorStatus(transactionConnectorId).status =
e7aeea18 562 OCPP16ChargePointStatus.UNAVAILABLE;
c0560973 563 } else {
08f130a0 564 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
565 OCPP16BootNotificationRequest,
566 OCPP16BootNotificationResponse
08f130a0 567 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
568 connectorId: transactionConnectorId,
569 status: OCPP16ChargePointStatus.AVAILABLE,
570 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
571 });
08f130a0 572 chargingStation.getConnectorStatus(transactionConnectorId).status =
e7aeea18 573 OCPP16ChargePointStatus.AVAILABLE;
c0560973 574 }
08f130a0 575 if (chargingStation.stationInfo.powerSharedByConnectors) {
fa7bccf4 576 chargingStation.powerDivider--;
c0560973 577 }
e7aeea18 578 logger.info(
08f130a0 579 chargingStation.logPrefix() +
e7aeea18
JB
580 ' Transaction ' +
581 requestPayload.transactionId.toString() +
582 ' STOPPED on ' +
08f130a0 583 chargingStation.stationInfo.chargingStationId +
e7aeea18
JB
584 '#' +
585 transactionConnectorId.toString()
586 );
08f130a0 587 chargingStation.resetConnectorStatus(transactionConnectorId);
c0560973 588 } else {
e7aeea18 589 logger.warn(
08f130a0 590 chargingStation.logPrefix() +
e7aeea18
JB
591 ' Stopping transaction id ' +
592 requestPayload.transactionId.toString() +
e8e865ea
JB
593 " REJECTED with status '" +
594 payload.idTagInfo?.status +
595 "'"
e7aeea18 596 );
c0560973
JB
597 }
598 }
c0560973 599}