Fix source map support in mocha
[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 ) {
c0560973
JB
578 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
579 }
ed3d2808 580 OCPP16ServiceUtils.setChargingProfile(
7cb5b17f 581 chargingStation,
e7aeea18
JB
582 commandPayload.connectorId,
583 commandPayload.csChargingProfiles
584 );
585 logger.debug(
08f130a0 586 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${
ad8537a7
JB
587 commandPayload.connectorId
588 }, dump their stack: %j`,
08f130a0 589 chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles
e7aeea18 590 );
c0560973
JB
591 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED;
592 }
593
e7aeea18 594 private handleRequestClearChargingProfile(
08f130a0 595 chargingStation: ChargingStation,
e7aeea18
JB
596 commandPayload: ClearChargingProfileRequest
597 ): ClearChargingProfileResponse {
370ae4ee 598 if (
a36bad10 599 OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 600 chargingStation,
370ae4ee
JB
601 OCPP16SupportedFeatureProfiles.SmartCharging,
602 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE
a36bad10 603 ) === false
370ae4ee 604 ) {
68cb8b91
JB
605 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
606 }
1789ba2c 607 if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
e7aeea18 608 logger.error(
08f130a0 609 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${
e7aeea18
JB
610 commandPayload.connectorId
611 }`
612 );
c0560973
JB
613 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
614 }
1789ba2c 615 const connectorStatus = chargingStation.getConnectorStatus(commandPayload.connectorId);
658e2d16
JB
616 if (commandPayload.connectorId && !Utils.isEmptyArray(connectorStatus.chargingProfiles)) {
617 connectorStatus.chargingProfiles = [];
e7aeea18 618 logger.debug(
08f130a0 619 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${
ad8537a7
JB
620 commandPayload.connectorId
621 }, dump their stack: %j`,
658e2d16 622 connectorStatus.chargingProfiles
e7aeea18 623 );
c0560973
JB
624 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
625 }
626 if (!commandPayload.connectorId) {
627 let clearedCP = false;
08f130a0
JB
628 for (const connectorId of chargingStation.connectors.keys()) {
629 if (!Utils.isEmptyArray(chargingStation.getConnectorStatus(connectorId).chargingProfiles)) {
630 chargingStation
e7aeea18
JB
631 .getConnectorStatus(connectorId)
632 .chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
633 let clearCurrentCP = false;
634 if (chargingProfile.chargingProfileId === commandPayload.id) {
635 clearCurrentCP = true;
636 }
637 if (
638 !commandPayload.chargingProfilePurpose &&
639 chargingProfile.stackLevel === commandPayload.stackLevel
640 ) {
641 clearCurrentCP = true;
642 }
643 if (
644 !chargingProfile.stackLevel &&
645 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
646 ) {
647 clearCurrentCP = true;
648 }
649 if (
650 chargingProfile.stackLevel === commandPayload.stackLevel &&
651 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
652 ) {
653 clearCurrentCP = true;
654 }
655 if (clearCurrentCP) {
ccb1d6e9 656 connectorStatus.chargingProfiles.splice(index, 1);
e7aeea18 657 logger.debug(
08f130a0 658 `${chargingStation.logPrefix()} Matching charging profile(s) cleared on connector id ${
ad8537a7
JB
659 commandPayload.connectorId
660 }, dump their stack: %j`,
658e2d16 661 connectorStatus.chargingProfiles
e7aeea18
JB
662 );
663 clearedCP = true;
664 }
665 });
c0560973
JB
666 }
667 }
668 if (clearedCP) {
669 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
670 }
671 }
672 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
673 }
674
e7aeea18 675 private async handleRequestChangeAvailability(
08f130a0 676 chargingStation: ChargingStation,
e7aeea18
JB
677 commandPayload: ChangeAvailabilityRequest
678 ): Promise<ChangeAvailabilityResponse> {
c0560973 679 const connectorId: number = commandPayload.connectorId;
08f130a0 680 if (!chargingStation.getConnectorStatus(connectorId)) {
e7aeea18 681 logger.error(
08f130a0 682 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`
e7aeea18 683 );
c0560973
JB
684 return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
685 }
e7aeea18
JB
686 const chargePointStatus: OCPP16ChargePointStatus =
687 commandPayload.type === OCPP16AvailabilityType.OPERATIVE
688 ? OCPP16ChargePointStatus.AVAILABLE
689 : OCPP16ChargePointStatus.UNAVAILABLE;
c0560973
JB
690 if (connectorId === 0) {
691 let response: ChangeAvailabilityResponse = Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
08f130a0 692 for (const id of chargingStation.connectors.keys()) {
5e3cb728 693 if (chargingStation.getConnectorStatus(id)?.transactionStarted === true) {
c0560973
JB
694 response = Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
695 }
08f130a0 696 chargingStation.getConnectorStatus(id).availability = commandPayload.type;
c0560973 697 if (response === Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) {
08f130a0 698 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
699 OCPP16StatusNotificationRequest,
700 OCPP16StatusNotificationResponse
08f130a0 701 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
702 connectorId: id,
703 status: chargePointStatus,
704 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
705 });
08f130a0 706 chargingStation.getConnectorStatus(id).status = chargePointStatus;
c0560973
JB
707 }
708 }
709 return response;
e7aeea18
JB
710 } else if (
711 connectorId > 0 &&
56eb297e
JB
712 (chargingStation.isChargingStationAvailable() === true ||
713 (chargingStation.isChargingStationAvailable() === false &&
e7aeea18
JB
714 commandPayload.type === OCPP16AvailabilityType.INOPERATIVE))
715 ) {
5e3cb728 716 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
08f130a0 717 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
c0560973
JB
718 return Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
719 }
08f130a0
JB
720 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
721 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
722 OCPP16StatusNotificationRequest,
723 OCPP16StatusNotificationResponse
08f130a0 724 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
725 connectorId,
726 status: chargePointStatus,
727 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
728 });
08f130a0 729 chargingStation.getConnectorStatus(connectorId).status = chargePointStatus;
c0560973
JB
730 return Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
731 }
732 return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
733 }
734
e7aeea18 735 private async handleRequestRemoteStartTransaction(
08f130a0 736 chargingStation: ChargingStation,
e7aeea18
JB
737 commandPayload: RemoteStartTransactionRequest
738 ): Promise<DefaultResponse> {
658e2d16 739 const transactionConnectorId = commandPayload.connectorId;
1789ba2c 740 if (chargingStation.connectors.has(transactionConnectorId) === true) {
91a4f151
JB
741 const remoteStartTransactionLogMsg =
742 chargingStation.logPrefix() +
743 ' Transaction remotely STARTED on ' +
744 chargingStation.stationInfo.chargingStationId +
745 '#' +
746 transactionConnectorId.toString() +
747 " for idTag '" +
748 commandPayload.idTag +
749 "'";
08f130a0 750 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
751 OCPP16StatusNotificationRequest,
752 OCPP16StatusNotificationResponse
08f130a0 753 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
754 connectorId: transactionConnectorId,
755 status: OCPP16ChargePointStatus.PREPARING,
756 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
757 });
1789ba2c 758 const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId);
658e2d16 759 connectorStatus.status = OCPP16ChargePointStatus.PREPARING;
1789ba2c 760 if (chargingStation.isChargingStationAvailable() === true) {
e060fe58 761 // Check if authorized
1789ba2c 762 if (chargingStation.getAuthorizeRemoteTxRequests() === true) {
a7fc8211 763 let authorized = false;
e7aeea18 764 if (
1789ba2c
JB
765 chargingStation.getLocalAuthListEnabled() === true &&
766 chargingStation.hasAuthorizedTags() === true &&
9d7484a4
JB
767 chargingStation.authorizedTagsCache
768 .getAuthorizedTags(
769 ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
770 )
1789ba2c 771 .find((idTag) => idTag === commandPayload.idTag)
e7aeea18 772 ) {
658e2d16
JB
773 connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
774 connectorStatus.idTagLocalAuthorized = true;
36f6a92e 775 authorized = true;
1789ba2c 776 } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
658e2d16 777 connectorStatus.authorizeIdTag = commandPayload.idTag;
2e3d65ae 778 const authorizeResponse: OCPP16AuthorizeResponse =
08f130a0 779 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
780 OCPP16AuthorizeRequest,
781 OCPP16AuthorizeResponse
08f130a0 782 >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
ef6fa3fb
JB
783 idTag: commandPayload.idTag,
784 });
a7fc8211
JB
785 if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
786 authorized = true;
a7fc8211 787 }
71068fb9 788 } else {
e7aeea18 789 logger.warn(
08f130a0 790 `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
e7aeea18 791 );
a7fc8211 792 }
1789ba2c 793 if (authorized === true) {
a7fc8211 794 // Authorization successful, start transaction
e7aeea18
JB
795 if (
796 this.setRemoteStartTransactionChargingProfile(
08f130a0 797 chargingStation,
e7aeea18
JB
798 transactionConnectorId,
799 commandPayload.chargingProfile
1789ba2c 800 ) === true
e7aeea18 801 ) {
658e2d16 802 connectorStatus.transactionRemoteStarted = true;
e7aeea18
JB
803 if (
804 (
08f130a0 805 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
806 OCPP16StartTransactionRequest,
807 OCPP16StartTransactionResponse
08f130a0 808 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
ef6fa3fb
JB
809 connectorId: transactionConnectorId,
810 idTag: commandPayload.idTag,
811 })
e7aeea18
JB
812 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
813 ) {
91a4f151 814 logger.debug(remoteStartTransactionLogMsg);
e060fe58
JB
815 return Constants.OCPP_RESPONSE_ACCEPTED;
816 }
e7aeea18 817 return this.notifyRemoteStartTransactionRejected(
08f130a0 818 chargingStation,
e7aeea18
JB
819 transactionConnectorId,
820 commandPayload.idTag
821 );
e060fe58 822 }
e7aeea18 823 return this.notifyRemoteStartTransactionRejected(
08f130a0 824 chargingStation,
e7aeea18
JB
825 transactionConnectorId,
826 commandPayload.idTag
827 );
a7fc8211 828 }
e7aeea18 829 return this.notifyRemoteStartTransactionRejected(
08f130a0 830 chargingStation,
e7aeea18
JB
831 transactionConnectorId,
832 commandPayload.idTag
833 );
36f6a92e 834 }
a7fc8211 835 // No authorization check required, start transaction
e7aeea18
JB
836 if (
837 this.setRemoteStartTransactionChargingProfile(
08f130a0 838 chargingStation,
e7aeea18
JB
839 transactionConnectorId,
840 commandPayload.chargingProfile
1789ba2c 841 ) === true
e7aeea18 842 ) {
658e2d16 843 connectorStatus.transactionRemoteStarted = true;
e7aeea18
JB
844 if (
845 (
08f130a0 846 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
847 OCPP16StartTransactionRequest,
848 OCPP16StartTransactionResponse
08f130a0 849 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
ef6fa3fb
JB
850 connectorId: transactionConnectorId,
851 idTag: commandPayload.idTag,
852 })
e7aeea18
JB
853 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
854 ) {
91a4f151 855 logger.debug(remoteStartTransactionLogMsg);
e060fe58
JB
856 return Constants.OCPP_RESPONSE_ACCEPTED;
857 }
e7aeea18 858 return this.notifyRemoteStartTransactionRejected(
08f130a0 859 chargingStation,
e7aeea18
JB
860 transactionConnectorId,
861 commandPayload.idTag
862 );
e060fe58 863 }
e7aeea18 864 return this.notifyRemoteStartTransactionRejected(
08f130a0 865 chargingStation,
e7aeea18
JB
866 transactionConnectorId,
867 commandPayload.idTag
868 );
c0560973 869 }
e7aeea18 870 return this.notifyRemoteStartTransactionRejected(
08f130a0 871 chargingStation,
e7aeea18
JB
872 transactionConnectorId,
873 commandPayload.idTag
874 );
c0560973 875 }
08f130a0
JB
876 return this.notifyRemoteStartTransactionRejected(
877 chargingStation,
878 transactionConnectorId,
879 commandPayload.idTag
880 );
a7fc8211
JB
881 }
882
e7aeea18 883 private async notifyRemoteStartTransactionRejected(
08f130a0 884 chargingStation: ChargingStation,
e7aeea18
JB
885 connectorId: number,
886 idTag: string
887 ): Promise<DefaultResponse> {
888 if (
08f130a0 889 chargingStation.getConnectorStatus(connectorId).status !== OCPP16ChargePointStatus.AVAILABLE
e7aeea18 890 ) {
08f130a0 891 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
892 OCPP16StatusNotificationRequest,
893 OCPP16StatusNotificationResponse
08f130a0 894 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
895 connectorId,
896 status: OCPP16ChargePointStatus.AVAILABLE,
897 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
898 });
08f130a0 899 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
e060fe58 900 }
e7aeea18 901 logger.warn(
08f130a0 902 chargingStation.logPrefix() +
e7aeea18
JB
903 ' Remote starting transaction REJECTED on connector Id ' +
904 connectorId.toString() +
91a4f151 905 ", idTag '" +
e7aeea18 906 idTag +
91a4f151 907 "', availability '" +
08f130a0 908 chargingStation.getConnectorStatus(connectorId).availability +
91a4f151
JB
909 "', status '" +
910 chargingStation.getConnectorStatus(connectorId).status +
911 "'"
e7aeea18 912 );
c0560973
JB
913 return Constants.OCPP_RESPONSE_REJECTED;
914 }
915
e7aeea18 916 private setRemoteStartTransactionChargingProfile(
08f130a0 917 chargingStation: ChargingStation,
e7aeea18
JB
918 connectorId: number,
919 cp: OCPP16ChargingProfile
920 ): boolean {
a7fc8211 921 if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) {
ed3d2808 922 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, cp);
e7aeea18 923 logger.debug(
08f130a0
JB
924 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}, dump their stack: %j`,
925 chargingStation.getConnectorStatus(connectorId).chargingProfiles
e7aeea18 926 );
a7fc8211
JB
927 return true;
928 } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) {
e7aeea18 929 logger.warn(
08f130a0 930 `${chargingStation.logPrefix()} Not allowed to set ${
e7aeea18
JB
931 cp.chargingProfilePurpose
932 } charging profile(s) at remote start transaction`
933 );
a7fc8211 934 return false;
e060fe58
JB
935 } else if (!cp) {
936 return true;
a7fc8211
JB
937 }
938 }
939
e7aeea18 940 private async handleRequestRemoteStopTransaction(
08f130a0 941 chargingStation: ChargingStation,
e7aeea18
JB
942 commandPayload: RemoteStopTransactionRequest
943 ): Promise<DefaultResponse> {
c0560973 944 const transactionId = commandPayload.transactionId;
08f130a0 945 for (const connectorId of chargingStation.connectors.keys()) {
e7aeea18
JB
946 if (
947 connectorId > 0 &&
08f130a0 948 chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId
e7aeea18 949 ) {
08f130a0 950 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
951 OCPP16StatusNotificationRequest,
952 OCPP16StatusNotificationResponse
08f130a0 953 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
954 connectorId,
955 status: OCPP16ChargePointStatus.FINISHING,
956 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
957 });
08f130a0 958 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.FINISHING;
5e3cb728
JB
959 const stopResponse = await chargingStation.stopTransactionOnConnector(
960 connectorId,
a65319ba 961 OCPP16StopTransactionReason.REMOTE
5e3cb728
JB
962 );
963 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
964 return Constants.OCPP_RESPONSE_ACCEPTED;
ef6fa3fb 965 }
5e3cb728 966 return Constants.OCPP_RESPONSE_REJECTED;
c0560973
JB
967 }
968 }
44b9b577 969 logger.warn(
08f130a0 970 chargingStation.logPrefix() +
e7aeea18
JB
971 ' Trying to remote stop a non existing transaction ' +
972 transactionId.toString()
973 );
c0560973
JB
974 return Constants.OCPP_RESPONSE_REJECTED;
975 }
47e22477 976
e7aeea18 977 private async handleRequestGetDiagnostics(
08f130a0 978 chargingStation: ChargingStation,
e7aeea18
JB
979 commandPayload: GetDiagnosticsRequest
980 ): Promise<GetDiagnosticsResponse> {
68cb8b91 981 if (
1789ba2c 982 OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 983 chargingStation,
370ae4ee
JB
984 OCPP16SupportedFeatureProfiles.FirmwareManagement,
985 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1789ba2c 986 ) === false
68cb8b91 987 ) {
68cb8b91
JB
988 return Constants.OCPP_RESPONSE_EMPTY;
989 }
e7aeea18 990 logger.debug(
08f130a0 991 chargingStation.logPrefix() +
e7aeea18
JB
992 ' ' +
993 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS +
994 ' request received: %j',
995 commandPayload
996 );
a3868ec4 997 const uri = new URL(commandPayload.location);
47e22477
JB
998 if (uri.protocol.startsWith('ftp:')) {
999 let ftpClient: Client;
1000 try {
e7aeea18 1001 const logFiles = fs
0d8140bd 1002 .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'))
e7aeea18
JB
1003 .filter((file) => file.endsWith('.log'))
1004 .map((file) => path.join('./', file));
08f130a0 1005 const diagnosticsArchive = chargingStation.stationInfo.chargingStationId + '_logs.tar.gz';
47e22477
JB
1006 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
1007 ftpClient = new Client();
1008 const accessResponse = await ftpClient.access({
1009 host: uri.host,
e8191622
JB
1010 ...(!Utils.isEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
1011 ...(!Utils.isEmptyString(uri.username) && { user: uri.username }),
1012 ...(!Utils.isEmptyString(uri.password) && { password: uri.password }),
47e22477
JB
1013 });
1014 let uploadResponse: FTPResponse;
1015 if (accessResponse.code === 220) {
1016 // eslint-disable-next-line @typescript-eslint/no-misused-promises
1017 ftpClient.trackProgress(async (info) => {
e7aeea18 1018 logger.info(
08f130a0 1019 `${chargingStation.logPrefix()} ${
e7aeea18
JB
1020 info.bytes / 1024
1021 } bytes transferred from diagnostics archive ${info.name}`
1022 );
08f130a0 1023 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
1024 DiagnosticsStatusNotificationRequest,
1025 DiagnosticsStatusNotificationResponse
08f130a0 1026 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1027 status: OCPP16DiagnosticsStatus.Uploading,
1028 });
47e22477 1029 });
e7aeea18 1030 uploadResponse = await ftpClient.uploadFrom(
0d8140bd
JB
1031 path.join(
1032 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'),
1033 diagnosticsArchive
1034 ),
e7aeea18
JB
1035 uri.pathname + diagnosticsArchive
1036 );
47e22477 1037 if (uploadResponse.code === 226) {
08f130a0 1038 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
1039 DiagnosticsStatusNotificationRequest,
1040 DiagnosticsStatusNotificationResponse
08f130a0 1041 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1042 status: OCPP16DiagnosticsStatus.Uploaded,
1043 });
47e22477
JB
1044 if (ftpClient) {
1045 ftpClient.close();
1046 }
1047 return { fileName: diagnosticsArchive };
1048 }
e7aeea18
JB
1049 throw new OCPPError(
1050 ErrorType.GENERIC_ERROR,
1051 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1052 uploadResponse?.code && '|' + uploadResponse?.code.toString()
1053 }`,
1054 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1055 );
47e22477 1056 }
e7aeea18
JB
1057 throw new OCPPError(
1058 ErrorType.GENERIC_ERROR,
1059 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1060 uploadResponse?.code && '|' + uploadResponse?.code.toString()
1061 }`,
1062 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1063 );
47e22477 1064 } catch (error) {
08f130a0 1065 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
1066 DiagnosticsStatusNotificationRequest,
1067 DiagnosticsStatusNotificationResponse
08f130a0 1068 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1069 status: OCPP16DiagnosticsStatus.UploadFailed,
1070 });
47e22477
JB
1071 if (ftpClient) {
1072 ftpClient.close();
1073 }
e7aeea18 1074 return this.handleIncomingRequestError(
08f130a0 1075 chargingStation,
e7aeea18
JB
1076 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1077 error as Error,
1078 { errorResponse: Constants.OCPP_RESPONSE_EMPTY }
1079 );
47e22477
JB
1080 }
1081 } else {
e7aeea18 1082 logger.error(
08f130a0 1083 `${chargingStation.logPrefix()} Unsupported protocol ${
e7aeea18
JB
1084 uri.protocol
1085 } to transfer the diagnostic logs archive`
1086 );
08f130a0 1087 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
1088 DiagnosticsStatusNotificationRequest,
1089 DiagnosticsStatusNotificationResponse
08f130a0 1090 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1091 status: OCPP16DiagnosticsStatus.UploadFailed,
1092 });
47e22477
JB
1093 return Constants.OCPP_RESPONSE_EMPTY;
1094 }
1095 }
802cfa13 1096
e7aeea18 1097 private handleRequestTriggerMessage(
08f130a0 1098 chargingStation: ChargingStation,
e7aeea18
JB
1099 commandPayload: OCPP16TriggerMessageRequest
1100 ): OCPP16TriggerMessageResponse {
370ae4ee
JB
1101 if (
1102 !OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 1103 chargingStation,
370ae4ee
JB
1104 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1105 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1106 )
1107 ) {
68cb8b91
JB
1108 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1109 }
dc661702
JB
1110 // TODO: factor out the check on connector id
1111 if (commandPayload?.connectorId < 0) {
1112 logger.warn(
08f130a0 1113 `${chargingStation.logPrefix()} ${
dc661702
JB
1114 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1115 } incoming request received with invalid connectorId ${commandPayload.connectorId}`
1116 );
1117 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
1118 }
802cfa13
JB
1119 try {
1120 switch (commandPayload.requestedMessage) {
1121 case MessageTrigger.BootNotification:
1122 setTimeout(() => {
08f130a0 1123 chargingStation.ocppRequestService
f7f98c68 1124 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
08f130a0 1125 chargingStation,
6a8b180d 1126 OCPP16RequestCommand.BOOT_NOTIFICATION,
8bfbc743 1127 chargingStation.bootNotificationRequest,
6a8b180d 1128 { skipBufferingOnError: true, triggerMessage: true }
e7aeea18 1129 )
8bfbc743
JB
1130 .then((response) => {
1131 chargingStation.bootNotificationResponse = response;
ae711c83 1132 })
e7aeea18
JB
1133 .catch(() => {
1134 /* This is intentional */
1135 });
802cfa13
JB
1136 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1137 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1138 case MessageTrigger.Heartbeat:
1139 setTimeout(() => {
08f130a0 1140 chargingStation.ocppRequestService
f7f98c68 1141 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
08f130a0 1142 chargingStation,
ef6fa3fb
JB
1143 OCPP16RequestCommand.HEARTBEAT,
1144 null,
1145 {
1146 triggerMessage: true,
1147 }
1148 )
e7aeea18
JB
1149 .catch(() => {
1150 /* This is intentional */
1151 });
802cfa13
JB
1152 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1153 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
dc661702
JB
1154 case MessageTrigger.StatusNotification:
1155 setTimeout(() => {
1156 if (commandPayload?.connectorId) {
08f130a0 1157 chargingStation.ocppRequestService
dc661702 1158 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
08f130a0 1159 chargingStation,
dc661702
JB
1160 OCPP16RequestCommand.STATUS_NOTIFICATION,
1161 {
1162 connectorId: commandPayload.connectorId,
1163 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
08f130a0 1164 status: chargingStation.getConnectorStatus(commandPayload.connectorId).status,
dc661702
JB
1165 },
1166 {
1167 triggerMessage: true,
1168 }
1169 )
1170 .catch(() => {
1171 /* This is intentional */
1172 });
1173 } else {
08f130a0
JB
1174 for (const connectorId of chargingStation.connectors.keys()) {
1175 chargingStation.ocppRequestService
dc661702
JB
1176 .requestHandler<
1177 OCPP16StatusNotificationRequest,
1178 OCPP16StatusNotificationResponse
1179 >(
08f130a0 1180 chargingStation,
dc661702
JB
1181 OCPP16RequestCommand.STATUS_NOTIFICATION,
1182 {
1183 connectorId,
1184 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
08f130a0 1185 status: chargingStation.getConnectorStatus(connectorId).status,
dc661702
JB
1186 },
1187 {
1188 triggerMessage: true,
1189 }
1190 )
1191 .catch(() => {
1192 /* This is intentional */
1193 });
1194 }
1195 }
1196 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1197 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
802cfa13
JB
1198 default:
1199 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1200 }
1201 } catch (error) {
e7aeea18 1202 return this.handleIncomingRequestError(
08f130a0 1203 chargingStation,
e7aeea18
JB
1204 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1205 error as Error,
1206 { errorResponse: Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
1207 );
802cfa13
JB
1208 }
1209 }
c0560973 1210}