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