feat(simulator): wait when necessary between phases at firmware update
[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.hasAuthorizedTags() === true &&
743 Utils.isNotEmptyString(
744 chargingStation.authorizedTagsCache
745 .getAuthorizedTags(
746 ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
747 )
748 ?.find((idTag) => idTag === commandPayload.idTag)
749 )
750 ) {
751 connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
752 connectorStatus.idTagLocalAuthorized = true;
753 authorized = true;
754 } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
755 connectorStatus.authorizeIdTag = commandPayload.idTag;
756 const authorizeResponse: OCPP16AuthorizeResponse =
757 await chargingStation.ocppRequestService.requestHandler<
758 OCPP16AuthorizeRequest,
759 OCPP16AuthorizeResponse
760 >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
761 idTag: commandPayload.idTag,
762 });
763 if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
764 authorized = true;
765 }
766 } else {
767 logger.warn(
768 `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
769 );
770 }
771 if (authorized === true) {
772 // Authorization successful, start transaction
773 if (
774 this.setRemoteStartTransactionChargingProfile(
775 chargingStation,
776 transactionConnectorId,
777 commandPayload.chargingProfile
778 ) === true
779 ) {
780 connectorStatus.transactionRemoteStarted = true;
781 if (
782 (
783 await chargingStation.ocppRequestService.requestHandler<
784 OCPP16StartTransactionRequest,
785 OCPP16StartTransactionResponse
786 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
787 connectorId: transactionConnectorId,
788 idTag: commandPayload.idTag,
789 })
790 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
791 ) {
792 logger.debug(remoteStartTransactionLogMsg);
793 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
794 }
795 return this.notifyRemoteStartTransactionRejected(
796 chargingStation,
797 transactionConnectorId,
798 commandPayload.idTag
799 );
800 }
801 return this.notifyRemoteStartTransactionRejected(
802 chargingStation,
803 transactionConnectorId,
804 commandPayload.idTag
805 );
806 }
807 return this.notifyRemoteStartTransactionRejected(
808 chargingStation,
809 transactionConnectorId,
810 commandPayload.idTag
811 );
812 }
813 // No authorization check required, start transaction
814 if (
815 this.setRemoteStartTransactionChargingProfile(
816 chargingStation,
817 transactionConnectorId,
818 commandPayload.chargingProfile
819 ) === true
820 ) {
821 connectorStatus.transactionRemoteStarted = true;
822 if (
823 (
824 await chargingStation.ocppRequestService.requestHandler<
825 OCPP16StartTransactionRequest,
826 OCPP16StartTransactionResponse
827 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
828 connectorId: transactionConnectorId,
829 idTag: commandPayload.idTag,
830 })
831 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
832 ) {
833 logger.debug(remoteStartTransactionLogMsg);
834 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
835 }
836 return this.notifyRemoteStartTransactionRejected(
837 chargingStation,
838 transactionConnectorId,
839 commandPayload.idTag
840 );
841 }
842 return this.notifyRemoteStartTransactionRejected(
843 chargingStation,
844 transactionConnectorId,
845 commandPayload.idTag
846 );
847 }
848 return this.notifyRemoteStartTransactionRejected(
849 chargingStation,
850 transactionConnectorId,
851 commandPayload.idTag
852 );
853 }
854 return this.notifyRemoteStartTransactionRejected(
855 chargingStation,
856 transactionConnectorId,
857 commandPayload.idTag
858 );
859 }
860
861 private async notifyRemoteStartTransactionRejected(
862 chargingStation: ChargingStation,
863 connectorId: number,
864 idTag: string
865 ): Promise<GenericResponse> {
866 if (
867 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
868 ) {
869 await chargingStation.ocppRequestService.requestHandler<
870 OCPP16StatusNotificationRequest,
871 OCPP16StatusNotificationResponse
872 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
873 connectorId,
874 status: OCPP16ChargePointStatus.Available,
875 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
876 });
877 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.Available;
878 }
879 logger.warn(
880 `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector Id ${connectorId.toString()}, idTag '${idTag}', availability '${
881 chargingStation.getConnectorStatus(connectorId)?.availability
882 }', status '${chargingStation.getConnectorStatus(connectorId)?.status}'`
883 );
884 return OCPPConstants.OCPP_RESPONSE_REJECTED;
885 }
886
887 private setRemoteStartTransactionChargingProfile(
888 chargingStation: ChargingStation,
889 connectorId: number,
890 cp: OCPP16ChargingProfile
891 ): boolean {
892 if (cp && cp.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE) {
893 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, cp);
894 logger.debug(
895 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`,
896 cp
897 );
898 return true;
899 } else if (cp && cp.chargingProfilePurpose !== OCPP16ChargingProfilePurposeType.TX_PROFILE) {
900 logger.warn(
901 `${chargingStation.logPrefix()} Not allowed to set ${
902 cp.chargingProfilePurpose
903 } charging profile(s) at remote start transaction`
904 );
905 return false;
906 } else if (!cp) {
907 return true;
908 }
909 }
910
911 private async handleRequestRemoteStopTransaction(
912 chargingStation: ChargingStation,
913 commandPayload: RemoteStopTransactionRequest
914 ): Promise<GenericResponse> {
915 const transactionId = commandPayload.transactionId;
916 for (const connectorId of chargingStation.connectors.keys()) {
917 if (
918 connectorId > 0 &&
919 chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId
920 ) {
921 await chargingStation.ocppRequestService.requestHandler<
922 OCPP16StatusNotificationRequest,
923 OCPP16StatusNotificationResponse
924 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
925 connectorId,
926 status: OCPP16ChargePointStatus.Finishing,
927 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
928 });
929 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.Finishing;
930 const stopResponse = await chargingStation.stopTransactionOnConnector(
931 connectorId,
932 OCPP16StopTransactionReason.REMOTE
933 );
934 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
935 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
936 }
937 return OCPPConstants.OCPP_RESPONSE_REJECTED;
938 }
939 }
940 logger.warn(
941 `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction ${transactionId.toString()}`
942 );
943 return OCPPConstants.OCPP_RESPONSE_REJECTED;
944 }
945
946 private handleRequestUpdateFirmware(
947 chargingStation: ChargingStation,
948 commandPayload: OCPP16UpdateFirmwareRequest
949 ): OCPP16UpdateFirmwareResponse {
950 if (
951 OCPP16ServiceUtils.checkFeatureProfile(
952 chargingStation,
953 OCPP16SupportedFeatureProfiles.FirmwareManagement,
954 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE
955 ) === false
956 ) {
957 logger.warn(
958 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported`
959 );
960 return OCPPConstants.OCPP_RESPONSE_EMPTY;
961 }
962 if (
963 !Utils.isNullOrUndefined(chargingStation.stationInfo.firmwareStatus) &&
964 chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed
965 ) {
966 logger.warn(
967 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`
968 );
969 return OCPPConstants.OCPP_RESPONSE_EMPTY;
970 }
971 const retrieveDate = Utils.convertToDate(commandPayload.retrieveDate);
972 const now = Date.now();
973 if (retrieveDate?.getTime() <= now) {
974 this.runInAsyncScope(
975 this.updateFirmwareSimulation.bind(this) as (
976 this: OCPP16IncomingRequestService,
977 ...args: any[]
978 ) => Promise<void>,
979 this,
980 chargingStation
981 ).catch(Constants.EMPTY_FUNCTION);
982 } else {
983 setTimeout(() => {
984 this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION);
985 }, retrieveDate?.getTime() - now);
986 }
987 return OCPPConstants.OCPP_RESPONSE_EMPTY;
988 }
989
990 private async updateFirmwareSimulation(
991 chargingStation: ChargingStation,
992 maxDelay = 30,
993 minDelay = 15
994 ): Promise<void> {
995 if (
996 ChargingStationUtils.checkChargingStation(chargingStation, chargingStation.logPrefix()) ===
997 false
998 ) {
999 return;
1000 }
1001 for (const connectorId of chargingStation.connectors.keys()) {
1002 if (
1003 connectorId > 0 &&
1004 chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false
1005 ) {
1006 await chargingStation.ocppRequestService.requestHandler<
1007 OCPP16StatusNotificationRequest,
1008 OCPP16StatusNotificationResponse
1009 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
1010 connectorId,
1011 status: OCPP16ChargePointStatus.Unavailable,
1012 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1013 });
1014 chargingStation.getConnectorStatus(connectorId).status =
1015 OCPP16ChargePointStatus.Unavailable;
1016 }
1017 }
1018 await chargingStation.ocppRequestService.requestHandler<
1019 OCPP16FirmwareStatusNotificationRequest,
1020 OCPP16FirmwareStatusNotificationResponse
1021 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1022 status: OCPP16FirmwareStatus.Downloading,
1023 });
1024 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloading;
1025 if (
1026 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
1027 OCPP16FirmwareStatus.DownloadFailed
1028 ) {
1029 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1030 await chargingStation.ocppRequestService.requestHandler<
1031 OCPP16FirmwareStatusNotificationRequest,
1032 OCPP16FirmwareStatusNotificationResponse
1033 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1034 status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus,
1035 });
1036 chargingStation.stationInfo.firmwareStatus =
1037 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus;
1038 return;
1039 }
1040 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1041 await chargingStation.ocppRequestService.requestHandler<
1042 OCPP16FirmwareStatusNotificationRequest,
1043 OCPP16FirmwareStatusNotificationResponse
1044 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1045 status: OCPP16FirmwareStatus.Downloaded,
1046 });
1047 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloaded;
1048 let wasTransactionsStarted = false;
1049 let transactionsStarted: boolean;
1050 do {
1051 let trxCount = 0;
1052 for (const connectorId of chargingStation.connectors.keys()) {
1053 if (
1054 connectorId > 0 &&
1055 chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true
1056 ) {
1057 trxCount++;
1058 }
1059 }
1060 if (trxCount > 0) {
1061 const waitTime = 15 * 1000;
1062 logger.debug(
1063 `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: ${trxCount} transaction(s) in progress, waiting ${
1064 waitTime / 1000
1065 } seconds before continuing firmware update simulation`
1066 );
1067 await Utils.sleep(waitTime);
1068 transactionsStarted = true;
1069 wasTransactionsStarted = true;
1070 } else {
1071 for (const connectorId of chargingStation.connectors.keys()) {
1072 if (
1073 connectorId > 0 &&
1074 chargingStation.getConnectorStatus(connectorId)?.status !==
1075 OCPP16ChargePointStatus.Unavailable
1076 ) {
1077 await chargingStation.ocppRequestService.requestHandler<
1078 OCPP16StatusNotificationRequest,
1079 OCPP16StatusNotificationResponse
1080 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
1081 connectorId,
1082 status: OCPP16ChargePointStatus.Unavailable,
1083 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1084 });
1085 chargingStation.getConnectorStatus(connectorId).status =
1086 OCPP16ChargePointStatus.Unavailable;
1087 }
1088 }
1089 transactionsStarted = false;
1090 }
1091 } while (transactionsStarted);
1092 !wasTransactionsStarted &&
1093 (await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000));
1094 if (
1095 ChargingStationUtils.checkChargingStation(chargingStation, chargingStation.logPrefix()) ===
1096 false
1097 ) {
1098 return;
1099 }
1100 await chargingStation.ocppRequestService.requestHandler<
1101 OCPP16FirmwareStatusNotificationRequest,
1102 OCPP16FirmwareStatusNotificationResponse
1103 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1104 status: OCPP16FirmwareStatus.Installing,
1105 });
1106 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Installing;
1107 if (
1108 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
1109 OCPP16FirmwareStatus.InstallationFailed
1110 ) {
1111 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1112 await chargingStation.ocppRequestService.requestHandler<
1113 OCPP16FirmwareStatusNotificationRequest,
1114 OCPP16FirmwareStatusNotificationResponse
1115 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1116 status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus,
1117 });
1118 chargingStation.stationInfo.firmwareStatus =
1119 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus;
1120 return;
1121 }
1122 if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) {
1123 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1124 await chargingStation.reset(OCPP16StopTransactionReason.REBOOT);
1125 }
1126 }
1127
1128 private async handleRequestGetDiagnostics(
1129 chargingStation: ChargingStation,
1130 commandPayload: GetDiagnosticsRequest
1131 ): Promise<GetDiagnosticsResponse> {
1132 if (
1133 OCPP16ServiceUtils.checkFeatureProfile(
1134 chargingStation,
1135 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1136 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1137 ) === false
1138 ) {
1139 logger.warn(
1140 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`
1141 );
1142 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1143 }
1144 const uri = new URL(commandPayload.location);
1145 if (uri.protocol.startsWith('ftp:')) {
1146 let ftpClient: Client;
1147 try {
1148 const logFiles = fs
1149 .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'))
1150 .filter((file) => file.endsWith('.log'))
1151 .map((file) => path.join('./', file));
1152 const diagnosticsArchive = `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`;
1153 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
1154 ftpClient = new Client();
1155 const accessResponse = await ftpClient.access({
1156 host: uri.host,
1157 ...(Utils.isNotEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
1158 ...(Utils.isNotEmptyString(uri.username) && { user: uri.username }),
1159 ...(Utils.isNotEmptyString(uri.password) && { password: uri.password }),
1160 });
1161 let uploadResponse: FTPResponse;
1162 if (accessResponse.code === 220) {
1163 ftpClient.trackProgress((info) => {
1164 logger.info(
1165 `${chargingStation.logPrefix()} ${
1166 info.bytes / 1024
1167 } bytes transferred from diagnostics archive ${info.name}`
1168 );
1169 chargingStation.ocppRequestService
1170 .requestHandler<
1171 OCPP16DiagnosticsStatusNotificationRequest,
1172 OCPP16DiagnosticsStatusNotificationResponse
1173 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1174 status: OCPP16DiagnosticsStatus.Uploading,
1175 })
1176 .catch((error) => {
1177 logger.error(
1178 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
1179 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
1180 }'`,
1181 error
1182 );
1183 });
1184 });
1185 uploadResponse = await ftpClient.uploadFrom(
1186 path.join(
1187 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'),
1188 diagnosticsArchive
1189 ),
1190 `${uri.pathname}${diagnosticsArchive}`
1191 );
1192 if (uploadResponse.code === 226) {
1193 await chargingStation.ocppRequestService.requestHandler<
1194 OCPP16DiagnosticsStatusNotificationRequest,
1195 OCPP16DiagnosticsStatusNotificationResponse
1196 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1197 status: OCPP16DiagnosticsStatus.Uploaded,
1198 });
1199 if (ftpClient) {
1200 ftpClient.close();
1201 }
1202 return { fileName: diagnosticsArchive };
1203 }
1204 throw new OCPPError(
1205 ErrorType.GENERIC_ERROR,
1206 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1207 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
1208 }`,
1209 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1210 );
1211 }
1212 throw new OCPPError(
1213 ErrorType.GENERIC_ERROR,
1214 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1215 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
1216 }`,
1217 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1218 );
1219 } catch (error) {
1220 await chargingStation.ocppRequestService.requestHandler<
1221 OCPP16DiagnosticsStatusNotificationRequest,
1222 OCPP16DiagnosticsStatusNotificationResponse
1223 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1224 status: OCPP16DiagnosticsStatus.UploadFailed,
1225 });
1226 if (ftpClient) {
1227 ftpClient.close();
1228 }
1229 return this.handleIncomingRequestError(
1230 chargingStation,
1231 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1232 error as Error,
1233 { errorResponse: OCPPConstants.OCPP_RESPONSE_EMPTY }
1234 );
1235 }
1236 } else {
1237 logger.error(
1238 `${chargingStation.logPrefix()} Unsupported protocol ${
1239 uri.protocol
1240 } to transfer the diagnostic logs archive`
1241 );
1242 await chargingStation.ocppRequestService.requestHandler<
1243 OCPP16DiagnosticsStatusNotificationRequest,
1244 OCPP16DiagnosticsStatusNotificationResponse
1245 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1246 status: OCPP16DiagnosticsStatus.UploadFailed,
1247 });
1248 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1249 }
1250 }
1251
1252 private handleRequestTriggerMessage(
1253 chargingStation: ChargingStation,
1254 commandPayload: OCPP16TriggerMessageRequest
1255 ): OCPP16TriggerMessageResponse {
1256 if (
1257 !OCPP16ServiceUtils.checkFeatureProfile(
1258 chargingStation,
1259 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1260 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1261 ) ||
1262 !OCPP16ServiceUtils.isMessageTriggerSupported(
1263 chargingStation,
1264 commandPayload.requestedMessage
1265 )
1266 ) {
1267 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1268 }
1269 if (
1270 !OCPP16ServiceUtils.isConnectorIdValid(
1271 chargingStation,
1272 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1273 commandPayload.connectorId
1274 )
1275 ) {
1276 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
1277 }
1278 try {
1279 switch (commandPayload.requestedMessage) {
1280 case OCPP16MessageTrigger.BootNotification:
1281 setTimeout(() => {
1282 chargingStation.ocppRequestService
1283 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
1284 chargingStation,
1285 OCPP16RequestCommand.BOOT_NOTIFICATION,
1286 chargingStation.bootNotificationRequest,
1287 { skipBufferingOnError: true, triggerMessage: true }
1288 )
1289 .then((response) => {
1290 chargingStation.bootNotificationResponse = response;
1291 })
1292 .catch(Constants.EMPTY_FUNCTION);
1293 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1294 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1295 case OCPP16MessageTrigger.Heartbeat:
1296 setTimeout(() => {
1297 chargingStation.ocppRequestService
1298 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
1299 chargingStation,
1300 OCPP16RequestCommand.HEARTBEAT,
1301 null,
1302 {
1303 triggerMessage: true,
1304 }
1305 )
1306 .catch(Constants.EMPTY_FUNCTION);
1307 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1308 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1309 case OCPP16MessageTrigger.StatusNotification:
1310 setTimeout(() => {
1311 if (!Utils.isNullOrUndefined(commandPayload?.connectorId)) {
1312 chargingStation.ocppRequestService
1313 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
1314 chargingStation,
1315 OCPP16RequestCommand.STATUS_NOTIFICATION,
1316 {
1317 connectorId: commandPayload.connectorId,
1318 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1319 status: chargingStation.getConnectorStatus(commandPayload.connectorId)?.status,
1320 },
1321 {
1322 triggerMessage: true,
1323 }
1324 )
1325 .catch(Constants.EMPTY_FUNCTION);
1326 } else {
1327 for (const connectorId of chargingStation.connectors.keys()) {
1328 chargingStation.ocppRequestService
1329 .requestHandler<
1330 OCPP16StatusNotificationRequest,
1331 OCPP16StatusNotificationResponse
1332 >(
1333 chargingStation,
1334 OCPP16RequestCommand.STATUS_NOTIFICATION,
1335 {
1336 connectorId,
1337 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1338 status: chargingStation.getConnectorStatus(connectorId)?.status,
1339 },
1340 {
1341 triggerMessage: true,
1342 }
1343 )
1344 .catch(Constants.EMPTY_FUNCTION);
1345 }
1346 }
1347 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1348 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1349 default:
1350 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1351 }
1352 } catch (error) {
1353 return this.handleIncomingRequestError(
1354 chargingStation,
1355 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1356 error as Error,
1357 { errorResponse: OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
1358 );
1359 }
1360 }
1361
1362 private handleRequestDataTransfer(
1363 chargingStation: ChargingStation,
1364 commandPayload: OCPP16DataTransferRequest
1365 ): OCPP16DataTransferResponse {
1366 try {
1367 if (Object.values(OCPP16DataTransferVendorId).includes(commandPayload.vendorId)) {
1368 return {
1369 status: OCPP16DataTransferStatus.ACCEPTED,
1370 };
1371 }
1372 return {
1373 status: OCPP16DataTransferStatus.UNKNOWN_VENDOR_ID,
1374 };
1375 } catch (error) {
1376 return this.handleIncomingRequestError(
1377 chargingStation,
1378 OCPP16IncomingRequestCommand.DATA_TRANSFER,
1379 error as Error,
1380 { errorResponse: OCPPConstants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }
1381 );
1382 }
1383 }
1384 }