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