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