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