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