fix: fix return at getting connector status with evses
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16IncomingRequestService.ts
CommitLineData
edd13439 1// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
c8eeb62b 2
130783a7
JB
3import fs from 'node:fs';
4import path from 'node:path';
5import { URL, fileURLToPath } from 'node:url';
8114d10e 6
6c1761d4 7import type { JSONSchemaType } from 'ajv';
27782dbc 8import { Client, type FTPResponse } from 'basic-ftp';
8114d10e
JB
9import tar from 'tar';
10
2896e06d
JB
11import {
12 type ChargingStation,
13 ChargingStationConfigurationUtils,
14 ChargingStationUtils,
15} from '../../../charging-station';
268a74bb 16import { OCPPError } from '../../../exception';
e7aeea18 17import {
27782dbc 18 type ChangeAvailabilityRequest,
268a74bb 19 type ChangeAvailabilityResponse,
27782dbc 20 type ChangeConfigurationRequest,
268a74bb 21 type ChangeConfigurationResponse,
27782dbc 22 type ClearChargingProfileRequest,
268a74bb 23 type ClearChargingProfileResponse,
4334db72 24 type ConnectorStatus,
268a74bb
JB
25 ErrorType,
26 type GenericResponse,
41189456 27 GenericStatus,
27782dbc 28 type GetConfigurationRequest,
268a74bb 29 type GetConfigurationResponse,
27782dbc 30 type GetDiagnosticsRequest,
268a74bb
JB
31 type GetDiagnosticsResponse,
32 type IncomingRequestHandler,
33 type JsonObject,
34 type JsonType,
35 OCPP16AuthorizationStatus,
36 type OCPP16AuthorizeRequest,
37 type OCPP16AuthorizeResponse,
e7aeea18 38 OCPP16AvailabilityType,
27782dbc 39 type OCPP16BootNotificationRequest,
268a74bb
JB
40 type OCPP16BootNotificationResponse,
41 OCPP16ChargePointErrorCode,
42 OCPP16ChargePointStatus,
43 type OCPP16ChargingProfile,
0ac97927 44 OCPP16ChargingProfilePurposeType,
41189456 45 type OCPP16ChargingSchedule,
27782dbc
JB
46 type OCPP16ClearCacheRequest,
47 type OCPP16DataTransferRequest,
268a74bb
JB
48 type OCPP16DataTransferResponse,
49 OCPP16DataTransferStatus,
77b95a89 50 OCPP16DataTransferVendorId,
268a74bb 51 OCPP16DiagnosticsStatus,
c9a4f9ea 52 type OCPP16DiagnosticsStatusNotificationRequest,
268a74bb 53 type OCPP16DiagnosticsStatusNotificationResponse,
c9a4f9ea
JB
54 OCPP16FirmwareStatus,
55 type OCPP16FirmwareStatusNotificationRequest,
268a74bb 56 type OCPP16FirmwareStatusNotificationResponse,
41189456
JB
57 type OCPP16GetCompositeScheduleRequest,
58 type OCPP16GetCompositeScheduleResponse,
27782dbc 59 type OCPP16HeartbeatRequest,
268a74bb 60 type OCPP16HeartbeatResponse,
e7aeea18 61 OCPP16IncomingRequestCommand,
c60ed4b8 62 OCPP16MessageTrigger,
94a464f9 63 OCPP16RequestCommand,
268a74bb
JB
64 OCPP16StandardParametersKey,
65 type OCPP16StartTransactionRequest,
66 type OCPP16StartTransactionResponse,
27782dbc 67 type OCPP16StatusNotificationRequest,
268a74bb
JB
68 type OCPP16StatusNotificationResponse,
69 OCPP16StopTransactionReason,
70 OCPP16SupportedFeatureProfiles,
27782dbc 71 type OCPP16TriggerMessageRequest,
268a74bb 72 type OCPP16TriggerMessageResponse,
27782dbc 73 type OCPP16UpdateFirmwareRequest,
268a74bb
JB
74 type OCPP16UpdateFirmwareResponse,
75 type OCPPConfigurationKey,
76 OCPPVersion,
27782dbc
JB
77 type RemoteStartTransactionRequest,
78 type RemoteStopTransactionRequest,
79 type ResetRequest,
80 type SetChargingProfileRequest,
27782dbc 81 type SetChargingProfileResponse,
268a74bb 82 type UnlockConnectorRequest,
27782dbc 83 type UnlockConnectorResponse,
268a74bb 84} from '../../../types';
60a74391 85import { Constants, Utils, logger } from '../../../utils';
d8b1fab1 86import { OCPP16Constants, OCPP16ServiceUtils, OCPPIncomingRequestService } from '../internal';
c0560973 87
2a115f87 88const moduleName = 'OCPP16IncomingRequestService';
909dcf2d 89
268a74bb 90export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
b3fc3ff5 91 protected jsonSchemas: Map<OCPP16IncomingRequestCommand, JSONSchemaType<JsonObject>>;
58144adb
JB
92 private incomingRequestHandlers: Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>;
93
08f130a0 94 public constructor() {
b768993d
JB
95 // if (new.target?.name === moduleName) {
96 // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
97 // }
d270cc87 98 super(OCPPVersion.VERSION_16);
58144adb
JB
99 this.incomingRequestHandlers = new Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>([
100 [OCPP16IncomingRequestCommand.RESET, this.handleRequestReset.bind(this)],
101 [OCPP16IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)],
102 [OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, this.handleRequestUnlockConnector.bind(this)],
e7aeea18
JB
103 [
104 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
105 this.handleRequestGetConfiguration.bind(this),
106 ],
107 [
108 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
109 this.handleRequestChangeConfiguration.bind(this),
110 ],
41189456
JB
111 [
112 OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
113 this.handleRequestGetCompositeSchedule.bind(this),
114 ],
e7aeea18
JB
115 [
116 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
117 this.handleRequestSetChargingProfile.bind(this),
118 ],
119 [
120 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
121 this.handleRequestClearChargingProfile.bind(this),
122 ],
123 [
124 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
125 this.handleRequestChangeAvailability.bind(this),
126 ],
127 [
128 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
129 this.handleRequestRemoteStartTransaction.bind(this),
130 ],
131 [
132 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
133 this.handleRequestRemoteStopTransaction.bind(this),
134 ],
734d790d 135 [OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, this.handleRequestGetDiagnostics.bind(this)],
e7aeea18 136 [OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, this.handleRequestTriggerMessage.bind(this)],
77b95a89 137 [OCPP16IncomingRequestCommand.DATA_TRANSFER, this.handleRequestDataTransfer.bind(this)],
c9a4f9ea 138 [OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, this.handleRequestUpdateFirmware.bind(this)],
58144adb 139 ]);
b52c969d
JB
140 this.jsonSchemas = new Map<OCPP16IncomingRequestCommand, JSONSchemaType<JsonObject>>([
141 [
142 OCPP16IncomingRequestCommand.RESET,
130783a7 143 OCPP16ServiceUtils.parseJsonSchemaFile<ResetRequest>(
1b271a54
JB
144 '../../../assets/json-schemas/ocpp/1.6/Reset.json',
145 moduleName,
146 'constructor'
130783a7 147 ),
b52c969d
JB
148 ],
149 [
150 OCPP16IncomingRequestCommand.CLEAR_CACHE,
130783a7 151 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ClearCacheRequest>(
1b271a54
JB
152 '../../../assets/json-schemas/ocpp/1.6/ClearCache.json',
153 moduleName,
154 'constructor'
e9a4164c 155 ),
b52c969d
JB
156 ],
157 [
158 OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
130783a7 159 OCPP16ServiceUtils.parseJsonSchemaFile<UnlockConnectorRequest>(
1b271a54
JB
160 '../../../assets/json-schemas/ocpp/1.6/UnlockConnector.json',
161 moduleName,
162 'constructor'
e9a4164c 163 ),
b52c969d
JB
164 ],
165 [
166 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
130783a7 167 OCPP16ServiceUtils.parseJsonSchemaFile<GetConfigurationRequest>(
1b271a54
JB
168 '../../../assets/json-schemas/ocpp/1.6/GetConfiguration.json',
169 moduleName,
170 'constructor'
e9a4164c 171 ),
b52c969d
JB
172 ],
173 [
174 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
130783a7 175 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeConfigurationRequest>(
1b271a54
JB
176 '../../../assets/json-schemas/ocpp/1.6/ChangeConfiguration.json',
177 moduleName,
178 'constructor'
e9a4164c 179 ),
b52c969d
JB
180 ],
181 [
182 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
130783a7 183 OCPP16ServiceUtils.parseJsonSchemaFile<GetDiagnosticsRequest>(
1b271a54
JB
184 '../../../assets/json-schemas/ocpp/1.6/GetDiagnostics.json',
185 moduleName,
186 'constructor'
e9a4164c 187 ),
b52c969d 188 ],
41189456
JB
189 [
190 OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
191 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16GetCompositeScheduleRequest>(
192 '../../../assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json',
193 moduleName,
194 'constructor'
195 ),
196 ],
b52c969d
JB
197 [
198 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
130783a7 199 OCPP16ServiceUtils.parseJsonSchemaFile<SetChargingProfileRequest>(
1b271a54
JB
200 '../../../assets/json-schemas/ocpp/1.6/SetChargingProfile.json',
201 moduleName,
202 'constructor'
e9a4164c 203 ),
b52c969d
JB
204 ],
205 [
206 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
130783a7 207 OCPP16ServiceUtils.parseJsonSchemaFile<ClearChargingProfileRequest>(
1b271a54
JB
208 '../../../assets/json-schemas/ocpp/1.6/ClearChargingProfile.json',
209 moduleName,
210 'constructor'
e9a4164c 211 ),
b52c969d
JB
212 ],
213 [
214 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
130783a7 215 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeAvailabilityRequest>(
1b271a54
JB
216 '../../../assets/json-schemas/ocpp/1.6/ChangeAvailability.json',
217 moduleName,
218 'constructor'
e9a4164c 219 ),
b52c969d
JB
220 ],
221 [
222 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
130783a7 223 OCPP16ServiceUtils.parseJsonSchemaFile<RemoteStartTransactionRequest>(
1b271a54
JB
224 '../../../assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json',
225 moduleName,
226 'constructor'
e9a4164c 227 ),
b52c969d
JB
228 ],
229 [
230 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
130783a7 231 OCPP16ServiceUtils.parseJsonSchemaFile<RemoteStopTransactionRequest>(
1b271a54
JB
232 '../../../assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json',
233 moduleName,
234 'constructor'
e9a4164c 235 ),
b52c969d
JB
236 ],
237 [
238 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
130783a7 239 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16TriggerMessageRequest>(
1b271a54
JB
240 '../../../assets/json-schemas/ocpp/1.6/TriggerMessage.json',
241 moduleName,
242 'constructor'
e9a4164c 243 ),
b52c969d 244 ],
77b95a89
JB
245 [
246 OCPP16IncomingRequestCommand.DATA_TRANSFER,
130783a7 247 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferRequest>(
1b271a54
JB
248 '../../../assets/json-schemas/ocpp/1.6/DataTransfer.json',
249 moduleName,
250 'constructor'
e9a4164c 251 ),
77b95a89 252 ],
bfbda738
JB
253 [
254 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE,
130783a7 255 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16UpdateFirmwareRequest>(
1b271a54
JB
256 '../../../assets/json-schemas/ocpp/1.6/UpdateFirmware.json',
257 moduleName,
258 'constructor'
e9a4164c 259 ),
bfbda738 260 ],
b52c969d 261 ]);
31f59c6d
JB
262 this.validatePayload = this.validatePayload.bind(this) as (
263 chargingStation: ChargingStation,
264 commandName: OCPP16IncomingRequestCommand,
265 commandPayload: JsonType
266 ) => boolean;
58144adb
JB
267 }
268
f7f98c68 269 public async incomingRequestHandler(
08f130a0 270 chargingStation: ChargingStation,
e7aeea18
JB
271 messageId: string,
272 commandName: OCPP16IncomingRequestCommand,
5cc4b63b 273 commandPayload: JsonType
e7aeea18 274 ): Promise<void> {
5cc4b63b 275 let response: JsonType;
e7aeea18 276 if (
3a13fc92
JB
277 chargingStation.getOcppStrictCompliance() === true &&
278 chargingStation.isInPendingState() === true &&
e7aeea18
JB
279 (commandName === OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION ||
280 commandName === OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION)
281 ) {
282 throw new OCPPError(
283 ErrorType.SECURITY_ERROR,
e3018bc4 284 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
e7aeea18
JB
285 commandPayload,
286 null,
287 2
288 )} while the charging station is in pending state on the central server`,
7369e417
JB
289 commandName,
290 commandPayload
e7aeea18 291 );
caad9d6b 292 }
e7aeea18 293 if (
ed6cfcff
JB
294 chargingStation.isRegistered() === true ||
295 (chargingStation.getOcppStrictCompliance() === false &&
296 chargingStation.isInUnknownState() === true)
e7aeea18 297 ) {
65554cc3 298 if (
ed6cfcff
JB
299 this.incomingRequestHandlers.has(commandName) === true &&
300 OCPP16ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName) === true
65554cc3 301 ) {
124f3553 302 try {
9c5c4195 303 this.validatePayload(chargingStation, commandName, commandPayload);
c75a6675 304 // Call the method to build the response
08f130a0
JB
305 response = await this.incomingRequestHandlers.get(commandName)(
306 chargingStation,
307 commandPayload
308 );
124f3553
JB
309 } catch (error) {
310 // Log
6c8f5d90
JB
311 logger.error(
312 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
313 error
314 );
124f3553
JB
315 throw error;
316 }
317 } else {
318 // Throw exception
e7aeea18
JB
319 throw new OCPPError(
320 ErrorType.NOT_IMPLEMENTED,
e3018bc4 321 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
e7aeea18
JB
322 commandPayload,
323 null,
324 2
325 )}`,
7369e417
JB
326 commandName,
327 commandPayload
e7aeea18 328 );
c0560973
JB
329 }
330 } else {
e7aeea18
JB
331 throw new OCPPError(
332 ErrorType.SECURITY_ERROR,
e3018bc4 333 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
e7aeea18
JB
334 commandPayload,
335 null,
336 2
337 )} while the charging station is not registered on the central server.`,
7369e417
JB
338 commandName,
339 commandPayload
e7aeea18 340 );
c0560973 341 }
c75a6675 342 // Send the built response
08f130a0
JB
343 await chargingStation.ocppRequestService.sendResponse(
344 chargingStation,
345 messageId,
346 response,
347 commandName
348 );
c0560973
JB
349 }
350
9c5c4195
JB
351 private validatePayload(
352 chargingStation: ChargingStation,
353 commandName: OCPP16IncomingRequestCommand,
354 commandPayload: JsonType
355 ): boolean {
45988780 356 if (this.jsonSchemas.has(commandName) === true) {
9c5c4195
JB
357 return this.validateIncomingRequestPayload(
358 chargingStation,
359 commandName,
360 this.jsonSchemas.get(commandName),
361 commandPayload
362 );
363 }
364 logger.warn(
b3fc3ff5 365 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
9c5c4195
JB
366 );
367 return false;
368 }
369
c0560973 370 // Simulate charging station restart
08f130a0
JB
371 private handleRequestReset(
372 chargingStation: ChargingStation,
373 commandPayload: ResetRequest
f03e1042 374 ): GenericResponse {
27f08ad3
JB
375 this.runInAsyncScope(
376 chargingStation.reset.bind(chargingStation) as (
377 this: ChargingStation,
378 ...args: any[]
379 ) => Promise<void>,
380 chargingStation,
381 `${commandPayload.type}Reset` as OCPP16StopTransactionReason
59b6ed8d 382 ).catch(Constants.EMPTY_FUNCTION);
e7aeea18 383 logger.info(
08f130a0 384 `${chargingStation.logPrefix()} ${
e7aeea18
JB
385 commandPayload.type
386 } reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds(
08f130a0 387 chargingStation.stationInfo.resetTime
e7aeea18
JB
388 )}`
389 );
d8b1fab1 390 return OCPP16Constants.OCPP_RESPONSE_ACCEPTED;
c0560973
JB
391 }
392
e7aeea18 393 private async handleRequestUnlockConnector(
08f130a0 394 chargingStation: ChargingStation,
e7aeea18
JB
395 commandPayload: UnlockConnectorRequest
396 ): Promise<UnlockConnectorResponse> {
c0560973 397 const connectorId = commandPayload.connectorId;
a14022a2 398 if (chargingStation.hasConnector(connectorId) === false) {
c60ed4b8 399 logger.error(
2585c6e9 400 `${chargingStation.logPrefix()} Trying to unlock a non existing connector id ${connectorId.toString()}`
c60ed4b8 401 );
d8b1fab1 402 return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
c60ed4b8 403 }
c0560973 404 if (connectorId === 0) {
e7aeea18 405 logger.error(
2585c6e9 406 `${chargingStation.logPrefix()} Trying to unlock connector id ${connectorId.toString()}`
e7aeea18 407 );
d8b1fab1 408 return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
c0560973 409 }
5e3cb728
JB
410 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
411 const stopResponse = await chargingStation.stopTransactionOnConnector(
412 connectorId,
413 OCPP16StopTransactionReason.UNLOCK_COMMAND
414 );
c0560973 415 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
d8b1fab1 416 return OCPP16Constants.OCPP_RESPONSE_UNLOCKED;
c0560973 417 }
d8b1fab1 418 return OCPP16Constants.OCPP_RESPONSE_UNLOCK_FAILED;
c0560973 419 }
4ecff7ce
JB
420 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
421 chargingStation,
ef6fa3fb 422 connectorId,
4ecff7ce
JB
423 OCPP16ChargePointStatus.Available
424 );
d8b1fab1 425 return OCPP16Constants.OCPP_RESPONSE_UNLOCKED;
c0560973
JB
426 }
427
e7aeea18 428 private handleRequestGetConfiguration(
08f130a0 429 chargingStation: ChargingStation,
e7aeea18
JB
430 commandPayload: GetConfigurationRequest
431 ): GetConfigurationResponse {
c0560973
JB
432 const configurationKey: OCPPConfigurationKey[] = [];
433 const unknownKey: string[] = [];
9a15316c 434 if (Utils.isUndefined(commandPayload.key) === true) {
08f130a0 435 for (const configuration of chargingStation.ocppConfiguration.configurationKey) {
a723e7e9 436 if (Utils.isUndefined(configuration.visible) === true) {
7f7b65ca 437 configuration.visible = true;
c0560973 438 }
da8629bb 439 if (configuration.visible === false) {
c0560973
JB
440 continue;
441 }
442 configurationKey.push({
7f7b65ca
JB
443 key: configuration.key,
444 readonly: configuration.readonly,
445 value: configuration.value,
c0560973
JB
446 });
447 }
9a15316c 448 } else if (Utils.isNotEmptyArray(commandPayload.key) === true) {
c0560973 449 for (const key of commandPayload.key) {
17ac262c
JB
450 const keyFound = ChargingStationConfigurationUtils.getConfigurationKey(
451 chargingStation,
acda9cab
JB
452 key,
453 true
17ac262c 454 );
c0560973 455 if (keyFound) {
a723e7e9 456 if (Utils.isUndefined(keyFound.visible) === true) {
c0560973
JB
457 keyFound.visible = true;
458 }
a723e7e9 459 if (keyFound.visible === false) {
c0560973
JB
460 continue;
461 }
462 configurationKey.push({
463 key: keyFound.key,
464 readonly: keyFound.readonly,
465 value: keyFound.value,
466 });
467 } else {
468 unknownKey.push(key);
469 }
470 }
471 }
472 return {
473 configurationKey,
474 unknownKey,
475 };
476 }
477
e7aeea18 478 private handleRequestChangeConfiguration(
08f130a0 479 chargingStation: ChargingStation,
e7aeea18
JB
480 commandPayload: ChangeConfigurationRequest
481 ): ChangeConfigurationResponse {
17ac262c
JB
482 const keyToChange = ChargingStationConfigurationUtils.getConfigurationKey(
483 chargingStation,
484 commandPayload.key,
485 true
486 );
c0560973 487 if (!keyToChange) {
d8b1fab1 488 return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED;
c0560973 489 } else if (keyToChange && keyToChange.readonly) {
d8b1fab1 490 return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REJECTED;
c0560973 491 } else if (keyToChange && !keyToChange.readonly) {
c0560973 492 let valueChanged = false;
a95873d8 493 if (keyToChange.value !== commandPayload.value) {
17ac262c
JB
494 ChargingStationConfigurationUtils.setConfigurationKeyValue(
495 chargingStation,
496 commandPayload.key,
497 commandPayload.value,
498 true
499 );
c0560973
JB
500 valueChanged = true;
501 }
502 let triggerHeartbeatRestart = false;
503 if (keyToChange.key === OCPP16StandardParametersKey.HeartBeatInterval && valueChanged) {
17ac262c
JB
504 ChargingStationConfigurationUtils.setConfigurationKeyValue(
505 chargingStation,
e7aeea18
JB
506 OCPP16StandardParametersKey.HeartbeatInterval,
507 commandPayload.value
508 );
c0560973
JB
509 triggerHeartbeatRestart = true;
510 }
511 if (keyToChange.key === OCPP16StandardParametersKey.HeartbeatInterval && valueChanged) {
17ac262c
JB
512 ChargingStationConfigurationUtils.setConfigurationKeyValue(
513 chargingStation,
e7aeea18
JB
514 OCPP16StandardParametersKey.HeartBeatInterval,
515 commandPayload.value
516 );
c0560973
JB
517 triggerHeartbeatRestart = true;
518 }
519 if (triggerHeartbeatRestart) {
08f130a0 520 chargingStation.restartHeartbeat();
c0560973
JB
521 }
522 if (keyToChange.key === OCPP16StandardParametersKey.WebSocketPingInterval && valueChanged) {
08f130a0 523 chargingStation.restartWebSocketPing();
c0560973
JB
524 }
525 if (keyToChange.reboot) {
d8b1fab1 526 return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED;
c0560973 527 }
d8b1fab1 528 return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED;
c0560973
JB
529 }
530 }
531
e7aeea18 532 private handleRequestSetChargingProfile(
08f130a0 533 chargingStation: ChargingStation,
e7aeea18
JB
534 commandPayload: SetChargingProfileRequest
535 ): SetChargingProfileResponse {
370ae4ee 536 if (
1789ba2c 537 OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 538 chargingStation,
370ae4ee
JB
539 OCPP16SupportedFeatureProfiles.SmartCharging,
540 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE
1789ba2c 541 ) === false
370ae4ee 542 ) {
d8b1fab1 543 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED;
68cb8b91 544 }
a14022a2 545 if (chargingStation.hasConnector(commandPayload.connectorId) === false) {
e7aeea18 546 logger.error(
2585c6e9 547 `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector id ${
e7aeea18
JB
548 commandPayload.connectorId
549 }`
550 );
d8b1fab1 551 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
c0560973 552 }
e7aeea18
JB
553 if (
554 commandPayload.csChargingProfiles.chargingProfilePurpose ===
0ac97927 555 OCPP16ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE &&
e7aeea18
JB
556 commandPayload.connectorId !== 0
557 ) {
d8b1fab1 558 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
c0560973 559 }
e7aeea18
JB
560 if (
561 commandPayload.csChargingProfiles.chargingProfilePurpose ===
0ac97927 562 OCPP16ChargingProfilePurposeType.TX_PROFILE &&
e7aeea18 563 (commandPayload.connectorId === 0 ||
5e3cb728
JB
564 chargingStation.getConnectorStatus(commandPayload.connectorId)?.transactionStarted ===
565 false)
e7aeea18 566 ) {
db0af086
JB
567 logger.error(
568 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${
569 commandPayload.connectorId
570 } without a started transaction`
571 );
d8b1fab1 572 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
c0560973 573 }
ed3d2808 574 OCPP16ServiceUtils.setChargingProfile(
7cb5b17f 575 chargingStation,
e7aeea18
JB
576 commandPayload.connectorId,
577 commandPayload.csChargingProfiles
578 );
579 logger.debug(
08f130a0 580 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${
ad8537a7 581 commandPayload.connectorId
ad67a158
JB
582 }: %j`,
583 commandPayload.csChargingProfiles
e7aeea18 584 );
d8b1fab1 585 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED;
c0560973
JB
586 }
587
41189456
JB
588 private handleRequestGetCompositeSchedule(
589 chargingStation: ChargingStation,
590 commandPayload: OCPP16GetCompositeScheduleRequest
591 ): OCPP16GetCompositeScheduleResponse {
592 if (
593 OCPP16ServiceUtils.checkFeatureProfile(
594 chargingStation,
595 OCPP16SupportedFeatureProfiles.SmartCharging,
596 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE
597 ) === false
598 ) {
d8b1fab1 599 return OCPP16Constants.OCPP_RESPONSE_REJECTED;
41189456 600 }
a14022a2 601 if (chargingStation.hasConnector(commandPayload.connectorId) === false) {
41189456 602 logger.error(
2585c6e9 603 `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector id ${
41189456
JB
604 commandPayload.connectorId
605 }`
606 );
d8b1fab1 607 return OCPP16Constants.OCPP_RESPONSE_REJECTED;
41189456
JB
608 }
609 if (
610 Utils.isEmptyArray(
611 chargingStation.getConnectorStatus(commandPayload.connectorId)?.chargingProfiles
612 )
613 ) {
d8b1fab1 614 return OCPP16Constants.OCPP_RESPONSE_REJECTED;
41189456
JB
615 }
616 const startDate = new Date();
617 const endDate = new Date(startDate.getTime() + commandPayload.duration * 1000);
618 let compositeSchedule: OCPP16ChargingSchedule;
619 for (const chargingProfile of chargingStation.getConnectorStatus(commandPayload.connectorId)
620 .chargingProfiles) {
621 // FIXME: build the composite schedule including the local power limit, the stack level, the charging rate unit, etc.
622 if (
623 chargingProfile.chargingSchedule?.startSchedule >= startDate &&
624 chargingProfile.chargingSchedule?.startSchedule <= endDate
625 ) {
626 compositeSchedule = chargingProfile.chargingSchedule;
627 break;
628 }
629 }
630 return {
631 status: GenericStatus.Accepted,
632 scheduleStart: compositeSchedule?.startSchedule,
633 connectorId: commandPayload.connectorId,
634 chargingSchedule: compositeSchedule,
635 };
636 }
637
e7aeea18 638 private handleRequestClearChargingProfile(
08f130a0 639 chargingStation: ChargingStation,
e7aeea18
JB
640 commandPayload: ClearChargingProfileRequest
641 ): ClearChargingProfileResponse {
370ae4ee 642 if (
a36bad10 643 OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 644 chargingStation,
370ae4ee
JB
645 OCPP16SupportedFeatureProfiles.SmartCharging,
646 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE
a36bad10 647 ) === false
370ae4ee 648 ) {
d8b1fab1 649 return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
68cb8b91 650 }
a14022a2 651 if (chargingStation.hasConnector(commandPayload.connectorId) === false) {
e7aeea18 652 logger.error(
2585c6e9 653 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector id ${
e7aeea18
JB
654 commandPayload.connectorId
655 }`
656 );
d8b1fab1 657 return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
c0560973 658 }
d812bdcb
JB
659 if (
660 !Utils.isNullOrUndefined(commandPayload.connectorId) &&
4334db72
JB
661 Utils.isNotEmptyArray(
662 chargingStation.getConnectorStatus(commandPayload.connectorId)?.chargingProfiles
663 )
d812bdcb 664 ) {
4334db72 665 chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles = [];
e7aeea18 666 logger.debug(
08f130a0 667 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${
ad8537a7 668 commandPayload.connectorId
ad67a158 669 }`
e7aeea18 670 );
d8b1fab1 671 return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
c0560973 672 }
d812bdcb 673 if (Utils.isNullOrUndefined(commandPayload.connectorId)) {
c0560973 674 let clearedCP = false;
4334db72
JB
675 const clearChargingProfiles = (connectorStatus: ConnectorStatus) => {
676 if (Utils.isNotEmptyArray(connectorStatus?.chargingProfiles)) {
677 connectorStatus?.chargingProfiles?.forEach(
678 (chargingProfile: OCPP16ChargingProfile, index: number) => {
e7aeea18
JB
679 let clearCurrentCP = false;
680 if (chargingProfile.chargingProfileId === commandPayload.id) {
681 clearCurrentCP = true;
682 }
683 if (
684 !commandPayload.chargingProfilePurpose &&
685 chargingProfile.stackLevel === commandPayload.stackLevel
686 ) {
687 clearCurrentCP = true;
688 }
689 if (
690 !chargingProfile.stackLevel &&
691 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
692 ) {
693 clearCurrentCP = true;
694 }
695 if (
696 chargingProfile.stackLevel === commandPayload.stackLevel &&
697 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
698 ) {
699 clearCurrentCP = true;
700 }
701 if (clearCurrentCP) {
72092cfc 702 connectorStatus?.chargingProfiles?.splice(index, 1);
e7aeea18 703 logger.debug(
ad67a158
JB
704 `${chargingStation.logPrefix()} Matching charging profile(s) cleared: %j`,
705 chargingProfile
e7aeea18
JB
706 );
707 clearedCP = true;
708 }
4334db72
JB
709 }
710 );
711 }
712 };
713 if (chargingStation.hasEvses) {
714 for (const evseStatus of chargingStation.evses.values()) {
715 for (const connectorStatus of evseStatus.connectors.values()) {
716 clearChargingProfiles(connectorStatus);
717 }
718 }
719 } else {
720 for (const connectorId of chargingStation.connectors.keys()) {
721 clearChargingProfiles(chargingStation.getConnectorStatus(connectorId));
c0560973
JB
722 }
723 }
724 if (clearedCP) {
d8b1fab1 725 return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
c0560973
JB
726 }
727 }
d8b1fab1 728 return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
c0560973
JB
729 }
730
e7aeea18 731 private async handleRequestChangeAvailability(
08f130a0 732 chargingStation: ChargingStation,
e7aeea18
JB
733 commandPayload: ChangeAvailabilityRequest
734 ): Promise<ChangeAvailabilityResponse> {
c0560973 735 const connectorId: number = commandPayload.connectorId;
a14022a2 736 if (chargingStation.hasConnector(connectorId) === false) {
e7aeea18 737 logger.error(
2585c6e9 738 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector id ${connectorId.toString()}`
e7aeea18 739 );
d8b1fab1 740 return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
c0560973 741 }
e7aeea18 742 const chargePointStatus: OCPP16ChargePointStatus =
0d6f335f 743 commandPayload.type === OCPP16AvailabilityType.Operative
721646e9
JB
744 ? OCPP16ChargePointStatus.Available
745 : OCPP16ChargePointStatus.Unavailable;
c0560973 746 if (connectorId === 0) {
d8b1fab1
JB
747 let response: ChangeAvailabilityResponse =
748 OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
08f130a0 749 for (const id of chargingStation.connectors.keys()) {
5e3cb728 750 if (chargingStation.getConnectorStatus(id)?.transactionStarted === true) {
d8b1fab1 751 response = OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
c0560973 752 }
08f130a0 753 chargingStation.getConnectorStatus(id).availability = commandPayload.type;
d8b1fab1 754 if (response === OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) {
4ecff7ce
JB
755 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
756 chargingStation,
757 id,
758 chargePointStatus
759 );
c0560973
JB
760 }
761 }
762 return response;
e7aeea18
JB
763 } else if (
764 connectorId > 0 &&
56eb297e
JB
765 (chargingStation.isChargingStationAvailable() === true ||
766 (chargingStation.isChargingStationAvailable() === false &&
0d6f335f 767 commandPayload.type === OCPP16AvailabilityType.Inoperative))
e7aeea18 768 ) {
5e3cb728 769 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
08f130a0 770 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
d8b1fab1 771 return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
c0560973 772 }
08f130a0 773 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
4ecff7ce
JB
774 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
775 chargingStation,
ef6fa3fb 776 connectorId,
4ecff7ce
JB
777 chargePointStatus
778 );
d8b1fab1 779 return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
c0560973 780 }
d8b1fab1 781 return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
c0560973
JB
782 }
783
e7aeea18 784 private async handleRequestRemoteStartTransaction(
08f130a0 785 chargingStation: ChargingStation,
e7aeea18 786 commandPayload: RemoteStartTransactionRequest
f03e1042 787 ): Promise<GenericResponse> {
658e2d16 788 const transactionConnectorId = commandPayload.connectorId;
a14022a2 789 if (chargingStation.hasConnector(transactionConnectorId) === true) {
44eb6026
JB
790 const remoteStartTransactionLogMsg = `${chargingStation.logPrefix()} Transaction remotely STARTED on ${
791 chargingStation.stationInfo.chargingStationId
792 }#${transactionConnectorId.toString()} for idTag '${commandPayload.idTag}'`;
4ecff7ce
JB
793 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
794 chargingStation,
795 transactionConnectorId,
796 OCPP16ChargePointStatus.Preparing
797 );
1789ba2c 798 const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId);
62340a29
JB
799 if (
800 chargingStation.isChargingStationAvailable() === true &&
801 chargingStation.isConnectorAvailable(transactionConnectorId) === true
802 ) {
e060fe58 803 // Check if authorized
1789ba2c 804 if (chargingStation.getAuthorizeRemoteTxRequests() === true) {
a7fc8211 805 let authorized = false;
e7aeea18 806 if (
1789ba2c 807 chargingStation.getLocalAuthListEnabled() === true &&
f911a4af 808 chargingStation.hasIdTags() === true &&
5a2a53cf 809 Utils.isNotEmptyString(
f911a4af 810 chargingStation.idTagsCache
e302df1d 811 .getIdTags(ChargingStationUtils.getIdTagsFile(chargingStation.stationInfo))
d812bdcb
JB
812 ?.find((idTag) => idTag === commandPayload.idTag)
813 )
e7aeea18 814 ) {
658e2d16
JB
815 connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
816 connectorStatus.idTagLocalAuthorized = true;
36f6a92e 817 authorized = true;
1789ba2c 818 } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
658e2d16 819 connectorStatus.authorizeIdTag = commandPayload.idTag;
2e3d65ae 820 const authorizeResponse: OCPP16AuthorizeResponse =
08f130a0 821 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
822 OCPP16AuthorizeRequest,
823 OCPP16AuthorizeResponse
08f130a0 824 >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
ef6fa3fb
JB
825 idTag: commandPayload.idTag,
826 });
a7fc8211
JB
827 if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
828 authorized = true;
a7fc8211 829 }
71068fb9 830 } else {
e7aeea18 831 logger.warn(
08f130a0 832 `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
e7aeea18 833 );
a7fc8211 834 }
1789ba2c 835 if (authorized === true) {
a7fc8211 836 // Authorization successful, start transaction
e7aeea18
JB
837 if (
838 this.setRemoteStartTransactionChargingProfile(
08f130a0 839 chargingStation,
e7aeea18
JB
840 transactionConnectorId,
841 commandPayload.chargingProfile
1789ba2c 842 ) === true
e7aeea18 843 ) {
658e2d16 844 connectorStatus.transactionRemoteStarted = true;
e7aeea18
JB
845 if (
846 (
08f130a0 847 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
848 OCPP16StartTransactionRequest,
849 OCPP16StartTransactionResponse
08f130a0 850 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
ef6fa3fb
JB
851 connectorId: transactionConnectorId,
852 idTag: commandPayload.idTag,
853 })
e7aeea18
JB
854 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
855 ) {
91a4f151 856 logger.debug(remoteStartTransactionLogMsg);
d8b1fab1 857 return OCPP16Constants.OCPP_RESPONSE_ACCEPTED;
e060fe58 858 }
e7aeea18 859 return this.notifyRemoteStartTransactionRejected(
08f130a0 860 chargingStation,
e7aeea18
JB
861 transactionConnectorId,
862 commandPayload.idTag
863 );
e060fe58 864 }
e7aeea18 865 return this.notifyRemoteStartTransactionRejected(
08f130a0 866 chargingStation,
e7aeea18
JB
867 transactionConnectorId,
868 commandPayload.idTag
869 );
a7fc8211 870 }
e7aeea18 871 return this.notifyRemoteStartTransactionRejected(
08f130a0 872 chargingStation,
e7aeea18
JB
873 transactionConnectorId,
874 commandPayload.idTag
875 );
36f6a92e 876 }
a7fc8211 877 // No authorization check required, start transaction
e7aeea18
JB
878 if (
879 this.setRemoteStartTransactionChargingProfile(
08f130a0 880 chargingStation,
e7aeea18
JB
881 transactionConnectorId,
882 commandPayload.chargingProfile
1789ba2c 883 ) === true
e7aeea18 884 ) {
658e2d16 885 connectorStatus.transactionRemoteStarted = true;
e7aeea18
JB
886 if (
887 (
08f130a0 888 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
889 OCPP16StartTransactionRequest,
890 OCPP16StartTransactionResponse
08f130a0 891 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
ef6fa3fb
JB
892 connectorId: transactionConnectorId,
893 idTag: commandPayload.idTag,
894 })
e7aeea18
JB
895 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
896 ) {
91a4f151 897 logger.debug(remoteStartTransactionLogMsg);
d8b1fab1 898 return OCPP16Constants.OCPP_RESPONSE_ACCEPTED;
e060fe58 899 }
e7aeea18 900 return this.notifyRemoteStartTransactionRejected(
08f130a0 901 chargingStation,
e7aeea18
JB
902 transactionConnectorId,
903 commandPayload.idTag
904 );
e060fe58 905 }
e7aeea18 906 return this.notifyRemoteStartTransactionRejected(
08f130a0 907 chargingStation,
e7aeea18
JB
908 transactionConnectorId,
909 commandPayload.idTag
910 );
c0560973 911 }
e7aeea18 912 return this.notifyRemoteStartTransactionRejected(
08f130a0 913 chargingStation,
e7aeea18
JB
914 transactionConnectorId,
915 commandPayload.idTag
916 );
c0560973 917 }
08f130a0
JB
918 return this.notifyRemoteStartTransactionRejected(
919 chargingStation,
920 transactionConnectorId,
921 commandPayload.idTag
922 );
a7fc8211
JB
923 }
924
e7aeea18 925 private async notifyRemoteStartTransactionRejected(
08f130a0 926 chargingStation: ChargingStation,
e7aeea18
JB
927 connectorId: number,
928 idTag: string
f03e1042 929 ): Promise<GenericResponse> {
e7aeea18 930 if (
721646e9 931 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
e7aeea18 932 ) {
4ecff7ce
JB
933 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
934 chargingStation,
ef6fa3fb 935 connectorId,
4ecff7ce
JB
936 OCPP16ChargePointStatus.Available
937 );
e060fe58 938 }
e7aeea18 939 logger.warn(
2585c6e9 940 `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id ${connectorId.toString()}, idTag '${idTag}', availability '${
72092cfc
JB
941 chargingStation.getConnectorStatus(connectorId)?.availability
942 }', status '${chargingStation.getConnectorStatus(connectorId)?.status}'`
e7aeea18 943 );
d8b1fab1 944 return OCPP16Constants.OCPP_RESPONSE_REJECTED;
c0560973
JB
945 }
946
e7aeea18 947 private setRemoteStartTransactionChargingProfile(
08f130a0 948 chargingStation: ChargingStation,
e7aeea18
JB
949 connectorId: number,
950 cp: OCPP16ChargingProfile
951 ): boolean {
0ac97927 952 if (cp && cp.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE) {
ed3d2808 953 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, cp);
e7aeea18 954 logger.debug(
ad67a158
JB
955 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`,
956 cp
e7aeea18 957 );
a7fc8211 958 return true;
0ac97927 959 } else if (cp && cp.chargingProfilePurpose !== OCPP16ChargingProfilePurposeType.TX_PROFILE) {
e7aeea18 960 logger.warn(
08f130a0 961 `${chargingStation.logPrefix()} Not allowed to set ${
e7aeea18
JB
962 cp.chargingProfilePurpose
963 } charging profile(s) at remote start transaction`
964 );
a7fc8211 965 return false;
e060fe58
JB
966 } else if (!cp) {
967 return true;
a7fc8211
JB
968 }
969 }
970
e7aeea18 971 private async handleRequestRemoteStopTransaction(
08f130a0 972 chargingStation: ChargingStation,
e7aeea18 973 commandPayload: RemoteStopTransactionRequest
f03e1042 974 ): Promise<GenericResponse> {
c0560973 975 const transactionId = commandPayload.transactionId;
08f130a0 976 for (const connectorId of chargingStation.connectors.keys()) {
e7aeea18
JB
977 if (
978 connectorId > 0 &&
08f130a0 979 chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId
e7aeea18 980 ) {
4ecff7ce
JB
981 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
982 chargingStation,
ef6fa3fb 983 connectorId,
4ecff7ce
JB
984 OCPP16ChargePointStatus.Finishing
985 );
5e3cb728
JB
986 const stopResponse = await chargingStation.stopTransactionOnConnector(
987 connectorId,
a65319ba 988 OCPP16StopTransactionReason.REMOTE
5e3cb728
JB
989 );
990 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
d8b1fab1 991 return OCPP16Constants.OCPP_RESPONSE_ACCEPTED;
ef6fa3fb 992 }
d8b1fab1 993 return OCPP16Constants.OCPP_RESPONSE_REJECTED;
c0560973
JB
994 }
995 }
44b9b577 996 logger.warn(
54ebb82c 997 `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction with id: ${transactionId.toString()}`
e7aeea18 998 );
d8b1fab1 999 return OCPP16Constants.OCPP_RESPONSE_REJECTED;
c0560973 1000 }
47e22477 1001
b03df580
JB
1002 private handleRequestUpdateFirmware(
1003 chargingStation: ChargingStation,
1004 commandPayload: OCPP16UpdateFirmwareRequest
1005 ): OCPP16UpdateFirmwareResponse {
1006 if (
1007 OCPP16ServiceUtils.checkFeatureProfile(
1008 chargingStation,
1009 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1010 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE
1011 ) === false
1012 ) {
5d280aae 1013 logger.warn(
90293abb 1014 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported`
5d280aae 1015 );
d8b1fab1 1016 return OCPP16Constants.OCPP_RESPONSE_EMPTY;
5d280aae
JB
1017 }
1018 if (
1019 !Utils.isNullOrUndefined(chargingStation.stationInfo.firmwareStatus) &&
1020 chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed
1021 ) {
1022 logger.warn(
90293abb 1023 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`
5d280aae 1024 );
d8b1fab1 1025 return OCPP16Constants.OCPP_RESPONSE_EMPTY;
b03df580 1026 }
c9a4f9ea 1027 const retrieveDate = Utils.convertToDate(commandPayload.retrieveDate);
2c7bdc61 1028 const now = Date.now();
72092cfc 1029 if (retrieveDate?.getTime() <= now) {
27f08ad3 1030 this.runInAsyncScope(
62340a29 1031 this.updateFirmwareSimulation.bind(this) as (
27f08ad3
JB
1032 this: OCPP16IncomingRequestService,
1033 ...args: any[]
1034 ) => Promise<void>,
1035 this,
1036 chargingStation
59b6ed8d 1037 ).catch(Constants.EMPTY_FUNCTION);
c9a4f9ea
JB
1038 } else {
1039 setTimeout(() => {
62340a29 1040 this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION);
72092cfc 1041 }, retrieveDate?.getTime() - now);
c9a4f9ea 1042 }
d8b1fab1 1043 return OCPP16Constants.OCPP_RESPONSE_EMPTY;
c9a4f9ea
JB
1044 }
1045
62340a29 1046 private async updateFirmwareSimulation(
c9a4f9ea 1047 chargingStation: ChargingStation,
90293abb
JB
1048 maxDelay = 30,
1049 minDelay = 15
c9a4f9ea 1050 ): Promise<void> {
1bf29f5b
JB
1051 if (
1052 ChargingStationUtils.checkChargingStation(chargingStation, chargingStation.logPrefix()) ===
1053 false
1054 ) {
1055 return;
1056 }
c9a4f9ea
JB
1057 for (const connectorId of chargingStation.connectors.keys()) {
1058 if (
1059 connectorId > 0 &&
72092cfc 1060 chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false
c9a4f9ea 1061 ) {
4ecff7ce
JB
1062 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
1063 chargingStation,
c9a4f9ea 1064 connectorId,
4ecff7ce
JB
1065 OCPP16ChargePointStatus.Unavailable
1066 );
c9a4f9ea
JB
1067 }
1068 }
93f0c2c8
JB
1069 await chargingStation.ocppRequestService.requestHandler<
1070 OCPP16FirmwareStatusNotificationRequest,
1071 OCPP16FirmwareStatusNotificationResponse
1072 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1073 status: OCPP16FirmwareStatus.Downloading,
1074 });
1075 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloading;
5d280aae 1076 if (
93f0c2c8
JB
1077 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
1078 OCPP16FirmwareStatus.DownloadFailed
5d280aae 1079 ) {
93f0c2c8 1080 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
5d280aae
JB
1081 await chargingStation.ocppRequestService.requestHandler<
1082 OCPP16FirmwareStatusNotificationRequest,
1083 OCPP16FirmwareStatusNotificationResponse
1084 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
15748260 1085 status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus,
5d280aae 1086 });
93f0c2c8
JB
1087 chargingStation.stationInfo.firmwareStatus =
1088 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus;
5d280aae
JB
1089 return;
1090 }
90293abb 1091 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
c9a4f9ea
JB
1092 await chargingStation.ocppRequestService.requestHandler<
1093 OCPP16FirmwareStatusNotificationRequest,
1094 OCPP16FirmwareStatusNotificationResponse
1095 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1096 status: OCPP16FirmwareStatus.Downloaded,
1097 });
1098 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloaded;
380ccc42 1099 let wasTransactionsStarted = false;
62340a29
JB
1100 let transactionsStarted: boolean;
1101 do {
1102 let trxCount = 0;
1103 for (const connectorId of chargingStation.connectors.keys()) {
1104 if (
1105 connectorId > 0 &&
1106 chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true
1107 ) {
1108 trxCount++;
1109 }
1110 }
1111 if (trxCount > 0) {
1112 const waitTime = 15 * 1000;
1113 logger.debug(
1114 `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: ${trxCount} transaction(s) in progress, waiting ${
1115 waitTime / 1000
1116 } seconds before continuing firmware update simulation`
1117 );
1118 await Utils.sleep(waitTime);
1119 transactionsStarted = true;
380ccc42 1120 wasTransactionsStarted = true;
62340a29
JB
1121 } else {
1122 for (const connectorId of chargingStation.connectors.keys()) {
1123 if (
1124 connectorId > 0 &&
1125 chargingStation.getConnectorStatus(connectorId)?.status !==
1126 OCPP16ChargePointStatus.Unavailable
1127 ) {
4ecff7ce
JB
1128 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
1129 chargingStation,
62340a29 1130 connectorId,
4ecff7ce
JB
1131 OCPP16ChargePointStatus.Unavailable
1132 );
62340a29
JB
1133 }
1134 }
1135 transactionsStarted = false;
1136 }
1137 } while (transactionsStarted);
380ccc42
JB
1138 !wasTransactionsStarted &&
1139 (await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000));
1bf29f5b
JB
1140 if (
1141 ChargingStationUtils.checkChargingStation(chargingStation, chargingStation.logPrefix()) ===
1142 false
1143 ) {
1144 return;
1145 }
c9a4f9ea
JB
1146 await chargingStation.ocppRequestService.requestHandler<
1147 OCPP16FirmwareStatusNotificationRequest,
1148 OCPP16FirmwareStatusNotificationResponse
1149 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1150 status: OCPP16FirmwareStatus.Installing,
1151 });
1152 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Installing;
93f0c2c8
JB
1153 if (
1154 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
1155 OCPP16FirmwareStatus.InstallationFailed
1156 ) {
1157 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1158 await chargingStation.ocppRequestService.requestHandler<
1159 OCPP16FirmwareStatusNotificationRequest,
1160 OCPP16FirmwareStatusNotificationResponse
1161 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1162 status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus,
1163 });
1164 chargingStation.stationInfo.firmwareStatus =
1165 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus;
1166 return;
1167 }
15748260 1168 if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) {
90293abb 1169 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
5d280aae
JB
1170 await chargingStation.reset(OCPP16StopTransactionReason.REBOOT);
1171 }
b03df580
JB
1172 }
1173
e7aeea18 1174 private async handleRequestGetDiagnostics(
08f130a0 1175 chargingStation: ChargingStation,
e7aeea18
JB
1176 commandPayload: GetDiagnosticsRequest
1177 ): Promise<GetDiagnosticsResponse> {
68cb8b91 1178 if (
1789ba2c 1179 OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 1180 chargingStation,
370ae4ee
JB
1181 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1182 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1789ba2c 1183 ) === false
68cb8b91 1184 ) {
90293abb
JB
1185 logger.warn(
1186 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`
1187 );
d8b1fab1 1188 return OCPP16Constants.OCPP_RESPONSE_EMPTY;
68cb8b91 1189 }
a3868ec4 1190 const uri = new URL(commandPayload.location);
47e22477
JB
1191 if (uri.protocol.startsWith('ftp:')) {
1192 let ftpClient: Client;
1193 try {
e7aeea18 1194 const logFiles = fs
0d8140bd 1195 .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'))
72092cfc
JB
1196 .filter((file) => file.endsWith('.log'))
1197 .map((file) => path.join('./', file));
44eb6026 1198 const diagnosticsArchive = `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`;
47e22477
JB
1199 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
1200 ftpClient = new Client();
1201 const accessResponse = await ftpClient.access({
1202 host: uri.host,
5a2a53cf
JB
1203 ...(Utils.isNotEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
1204 ...(Utils.isNotEmptyString(uri.username) && { user: uri.username }),
1205 ...(Utils.isNotEmptyString(uri.password) && { password: uri.password }),
47e22477
JB
1206 });
1207 let uploadResponse: FTPResponse;
1208 if (accessResponse.code === 220) {
72092cfc 1209 ftpClient.trackProgress((info) => {
e7aeea18 1210 logger.info(
08f130a0 1211 `${chargingStation.logPrefix()} ${
e7aeea18
JB
1212 info.bytes / 1024
1213 } bytes transferred from diagnostics archive ${info.name}`
1214 );
6a8329b4
JB
1215 chargingStation.ocppRequestService
1216 .requestHandler<
1217 OCPP16DiagnosticsStatusNotificationRequest,
1218 OCPP16DiagnosticsStatusNotificationResponse
1219 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1220 status: OCPP16DiagnosticsStatus.Uploading,
1221 })
72092cfc 1222 .catch((error) => {
6a8329b4
JB
1223 logger.error(
1224 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
1225 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
1226 }'`,
1227 error
1228 );
1229 });
47e22477 1230 });
e7aeea18 1231 uploadResponse = await ftpClient.uploadFrom(
0d8140bd
JB
1232 path.join(
1233 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'),
1234 diagnosticsArchive
1235 ),
14ecae6a 1236 `${uri.pathname}${diagnosticsArchive}`
e7aeea18 1237 );
47e22477 1238 if (uploadResponse.code === 226) {
08f130a0 1239 await chargingStation.ocppRequestService.requestHandler<
c9a4f9ea
JB
1240 OCPP16DiagnosticsStatusNotificationRequest,
1241 OCPP16DiagnosticsStatusNotificationResponse
08f130a0 1242 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1243 status: OCPP16DiagnosticsStatus.Uploaded,
1244 });
47e22477
JB
1245 if (ftpClient) {
1246 ftpClient.close();
1247 }
1248 return { fileName: diagnosticsArchive };
1249 }
e7aeea18
JB
1250 throw new OCPPError(
1251 ErrorType.GENERIC_ERROR,
1252 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
44eb6026 1253 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
e7aeea18
JB
1254 }`,
1255 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1256 );
47e22477 1257 }
e7aeea18
JB
1258 throw new OCPPError(
1259 ErrorType.GENERIC_ERROR,
1260 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
44eb6026 1261 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
e7aeea18
JB
1262 }`,
1263 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1264 );
47e22477 1265 } catch (error) {
08f130a0 1266 await chargingStation.ocppRequestService.requestHandler<
c9a4f9ea
JB
1267 OCPP16DiagnosticsStatusNotificationRequest,
1268 OCPP16DiagnosticsStatusNotificationResponse
08f130a0 1269 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1270 status: OCPP16DiagnosticsStatus.UploadFailed,
1271 });
47e22477
JB
1272 if (ftpClient) {
1273 ftpClient.close();
1274 }
e7aeea18 1275 return this.handleIncomingRequestError(
08f130a0 1276 chargingStation,
e7aeea18
JB
1277 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1278 error as Error,
d8b1fab1 1279 { errorResponse: OCPP16Constants.OCPP_RESPONSE_EMPTY }
e7aeea18 1280 );
47e22477
JB
1281 }
1282 } else {
e7aeea18 1283 logger.error(
08f130a0 1284 `${chargingStation.logPrefix()} Unsupported protocol ${
e7aeea18
JB
1285 uri.protocol
1286 } to transfer the diagnostic logs archive`
1287 );
08f130a0 1288 await chargingStation.ocppRequestService.requestHandler<
c9a4f9ea
JB
1289 OCPP16DiagnosticsStatusNotificationRequest,
1290 OCPP16DiagnosticsStatusNotificationResponse
08f130a0 1291 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1292 status: OCPP16DiagnosticsStatus.UploadFailed,
1293 });
d8b1fab1 1294 return OCPP16Constants.OCPP_RESPONSE_EMPTY;
47e22477
JB
1295 }
1296 }
802cfa13 1297
e7aeea18 1298 private handleRequestTriggerMessage(
08f130a0 1299 chargingStation: ChargingStation,
e7aeea18
JB
1300 commandPayload: OCPP16TriggerMessageRequest
1301 ): OCPP16TriggerMessageResponse {
370ae4ee
JB
1302 if (
1303 !OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 1304 chargingStation,
370ae4ee
JB
1305 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1306 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
c60ed4b8
JB
1307 ) ||
1308 !OCPP16ServiceUtils.isMessageTriggerSupported(
1309 chargingStation,
1310 commandPayload.requestedMessage
370ae4ee
JB
1311 )
1312 ) {
d8b1fab1 1313 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
68cb8b91 1314 }
c60ed4b8 1315 if (
4caa7e67 1316 !OCPP16ServiceUtils.isConnectorIdValid(
c60ed4b8
JB
1317 chargingStation,
1318 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1319 commandPayload.connectorId
1320 )
1321 ) {
d8b1fab1 1322 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
dc661702 1323 }
802cfa13
JB
1324 try {
1325 switch (commandPayload.requestedMessage) {
c60ed4b8 1326 case OCPP16MessageTrigger.BootNotification:
802cfa13 1327 setTimeout(() => {
08f130a0 1328 chargingStation.ocppRequestService
f7f98c68 1329 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
08f130a0 1330 chargingStation,
6a8b180d 1331 OCPP16RequestCommand.BOOT_NOTIFICATION,
8bfbc743 1332 chargingStation.bootNotificationRequest,
6a8b180d 1333 { skipBufferingOnError: true, triggerMessage: true }
e7aeea18 1334 )
72092cfc 1335 .then((response) => {
8bfbc743 1336 chargingStation.bootNotificationResponse = response;
ae711c83 1337 })
59b6ed8d 1338 .catch(Constants.EMPTY_FUNCTION);
d8b1fab1
JB
1339 }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1340 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
c60ed4b8 1341 case OCPP16MessageTrigger.Heartbeat:
802cfa13 1342 setTimeout(() => {
08f130a0 1343 chargingStation.ocppRequestService
f7f98c68 1344 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
08f130a0 1345 chargingStation,
ef6fa3fb
JB
1346 OCPP16RequestCommand.HEARTBEAT,
1347 null,
1348 {
1349 triggerMessage: true,
1350 }
1351 )
59b6ed8d 1352 .catch(Constants.EMPTY_FUNCTION);
d8b1fab1
JB
1353 }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1354 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
c60ed4b8 1355 case OCPP16MessageTrigger.StatusNotification:
dc661702 1356 setTimeout(() => {
d812bdcb 1357 if (!Utils.isNullOrUndefined(commandPayload?.connectorId)) {
08f130a0 1358 chargingStation.ocppRequestService
dc661702 1359 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
08f130a0 1360 chargingStation,
dc661702
JB
1361 OCPP16RequestCommand.STATUS_NOTIFICATION,
1362 {
1363 connectorId: commandPayload.connectorId,
1364 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
72092cfc 1365 status: chargingStation.getConnectorStatus(commandPayload.connectorId)?.status,
dc661702
JB
1366 },
1367 {
1368 triggerMessage: true,
1369 }
1370 )
59b6ed8d 1371 .catch(Constants.EMPTY_FUNCTION);
dc661702 1372 } else {
08f130a0
JB
1373 for (const connectorId of chargingStation.connectors.keys()) {
1374 chargingStation.ocppRequestService
dc661702
JB
1375 .requestHandler<
1376 OCPP16StatusNotificationRequest,
1377 OCPP16StatusNotificationResponse
1378 >(
08f130a0 1379 chargingStation,
dc661702
JB
1380 OCPP16RequestCommand.STATUS_NOTIFICATION,
1381 {
1382 connectorId,
1383 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
72092cfc 1384 status: chargingStation.getConnectorStatus(connectorId)?.status,
dc661702
JB
1385 },
1386 {
1387 triggerMessage: true,
1388 }
1389 )
59b6ed8d 1390 .catch(Constants.EMPTY_FUNCTION);
dc661702
JB
1391 }
1392 }
d8b1fab1
JB
1393 }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1394 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
802cfa13 1395 default:
d8b1fab1 1396 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
802cfa13
JB
1397 }
1398 } catch (error) {
e7aeea18 1399 return this.handleIncomingRequestError(
08f130a0 1400 chargingStation,
e7aeea18
JB
1401 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1402 error as Error,
d8b1fab1 1403 { errorResponse: OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
e7aeea18 1404 );
802cfa13
JB
1405 }
1406 }
77b95a89
JB
1407
1408 private handleRequestDataTransfer(
1409 chargingStation: ChargingStation,
1410 commandPayload: OCPP16DataTransferRequest
1411 ): OCPP16DataTransferResponse {
1412 try {
1413 if (Object.values(OCPP16DataTransferVendorId).includes(commandPayload.vendorId)) {
1414 return {
1415 status: OCPP16DataTransferStatus.ACCEPTED,
1416 };
1417 }
1418 return {
1419 status: OCPP16DataTransferStatus.UNKNOWN_VENDOR_ID,
1420 };
1421 } catch (error) {
1422 return this.handleIncomingRequestError(
1423 chargingStation,
1424 OCPP16IncomingRequestCommand.DATA_TRANSFER,
1425 error as Error,
d8b1fab1 1426 { errorResponse: OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }
77b95a89
JB
1427 );
1428 }
1429 }
c0560973 1430}