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