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