feat: save connectors/evses map in charging station configuration file
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 28 Apr 2023 10:10:54 +0000 (12:10 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 28 Apr 2023 10:10:54 +0000 (12:10 +0200)
Reference: https://github.com/SAP/e-mobility-charging-stations-simulator/issues/505

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/ChargingStation.ts
src/charging-station/ChargingStationUtils.ts
src/charging-station/MessageChannelUtils.ts
src/types/ChargingStationConfiguration.ts
src/types/ChargingStationWorker.ts
src/types/index.ts
ui/web/.vscode/settings.json
ui/web/src/types/ChargingStationType.ts

index 928a8317f94db5f04c65d53be16c844b7a5705c9..0d7f286532425e708632a8ea80f632c54d3a53e5 100644 (file)
@@ -54,6 +54,7 @@ import {
   type ErrorResponse,
   ErrorType,
   type EvseStatus,
+  type EvseStatusConfiguration,
   FileType,
   FirmwareStatus,
   type FirmwareStatusNotificationRequest,
@@ -679,7 +680,7 @@ export class ChargingStation {
 
   public saveOcppConfiguration(): void {
     if (this.getOcppPersistentConfiguration()) {
-      this.saveConfiguration();
+      this.saveConfiguration({ stationInfo: false, connectors: false, evses: false });
     }
   }
 
@@ -1000,7 +1001,7 @@ export class ChargingStation {
 
   private saveStationInfo(): void {
     if (this.getStationInfoPersistentConfiguration()) {
-      this.saveConfiguration();
+      this.saveConfiguration({ ocppConfiguration: false, connectors: false, evses: false });
     }
   }
 
@@ -1375,12 +1376,9 @@ export class ChargingStation {
               this.templateFile
             );
             this.connectors.set(connectorId, Utils.cloneObject<ConnectorStatus>(connectorStatus));
-            this.getConnectorStatus(connectorId).availability = AvailabilityType.Operative;
-            if (Utils.isUndefined(this.getConnectorStatus(connectorId)?.chargingProfiles)) {
-              this.getConnectorStatus(connectorId).chargingProfiles = [];
-            }
-            ChargingStationUtils.initializeConnectorsMapStatus(this.connectors, this.logPrefix());
           }
+          ChargingStationUtils.initializeConnectorsMapStatus(this.connectors, this.logPrefix());
+          this.saveConnectorsStatus();
         } else {
           logger.warn(
             `${this.logPrefix()} Charging station information from template ${
@@ -1431,7 +1429,8 @@ export class ChargingStation {
         const templateMaxEvses = ChargingStationUtils.getMaxNumberOfEvses(stationInfo?.Evses);
         if (templateMaxEvses > 0) {
           for (const evse in stationInfo.Evses) {
-            this.evses.set(Utils.convertToInt(evse), {
+            const evseId = Utils.convertToInt(evse);
+            this.evses.set(evseId, {
               connectors: ChargingStationUtils.buildConnectorsMap(
                 stationInfo?.Evses[evse]?.Connectors,
                 this.logPrefix(),
@@ -1440,10 +1439,11 @@ export class ChargingStation {
               availability: AvailabilityType.Operative,
             });
             ChargingStationUtils.initializeConnectorsMapStatus(
-              this.evses.get(Utils.convertToInt(evse))?.connectors,
+              this.evses.get(evseId)?.connectors,
               this.logPrefix()
             );
           }
+          this.saveEvsesStatus();
         } else {
           logger.warn(
             `${this.logPrefix()} Charging station information from template ${
@@ -1491,17 +1491,62 @@ export class ChargingStation {
     return configuration;
   }
 
-  private saveConfiguration(): void {
+  private saveConnectorsStatus() {
+    if (this.getOcppPersistentConfiguration()) {
+      this.saveConfiguration({ stationInfo: false, ocppConfiguration: false, evses: false });
+    }
+  }
+
+  private saveEvsesStatus() {
+    if (this.getOcppPersistentConfiguration()) {
+      this.saveConfiguration({ stationInfo: false, ocppConfiguration: false, connectors: false });
+    }
+  }
+
+  private saveConfiguration(
+    params: {
+      stationInfo?: boolean;
+      ocppConfiguration?: boolean;
+      connectors?: boolean;
+      evses?: boolean;
+    } = { stationInfo: true, ocppConfiguration: true, connectors: true, evses: true }
+  ): void {
     if (this.configurationFile) {
+      params = {
+        ...params,
+        ...{ stationInfo: true, ocppConfiguration: true, connectors: true, evses: true },
+      };
       try {
         if (!fs.existsSync(path.dirname(this.configurationFile))) {
           fs.mkdirSync(path.dirname(this.configurationFile), { recursive: true });
         }
         const configurationData: ChargingStationConfiguration =
           Utils.cloneObject(this.getConfigurationFromFile()) ?? {};
-        this.ocppConfiguration?.configurationKey &&
-          (configurationData.configurationKey = this.ocppConfiguration.configurationKey);
-        this.stationInfo && (configurationData.stationInfo = this.stationInfo);
+        if (params.stationInfo && this.stationInfo) {
+          configurationData.stationInfo = this.stationInfo;
+        }
+        if (params.ocppConfiguration && this.ocppConfiguration?.configurationKey) {
+          configurationData.configurationKey = this.ocppConfiguration.configurationKey;
+        }
+        if (params.connectors && this.connectors.size > 0) {
+          configurationData.connectorsStatus = [...this.connectors.values()].map(
+            // eslint-disable-next-line @typescript-eslint/no-unused-vars
+            ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest
+          );
+        }
+        if (params.evses && this.evses.size > 0) {
+          configurationData.evsesStatus = [...this.evses.values()].map((evseStatus) => {
+            const status = {
+              ...evseStatus,
+              connectorsStatus: [...evseStatus.connectors.values()].map(
+                // eslint-disable-next-line @typescript-eslint/no-unused-vars
+                ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest
+              ),
+            };
+            delete status.connectors;
+            return status as EvseStatusConfiguration;
+          });
+        }
         delete configurationData.configurationHash;
         const configurationHash = crypto
           .createHash(Constants.DEFAULT_HASH_ALGORITHM)
index c5c42f02944a451c2b8469c0d325361ed6a77376..9f93e5ffe0c8be18c33d26604f3d96af65be7682 100644 (file)
@@ -194,10 +194,6 @@ export class ChargingStationUtils {
           templateFile
         );
         connectorsMap.set(connectorId, Utils.cloneObject<ConnectorStatus>(connectorStatus));
-        connectorsMap.get(connectorId).availability = AvailabilityType.Operative;
-        if (Utils.isUndefined(connectorsMap.get(connectorId)?.chargingProfiles)) {
-          connectorsMap.get(connectorId).chargingProfiles = [];
-        }
       }
     } else {
       logger.warn(
@@ -220,6 +216,14 @@ export class ChargingStationUtils {
         );
       }
       if (
+        connectorId === 0 &&
+        Utils.isNullOrUndefined(connectors.get(connectorId)?.transactionStarted)
+      ) {
+        connectors.get(connectorId).availability = AvailabilityType.Operative;
+        if (Utils.isUndefined(connectors.get(connectorId)?.chargingProfiles)) {
+          connectors.get(connectorId).chargingProfiles = [];
+        }
+      } else if (
         connectorId > 0 &&
         Utils.isNullOrUndefined(connectors.get(connectorId)?.transactionStarted)
       ) {
@@ -228,15 +232,6 @@ export class ChargingStationUtils {
     }
   }
 
-  public static initializeConnectorStatus(connectorStatus: ConnectorStatus): void {
-    connectorStatus.idTagLocalAuthorized = false;
-    connectorStatus.idTagAuthorized = false;
-    connectorStatus.transactionRemoteStarted = false;
-    connectorStatus.transactionStarted = false;
-    connectorStatus.energyActiveImportRegisterValue = 0;
-    connectorStatus.transactionEnergyActiveImportRegisterValue = 0;
-  }
-
   public static resetConnectorStatus(connectorStatus: ConnectorStatus): void {
     connectorStatus.idTagLocalAuthorized = false;
     connectorStatus.idTagAuthorized = false;
@@ -523,6 +518,19 @@ export class ChargingStationUtils {
     );
   }
 
+  private static initializeConnectorStatus(connectorStatus: ConnectorStatus): void {
+    connectorStatus.availability = AvailabilityType.Operative;
+    connectorStatus.idTagLocalAuthorized = false;
+    connectorStatus.idTagAuthorized = false;
+    connectorStatus.transactionRemoteStarted = false;
+    connectorStatus.transactionStarted = false;
+    connectorStatus.energyActiveImportRegisterValue = 0;
+    connectorStatus.transactionEnergyActiveImportRegisterValue = 0;
+    if (Utils.isUndefined(connectorStatus.chargingProfiles)) {
+      connectorStatus.chargingProfiles = [];
+    }
+  }
+
   private static warnDeprecatedTemplateKey(
     template: ChargingStationTemplate,
     key: string,
index 446ecfbbdef0b0ee2371e57b8a0668459e165024..28bfa8c6b89cf9fe1382594cf2bcbf07b85867bd 100644 (file)
@@ -57,6 +57,15 @@ export class MessageChannelUtils {
         // eslint-disable-next-line @typescript-eslint/no-unused-vars
         ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest
       ),
+      evses: [...chargingStation.evses.values()].map((evseStatus) => {
+        return {
+          ...evseStatus,
+          connectors: [...evseStatus.connectors.values()].map(
+            // eslint-disable-next-line @typescript-eslint/no-unused-vars
+            ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest
+          ),
+        };
+      }),
       ocppConfiguration: chargingStation.ocppConfiguration,
       wsState: chargingStation?.wsConnection?.readyState,
       bootNotificationResponse: chargingStation.bootNotificationResponse,
index 095090381a578e894bf87ef219ac5646684acb87..9f0d3423343c30521ffee5d963053760c080ddab 100644 (file)
@@ -2,10 +2,26 @@ import type {
   ChargingStationAutomaticTransactionGeneratorConfiguration,
   ChargingStationInfoConfiguration,
   ChargingStationOcppConfiguration,
+  ConnectorStatus,
+  EvseStatus,
 } from './internal';
 
+type ConnectorsConfiguration = {
+  connectorsStatus?: ConnectorStatus[];
+};
+
+export type EvseStatusConfiguration = Omit<EvseStatus, 'connectors'> & {
+  connectorsStatus?: ConnectorStatus[];
+};
+
+type EvsesConfiguration = {
+  evsesStatus?: EvseStatusConfiguration[];
+};
+
 export type ChargingStationConfiguration = ChargingStationInfoConfiguration &
   ChargingStationOcppConfiguration &
-  ChargingStationAutomaticTransactionGeneratorConfiguration & {
+  ChargingStationAutomaticTransactionGeneratorConfiguration &
+  ConnectorsConfiguration &
+  EvsesConfiguration & {
     configurationHash?: string;
   };
index 7578b217270a8e7f6342ef68c7bc90638d9efcd9..3164414ae59a5b558a8a70693f8dc3f879510a02 100644 (file)
@@ -6,6 +6,7 @@ import type {
   ChargingStationInfo,
   ChargingStationOcppConfiguration,
   ConnectorStatus,
+  EvseStatus,
   JsonObject,
   Statistics,
 } from './internal';
@@ -21,10 +22,15 @@ export interface ChargingStationWorkerData extends WorkerData {
   chargingStationWorkerOptions?: ChargingStationWorkerOptions;
 }
 
+type EvseStatusType = Omit<EvseStatus, 'connectors'> & {
+  connectors?: ConnectorStatus[];
+};
+
 export interface ChargingStationData extends WorkerData {
   started: boolean;
   stationInfo: ChargingStationInfo;
   connectors: ConnectorStatus[];
+  evses: EvseStatusType[];
   ocppConfiguration: ChargingStationOcppConfiguration;
   wsState?:
     | typeof WebSocket.CONNECTING
index 1a349c3a6ddf521287602488b71610d4878e8196..b750cf35c1798445fc2877b65b46d1d9c2a4fd15 100644 (file)
@@ -60,6 +60,7 @@ export {
   type ErrorResponse,
   ErrorType,
   type EvseStatus,
+  type EvseStatusConfiguration,
   type EvseTemplate,
   FileType,
   FirmwareStatus,
index f7432095e5d5046ca43acbedfbae39e8fb8bae79..291c11c1a9a35744b9d9fb680eeb518476073d8c 100644 (file)
@@ -5,6 +5,8 @@
   "cSpell.words": [
     "Avenir",
     "composables",
+    "evse",
+    "evses",
     "finalhandler",
     "iccid",
     "idtag",
index 9d2b530a495610f63101d5847d3a809d252924bc..e9f5f108afffc67ede6edb30133506d0868cb328 100644 (file)
@@ -1,16 +1,17 @@
 import type { JsonObject } from './JsonType';
 
 export type ChargingStationData = {
-  stationInfo: ChargingStationInfo;
   started: boolean;
+  stationInfo: ChargingStationInfo;
+  connectors: ConnectorStatus[];
+  evses: EvseStatus[];
   wsState?:
     | typeof WebSocket.CONNECTING
     | typeof WebSocket.OPEN
     | typeof WebSocket.CLOSING
     | typeof WebSocket.CLOSED;
-  bootNotificationResponse: BootNotificationResponse;
-  connectors: ConnectorStatus[];
-  automaticTransactionGeneratorStatuses?: Status[];
+  bootNotificationResponse?: BootNotificationResponse;
+  automaticTransactionGenerator?: Status[];
 };
 
 export type ChargingStationInfo = {
@@ -184,6 +185,11 @@ export type ConnectorStatus = {
   transactionEnergyActiveImportRegisterValue?: number; // In Wh
 };
 
+export type EvseStatus = {
+  availability: AvailabilityType;
+  connectors?: ConnectorStatus[];
+};
+
 export type AvailabilityType = OCPP16AvailabilityType;
 
 export enum OCPP16AvailabilityType {