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