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