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