Silence rollup warning: (!) Unresolved dependencies
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16IncomingRequestService.ts
CommitLineData
c8eeb62b
JB
1// Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
2
8114d10e
JB
3import fs from 'fs';
4import path from 'path';
5import { URL, fileURLToPath } from 'url';
6
6c1761d4 7import type { JSONSchemaType } from 'ajv';
8114d10e
JB
8import { Client, FTPResponse } from 'basic-ftp';
9import tar from 'tar';
10
11import OCPPError from '../../../exception/OCPPError';
6c1761d4 12import type { JsonObject, JsonType } from '../../../types/JsonType';
8114d10e
JB
13import { OCPP16ChargePointErrorCode } from '../../../types/ocpp/1.6/ChargePointErrorCode';
14import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
15import {
16 ChargingProfilePurposeType,
17 OCPP16ChargingProfile,
18} from '../../../types/ocpp/1.6/ChargingProfile';
19import {
20 OCPP16StandardParametersKey,
21 OCPP16SupportedFeatureProfiles,
22} from '../../../types/ocpp/1.6/Configuration';
23import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStatus';
e7aeea18
JB
24import {
25 ChangeAvailabilityRequest,
26 ChangeConfigurationRequest,
27 ClearChargingProfileRequest,
ef6fa3fb 28 DiagnosticsStatusNotificationRequest,
e7aeea18
JB
29 GetConfigurationRequest,
30 GetDiagnosticsRequest,
31 MessageTrigger,
32 OCPP16AvailabilityType,
ef6fa3fb 33 OCPP16BootNotificationRequest,
e3018bc4 34 OCPP16ClearCacheRequest,
ef6fa3fb 35 OCPP16HeartbeatRequest,
e7aeea18 36 OCPP16IncomingRequestCommand,
94a464f9 37 OCPP16RequestCommand,
ef6fa3fb 38 OCPP16StatusNotificationRequest,
e7aeea18
JB
39 OCPP16TriggerMessageRequest,
40 RemoteStartTransactionRequest,
41 RemoteStopTransactionRequest,
42 ResetRequest,
43 SetChargingProfileRequest,
44 UnlockConnectorRequest,
45} from '../../../types/ocpp/1.6/Requests';
6c1761d4 46import type {
e7aeea18
JB
47 ChangeAvailabilityResponse,
48 ChangeConfigurationResponse,
49 ClearChargingProfileResponse,
f22266fd 50 DiagnosticsStatusNotificationResponse,
e7aeea18
JB
51 GetConfigurationResponse,
52 GetDiagnosticsResponse,
f22266fd
JB
53 OCPP16BootNotificationResponse,
54 OCPP16HeartbeatResponse,
55 OCPP16StatusNotificationResponse,
e7aeea18
JB
56 OCPP16TriggerMessageResponse,
57 SetChargingProfileResponse,
58 UnlockConnectorResponse,
59} from '../../../types/ocpp/1.6/Responses';
e7aeea18
JB
60import {
61 OCPP16AuthorizationStatus,
ef6fa3fb 62 OCPP16AuthorizeRequest,
2e3d65ae 63 OCPP16AuthorizeResponse,
ef6fa3fb 64 OCPP16StartTransactionRequest,
e7454a1f 65 OCPP16StartTransactionResponse,
e7aeea18
JB
66 OCPP16StopTransactionReason,
67} from '../../../types/ocpp/1.6/Transaction';
6c1761d4 68import type { OCPPConfigurationKey } from '../../../types/ocpp/Configuration';
8114d10e 69import { ErrorType } from '../../../types/ocpp/ErrorType';
6c1761d4
JB
70import type { IncomingRequestHandler } from '../../../types/ocpp/Requests';
71import type { DefaultResponse } from '../../../types/ocpp/Responses';
8114d10e
JB
72import Constants from '../../../utils/Constants';
73import logger from '../../../utils/Logger';
74import Utils from '../../../utils/Utils';
73b9adec 75import type ChargingStation from '../../ChargingStation';
17ac262c 76import { ChargingStationConfigurationUtils } from '../../ChargingStationConfigurationUtils';
9d7484a4 77import { ChargingStationUtils } from '../../ChargingStationUtils';
c0560973 78import OCPPIncomingRequestService from '../OCPPIncomingRequestService';
8114d10e 79import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
c0560973 80
2a115f87 81const moduleName = 'OCPP16IncomingRequestService';
909dcf2d 82
c0560973 83export default class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
58144adb 84 private incomingRequestHandlers: Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>;
b52c969d 85 private jsonSchemas: Map<OCPP16IncomingRequestCommand, JSONSchemaType<JsonObject>>;
58144adb 86
08f130a0 87 public constructor() {
909dcf2d 88 if (new.target?.name === moduleName) {
06127450 89 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
9f2e3130 90 }
08f130a0 91 super();
58144adb
JB
92 this.incomingRequestHandlers = new Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>([
93 [OCPP16IncomingRequestCommand.RESET, this.handleRequestReset.bind(this)],
94 [OCPP16IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)],
95 [OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, this.handleRequestUnlockConnector.bind(this)],
e7aeea18
JB
96 [
97 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
98 this.handleRequestGetConfiguration.bind(this),
99 ],
100 [
101 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
102 this.handleRequestChangeConfiguration.bind(this),
103 ],
104 [
105 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
106 this.handleRequestSetChargingProfile.bind(this),
107 ],
108 [
109 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
110 this.handleRequestClearChargingProfile.bind(this),
111 ],
112 [
113 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
114 this.handleRequestChangeAvailability.bind(this),
115 ],
116 [
117 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
118 this.handleRequestRemoteStartTransaction.bind(this),
119 ],
120 [
121 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
122 this.handleRequestRemoteStopTransaction.bind(this),
123 ],
734d790d 124 [OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, this.handleRequestGetDiagnostics.bind(this)],
e7aeea18 125 [OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, this.handleRequestTriggerMessage.bind(this)],
58144adb 126 ]);
b52c969d
JB
127 this.jsonSchemas = new Map<OCPP16IncomingRequestCommand, JSONSchemaType<JsonObject>>([
128 [
129 OCPP16IncomingRequestCommand.RESET,
130 JSON.parse(
131 fs.readFileSync(
132 path.resolve(
133 path.dirname(fileURLToPath(import.meta.url)),
134 '../../../assets/json-schemas/ocpp/1.6/Reset.json'
135 ),
136 'utf8'
137 )
138 ) as JSONSchemaType<ResetRequest>,
139 ],
140 [
141 OCPP16IncomingRequestCommand.CLEAR_CACHE,
142 JSON.parse(
143 fs.readFileSync(
144 path.resolve(
145 path.dirname(fileURLToPath(import.meta.url)),
146 '../../../assets/json-schemas/ocpp/1.6/ClearCache.json'
147 ),
148 'utf8'
149 )
150 ) as JSONSchemaType<OCPP16ClearCacheRequest>,
151 ],
152 [
153 OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
154 JSON.parse(
155 fs.readFileSync(
156 path.resolve(
157 path.dirname(fileURLToPath(import.meta.url)),
158 '../../../assets/json-schemas/ocpp/1.6/UnlockConnector.json'
159 ),
160 'utf8'
161 )
162 ) as JSONSchemaType<UnlockConnectorRequest>,
163 ],
164 [
165 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
166 JSON.parse(
167 fs.readFileSync(
168 path.resolve(
169 path.dirname(fileURLToPath(import.meta.url)),
170 '../../../assets/json-schemas/ocpp/1.6/GetConfiguration.json'
171 ),
172 'utf8'
173 )
174 ) as JSONSchemaType<GetConfigurationRequest>,
175 ],
176 [
177 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
178 JSON.parse(
179 fs.readFileSync(
180 path.resolve(
181 path.dirname(fileURLToPath(import.meta.url)),
182 '../../../assets/json-schemas/ocpp/1.6/ChangeConfiguration.json'
183 ),
184 'utf8'
185 )
186 ) as JSONSchemaType<ChangeConfigurationRequest>,
187 ],
188 [
189 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
190 JSON.parse(
191 fs.readFileSync(
192 path.resolve(
193 path.dirname(fileURLToPath(import.meta.url)),
194 '../../../assets/json-schemas/ocpp/1.6/GetDiagnostics.json'
195 ),
196 'utf8'
197 )
198 ) as JSONSchemaType<GetDiagnosticsRequest>,
199 ],
200 [
201 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
202 JSON.parse(
203 fs.readFileSync(
204 path.resolve(
205 path.dirname(fileURLToPath(import.meta.url)),
206 '../../../assets/json-schemas/ocpp/1.6/SetChargingProfile.json'
207 ),
208 'utf8'
209 )
210 ) as JSONSchemaType<SetChargingProfileRequest>,
211 ],
212 [
213 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
214 JSON.parse(
215 fs.readFileSync(
216 path.resolve(
217 path.dirname(fileURLToPath(import.meta.url)),
218 '../../../assets/json-schemas/ocpp/1.6/ClearChargingProfile.json'
219 ),
220 'utf8'
221 )
222 ) as JSONSchemaType<ClearChargingProfileRequest>,
223 ],
224 [
225 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
226 JSON.parse(
227 fs.readFileSync(
228 path.resolve(
229 path.dirname(fileURLToPath(import.meta.url)),
230 '../../../assets/json-schemas/ocpp/1.6/ChangeAvailability.json'
231 ),
232 'utf8'
233 )
234 ) as JSONSchemaType<ChangeAvailabilityRequest>,
235 ],
236 [
237 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
238 JSON.parse(
239 fs.readFileSync(
240 path.resolve(
241 path.dirname(fileURLToPath(import.meta.url)),
242 '../../../assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json'
243 ),
244 'utf8'
245 )
246 ) as JSONSchemaType<RemoteStartTransactionRequest>,
247 ],
248 [
249 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
250 JSON.parse(
251 fs.readFileSync(
252 path.resolve(
253 path.dirname(fileURLToPath(import.meta.url)),
254 '../../../assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json'
255 ),
256 'utf8'
257 )
258 ) as JSONSchemaType<RemoteStopTransactionRequest>,
259 ],
260 [
261 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
262 JSON.parse(
263 fs.readFileSync(
264 path.resolve(
265 path.dirname(fileURLToPath(import.meta.url)),
266 '../../../assets/json-schemas/ocpp/1.6/TriggerMessage.json'
267 ),
268 'utf8'
269 )
270 ) as JSONSchemaType<OCPP16TriggerMessageRequest>,
271 ],
272 ]);
9952c548 273 this.validatePayload.bind(this);
58144adb
JB
274 }
275
f7f98c68 276 public async incomingRequestHandler(
08f130a0 277 chargingStation: ChargingStation,
e7aeea18
JB
278 messageId: string,
279 commandName: OCPP16IncomingRequestCommand,
5cc4b63b 280 commandPayload: JsonType
e7aeea18 281 ): Promise<void> {
5cc4b63b 282 let response: JsonType;
e7aeea18 283 if (
08f130a0
JB
284 chargingStation.getOcppStrictCompliance() &&
285 chargingStation.isInPendingState() &&
e7aeea18
JB
286 (commandName === OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION ||
287 commandName === OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION)
288 ) {
289 throw new OCPPError(
290 ErrorType.SECURITY_ERROR,
e3018bc4 291 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
e7aeea18
JB
292 commandPayload,
293 null,
294 2
295 )} while the charging station is in pending state on the central server`,
7369e417
JB
296 commandName,
297 commandPayload
e7aeea18 298 );
caad9d6b 299 }
e7aeea18 300 if (
ed6cfcff
JB
301 chargingStation.isRegistered() === true ||
302 (chargingStation.getOcppStrictCompliance() === false &&
303 chargingStation.isInUnknownState() === true)
e7aeea18 304 ) {
65554cc3 305 if (
ed6cfcff
JB
306 this.incomingRequestHandlers.has(commandName) === true &&
307 OCPP16ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName) === true
65554cc3 308 ) {
124f3553 309 try {
9c5c4195 310 this.validatePayload(chargingStation, commandName, commandPayload);
c75a6675 311 // Call the method to build the response
08f130a0
JB
312 response = await this.incomingRequestHandlers.get(commandName)(
313 chargingStation,
314 commandPayload
315 );
124f3553
JB
316 } catch (error) {
317 // Log
6c8f5d90
JB
318 logger.error(
319 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
320 error
321 );
124f3553
JB
322 throw error;
323 }
324 } else {
325 // Throw exception
e7aeea18
JB
326 throw new OCPPError(
327 ErrorType.NOT_IMPLEMENTED,
e3018bc4 328 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
e7aeea18
JB
329 commandPayload,
330 null,
331 2
332 )}`,
7369e417
JB
333 commandName,
334 commandPayload
e7aeea18 335 );
c0560973
JB
336 }
337 } else {
e7aeea18
JB
338 throw new OCPPError(
339 ErrorType.SECURITY_ERROR,
e3018bc4 340 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
e7aeea18
JB
341 commandPayload,
342 null,
343 2
344 )} while the charging station is not registered on the central server.`,
7369e417
JB
345 commandName,
346 commandPayload
e7aeea18 347 );
c0560973 348 }
c75a6675 349 // Send the built response
08f130a0
JB
350 await chargingStation.ocppRequestService.sendResponse(
351 chargingStation,
352 messageId,
353 response,
354 commandName
355 );
c0560973
JB
356 }
357
9c5c4195
JB
358 private validatePayload(
359 chargingStation: ChargingStation,
360 commandName: OCPP16IncomingRequestCommand,
361 commandPayload: JsonType
362 ): boolean {
363 if (this.jsonSchemas.has(commandName)) {
364 return this.validateIncomingRequestPayload(
365 chargingStation,
366 commandName,
367 this.jsonSchemas.get(commandName),
368 commandPayload
369 );
370 }
371 logger.warn(
ee307db5 372 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command ${commandName} PDU validation`
9c5c4195
JB
373 );
374 return false;
375 }
376
c0560973 377 // Simulate charging station restart
08f130a0
JB
378 private handleRequestReset(
379 chargingStation: ChargingStation,
380 commandPayload: ResetRequest
381 ): DefaultResponse {
e6159ce8
JB
382 this.asyncResource.runInAsyncScope(
383 chargingStation.reset.bind(chargingStation) as (
384 this: ChargingStation,
385 ...args: any[]
386 ) => void,
387 chargingStation,
388 (commandPayload.type + 'Reset') as OCPP16StopTransactionReason
389 );
e7aeea18 390 logger.info(
08f130a0 391 `${chargingStation.logPrefix()} ${
e7aeea18
JB
392 commandPayload.type
393 } reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds(
08f130a0 394 chargingStation.stationInfo.resetTime
e7aeea18
JB
395 )}`
396 );
c0560973
JB
397 return Constants.OCPP_RESPONSE_ACCEPTED;
398 }
399
a36bad10
JB
400 private handleRequestClearCache(chargingStation: ChargingStation): DefaultResponse {
401 chargingStation.authorizedTagsCache.deleteAuthorizedTags(
402 ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
403 );
c0560973
JB
404 return Constants.OCPP_RESPONSE_ACCEPTED;
405 }
406
e7aeea18 407 private async handleRequestUnlockConnector(
08f130a0 408 chargingStation: ChargingStation,
e7aeea18
JB
409 commandPayload: UnlockConnectorRequest
410 ): Promise<UnlockConnectorResponse> {
c0560973
JB
411 const connectorId = commandPayload.connectorId;
412 if (connectorId === 0) {
e7aeea18 413 logger.error(
08f130a0 414 chargingStation.logPrefix() + ' Trying to unlock connector ' + connectorId.toString()
e7aeea18 415 );
c0560973
JB
416 return Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
417 }
5e3cb728
JB
418 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
419 const stopResponse = await chargingStation.stopTransactionOnConnector(
420 connectorId,
421 OCPP16StopTransactionReason.UNLOCK_COMMAND
422 );
c0560973
JB
423 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
424 return Constants.OCPP_RESPONSE_UNLOCKED;
425 }
426 return Constants.OCPP_RESPONSE_UNLOCK_FAILED;
427 }
08f130a0 428 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
429 OCPP16StatusNotificationRequest,
430 OCPP16StatusNotificationResponse
08f130a0 431 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
432 connectorId,
433 status: OCPP16ChargePointStatus.AVAILABLE,
434 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
435 });
08f130a0 436 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
c0560973
JB
437 return Constants.OCPP_RESPONSE_UNLOCKED;
438 }
439
e7aeea18 440 private handleRequestGetConfiguration(
08f130a0 441 chargingStation: ChargingStation,
e7aeea18
JB
442 commandPayload: GetConfigurationRequest
443 ): GetConfigurationResponse {
c0560973
JB
444 const configurationKey: OCPPConfigurationKey[] = [];
445 const unknownKey: string[] = [];
a723e7e9 446 if (Utils.isEmptyArray(commandPayload.key) === true) {
08f130a0 447 for (const configuration of chargingStation.ocppConfiguration.configurationKey) {
a723e7e9 448 if (Utils.isUndefined(configuration.visible) === true) {
7f7b65ca 449 configuration.visible = true;
c0560973 450 }
da8629bb 451 if (configuration.visible === false) {
c0560973
JB
452 continue;
453 }
454 configurationKey.push({
7f7b65ca
JB
455 key: configuration.key,
456 readonly: configuration.readonly,
457 value: configuration.value,
c0560973
JB
458 });
459 }
460 } else {
461 for (const key of commandPayload.key) {
17ac262c
JB
462 const keyFound = ChargingStationConfigurationUtils.getConfigurationKey(
463 chargingStation,
464 key
465 );
c0560973 466 if (keyFound) {
a723e7e9 467 if (Utils.isUndefined(keyFound.visible) === true) {
c0560973
JB
468 keyFound.visible = true;
469 }
a723e7e9 470 if (keyFound.visible === false) {
c0560973
JB
471 continue;
472 }
473 configurationKey.push({
474 key: keyFound.key,
475 readonly: keyFound.readonly,
476 value: keyFound.value,
477 });
478 } else {
479 unknownKey.push(key);
480 }
481 }
482 }
483 return {
484 configurationKey,
485 unknownKey,
486 };
487 }
488
e7aeea18 489 private handleRequestChangeConfiguration(
08f130a0 490 chargingStation: ChargingStation,
e7aeea18
JB
491 commandPayload: ChangeConfigurationRequest
492 ): ChangeConfigurationResponse {
17ac262c
JB
493 const keyToChange = ChargingStationConfigurationUtils.getConfigurationKey(
494 chargingStation,
495 commandPayload.key,
496 true
497 );
c0560973
JB
498 if (!keyToChange) {
499 return Constants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED;
500 } else if (keyToChange && keyToChange.readonly) {
501 return Constants.OCPP_CONFIGURATION_RESPONSE_REJECTED;
502 } else if (keyToChange && !keyToChange.readonly) {
c0560973 503 let valueChanged = false;
a95873d8 504 if (keyToChange.value !== commandPayload.value) {
17ac262c
JB
505 ChargingStationConfigurationUtils.setConfigurationKeyValue(
506 chargingStation,
507 commandPayload.key,
508 commandPayload.value,
509 true
510 );
c0560973
JB
511 valueChanged = true;
512 }
513 let triggerHeartbeatRestart = false;
514 if (keyToChange.key === OCPP16StandardParametersKey.HeartBeatInterval && valueChanged) {
17ac262c
JB
515 ChargingStationConfigurationUtils.setConfigurationKeyValue(
516 chargingStation,
e7aeea18
JB
517 OCPP16StandardParametersKey.HeartbeatInterval,
518 commandPayload.value
519 );
c0560973
JB
520 triggerHeartbeatRestart = true;
521 }
522 if (keyToChange.key === OCPP16StandardParametersKey.HeartbeatInterval && valueChanged) {
17ac262c
JB
523 ChargingStationConfigurationUtils.setConfigurationKeyValue(
524 chargingStation,
e7aeea18
JB
525 OCPP16StandardParametersKey.HeartBeatInterval,
526 commandPayload.value
527 );
c0560973
JB
528 triggerHeartbeatRestart = true;
529 }
530 if (triggerHeartbeatRestart) {
08f130a0 531 chargingStation.restartHeartbeat();
c0560973
JB
532 }
533 if (keyToChange.key === OCPP16StandardParametersKey.WebSocketPingInterval && valueChanged) {
08f130a0 534 chargingStation.restartWebSocketPing();
c0560973
JB
535 }
536 if (keyToChange.reboot) {
537 return Constants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED;
538 }
539 return Constants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED;
540 }
541 }
542
e7aeea18 543 private handleRequestSetChargingProfile(
08f130a0 544 chargingStation: ChargingStation,
e7aeea18
JB
545 commandPayload: SetChargingProfileRequest
546 ): SetChargingProfileResponse {
370ae4ee 547 if (
1789ba2c 548 OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 549 chargingStation,
370ae4ee
JB
550 OCPP16SupportedFeatureProfiles.SmartCharging,
551 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE
1789ba2c 552 ) === false
370ae4ee 553 ) {
68cb8b91
JB
554 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED;
555 }
1789ba2c 556 if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
e7aeea18 557 logger.error(
08f130a0 558 `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${
e7aeea18
JB
559 commandPayload.connectorId
560 }`
561 );
c0560973
JB
562 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
563 }
e7aeea18
JB
564 if (
565 commandPayload.csChargingProfiles.chargingProfilePurpose ===
566 ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE &&
567 commandPayload.connectorId !== 0
568 ) {
c0560973
JB
569 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
570 }
e7aeea18
JB
571 if (
572 commandPayload.csChargingProfiles.chargingProfilePurpose ===
573 ChargingProfilePurposeType.TX_PROFILE &&
574 (commandPayload.connectorId === 0 ||
5e3cb728
JB
575 chargingStation.getConnectorStatus(commandPayload.connectorId)?.transactionStarted ===
576 false)
e7aeea18 577 ) {
db0af086
JB
578 logger.error(
579 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${
580 commandPayload.connectorId
581 } without a started transaction`
582 );
c0560973
JB
583 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
584 }
ed3d2808 585 OCPP16ServiceUtils.setChargingProfile(
7cb5b17f 586 chargingStation,
e7aeea18
JB
587 commandPayload.connectorId,
588 commandPayload.csChargingProfiles
589 );
590 logger.debug(
08f130a0 591 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${
ad8537a7
JB
592 commandPayload.connectorId
593 }, dump their stack: %j`,
08f130a0 594 chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles
e7aeea18 595 );
c0560973
JB
596 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED;
597 }
598
e7aeea18 599 private handleRequestClearChargingProfile(
08f130a0 600 chargingStation: ChargingStation,
e7aeea18
JB
601 commandPayload: ClearChargingProfileRequest
602 ): ClearChargingProfileResponse {
370ae4ee 603 if (
a36bad10 604 OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 605 chargingStation,
370ae4ee
JB
606 OCPP16SupportedFeatureProfiles.SmartCharging,
607 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE
a36bad10 608 ) === false
370ae4ee 609 ) {
68cb8b91
JB
610 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
611 }
1789ba2c 612 if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
e7aeea18 613 logger.error(
08f130a0 614 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${
e7aeea18
JB
615 commandPayload.connectorId
616 }`
617 );
c0560973
JB
618 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
619 }
1789ba2c 620 const connectorStatus = chargingStation.getConnectorStatus(commandPayload.connectorId);
658e2d16
JB
621 if (commandPayload.connectorId && !Utils.isEmptyArray(connectorStatus.chargingProfiles)) {
622 connectorStatus.chargingProfiles = [];
e7aeea18 623 logger.debug(
08f130a0 624 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${
ad8537a7
JB
625 commandPayload.connectorId
626 }, dump their stack: %j`,
658e2d16 627 connectorStatus.chargingProfiles
e7aeea18 628 );
c0560973
JB
629 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
630 }
631 if (!commandPayload.connectorId) {
632 let clearedCP = false;
08f130a0
JB
633 for (const connectorId of chargingStation.connectors.keys()) {
634 if (!Utils.isEmptyArray(chargingStation.getConnectorStatus(connectorId).chargingProfiles)) {
635 chargingStation
e7aeea18
JB
636 .getConnectorStatus(connectorId)
637 .chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
638 let clearCurrentCP = false;
639 if (chargingProfile.chargingProfileId === commandPayload.id) {
640 clearCurrentCP = true;
641 }
642 if (
643 !commandPayload.chargingProfilePurpose &&
644 chargingProfile.stackLevel === commandPayload.stackLevel
645 ) {
646 clearCurrentCP = true;
647 }
648 if (
649 !chargingProfile.stackLevel &&
650 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
651 ) {
652 clearCurrentCP = true;
653 }
654 if (
655 chargingProfile.stackLevel === commandPayload.stackLevel &&
656 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
657 ) {
658 clearCurrentCP = true;
659 }
660 if (clearCurrentCP) {
ccb1d6e9 661 connectorStatus.chargingProfiles.splice(index, 1);
e7aeea18 662 logger.debug(
08f130a0 663 `${chargingStation.logPrefix()} Matching charging profile(s) cleared on connector id ${
ad8537a7
JB
664 commandPayload.connectorId
665 }, dump their stack: %j`,
658e2d16 666 connectorStatus.chargingProfiles
e7aeea18
JB
667 );
668 clearedCP = true;
669 }
670 });
c0560973
JB
671 }
672 }
673 if (clearedCP) {
674 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
675 }
676 }
677 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
678 }
679
e7aeea18 680 private async handleRequestChangeAvailability(
08f130a0 681 chargingStation: ChargingStation,
e7aeea18
JB
682 commandPayload: ChangeAvailabilityRequest
683 ): Promise<ChangeAvailabilityResponse> {
c0560973 684 const connectorId: number = commandPayload.connectorId;
08f130a0 685 if (!chargingStation.getConnectorStatus(connectorId)) {
e7aeea18 686 logger.error(
08f130a0 687 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`
e7aeea18 688 );
c0560973
JB
689 return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
690 }
e7aeea18
JB
691 const chargePointStatus: OCPP16ChargePointStatus =
692 commandPayload.type === OCPP16AvailabilityType.OPERATIVE
693 ? OCPP16ChargePointStatus.AVAILABLE
694 : OCPP16ChargePointStatus.UNAVAILABLE;
c0560973
JB
695 if (connectorId === 0) {
696 let response: ChangeAvailabilityResponse = Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
08f130a0 697 for (const id of chargingStation.connectors.keys()) {
5e3cb728 698 if (chargingStation.getConnectorStatus(id)?.transactionStarted === true) {
c0560973
JB
699 response = Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
700 }
08f130a0 701 chargingStation.getConnectorStatus(id).availability = commandPayload.type;
c0560973 702 if (response === Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) {
08f130a0 703 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
704 OCPP16StatusNotificationRequest,
705 OCPP16StatusNotificationResponse
08f130a0 706 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
707 connectorId: id,
708 status: chargePointStatus,
709 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
710 });
08f130a0 711 chargingStation.getConnectorStatus(id).status = chargePointStatus;
c0560973
JB
712 }
713 }
714 return response;
e7aeea18
JB
715 } else if (
716 connectorId > 0 &&
56eb297e
JB
717 (chargingStation.isChargingStationAvailable() === true ||
718 (chargingStation.isChargingStationAvailable() === false &&
e7aeea18
JB
719 commandPayload.type === OCPP16AvailabilityType.INOPERATIVE))
720 ) {
5e3cb728 721 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
08f130a0 722 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
c0560973
JB
723 return Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
724 }
08f130a0
JB
725 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
726 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
727 OCPP16StatusNotificationRequest,
728 OCPP16StatusNotificationResponse
08f130a0 729 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
730 connectorId,
731 status: chargePointStatus,
732 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
733 });
08f130a0 734 chargingStation.getConnectorStatus(connectorId).status = chargePointStatus;
c0560973
JB
735 return Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
736 }
737 return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
738 }
739
e7aeea18 740 private async handleRequestRemoteStartTransaction(
08f130a0 741 chargingStation: ChargingStation,
e7aeea18
JB
742 commandPayload: RemoteStartTransactionRequest
743 ): Promise<DefaultResponse> {
658e2d16 744 const transactionConnectorId = commandPayload.connectorId;
1789ba2c 745 if (chargingStation.connectors.has(transactionConnectorId) === true) {
91a4f151
JB
746 const remoteStartTransactionLogMsg =
747 chargingStation.logPrefix() +
748 ' Transaction remotely STARTED on ' +
749 chargingStation.stationInfo.chargingStationId +
750 '#' +
751 transactionConnectorId.toString() +
752 " for idTag '" +
753 commandPayload.idTag +
754 "'";
08f130a0 755 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
756 OCPP16StatusNotificationRequest,
757 OCPP16StatusNotificationResponse
08f130a0 758 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
759 connectorId: transactionConnectorId,
760 status: OCPP16ChargePointStatus.PREPARING,
761 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
762 });
1789ba2c 763 const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId);
658e2d16 764 connectorStatus.status = OCPP16ChargePointStatus.PREPARING;
1789ba2c 765 if (chargingStation.isChargingStationAvailable() === true) {
e060fe58 766 // Check if authorized
1789ba2c 767 if (chargingStation.getAuthorizeRemoteTxRequests() === true) {
a7fc8211 768 let authorized = false;
e7aeea18 769 if (
1789ba2c
JB
770 chargingStation.getLocalAuthListEnabled() === true &&
771 chargingStation.hasAuthorizedTags() === true &&
9d7484a4
JB
772 chargingStation.authorizedTagsCache
773 .getAuthorizedTags(
774 ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
775 )
1789ba2c 776 .find((idTag) => idTag === commandPayload.idTag)
e7aeea18 777 ) {
658e2d16
JB
778 connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
779 connectorStatus.idTagLocalAuthorized = true;
36f6a92e 780 authorized = true;
1789ba2c 781 } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
658e2d16 782 connectorStatus.authorizeIdTag = commandPayload.idTag;
2e3d65ae 783 const authorizeResponse: OCPP16AuthorizeResponse =
08f130a0 784 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
785 OCPP16AuthorizeRequest,
786 OCPP16AuthorizeResponse
08f130a0 787 >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
ef6fa3fb
JB
788 idTag: commandPayload.idTag,
789 });
a7fc8211
JB
790 if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
791 authorized = true;
a7fc8211 792 }
71068fb9 793 } else {
e7aeea18 794 logger.warn(
08f130a0 795 `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
e7aeea18 796 );
a7fc8211 797 }
1789ba2c 798 if (authorized === true) {
a7fc8211 799 // Authorization successful, start transaction
e7aeea18
JB
800 if (
801 this.setRemoteStartTransactionChargingProfile(
08f130a0 802 chargingStation,
e7aeea18
JB
803 transactionConnectorId,
804 commandPayload.chargingProfile
1789ba2c 805 ) === true
e7aeea18 806 ) {
658e2d16 807 connectorStatus.transactionRemoteStarted = true;
e7aeea18
JB
808 if (
809 (
08f130a0 810 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
811 OCPP16StartTransactionRequest,
812 OCPP16StartTransactionResponse
08f130a0 813 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
ef6fa3fb
JB
814 connectorId: transactionConnectorId,
815 idTag: commandPayload.idTag,
816 })
e7aeea18
JB
817 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
818 ) {
91a4f151 819 logger.debug(remoteStartTransactionLogMsg);
e060fe58
JB
820 return Constants.OCPP_RESPONSE_ACCEPTED;
821 }
e7aeea18 822 return this.notifyRemoteStartTransactionRejected(
08f130a0 823 chargingStation,
e7aeea18
JB
824 transactionConnectorId,
825 commandPayload.idTag
826 );
e060fe58 827 }
e7aeea18 828 return this.notifyRemoteStartTransactionRejected(
08f130a0 829 chargingStation,
e7aeea18
JB
830 transactionConnectorId,
831 commandPayload.idTag
832 );
a7fc8211 833 }
e7aeea18 834 return this.notifyRemoteStartTransactionRejected(
08f130a0 835 chargingStation,
e7aeea18
JB
836 transactionConnectorId,
837 commandPayload.idTag
838 );
36f6a92e 839 }
a7fc8211 840 // No authorization check required, start transaction
e7aeea18
JB
841 if (
842 this.setRemoteStartTransactionChargingProfile(
08f130a0 843 chargingStation,
e7aeea18
JB
844 transactionConnectorId,
845 commandPayload.chargingProfile
1789ba2c 846 ) === true
e7aeea18 847 ) {
658e2d16 848 connectorStatus.transactionRemoteStarted = true;
e7aeea18
JB
849 if (
850 (
08f130a0 851 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
852 OCPP16StartTransactionRequest,
853 OCPP16StartTransactionResponse
08f130a0 854 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
ef6fa3fb
JB
855 connectorId: transactionConnectorId,
856 idTag: commandPayload.idTag,
857 })
e7aeea18
JB
858 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
859 ) {
91a4f151 860 logger.debug(remoteStartTransactionLogMsg);
e060fe58
JB
861 return Constants.OCPP_RESPONSE_ACCEPTED;
862 }
e7aeea18 863 return this.notifyRemoteStartTransactionRejected(
08f130a0 864 chargingStation,
e7aeea18
JB
865 transactionConnectorId,
866 commandPayload.idTag
867 );
e060fe58 868 }
e7aeea18 869 return this.notifyRemoteStartTransactionRejected(
08f130a0 870 chargingStation,
e7aeea18
JB
871 transactionConnectorId,
872 commandPayload.idTag
873 );
c0560973 874 }
e7aeea18 875 return this.notifyRemoteStartTransactionRejected(
08f130a0 876 chargingStation,
e7aeea18
JB
877 transactionConnectorId,
878 commandPayload.idTag
879 );
c0560973 880 }
08f130a0
JB
881 return this.notifyRemoteStartTransactionRejected(
882 chargingStation,
883 transactionConnectorId,
884 commandPayload.idTag
885 );
a7fc8211
JB
886 }
887
e7aeea18 888 private async notifyRemoteStartTransactionRejected(
08f130a0 889 chargingStation: ChargingStation,
e7aeea18
JB
890 connectorId: number,
891 idTag: string
892 ): Promise<DefaultResponse> {
893 if (
08f130a0 894 chargingStation.getConnectorStatus(connectorId).status !== OCPP16ChargePointStatus.AVAILABLE
e7aeea18 895 ) {
08f130a0 896 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
897 OCPP16StatusNotificationRequest,
898 OCPP16StatusNotificationResponse
08f130a0 899 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
900 connectorId,
901 status: OCPP16ChargePointStatus.AVAILABLE,
902 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
903 });
08f130a0 904 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
e060fe58 905 }
e7aeea18 906 logger.warn(
08f130a0 907 chargingStation.logPrefix() +
e7aeea18
JB
908 ' Remote starting transaction REJECTED on connector Id ' +
909 connectorId.toString() +
91a4f151 910 ", idTag '" +
e7aeea18 911 idTag +
91a4f151 912 "', availability '" +
08f130a0 913 chargingStation.getConnectorStatus(connectorId).availability +
91a4f151
JB
914 "', status '" +
915 chargingStation.getConnectorStatus(connectorId).status +
916 "'"
e7aeea18 917 );
c0560973
JB
918 return Constants.OCPP_RESPONSE_REJECTED;
919 }
920
e7aeea18 921 private setRemoteStartTransactionChargingProfile(
08f130a0 922 chargingStation: ChargingStation,
e7aeea18
JB
923 connectorId: number,
924 cp: OCPP16ChargingProfile
925 ): boolean {
a7fc8211 926 if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) {
ed3d2808 927 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, cp);
e7aeea18 928 logger.debug(
08f130a0
JB
929 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}, dump their stack: %j`,
930 chargingStation.getConnectorStatus(connectorId).chargingProfiles
e7aeea18 931 );
a7fc8211
JB
932 return true;
933 } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) {
e7aeea18 934 logger.warn(
08f130a0 935 `${chargingStation.logPrefix()} Not allowed to set ${
e7aeea18
JB
936 cp.chargingProfilePurpose
937 } charging profile(s) at remote start transaction`
938 );
a7fc8211 939 return false;
e060fe58
JB
940 } else if (!cp) {
941 return true;
a7fc8211
JB
942 }
943 }
944
e7aeea18 945 private async handleRequestRemoteStopTransaction(
08f130a0 946 chargingStation: ChargingStation,
e7aeea18
JB
947 commandPayload: RemoteStopTransactionRequest
948 ): Promise<DefaultResponse> {
c0560973 949 const transactionId = commandPayload.transactionId;
08f130a0 950 for (const connectorId of chargingStation.connectors.keys()) {
e7aeea18
JB
951 if (
952 connectorId > 0 &&
08f130a0 953 chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId
e7aeea18 954 ) {
08f130a0 955 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
956 OCPP16StatusNotificationRequest,
957 OCPP16StatusNotificationResponse
08f130a0 958 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
959 connectorId,
960 status: OCPP16ChargePointStatus.FINISHING,
961 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
962 });
08f130a0 963 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.FINISHING;
5e3cb728
JB
964 const stopResponse = await chargingStation.stopTransactionOnConnector(
965 connectorId,
a65319ba 966 OCPP16StopTransactionReason.REMOTE
5e3cb728
JB
967 );
968 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
969 return Constants.OCPP_RESPONSE_ACCEPTED;
ef6fa3fb 970 }
5e3cb728 971 return Constants.OCPP_RESPONSE_REJECTED;
c0560973
JB
972 }
973 }
44b9b577 974 logger.warn(
08f130a0 975 chargingStation.logPrefix() +
e7aeea18
JB
976 ' Trying to remote stop a non existing transaction ' +
977 transactionId.toString()
978 );
c0560973
JB
979 return Constants.OCPP_RESPONSE_REJECTED;
980 }
47e22477 981
e7aeea18 982 private async handleRequestGetDiagnostics(
08f130a0 983 chargingStation: ChargingStation,
e7aeea18
JB
984 commandPayload: GetDiagnosticsRequest
985 ): Promise<GetDiagnosticsResponse> {
68cb8b91 986 if (
1789ba2c 987 OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 988 chargingStation,
370ae4ee
JB
989 OCPP16SupportedFeatureProfiles.FirmwareManagement,
990 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1789ba2c 991 ) === false
68cb8b91 992 ) {
68cb8b91
JB
993 return Constants.OCPP_RESPONSE_EMPTY;
994 }
e7aeea18 995 logger.debug(
08f130a0 996 chargingStation.logPrefix() +
e7aeea18
JB
997 ' ' +
998 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS +
999 ' request received: %j',
1000 commandPayload
1001 );
a3868ec4 1002 const uri = new URL(commandPayload.location);
47e22477
JB
1003 if (uri.protocol.startsWith('ftp:')) {
1004 let ftpClient: Client;
1005 try {
e7aeea18 1006 const logFiles = fs
0d8140bd 1007 .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'))
e7aeea18
JB
1008 .filter((file) => file.endsWith('.log'))
1009 .map((file) => path.join('./', file));
08f130a0 1010 const diagnosticsArchive = chargingStation.stationInfo.chargingStationId + '_logs.tar.gz';
47e22477
JB
1011 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
1012 ftpClient = new Client();
1013 const accessResponse = await ftpClient.access({
1014 host: uri.host,
e8191622
JB
1015 ...(!Utils.isEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
1016 ...(!Utils.isEmptyString(uri.username) && { user: uri.username }),
1017 ...(!Utils.isEmptyString(uri.password) && { password: uri.password }),
47e22477
JB
1018 });
1019 let uploadResponse: FTPResponse;
1020 if (accessResponse.code === 220) {
1021 // eslint-disable-next-line @typescript-eslint/no-misused-promises
1022 ftpClient.trackProgress(async (info) => {
e7aeea18 1023 logger.info(
08f130a0 1024 `${chargingStation.logPrefix()} ${
e7aeea18
JB
1025 info.bytes / 1024
1026 } bytes transferred from diagnostics archive ${info.name}`
1027 );
08f130a0 1028 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
1029 DiagnosticsStatusNotificationRequest,
1030 DiagnosticsStatusNotificationResponse
08f130a0 1031 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1032 status: OCPP16DiagnosticsStatus.Uploading,
1033 });
47e22477 1034 });
e7aeea18 1035 uploadResponse = await ftpClient.uploadFrom(
0d8140bd
JB
1036 path.join(
1037 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'),
1038 diagnosticsArchive
1039 ),
e7aeea18
JB
1040 uri.pathname + diagnosticsArchive
1041 );
47e22477 1042 if (uploadResponse.code === 226) {
08f130a0 1043 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
1044 DiagnosticsStatusNotificationRequest,
1045 DiagnosticsStatusNotificationResponse
08f130a0 1046 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1047 status: OCPP16DiagnosticsStatus.Uploaded,
1048 });
47e22477
JB
1049 if (ftpClient) {
1050 ftpClient.close();
1051 }
1052 return { fileName: diagnosticsArchive };
1053 }
e7aeea18
JB
1054 throw new OCPPError(
1055 ErrorType.GENERIC_ERROR,
1056 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1057 uploadResponse?.code && '|' + uploadResponse?.code.toString()
1058 }`,
1059 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1060 );
47e22477 1061 }
e7aeea18
JB
1062 throw new OCPPError(
1063 ErrorType.GENERIC_ERROR,
1064 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1065 uploadResponse?.code && '|' + uploadResponse?.code.toString()
1066 }`,
1067 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1068 );
47e22477 1069 } catch (error) {
08f130a0 1070 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
1071 DiagnosticsStatusNotificationRequest,
1072 DiagnosticsStatusNotificationResponse
08f130a0 1073 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1074 status: OCPP16DiagnosticsStatus.UploadFailed,
1075 });
47e22477
JB
1076 if (ftpClient) {
1077 ftpClient.close();
1078 }
e7aeea18 1079 return this.handleIncomingRequestError(
08f130a0 1080 chargingStation,
e7aeea18
JB
1081 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1082 error as Error,
1083 { errorResponse: Constants.OCPP_RESPONSE_EMPTY }
1084 );
47e22477
JB
1085 }
1086 } else {
e7aeea18 1087 logger.error(
08f130a0 1088 `${chargingStation.logPrefix()} Unsupported protocol ${
e7aeea18
JB
1089 uri.protocol
1090 } to transfer the diagnostic logs archive`
1091 );
08f130a0 1092 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
1093 DiagnosticsStatusNotificationRequest,
1094 DiagnosticsStatusNotificationResponse
08f130a0 1095 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1096 status: OCPP16DiagnosticsStatus.UploadFailed,
1097 });
47e22477
JB
1098 return Constants.OCPP_RESPONSE_EMPTY;
1099 }
1100 }
802cfa13 1101
e7aeea18 1102 private handleRequestTriggerMessage(
08f130a0 1103 chargingStation: ChargingStation,
e7aeea18
JB
1104 commandPayload: OCPP16TriggerMessageRequest
1105 ): OCPP16TriggerMessageResponse {
370ae4ee
JB
1106 if (
1107 !OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 1108 chargingStation,
370ae4ee
JB
1109 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1110 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1111 )
1112 ) {
68cb8b91
JB
1113 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1114 }
dc661702
JB
1115 // TODO: factor out the check on connector id
1116 if (commandPayload?.connectorId < 0) {
1117 logger.warn(
08f130a0 1118 `${chargingStation.logPrefix()} ${
dc661702
JB
1119 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1120 } incoming request received with invalid connectorId ${commandPayload.connectorId}`
1121 );
1122 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
1123 }
802cfa13
JB
1124 try {
1125 switch (commandPayload.requestedMessage) {
1126 case MessageTrigger.BootNotification:
1127 setTimeout(() => {
08f130a0 1128 chargingStation.ocppRequestService
f7f98c68 1129 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
08f130a0 1130 chargingStation,
6a8b180d 1131 OCPP16RequestCommand.BOOT_NOTIFICATION,
8bfbc743 1132 chargingStation.bootNotificationRequest,
6a8b180d 1133 { skipBufferingOnError: true, triggerMessage: true }
e7aeea18 1134 )
8bfbc743
JB
1135 .then((response) => {
1136 chargingStation.bootNotificationResponse = response;
ae711c83 1137 })
e7aeea18
JB
1138 .catch(() => {
1139 /* This is intentional */
1140 });
802cfa13
JB
1141 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1142 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1143 case MessageTrigger.Heartbeat:
1144 setTimeout(() => {
08f130a0 1145 chargingStation.ocppRequestService
f7f98c68 1146 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
08f130a0 1147 chargingStation,
ef6fa3fb
JB
1148 OCPP16RequestCommand.HEARTBEAT,
1149 null,
1150 {
1151 triggerMessage: true,
1152 }
1153 )
e7aeea18
JB
1154 .catch(() => {
1155 /* This is intentional */
1156 });
802cfa13
JB
1157 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1158 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
dc661702
JB
1159 case MessageTrigger.StatusNotification:
1160 setTimeout(() => {
1161 if (commandPayload?.connectorId) {
08f130a0 1162 chargingStation.ocppRequestService
dc661702 1163 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
08f130a0 1164 chargingStation,
dc661702
JB
1165 OCPP16RequestCommand.STATUS_NOTIFICATION,
1166 {
1167 connectorId: commandPayload.connectorId,
1168 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
08f130a0 1169 status: chargingStation.getConnectorStatus(commandPayload.connectorId).status,
dc661702
JB
1170 },
1171 {
1172 triggerMessage: true,
1173 }
1174 )
1175 .catch(() => {
1176 /* This is intentional */
1177 });
1178 } else {
08f130a0
JB
1179 for (const connectorId of chargingStation.connectors.keys()) {
1180 chargingStation.ocppRequestService
dc661702
JB
1181 .requestHandler<
1182 OCPP16StatusNotificationRequest,
1183 OCPP16StatusNotificationResponse
1184 >(
08f130a0 1185 chargingStation,
dc661702
JB
1186 OCPP16RequestCommand.STATUS_NOTIFICATION,
1187 {
1188 connectorId,
1189 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
08f130a0 1190 status: chargingStation.getConnectorStatus(connectorId).status,
dc661702
JB
1191 },
1192 {
1193 triggerMessage: true,
1194 }
1195 )
1196 .catch(() => {
1197 /* This is intentional */
1198 });
1199 }
1200 }
1201 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1202 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
802cfa13
JB
1203 default:
1204 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1205 }
1206 } catch (error) {
e7aeea18 1207 return this.handleIncomingRequestError(
08f130a0 1208 chargingStation,
e7aeea18
JB
1209 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1210 error as Error,
1211 { errorResponse: Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
1212 );
802cfa13
JB
1213 }
1214 }
c0560973 1215}