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