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