UI Protocol: add Authorize command support
[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 }
1984f194 292 const isAuthorizeConnectorIdDefined = authorizeConnectorId !== undefined;
58144adb 293 if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
1984f194
JB
294 isAuthorizeConnectorIdDefined &&
295 (chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true);
e7aeea18 296 logger.debug(
1984f194
JB
297 `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' accepted${
298 isAuthorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''
299 }`
e7aeea18 300 );
58144adb 301 } else {
1984f194
JB
302 if (isAuthorizeConnectorIdDefined) {
303 chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false;
304 delete chargingStation.getConnectorStatus(authorizeConnectorId).authorizeIdTag;
305 }
e7aeea18 306 logger.debug(
1984f194 307 `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' rejected with status '${
e7aeea18 308 payload.idTagInfo.status
1984f194 309 }'${isAuthorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''}`
e7aeea18 310 );
58144adb
JB
311 }
312 }
313
e7aeea18 314 private async handleResponseStartTransaction(
08f130a0 315 chargingStation: ChargingStation,
e7aeea18 316 payload: OCPP16StartTransactionResponse,
ef6fa3fb 317 requestPayload: OCPP16StartTransactionRequest
e7aeea18 318 ): Promise<void> {
c0560973
JB
319 const connectorId = requestPayload.connectorId;
320
321 let transactionConnectorId: number;
08f130a0 322 for (const id of chargingStation.connectors.keys()) {
734d790d
JB
323 if (id > 0 && id === connectorId) {
324 transactionConnectorId = id;
c0560973
JB
325 break;
326 }
327 }
328 if (!transactionConnectorId) {
e7aeea18 329 logger.error(
08f130a0 330 chargingStation.logPrefix() +
e7aeea18
JB
331 ' Trying to start a transaction on a non existing connector Id ' +
332 connectorId.toString()
333 );
c0560973
JB
334 return;
335 }
e7aeea18 336 if (
5e3cb728 337 chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted === true &&
08f130a0
JB
338 chargingStation.getAuthorizeRemoteTxRequests() &&
339 chargingStation.getLocalAuthListEnabled() &&
340 chargingStation.hasAuthorizedTags() &&
5e3cb728 341 chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized === false
e7aeea18
JB
342 ) {
343 logger.error(
08f130a0 344 chargingStation.logPrefix() +
e7aeea18 345 ' Trying to start a transaction with a not local authorized idTag ' +
08f130a0 346 chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag +
e7aeea18
JB
347 ' on connector Id ' +
348 connectorId.toString()
349 );
08f130a0 350 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
351 return;
352 }
e7aeea18 353 if (
5e3cb728 354 chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted === true &&
08f130a0 355 chargingStation.getAuthorizeRemoteTxRequests() &&
03ebf4c1 356 chargingStation.getMustAuthorizeAtRemoteStart() &&
5e3cb728
JB
357 chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized === false &&
358 chargingStation.getConnectorStatus(connectorId).idTagAuthorized === false
e7aeea18
JB
359 ) {
360 logger.error(
08f130a0 361 chargingStation.logPrefix() +
e7aeea18 362 ' Trying to start a transaction with a not authorized idTag ' +
08f130a0 363 chargingStation.getConnectorStatus(connectorId).authorizeIdTag +
e7aeea18
JB
364 ' on connector Id ' +
365 connectorId.toString()
366 );
08f130a0 367 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
368 return;
369 }
e7aeea18 370 if (
08f130a0
JB
371 chargingStation.getConnectorStatus(connectorId).idTagAuthorized &&
372 chargingStation.getConnectorStatus(connectorId).authorizeIdTag !== requestPayload.idTag
e7aeea18
JB
373 ) {
374 logger.error(
08f130a0 375 chargingStation.logPrefix() +
e7aeea18
JB
376 ' Trying to start a transaction with an idTag ' +
377 requestPayload.idTag +
378 ' different from the authorize request one ' +
08f130a0 379 chargingStation.getConnectorStatus(connectorId).authorizeIdTag +
e7aeea18
JB
380 ' on connector Id ' +
381 connectorId.toString()
382 );
08f130a0 383 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
384 return;
385 }
e7aeea18 386 if (
08f130a0
JB
387 chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized &&
388 chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag !== requestPayload.idTag
e7aeea18
JB
389 ) {
390 logger.error(
08f130a0 391 chargingStation.logPrefix() +
e7aeea18
JB
392 ' Trying to start a transaction with an idTag ' +
393 requestPayload.idTag +
394 ' different from the local authorized one ' +
08f130a0 395 chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag +
e7aeea18
JB
396 ' on connector Id ' +
397 connectorId.toString()
398 );
08f130a0 399 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
163547b1
JB
400 return;
401 }
5e3cb728 402 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
e7aeea18 403 logger.debug(
08f130a0 404 chargingStation.logPrefix() +
e7aeea18
JB
405 ' Trying to start a transaction on an already used connector ' +
406 connectorId.toString() +
407 ': %j',
08f130a0 408 chargingStation.getConnectorStatus(connectorId)
e7aeea18 409 );
c0560973
JB
410 return;
411 }
e7aeea18 412 if (
08f130a0 413 chargingStation.getConnectorStatus(connectorId)?.status !==
e7aeea18 414 OCPP16ChargePointStatus.AVAILABLE &&
08f130a0 415 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.PREPARING
e7aeea18
JB
416 ) {
417 logger.error(
08f130a0
JB
418 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
419 chargingStation.getConnectorStatus(connectorId)?.status
e7aeea18
JB
420 }`
421 );
290d006c
JB
422 return;
423 }
fc040c43
JB
424 // if (!Number.isInteger(payload.transactionId)) {
425 // logger.warn(
426 // `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${
427 // payload.transactionId
428 // }, converting to integer`
429 // );
430 // payload.transactionId = Utils.convertToInt(payload.transactionId);
431 // }
c0560973 432
f0c6ed89 433 if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
08f130a0
JB
434 chargingStation.getConnectorStatus(connectorId).transactionStarted = true;
435 chargingStation.getConnectorStatus(connectorId).transactionId = payload.transactionId;
436 chargingStation.getConnectorStatus(connectorId).transactionIdTag = requestPayload.idTag;
437 chargingStation.getConnectorStatus(
e7aeea18
JB
438 connectorId
439 ).transactionEnergyActiveImportRegisterValue = 0;
08f130a0 440 chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue =
e7aeea18 441 OCPP16ServiceUtils.buildTransactionBeginMeterValue(
08f130a0 442 chargingStation,
e7aeea18
JB
443 connectorId,
444 requestPayload.meterStart
445 );
08f130a0
JB
446 chargingStation.getBeginEndMeterValues() &&
447 (await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
448 OCPP16MeterValuesRequest,
449 OCPP16MeterValuesResponse
08f130a0 450 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
93b4a429 451 connectorId,
ef6fa3fb 452 transactionId: payload.transactionId,
7369e417 453 meterValue: [chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue],
ef6fa3fb 454 }));
08f130a0 455 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
456 OCPP16StatusNotificationRequest,
457 OCPP16StatusNotificationResponse
08f130a0 458 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
459 connectorId,
460 status: OCPP16ChargePointStatus.CHARGING,
461 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
462 });
08f130a0 463 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.CHARGING;
e7aeea18 464 logger.info(
08f130a0 465 chargingStation.logPrefix() +
e7aeea18
JB
466 ' Transaction ' +
467 payload.transactionId.toString() +
468 ' STARTED on ' +
08f130a0 469 chargingStation.stationInfo.chargingStationId +
e7aeea18
JB
470 '#' +
471 connectorId.toString() +
5cf9050d
JB
472 " for idTag '" +
473 requestPayload.idTag +
474 "'"
e7aeea18 475 );
08f130a0 476 if (chargingStation.stationInfo.powerSharedByConnectors) {
fa7bccf4 477 chargingStation.powerDivider++;
c0560973 478 }
17ac262c
JB
479 const configuredMeterValueSampleInterval =
480 ChargingStationConfigurationUtils.getConfigurationKey(
481 chargingStation,
482 OCPP16StandardParametersKey.MeterValueSampleInterval
483 );
08f130a0 484 chargingStation.startMeterValues(
e7aeea18
JB
485 connectorId,
486 configuredMeterValueSampleInterval
487 ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000
488 : 60000
489 );
c0560973 490 } else {
e7aeea18 491 logger.warn(
08f130a0 492 chargingStation.logPrefix() +
e7aeea18
JB
493 ' Starting transaction id ' +
494 payload.transactionId.toString() +
e8e865ea 495 " REJECTED with status '" +
e7aeea18 496 payload?.idTagInfo?.status +
fc040c43
JB
497 "', idTag '" +
498 requestPayload.idTag +
499 "'"
e7aeea18 500 );
08f130a0 501 await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
a2653482
JB
502 }
503 }
504
08f130a0
JB
505 private async resetConnectorOnStartTransactionError(
506 chargingStation: ChargingStation,
507 connectorId: number
508 ): Promise<void> {
509 chargingStation.resetConnectorStatus(connectorId);
e7aeea18 510 if (
08f130a0 511 chargingStation.getConnectorStatus(connectorId).status !== OCPP16ChargePointStatus.AVAILABLE
e7aeea18 512 ) {
08f130a0 513 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
514 OCPP16StatusNotificationRequest,
515 OCPP16StatusNotificationResponse
08f130a0 516 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
517 connectorId,
518 status: OCPP16ChargePointStatus.AVAILABLE,
519 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
520 });
08f130a0 521 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
c0560973
JB
522 }
523 }
524
e7aeea18 525 private async handleResponseStopTransaction(
08f130a0 526 chargingStation: ChargingStation,
e7aeea18 527 payload: OCPP16StopTransactionResponse,
ef6fa3fb 528 requestPayload: OCPP16StopTransactionRequest
e7aeea18 529 ): Promise<void> {
08f130a0 530 const transactionConnectorId = chargingStation.getConnectorIdByTransactionId(
f479a792
JB
531 requestPayload.transactionId
532 );
c0560973 533 if (!transactionConnectorId) {
e7aeea18 534 logger.error(
08f130a0 535 chargingStation.logPrefix() +
e7aeea18
JB
536 ' Trying to stop a non existing transaction ' +
537 requestPayload.transactionId.toString()
538 );
c0560973
JB
539 return;
540 }
541 if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
08f130a0
JB
542 chargingStation.getBeginEndMeterValues() &&
543 !chargingStation.getOcppStrictCompliance() &&
544 chargingStation.getOutOfOrderEndMeterValues() &&
545 (await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
546 OCPP16MeterValuesRequest,
547 OCPP16MeterValuesResponse
08f130a0 548 >(chargingStation, OCPP16RequestCommand.METER_VALUES, {
ef6fa3fb
JB
549 connectorId: transactionConnectorId,
550 transactionId: requestPayload.transactionId,
7369e417
JB
551 meterValue: [
552 OCPP16ServiceUtils.buildTransactionEndMeterValue(
553 chargingStation,
554 transactionConnectorId,
555 requestPayload.meterStop
556 ),
557 ],
ef6fa3fb 558 }));
e7aeea18 559 if (
08f130a0
JB
560 !chargingStation.isChargingStationAvailable() ||
561 !chargingStation.isConnectorAvailable(transactionConnectorId)
e7aeea18 562 ) {
08f130a0 563 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
564 OCPP16StatusNotificationRequest,
565 OCPP16StatusNotificationResponse
08f130a0 566 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
567 connectorId: transactionConnectorId,
568 status: OCPP16ChargePointStatus.UNAVAILABLE,
569 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
570 });
08f130a0 571 chargingStation.getConnectorStatus(transactionConnectorId).status =
e7aeea18 572 OCPP16ChargePointStatus.UNAVAILABLE;
c0560973 573 } else {
08f130a0 574 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
575 OCPP16BootNotificationRequest,
576 OCPP16BootNotificationResponse
08f130a0 577 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
578 connectorId: transactionConnectorId,
579 status: OCPP16ChargePointStatus.AVAILABLE,
580 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
581 });
08f130a0 582 chargingStation.getConnectorStatus(transactionConnectorId).status =
e7aeea18 583 OCPP16ChargePointStatus.AVAILABLE;
c0560973 584 }
08f130a0 585 if (chargingStation.stationInfo.powerSharedByConnectors) {
fa7bccf4 586 chargingStation.powerDivider--;
c0560973 587 }
6d9876e7 588 chargingStation.resetConnectorStatus(transactionConnectorId);
e7aeea18 589 logger.info(
08f130a0 590 chargingStation.logPrefix() +
e7aeea18
JB
591 ' Transaction ' +
592 requestPayload.transactionId.toString() +
593 ' STOPPED on ' +
08f130a0 594 chargingStation.stationInfo.chargingStationId +
e7aeea18
JB
595 '#' +
596 transactionConnectorId.toString()
597 );
c0560973 598 } else {
e7aeea18 599 logger.warn(
08f130a0 600 chargingStation.logPrefix() +
e7aeea18
JB
601 ' Stopping transaction id ' +
602 requestPayload.transactionId.toString() +
e8e865ea
JB
603 " REJECTED with status '" +
604 payload.idTagInfo?.status +
605 "'"
e7aeea18 606 );
c0560973
JB
607 }
608 }
c0560973 609}