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