fix(simulator): detect string emptyness properly
[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.asyncResource
364 .runInAsyncScope(
365 chargingStation.reset.bind(chargingStation) as (
366 this: ChargingStation,
367 ...args: any[]
368 ) => Promise<void>,
369 chargingStation,
370 `${commandPayload.type}Reset` as OCPP16StopTransactionReason
371 )
372 .catch(() => {
373 /* This is intentional */
374 });
375 logger.info(
376 `${chargingStation.logPrefix()} ${
377 commandPayload.type
378 } reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds(
379 chargingStation.stationInfo.resetTime
380 )}`
381 );
382 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
383 }
384
385 private async handleRequestUnlockConnector(
386 chargingStation: ChargingStation,
387 commandPayload: UnlockConnectorRequest
388 ): Promise<UnlockConnectorResponse> {
389 const connectorId = commandPayload.connectorId;
390 if (chargingStation.connectors.has(connectorId) === false) {
391 logger.error(
392 `${chargingStation.logPrefix()} Trying to unlock a non existing connector Id ${connectorId.toString()}`
393 );
394 return OCPPConstants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
395 }
396 if (connectorId === 0) {
397 logger.error(
398 `${chargingStation.logPrefix()} Trying to unlock connector Id ${connectorId.toString()}`
399 );
400 return OCPPConstants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
401 }
402 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
403 const stopResponse = await chargingStation.stopTransactionOnConnector(
404 connectorId,
405 OCPP16StopTransactionReason.UNLOCK_COMMAND
406 );
407 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
408 return OCPPConstants.OCPP_RESPONSE_UNLOCKED;
409 }
410 return OCPPConstants.OCPP_RESPONSE_UNLOCK_FAILED;
411 }
412 await chargingStation.ocppRequestService.requestHandler<
413 OCPP16StatusNotificationRequest,
414 OCPP16StatusNotificationResponse
415 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
416 connectorId,
417 status: OCPP16ChargePointStatus.AVAILABLE,
418 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
419 });
420 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
421 return OCPPConstants.OCPP_RESPONSE_UNLOCKED;
422 }
423
424 private handleRequestGetConfiguration(
425 chargingStation: ChargingStation,
426 commandPayload: GetConfigurationRequest
427 ): GetConfigurationResponse {
428 const configurationKey: OCPPConfigurationKey[] = [];
429 const unknownKey: string[] = [];
430 if (Utils.isEmptyArray(commandPayload.key) === true) {
431 for (const configuration of chargingStation.ocppConfiguration.configurationKey) {
432 if (Utils.isUndefined(configuration.visible) === true) {
433 configuration.visible = true;
434 }
435 if (configuration.visible === false) {
436 continue;
437 }
438 configurationKey.push({
439 key: configuration.key,
440 readonly: configuration.readonly,
441 value: configuration.value,
442 });
443 }
444 } else {
445 for (const key of commandPayload.key) {
446 const keyFound = ChargingStationConfigurationUtils.getConfigurationKey(
447 chargingStation,
448 key
449 );
450 if (keyFound) {
451 if (Utils.isUndefined(keyFound.visible) === true) {
452 keyFound.visible = true;
453 }
454 if (keyFound.visible === false) {
455 continue;
456 }
457 configurationKey.push({
458 key: keyFound.key,
459 readonly: keyFound.readonly,
460 value: keyFound.value,
461 });
462 } else {
463 unknownKey.push(key);
464 }
465 }
466 }
467 return {
468 configurationKey,
469 unknownKey,
470 };
471 }
472
473 private handleRequestChangeConfiguration(
474 chargingStation: ChargingStation,
475 commandPayload: ChangeConfigurationRequest
476 ): ChangeConfigurationResponse {
477 const keyToChange = ChargingStationConfigurationUtils.getConfigurationKey(
478 chargingStation,
479 commandPayload.key,
480 true
481 );
482 if (!keyToChange) {
483 return OCPPConstants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED;
484 } else if (keyToChange && keyToChange.readonly) {
485 return OCPPConstants.OCPP_CONFIGURATION_RESPONSE_REJECTED;
486 } else if (keyToChange && !keyToChange.readonly) {
487 let valueChanged = false;
488 if (keyToChange.value !== commandPayload.value) {
489 ChargingStationConfigurationUtils.setConfigurationKeyValue(
490 chargingStation,
491 commandPayload.key,
492 commandPayload.value,
493 true
494 );
495 valueChanged = true;
496 }
497 let triggerHeartbeatRestart = false;
498 if (keyToChange.key === OCPP16StandardParametersKey.HeartBeatInterval && valueChanged) {
499 ChargingStationConfigurationUtils.setConfigurationKeyValue(
500 chargingStation,
501 OCPP16StandardParametersKey.HeartbeatInterval,
502 commandPayload.value
503 );
504 triggerHeartbeatRestart = true;
505 }
506 if (keyToChange.key === OCPP16StandardParametersKey.HeartbeatInterval && valueChanged) {
507 ChargingStationConfigurationUtils.setConfigurationKeyValue(
508 chargingStation,
509 OCPP16StandardParametersKey.HeartBeatInterval,
510 commandPayload.value
511 );
512 triggerHeartbeatRestart = true;
513 }
514 if (triggerHeartbeatRestart) {
515 chargingStation.restartHeartbeat();
516 }
517 if (keyToChange.key === OCPP16StandardParametersKey.WebSocketPingInterval && valueChanged) {
518 chargingStation.restartWebSocketPing();
519 }
520 if (keyToChange.reboot) {
521 return OCPPConstants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED;
522 }
523 return OCPPConstants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED;
524 }
525 }
526
527 private handleRequestSetChargingProfile(
528 chargingStation: ChargingStation,
529 commandPayload: SetChargingProfileRequest
530 ): SetChargingProfileResponse {
531 if (
532 OCPP16ServiceUtils.checkFeatureProfile(
533 chargingStation,
534 OCPP16SupportedFeatureProfiles.SmartCharging,
535 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE
536 ) === false
537 ) {
538 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED;
539 }
540 if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
541 logger.error(
542 `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${
543 commandPayload.connectorId
544 }`
545 );
546 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
547 }
548 if (
549 commandPayload.csChargingProfiles.chargingProfilePurpose ===
550 ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE &&
551 commandPayload.connectorId !== 0
552 ) {
553 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
554 }
555 if (
556 commandPayload.csChargingProfiles.chargingProfilePurpose ===
557 ChargingProfilePurposeType.TX_PROFILE &&
558 (commandPayload.connectorId === 0 ||
559 chargingStation.getConnectorStatus(commandPayload.connectorId)?.transactionStarted ===
560 false)
561 ) {
562 logger.error(
563 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${
564 commandPayload.connectorId
565 } without a started transaction`
566 );
567 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
568 }
569 OCPP16ServiceUtils.setChargingProfile(
570 chargingStation,
571 commandPayload.connectorId,
572 commandPayload.csChargingProfiles
573 );
574 logger.debug(
575 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${
576 commandPayload.connectorId
577 }: %j`,
578 commandPayload.csChargingProfiles
579 );
580 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED;
581 }
582
583 private handleRequestClearChargingProfile(
584 chargingStation: ChargingStation,
585 commandPayload: ClearChargingProfileRequest
586 ): ClearChargingProfileResponse {
587 if (
588 OCPP16ServiceUtils.checkFeatureProfile(
589 chargingStation,
590 OCPP16SupportedFeatureProfiles.SmartCharging,
591 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE
592 ) === false
593 ) {
594 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
595 }
596 if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
597 logger.error(
598 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${
599 commandPayload.connectorId
600 }`
601 );
602 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
603 }
604 const connectorStatus = chargingStation.getConnectorStatus(commandPayload.connectorId);
605 if (
606 !Utils.isNullOrUndefined(commandPayload.connectorId) &&
607 !Utils.isEmptyArray(connectorStatus?.chargingProfiles)
608 ) {
609 connectorStatus.chargingProfiles = [];
610 logger.debug(
611 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${
612 commandPayload.connectorId
613 }`
614 );
615 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
616 }
617 if (Utils.isNullOrUndefined(commandPayload.connectorId)) {
618 let clearedCP = false;
619 for (const connectorId of chargingStation.connectors.keys()) {
620 if (
621 !Utils.isEmptyArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)
622 ) {
623 chargingStation
624 .getConnectorStatus(connectorId)
625 ?.chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
626 let clearCurrentCP = false;
627 if (chargingProfile.chargingProfileId === commandPayload.id) {
628 clearCurrentCP = true;
629 }
630 if (
631 !commandPayload.chargingProfilePurpose &&
632 chargingProfile.stackLevel === commandPayload.stackLevel
633 ) {
634 clearCurrentCP = true;
635 }
636 if (
637 !chargingProfile.stackLevel &&
638 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
639 ) {
640 clearCurrentCP = true;
641 }
642 if (
643 chargingProfile.stackLevel === commandPayload.stackLevel &&
644 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
645 ) {
646 clearCurrentCP = true;
647 }
648 if (clearCurrentCP) {
649 connectorStatus?.chargingProfiles?.splice(index, 1);
650 logger.debug(
651 `${chargingStation.logPrefix()} Matching charging profile(s) cleared: %j`,
652 chargingProfile
653 );
654 clearedCP = true;
655 }
656 });
657 }
658 }
659 if (clearedCP) {
660 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
661 }
662 }
663 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
664 }
665
666 private async handleRequestChangeAvailability(
667 chargingStation: ChargingStation,
668 commandPayload: ChangeAvailabilityRequest
669 ): Promise<ChangeAvailabilityResponse> {
670 const connectorId: number = commandPayload.connectorId;
671 if (chargingStation.connectors.has(connectorId) === false) {
672 logger.error(
673 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`
674 );
675 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
676 }
677 const chargePointStatus: OCPP16ChargePointStatus =
678 commandPayload.type === OCPP16AvailabilityType.OPERATIVE
679 ? OCPP16ChargePointStatus.AVAILABLE
680 : OCPP16ChargePointStatus.UNAVAILABLE;
681 if (connectorId === 0) {
682 let response: ChangeAvailabilityResponse = OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
683 for (const id of chargingStation.connectors.keys()) {
684 if (chargingStation.getConnectorStatus(id)?.transactionStarted === true) {
685 response = OCPPConstants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
686 }
687 chargingStation.getConnectorStatus(id).availability = commandPayload.type;
688 if (response === OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) {
689 await chargingStation.ocppRequestService.requestHandler<
690 OCPP16StatusNotificationRequest,
691 OCPP16StatusNotificationResponse
692 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
693 connectorId: id,
694 status: chargePointStatus,
695 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
696 });
697 chargingStation.getConnectorStatus(id).status = chargePointStatus;
698 }
699 }
700 return response;
701 } else if (
702 connectorId > 0 &&
703 (chargingStation.isChargingStationAvailable() === true ||
704 (chargingStation.isChargingStationAvailable() === false &&
705 commandPayload.type === OCPP16AvailabilityType.INOPERATIVE))
706 ) {
707 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
708 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
709 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
710 }
711 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
712 await chargingStation.ocppRequestService.requestHandler<
713 OCPP16StatusNotificationRequest,
714 OCPP16StatusNotificationResponse
715 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
716 connectorId,
717 status: chargePointStatus,
718 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
719 });
720 chargingStation.getConnectorStatus(connectorId).status = chargePointStatus;
721 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
722 }
723 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
724 }
725
726 private async handleRequestRemoteStartTransaction(
727 chargingStation: ChargingStation,
728 commandPayload: RemoteStartTransactionRequest
729 ): Promise<GenericResponse> {
730 const transactionConnectorId = commandPayload.connectorId;
731 if (chargingStation.connectors.has(transactionConnectorId) === true) {
732 const remoteStartTransactionLogMsg = `${chargingStation.logPrefix()} Transaction remotely STARTED on ${
733 chargingStation.stationInfo.chargingStationId
734 }#${transactionConnectorId.toString()} for idTag '${commandPayload.idTag}'`;
735 await chargingStation.ocppRequestService.requestHandler<
736 OCPP16StatusNotificationRequest,
737 OCPP16StatusNotificationResponse
738 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
739 connectorId: transactionConnectorId,
740 status: OCPP16ChargePointStatus.PREPARING,
741 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
742 });
743 const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId);
744 connectorStatus.status = OCPP16ChargePointStatus.PREPARING;
745 if (chargingStation.isChargingStationAvailable() === true) {
746 // Check if authorized
747 if (chargingStation.getAuthorizeRemoteTxRequests() === true) {
748 let authorized = false;
749 if (
750 chargingStation.getLocalAuthListEnabled() === true &&
751 chargingStation.hasAuthorizedTags() === true &&
752 Utils.isNotEmptyString(
753 chargingStation.authorizedTagsCache
754 .getAuthorizedTags(
755 ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
756 )
757 ?.find((idTag) => idTag === commandPayload.idTag)
758 )
759 ) {
760 connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
761 connectorStatus.idTagLocalAuthorized = true;
762 authorized = true;
763 } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
764 connectorStatus.authorizeIdTag = commandPayload.idTag;
765 const authorizeResponse: OCPP16AuthorizeResponse =
766 await chargingStation.ocppRequestService.requestHandler<
767 OCPP16AuthorizeRequest,
768 OCPP16AuthorizeResponse
769 >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
770 idTag: commandPayload.idTag,
771 });
772 if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
773 authorized = true;
774 }
775 } else {
776 logger.warn(
777 `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
778 );
779 }
780 if (authorized === true) {
781 // Authorization successful, start transaction
782 if (
783 this.setRemoteStartTransactionChargingProfile(
784 chargingStation,
785 transactionConnectorId,
786 commandPayload.chargingProfile
787 ) === true
788 ) {
789 connectorStatus.transactionRemoteStarted = true;
790 if (
791 (
792 await chargingStation.ocppRequestService.requestHandler<
793 OCPP16StartTransactionRequest,
794 OCPP16StartTransactionResponse
795 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
796 connectorId: transactionConnectorId,
797 idTag: commandPayload.idTag,
798 })
799 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
800 ) {
801 logger.debug(remoteStartTransactionLogMsg);
802 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
803 }
804 return this.notifyRemoteStartTransactionRejected(
805 chargingStation,
806 transactionConnectorId,
807 commandPayload.idTag
808 );
809 }
810 return this.notifyRemoteStartTransactionRejected(
811 chargingStation,
812 transactionConnectorId,
813 commandPayload.idTag
814 );
815 }
816 return this.notifyRemoteStartTransactionRejected(
817 chargingStation,
818 transactionConnectorId,
819 commandPayload.idTag
820 );
821 }
822 // No authorization check required, start transaction
823 if (
824 this.setRemoteStartTransactionChargingProfile(
825 chargingStation,
826 transactionConnectorId,
827 commandPayload.chargingProfile
828 ) === true
829 ) {
830 connectorStatus.transactionRemoteStarted = true;
831 if (
832 (
833 await chargingStation.ocppRequestService.requestHandler<
834 OCPP16StartTransactionRequest,
835 OCPP16StartTransactionResponse
836 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
837 connectorId: transactionConnectorId,
838 idTag: commandPayload.idTag,
839 })
840 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
841 ) {
842 logger.debug(remoteStartTransactionLogMsg);
843 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
844 }
845 return this.notifyRemoteStartTransactionRejected(
846 chargingStation,
847 transactionConnectorId,
848 commandPayload.idTag
849 );
850 }
851 return this.notifyRemoteStartTransactionRejected(
852 chargingStation,
853 transactionConnectorId,
854 commandPayload.idTag
855 );
856 }
857 return this.notifyRemoteStartTransactionRejected(
858 chargingStation,
859 transactionConnectorId,
860 commandPayload.idTag
861 );
862 }
863 return this.notifyRemoteStartTransactionRejected(
864 chargingStation,
865 transactionConnectorId,
866 commandPayload.idTag
867 );
868 }
869
870 private async notifyRemoteStartTransactionRejected(
871 chargingStation: ChargingStation,
872 connectorId: number,
873 idTag: string
874 ): Promise<GenericResponse> {
875 if (
876 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.AVAILABLE
877 ) {
878 await chargingStation.ocppRequestService.requestHandler<
879 OCPP16StatusNotificationRequest,
880 OCPP16StatusNotificationResponse
881 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
882 connectorId,
883 status: OCPP16ChargePointStatus.AVAILABLE,
884 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
885 });
886 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
887 }
888 logger.warn(
889 `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector Id ${connectorId.toString()}, idTag '${idTag}', availability '${
890 chargingStation.getConnectorStatus(connectorId)?.availability
891 }', status '${chargingStation.getConnectorStatus(connectorId)?.status}'`
892 );
893 return OCPPConstants.OCPP_RESPONSE_REJECTED;
894 }
895
896 private setRemoteStartTransactionChargingProfile(
897 chargingStation: ChargingStation,
898 connectorId: number,
899 cp: OCPP16ChargingProfile
900 ): boolean {
901 if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) {
902 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, cp);
903 logger.debug(
904 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`,
905 cp
906 );
907 return true;
908 } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) {
909 logger.warn(
910 `${chargingStation.logPrefix()} Not allowed to set ${
911 cp.chargingProfilePurpose
912 } charging profile(s) at remote start transaction`
913 );
914 return false;
915 } else if (!cp) {
916 return true;
917 }
918 }
919
920 private async handleRequestRemoteStopTransaction(
921 chargingStation: ChargingStation,
922 commandPayload: RemoteStopTransactionRequest
923 ): Promise<GenericResponse> {
924 const transactionId = commandPayload.transactionId;
925 for (const connectorId of chargingStation.connectors.keys()) {
926 if (
927 connectorId > 0 &&
928 chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId
929 ) {
930 await chargingStation.ocppRequestService.requestHandler<
931 OCPP16StatusNotificationRequest,
932 OCPP16StatusNotificationResponse
933 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
934 connectorId,
935 status: OCPP16ChargePointStatus.FINISHING,
936 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
937 });
938 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.FINISHING;
939 const stopResponse = await chargingStation.stopTransactionOnConnector(
940 connectorId,
941 OCPP16StopTransactionReason.REMOTE
942 );
943 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
944 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
945 }
946 return OCPPConstants.OCPP_RESPONSE_REJECTED;
947 }
948 }
949 logger.warn(
950 `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction ${transactionId.toString()}`
951 );
952 return OCPPConstants.OCPP_RESPONSE_REJECTED;
953 }
954
955 private handleRequestUpdateFirmware(
956 chargingStation: ChargingStation,
957 commandPayload: OCPP16UpdateFirmwareRequest
958 ): OCPP16UpdateFirmwareResponse {
959 if (
960 OCPP16ServiceUtils.checkFeatureProfile(
961 chargingStation,
962 OCPP16SupportedFeatureProfiles.FirmwareManagement,
963 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE
964 ) === false
965 ) {
966 logger.warn(
967 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported`
968 );
969 return OCPPConstants.OCPP_RESPONSE_EMPTY;
970 }
971 if (
972 !Utils.isNullOrUndefined(chargingStation.stationInfo.firmwareStatus) &&
973 chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed
974 ) {
975 logger.warn(
976 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`
977 );
978 return OCPPConstants.OCPP_RESPONSE_EMPTY;
979 }
980 const retrieveDate = Utils.convertToDate(commandPayload.retrieveDate);
981 const now = Date.now();
982 if (retrieveDate?.getTime() <= now) {
983 this.asyncResource
984 .runInAsyncScope(
985 this.updateFirmware.bind(this) as (
986 this: OCPP16IncomingRequestService,
987 ...args: any[]
988 ) => Promise<void>,
989 this,
990 chargingStation
991 )
992 .catch(() => {
993 /* This is intentional */
994 });
995 } else {
996 setTimeout(() => {
997 this.updateFirmware(chargingStation).catch(() => {
998 /* Intentional */
999 });
1000 }, retrieveDate?.getTime() - now);
1001 }
1002 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1003 }
1004
1005 private async updateFirmware(
1006 chargingStation: ChargingStation,
1007 maxDelay = 30,
1008 minDelay = 15
1009 ): Promise<void> {
1010 chargingStation.stopAutomaticTransactionGenerator();
1011 for (const connectorId of chargingStation.connectors.keys()) {
1012 if (
1013 connectorId > 0 &&
1014 chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false
1015 ) {
1016 await chargingStation.ocppRequestService.requestHandler<
1017 OCPP16StatusNotificationRequest,
1018 OCPP16StatusNotificationResponse
1019 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
1020 connectorId,
1021 status: OCPP16ChargePointStatus.UNAVAILABLE,
1022 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1023 });
1024 chargingStation.getConnectorStatus(connectorId).status =
1025 OCPP16ChargePointStatus.UNAVAILABLE;
1026 }
1027 }
1028 if (
1029 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus &&
1030 Utils.isNotEmptyString(chargingStation.stationInfo?.firmwareUpgrade?.failureStatus)
1031 ) {
1032 await chargingStation.ocppRequestService.requestHandler<
1033 OCPP16FirmwareStatusNotificationRequest,
1034 OCPP16FirmwareStatusNotificationResponse
1035 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1036 status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus,
1037 });
1038 return;
1039 }
1040 await chargingStation.ocppRequestService.requestHandler<
1041 OCPP16FirmwareStatusNotificationRequest,
1042 OCPP16FirmwareStatusNotificationResponse
1043 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1044 status: OCPP16FirmwareStatus.Downloading,
1045 });
1046 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloading;
1047 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1048 await chargingStation.ocppRequestService.requestHandler<
1049 OCPP16FirmwareStatusNotificationRequest,
1050 OCPP16FirmwareStatusNotificationResponse
1051 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1052 status: OCPP16FirmwareStatus.Downloaded,
1053 });
1054 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloaded;
1055 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1056 await chargingStation.ocppRequestService.requestHandler<
1057 OCPP16FirmwareStatusNotificationRequest,
1058 OCPP16FirmwareStatusNotificationResponse
1059 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1060 status: OCPP16FirmwareStatus.Installing,
1061 });
1062 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Installing;
1063 if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) {
1064 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1065 await chargingStation.reset(OCPP16StopTransactionReason.REBOOT);
1066 }
1067 }
1068
1069 private async handleRequestGetDiagnostics(
1070 chargingStation: ChargingStation,
1071 commandPayload: GetDiagnosticsRequest
1072 ): Promise<GetDiagnosticsResponse> {
1073 if (
1074 OCPP16ServiceUtils.checkFeatureProfile(
1075 chargingStation,
1076 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1077 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1078 ) === false
1079 ) {
1080 logger.warn(
1081 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`
1082 );
1083 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1084 }
1085 const uri = new URL(commandPayload.location);
1086 if (uri.protocol.startsWith('ftp:')) {
1087 let ftpClient: Client;
1088 try {
1089 const logFiles = fs
1090 .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'))
1091 .filter((file) => file.endsWith('.log'))
1092 .map((file) => path.join('./', file));
1093 const diagnosticsArchive = `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`;
1094 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
1095 ftpClient = new Client();
1096 const accessResponse = await ftpClient.access({
1097 host: uri.host,
1098 ...(Utils.isNotEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
1099 ...(Utils.isNotEmptyString(uri.username) && { user: uri.username }),
1100 ...(Utils.isNotEmptyString(uri.password) && { password: uri.password }),
1101 });
1102 let uploadResponse: FTPResponse;
1103 if (accessResponse.code === 220) {
1104 ftpClient.trackProgress((info) => {
1105 logger.info(
1106 `${chargingStation.logPrefix()} ${
1107 info.bytes / 1024
1108 } bytes transferred from diagnostics archive ${info.name}`
1109 );
1110 chargingStation.ocppRequestService
1111 .requestHandler<
1112 OCPP16DiagnosticsStatusNotificationRequest,
1113 OCPP16DiagnosticsStatusNotificationResponse
1114 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1115 status: OCPP16DiagnosticsStatus.Uploading,
1116 })
1117 .catch((error) => {
1118 logger.error(
1119 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
1120 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
1121 }'`,
1122 error
1123 );
1124 });
1125 });
1126 uploadResponse = await ftpClient.uploadFrom(
1127 path.join(
1128 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'),
1129 diagnosticsArchive
1130 ),
1131 `${uri.pathname}${diagnosticsArchive}`
1132 );
1133 if (uploadResponse.code === 226) {
1134 await chargingStation.ocppRequestService.requestHandler<
1135 OCPP16DiagnosticsStatusNotificationRequest,
1136 OCPP16DiagnosticsStatusNotificationResponse
1137 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1138 status: OCPP16DiagnosticsStatus.Uploaded,
1139 });
1140 if (ftpClient) {
1141 ftpClient.close();
1142 }
1143 return { fileName: diagnosticsArchive };
1144 }
1145 throw new OCPPError(
1146 ErrorType.GENERIC_ERROR,
1147 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1148 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
1149 }`,
1150 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1151 );
1152 }
1153 throw new OCPPError(
1154 ErrorType.GENERIC_ERROR,
1155 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1156 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
1157 }`,
1158 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1159 );
1160 } catch (error) {
1161 await chargingStation.ocppRequestService.requestHandler<
1162 OCPP16DiagnosticsStatusNotificationRequest,
1163 OCPP16DiagnosticsStatusNotificationResponse
1164 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1165 status: OCPP16DiagnosticsStatus.UploadFailed,
1166 });
1167 if (ftpClient) {
1168 ftpClient.close();
1169 }
1170 return this.handleIncomingRequestError(
1171 chargingStation,
1172 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1173 error as Error,
1174 { errorResponse: OCPPConstants.OCPP_RESPONSE_EMPTY }
1175 );
1176 }
1177 } else {
1178 logger.error(
1179 `${chargingStation.logPrefix()} Unsupported protocol ${
1180 uri.protocol
1181 } to transfer the diagnostic logs archive`
1182 );
1183 await chargingStation.ocppRequestService.requestHandler<
1184 OCPP16DiagnosticsStatusNotificationRequest,
1185 OCPP16DiagnosticsStatusNotificationResponse
1186 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1187 status: OCPP16DiagnosticsStatus.UploadFailed,
1188 });
1189 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1190 }
1191 }
1192
1193 private handleRequestTriggerMessage(
1194 chargingStation: ChargingStation,
1195 commandPayload: OCPP16TriggerMessageRequest
1196 ): OCPP16TriggerMessageResponse {
1197 if (
1198 !OCPP16ServiceUtils.checkFeatureProfile(
1199 chargingStation,
1200 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1201 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1202 ) ||
1203 !OCPP16ServiceUtils.isMessageTriggerSupported(
1204 chargingStation,
1205 commandPayload.requestedMessage
1206 )
1207 ) {
1208 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1209 }
1210 if (
1211 !OCPP16ServiceUtils.isConnectorIdValid(
1212 chargingStation,
1213 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1214 commandPayload.connectorId
1215 )
1216 ) {
1217 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
1218 }
1219 try {
1220 switch (commandPayload.requestedMessage) {
1221 case OCPP16MessageTrigger.BootNotification:
1222 setTimeout(() => {
1223 chargingStation.ocppRequestService
1224 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
1225 chargingStation,
1226 OCPP16RequestCommand.BOOT_NOTIFICATION,
1227 chargingStation.bootNotificationRequest,
1228 { skipBufferingOnError: true, triggerMessage: true }
1229 )
1230 .then((response) => {
1231 chargingStation.bootNotificationResponse = response;
1232 })
1233 .catch(() => {
1234 /* This is intentional */
1235 });
1236 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1237 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1238 case OCPP16MessageTrigger.Heartbeat:
1239 setTimeout(() => {
1240 chargingStation.ocppRequestService
1241 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
1242 chargingStation,
1243 OCPP16RequestCommand.HEARTBEAT,
1244 null,
1245 {
1246 triggerMessage: true,
1247 }
1248 )
1249 .catch(() => {
1250 /* This is intentional */
1251 });
1252 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1253 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1254 case OCPP16MessageTrigger.StatusNotification:
1255 setTimeout(() => {
1256 if (!Utils.isNullOrUndefined(commandPayload?.connectorId)) {
1257 chargingStation.ocppRequestService
1258 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
1259 chargingStation,
1260 OCPP16RequestCommand.STATUS_NOTIFICATION,
1261 {
1262 connectorId: commandPayload.connectorId,
1263 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1264 status: chargingStation.getConnectorStatus(commandPayload.connectorId)?.status,
1265 },
1266 {
1267 triggerMessage: true,
1268 }
1269 )
1270 .catch(() => {
1271 /* This is intentional */
1272 });
1273 } else {
1274 for (const connectorId of chargingStation.connectors.keys()) {
1275 chargingStation.ocppRequestService
1276 .requestHandler<
1277 OCPP16StatusNotificationRequest,
1278 OCPP16StatusNotificationResponse
1279 >(
1280 chargingStation,
1281 OCPP16RequestCommand.STATUS_NOTIFICATION,
1282 {
1283 connectorId,
1284 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1285 status: chargingStation.getConnectorStatus(connectorId)?.status,
1286 },
1287 {
1288 triggerMessage: true,
1289 }
1290 )
1291 .catch(() => {
1292 /* This is intentional */
1293 });
1294 }
1295 }
1296 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1297 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1298 default:
1299 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1300 }
1301 } catch (error) {
1302 return this.handleIncomingRequestError(
1303 chargingStation,
1304 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1305 error as Error,
1306 { errorResponse: OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
1307 );
1308 }
1309 }
1310
1311 private handleRequestDataTransfer(
1312 chargingStation: ChargingStation,
1313 commandPayload: OCPP16DataTransferRequest
1314 ): OCPP16DataTransferResponse {
1315 try {
1316 if (Object.values(OCPP16DataTransferVendorId).includes(commandPayload.vendorId)) {
1317 return {
1318 status: OCPP16DataTransferStatus.ACCEPTED,
1319 };
1320 }
1321 return {
1322 status: OCPP16DataTransferStatus.UNKNOWN_VENDOR_ID,
1323 };
1324 } catch (error) {
1325 return this.handleIncomingRequestError(
1326 chargingStation,
1327 OCPP16IncomingRequestCommand.DATA_TRANSFER,
1328 error as Error,
1329 { errorResponse: OCPPConstants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }
1330 );
1331 }
1332 }
1333 }