refactor: cleanup imports
[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
d972af76
JB
3import { createWriteStream, readdirSync } from 'node:fs';
4import { dirname, join, resolve } from 'node:path';
130783a7 5import { URL, fileURLToPath } from 'node:url';
8114d10e 6
6c1761d4 7import type { JSONSchemaType } from 'ajv';
27782dbc 8import { Client, type FTPResponse } from 'basic-ftp';
d972af76 9import { create } from 'tar';
8114d10e 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 {
d972af76 1274 const logFiles = readdirSync(resolve(dirname(fileURLToPath(import.meta.url)), '../'))
72092cfc 1275 .filter((file) => file.endsWith('.log'))
d972af76 1276 .map((file) => join('./', file));
44eb6026 1277 const diagnosticsArchive = `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`;
d972af76 1278 create({ gzip: true }, logFiles).pipe(createWriteStream(diagnosticsArchive));
47e22477
JB
1279 ftpClient = new Client();
1280 const accessResponse = await ftpClient.access({
1281 host: uri.host,
5a2a53cf
JB
1282 ...(Utils.isNotEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
1283 ...(Utils.isNotEmptyString(uri.username) && { user: uri.username }),
1284 ...(Utils.isNotEmptyString(uri.password) && { password: uri.password }),
47e22477
JB
1285 });
1286 let uploadResponse: FTPResponse;
1287 if (accessResponse.code === 220) {
72092cfc 1288 ftpClient.trackProgress((info) => {
e7aeea18 1289 logger.info(
08f130a0 1290 `${chargingStation.logPrefix()} ${
e7aeea18
JB
1291 info.bytes / 1024
1292 } bytes transferred from diagnostics archive ${info.name}`
1293 );
6a8329b4
JB
1294 chargingStation.ocppRequestService
1295 .requestHandler<
1296 OCPP16DiagnosticsStatusNotificationRequest,
1297 OCPP16DiagnosticsStatusNotificationResponse
1298 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1299 status: OCPP16DiagnosticsStatus.Uploading,
1300 })
72092cfc 1301 .catch((error) => {
6a8329b4 1302 logger.error(
66dd3447
JB
1303 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics:
1304 Error while sending '${OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION}'`,
6a8329b4
JB
1305 error
1306 );
1307 });
47e22477 1308 });
e7aeea18 1309 uploadResponse = await ftpClient.uploadFrom(
d972af76 1310 join(resolve(dirname(fileURLToPath(import.meta.url)), '../'), diagnosticsArchive),
14ecae6a 1311 `${uri.pathname}${diagnosticsArchive}`
e7aeea18 1312 );
47e22477 1313 if (uploadResponse.code === 226) {
08f130a0 1314 await chargingStation.ocppRequestService.requestHandler<
c9a4f9ea
JB
1315 OCPP16DiagnosticsStatusNotificationRequest,
1316 OCPP16DiagnosticsStatusNotificationResponse
08f130a0 1317 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1318 status: OCPP16DiagnosticsStatus.Uploaded,
1319 });
47e22477
JB
1320 if (ftpClient) {
1321 ftpClient.close();
1322 }
1323 return { fileName: diagnosticsArchive };
1324 }
e7aeea18
JB
1325 throw new OCPPError(
1326 ErrorType.GENERIC_ERROR,
1327 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
44eb6026 1328 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
e7aeea18
JB
1329 }`,
1330 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1331 );
47e22477 1332 }
e7aeea18
JB
1333 throw new OCPPError(
1334 ErrorType.GENERIC_ERROR,
1335 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
44eb6026 1336 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
e7aeea18
JB
1337 }`,
1338 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1339 );
47e22477 1340 } catch (error) {
08f130a0 1341 await chargingStation.ocppRequestService.requestHandler<
c9a4f9ea
JB
1342 OCPP16DiagnosticsStatusNotificationRequest,
1343 OCPP16DiagnosticsStatusNotificationResponse
08f130a0 1344 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1345 status: OCPP16DiagnosticsStatus.UploadFailed,
1346 });
47e22477
JB
1347 if (ftpClient) {
1348 ftpClient.close();
1349 }
e7aeea18 1350 return this.handleIncomingRequestError(
08f130a0 1351 chargingStation,
e7aeea18
JB
1352 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1353 error as Error,
d8b1fab1 1354 { errorResponse: OCPP16Constants.OCPP_RESPONSE_EMPTY }
e7aeea18 1355 );
47e22477
JB
1356 }
1357 } else {
e7aeea18 1358 logger.error(
08f130a0 1359 `${chargingStation.logPrefix()} Unsupported protocol ${
e7aeea18
JB
1360 uri.protocol
1361 } to transfer the diagnostic logs archive`
1362 );
08f130a0 1363 await chargingStation.ocppRequestService.requestHandler<
c9a4f9ea
JB
1364 OCPP16DiagnosticsStatusNotificationRequest,
1365 OCPP16DiagnosticsStatusNotificationResponse
08f130a0 1366 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1367 status: OCPP16DiagnosticsStatus.UploadFailed,
1368 });
d8b1fab1 1369 return OCPP16Constants.OCPP_RESPONSE_EMPTY;
47e22477
JB
1370 }
1371 }
802cfa13 1372
e7aeea18 1373 private handleRequestTriggerMessage(
08f130a0 1374 chargingStation: ChargingStation,
e7aeea18
JB
1375 commandPayload: OCPP16TriggerMessageRequest
1376 ): OCPP16TriggerMessageResponse {
370ae4ee
JB
1377 if (
1378 !OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 1379 chargingStation,
370ae4ee
JB
1380 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1381 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
c60ed4b8
JB
1382 ) ||
1383 !OCPP16ServiceUtils.isMessageTriggerSupported(
1384 chargingStation,
1385 commandPayload.requestedMessage
370ae4ee
JB
1386 )
1387 ) {
d8b1fab1 1388 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
68cb8b91 1389 }
c60ed4b8 1390 if (
4caa7e67 1391 !OCPP16ServiceUtils.isConnectorIdValid(
c60ed4b8
JB
1392 chargingStation,
1393 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1394 commandPayload.connectorId
1395 )
1396 ) {
d8b1fab1 1397 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
dc661702 1398 }
802cfa13
JB
1399 try {
1400 switch (commandPayload.requestedMessage) {
c60ed4b8 1401 case OCPP16MessageTrigger.BootNotification:
802cfa13 1402 setTimeout(() => {
08f130a0 1403 chargingStation.ocppRequestService
f7f98c68 1404 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
08f130a0 1405 chargingStation,
6a8b180d 1406 OCPP16RequestCommand.BOOT_NOTIFICATION,
8bfbc743 1407 chargingStation.bootNotificationRequest,
6a8b180d 1408 { skipBufferingOnError: true, triggerMessage: true }
e7aeea18 1409 )
72092cfc 1410 .then((response) => {
8bfbc743 1411 chargingStation.bootNotificationResponse = response;
ae711c83 1412 })
59b6ed8d 1413 .catch(Constants.EMPTY_FUNCTION);
d8b1fab1
JB
1414 }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1415 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
c60ed4b8 1416 case OCPP16MessageTrigger.Heartbeat:
802cfa13 1417 setTimeout(() => {
08f130a0 1418 chargingStation.ocppRequestService
f7f98c68 1419 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
08f130a0 1420 chargingStation,
ef6fa3fb
JB
1421 OCPP16RequestCommand.HEARTBEAT,
1422 null,
1423 {
1424 triggerMessage: true,
1425 }
1426 )
59b6ed8d 1427 .catch(Constants.EMPTY_FUNCTION);
d8b1fab1
JB
1428 }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1429 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
c60ed4b8 1430 case OCPP16MessageTrigger.StatusNotification:
dc661702 1431 setTimeout(() => {
d812bdcb 1432 if (!Utils.isNullOrUndefined(commandPayload?.connectorId)) {
08f130a0 1433 chargingStation.ocppRequestService
dc661702 1434 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
08f130a0 1435 chargingStation,
dc661702
JB
1436 OCPP16RequestCommand.STATUS_NOTIFICATION,
1437 {
1438 connectorId: commandPayload.connectorId,
1439 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
72092cfc 1440 status: chargingStation.getConnectorStatus(commandPayload.connectorId)?.status,
dc661702
JB
1441 },
1442 {
1443 triggerMessage: true,
1444 }
1445 )
59b6ed8d 1446 .catch(Constants.EMPTY_FUNCTION);
dc661702 1447 } else {
ded57f02
JB
1448 // eslint-disable-next-line no-lonely-if
1449 if (chargingStation.hasEvses) {
1450 for (const evseStatus of chargingStation.evses.values()) {
1451 for (const [connectorId, connectorStatus] of evseStatus.connectors) {
1452 chargingStation.ocppRequestService
1453 .requestHandler<
1454 OCPP16StatusNotificationRequest,
1455 OCPP16StatusNotificationResponse
1456 >(
1457 chargingStation,
1458 OCPP16RequestCommand.STATUS_NOTIFICATION,
1459 {
1460 connectorId,
1461 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1462 status: connectorStatus.status,
1463 },
1464 {
1465 triggerMessage: true,
1466 }
1467 )
1468 .catch(Constants.EMPTY_FUNCTION);
1469 }
1470 }
1471 } else {
1472 for (const connectorId of chargingStation.connectors.keys()) {
1473 chargingStation.ocppRequestService
1474 .requestHandler<
1475 OCPP16StatusNotificationRequest,
1476 OCPP16StatusNotificationResponse
1477 >(
1478 chargingStation,
1479 OCPP16RequestCommand.STATUS_NOTIFICATION,
1480 {
1481 connectorId,
1482 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1483 status: chargingStation.getConnectorStatus(connectorId)?.status,
1484 },
1485 {
1486 triggerMessage: true,
1487 }
1488 )
1489 .catch(Constants.EMPTY_FUNCTION);
1490 }
dc661702
JB
1491 }
1492 }
d8b1fab1
JB
1493 }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1494 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
802cfa13 1495 default:
d8b1fab1 1496 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
802cfa13
JB
1497 }
1498 } catch (error) {
e7aeea18 1499 return this.handleIncomingRequestError(
08f130a0 1500 chargingStation,
e7aeea18
JB
1501 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1502 error as Error,
d8b1fab1 1503 { errorResponse: OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
e7aeea18 1504 );
802cfa13
JB
1505 }
1506 }
77b95a89
JB
1507
1508 private handleRequestDataTransfer(
1509 chargingStation: ChargingStation,
1510 commandPayload: OCPP16DataTransferRequest
1511 ): OCPP16DataTransferResponse {
1512 try {
1513 if (Object.values(OCPP16DataTransferVendorId).includes(commandPayload.vendorId)) {
b63b4a73 1514 return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED;
77b95a89 1515 }
b63b4a73 1516 return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID;
77b95a89
JB
1517 } catch (error) {
1518 return this.handleIncomingRequestError(
1519 chargingStation,
1520 OCPP16IncomingRequestCommand.DATA_TRANSFER,
1521 error as Error,
d8b1fab1 1522 { errorResponse: OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }
77b95a89
JB
1523 );
1524 }
1525 }
24578c31
JB
1526
1527 private async handleRequestReserveNow(
1528 chargingStation: ChargingStation,
1529 commandPayload: OCPP16ReserveNowRequest
1530 ): Promise<OCPP16ReserveNowResponse> {
66dd3447
JB
1531 if (
1532 !OCPP16ServiceUtils.checkFeatureProfile(
1533 chargingStation,
1534 OCPP16SupportedFeatureProfiles.Reservation,
1535 OCPP16IncomingRequestCommand.RESERVE_NOW
1536 )
1537 ) {
178956d8 1538 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED;
66dd3447 1539 }
24578c31 1540 const { reservationId, idTag, connectorId } = commandPayload;
24578c31
JB
1541 let response: OCPP16ReserveNowResponse;
1542 try {
66dd3447 1543 if (!chargingStation.isConnectorAvailable(connectorId) && connectorId > 0) {
178956d8 1544 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED;
24578c31 1545 }
66dd3447 1546 if (connectorId === 0 && !chargingStation.getReservationOnConnectorId0Enabled()) {
178956d8 1547 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED;
24578c31 1548 }
66dd3447 1549 if (!(await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, connectorId, idTag))) {
178956d8 1550 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED;
24578c31
JB
1551 }
1552 switch (chargingStation.getConnectorStatus(connectorId).status) {
178956d8
JB
1553 case OCPP16ChargePointStatus.Faulted:
1554 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED;
24578c31 1555 break;
178956d8
JB
1556 case OCPP16ChargePointStatus.Preparing:
1557 case OCPP16ChargePointStatus.Charging:
1558 case OCPP16ChargePointStatus.SuspendedEV:
1559 case OCPP16ChargePointStatus.SuspendedEVSE:
1560 case OCPP16ChargePointStatus.Finishing:
1561 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
24578c31 1562 break;
178956d8
JB
1563 case OCPP16ChargePointStatus.Unavailable:
1564 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_UNAVAILABLE;
24578c31 1565 break;
178956d8 1566 case OCPP16ChargePointStatus.Reserved:
66dd3447 1567 if (!chargingStation.isConnectorReservable(reservationId, idTag, connectorId)) {
178956d8 1568 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
24578c31
JB
1569 break;
1570 }
1571 // eslint-disable-next-line no-fallthrough
1572 default:
66dd3447 1573 if (!chargingStation.isConnectorReservable(reservationId, idTag)) {
178956d8 1574 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
d193a949
JB
1575 break;
1576 }
1577 await chargingStation.addReservation({
1578 id: commandPayload.reservationId,
1579 ...commandPayload,
1580 });
178956d8 1581 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_ACCEPTED;
24578c31
JB
1582 break;
1583 }
1584 return response;
1585 } catch (error) {
178956d8 1586 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.Available;
24578c31
JB
1587 return this.handleIncomingRequestError(
1588 chargingStation,
1589 OCPP16IncomingRequestCommand.RESERVE_NOW,
1590 error as Error,
178956d8 1591 { errorResponse: OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED }
24578c31
JB
1592 );
1593 }
1594 }
1595
1596 private async handleRequestCancelReservation(
1597 chargingStation: ChargingStation,
1598 commandPayload: OCPP16CancelReservationRequest
b1f1b0f6 1599 ): Promise<GenericResponse> {
66dd3447
JB
1600 if (
1601 !OCPP16ServiceUtils.checkFeatureProfile(
1602 chargingStation,
1603 OCPP16SupportedFeatureProfiles.Reservation,
1604 OCPP16IncomingRequestCommand.CANCEL_RESERVATION
1605 )
1606 ) {
178956d8 1607 return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED;
66dd3447 1608 }
24578c31 1609 try {
d193a949
JB
1610 const { reservationId } = commandPayload;
1611 const [exists, reservation] = chargingStation.doesReservationExists({ id: reservationId });
24578c31
JB
1612 if (!exists) {
1613 logger.error(
66dd3447
JB
1614 `${chargingStation.logPrefix()} Reservation with ID ${reservationId}
1615 does not exist on charging station`
24578c31 1616 );
178956d8 1617 return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED;
24578c31 1618 }
ec9f36cc
JB
1619 await chargingStation.removeReservation(
1620 reservation,
1621 ReservationTerminationReason.RESERVATION_CANCELED
1622 );
178956d8 1623 return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED;
24578c31
JB
1624 } catch (error) {
1625 return this.handleIncomingRequestError(
1626 chargingStation,
1627 OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
1628 error as Error,
178956d8 1629 { errorResponse: OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED }
24578c31 1630 );
24578c31
JB
1631 }
1632 }
c0560973 1633}