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