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