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