feat: add initial support for evse definition in template
authorJérôme Benoit <jerome.benoit@sap.com>
Tue, 25 Apr 2023 21:50:27 +0000 (23:50 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Tue, 25 Apr 2023 21:50:27 +0000 (23:50 +0200)
Reference: https://github.com/SAP/e-mobility-charging-stations-simulator/issues/349

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
.vscode/settings.json
src/charging-station/ChargingStation.ts
src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts
src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
src/charging-station/ocpp/OCPPServiceUtils.ts
src/types/ChargingStationTemplate.ts
src/types/index.ts
src/types/internal.ts
src/types/ocpp/2.0/Common.ts
src/types/ocpp/Requests.ts

index c3f6a9320129e8391fe9f4f04918b70b02bf81f9..ff191d38b4fd0832541cd2b7c388810c43386028 100644 (file)
@@ -16,6 +16,8 @@
     "cacheable",
     "commitlint",
     "CSMS",
+    "evse",
+    "evses",
     "iccid",
     "imsi",
     "lcov",
index 92f15e4c20f7c71fdd5f1fffe55723eec306532e..bd96d646c773da0cee233562b42fc4b2b0c9cec6 100644 (file)
@@ -53,6 +53,7 @@ import {
   type ErrorCallback,
   type ErrorResponse,
   ErrorType,
+  type EvseStatus,
   FileType,
   FirmwareStatus,
   type FirmwareStatusNotificationRequest,
@@ -108,6 +109,7 @@ export class ChargingStation {
   public ocppConfiguration!: ChargingStationOcppConfiguration | undefined;
   public wsConnection!: WebSocket | null;
   public readonly connectors: Map<number, ConnectorStatus>;
+  public readonly evses: Map<number, EvseStatus>;
   public readonly requests: Map<string, CachedRequest>;
   public performanceStatistics!: PerformanceStatistics | undefined;
   public heartbeatSetInterval!: NodeJS.Timeout;
@@ -119,6 +121,7 @@ export class ChargingStation {
   private configurationFile!: string;
   private configurationFileHash!: string;
   private connectorsConfigurationHash!: string;
+  private evsesConfigurationHash!: string;
   private ocppIncomingRequestService!: OCPPIncomingRequestService;
   private readonly messageBuffer: Set<string>;
   private configuredSupervisionUrl!: URL;
@@ -138,6 +141,7 @@ export class ChargingStation {
     this.index = index;
     this.templateFile = templateFile;
     this.connectors = new Map<number, ConnectorStatus>();
+    this.evses = new Map<number, EvseStatus>();
     this.requests = new Map<string, CachedRequest>();
     this.messageBuffer = new Set<string>();
     this.sharedLRUCache = SharedLRUCache.getInstance();
@@ -471,19 +475,19 @@ export class ChargingStation {
   public startMeterValues(connectorId: number, interval: number): void {
     if (connectorId === 0) {
       logger.error(
-        `${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId.toString()}`
+        `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId.toString()}`
       );
       return;
     }
     if (!this.getConnectorStatus(connectorId)) {
       logger.error(
-        `${this.logPrefix()} Trying to start MeterValues on non existing connector Id ${connectorId.toString()}`
+        `${this.logPrefix()} Trying to start MeterValues on non existing connector id ${connectorId.toString()}`
       );
       return;
     }
     if (this.getConnectorStatus(connectorId)?.transactionStarted === false) {
       logger.error(
-        `${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction started`
+        `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction started`
       );
       return;
     } else if (
@@ -491,7 +495,7 @@ export class ChargingStation {
       Utils.isNullOrUndefined(this.getConnectorStatus(connectorId)?.transactionId)
     ) {
       logger.error(
-        `${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction id`
+        `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction id`
       );
       return;
     }
@@ -946,6 +950,7 @@ export class ChargingStation {
     }
     // Build connectors if needed (FIXME: should be factored out)
     this.initializeConnectors(stationInfo, configuredMaxConnectors, templateMaxConnectors);
+    this.initializeEvses(stationInfo);
     stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo);
     ChargingStationUtils.createStationInfoHash(stationInfo);
     return stationInfo;
@@ -1255,7 +1260,7 @@ export class ChargingStation {
       logger.warn(
         `${this.logPrefix()} Charging station information from template ${
           this.templateFile
-        } with no connector Id 0 configuration`
+        } with no connector id 0 configuration`
       );
     }
     if (stationInfo?.Connectors) {
@@ -1268,7 +1273,7 @@ export class ChargingStation {
       if (this.connectors?.size === 0 || connectorsConfigChanged) {
         connectorsConfigChanged && this.connectors.clear();
         this.connectorsConfigurationHash = connectorsConfigHash;
-        // Add connector Id 0
+        // Add connector id 0
         let lastConnector = '0';
         for (lastConnector in stationInfo?.Connectors) {
           const connectorStatus = stationInfo?.Connectors[lastConnector];
@@ -1330,6 +1335,55 @@ export class ChargingStation {
     }
   }
 
+  private initializeEvses(stationInfo: ChargingStationInfo): void {
+    if (!stationInfo?.Evses && this.evses.size === 0) {
+      const logMsg = `No already defined evses and charging station information from template ${this.templateFile} with no evses configuration defined`;
+      logger.warn(`${this.logPrefix()} ${logMsg}`);
+      return;
+    }
+    if (!stationInfo?.Evses[0]) {
+      logger.warn(
+        `${this.logPrefix()} Charging station information from template ${
+          this.templateFile
+        } with no evse id 0 configuration`
+      );
+    }
+    if (stationInfo?.Evses) {
+      const evsesConfigHash = crypto
+        .createHash(Constants.DEFAULT_HASH_ALGORITHM)
+        .update(`${JSON.stringify(stationInfo?.Evses)}`)
+        .digest('hex');
+      const evsesConfigChanged =
+        this.evses?.size !== 0 && this.evsesConfigurationHash !== evsesConfigHash;
+      if (this.evses?.size === 0 || evsesConfigChanged) {
+        evsesConfigChanged && this.evses.clear();
+        this.evsesConfigurationHash = evsesConfigHash;
+        for (const evse in stationInfo?.Evses) {
+          const evseId = Utils.convertToInt(evse);
+          this.evses.set(evseId, Utils.cloneObject<EvseStatus>(stationInfo?.Evses[evse]));
+          this.evses.get(evseId).availability = AvailabilityType.OPERATIVE;
+        }
+      }
+    } else {
+      if (this.connectors.size === 0) {
+        const logMsg = `No already defined connectors and charging station information from template ${this.templateFile} with no evses configuration defined`;
+        logger.error(`${this.logPrefix()} ${logMsg}`);
+        throw new BaseError(logMsg);
+      }
+      logger.info(
+        `${this.logPrefix()} Charging station information from template ${
+          this.templateFile
+        } with no evses configuration defined, mapping one connector to one evse`
+      );
+      for (const [connectorId, connectorStatus] of this.connectors) {
+        this.evses.set(connectorId, {
+          connectorIds: [connectorId],
+          availability: connectorStatus.availability,
+        });
+      }
+    }
+  }
+
   private checkStationInfoConnectorStatus(
     connectorId: number,
     connectorStatus: ConnectorStatus
index ed66f9dfb6d0ffeeef57ca2d723bca3074844255..7a2a3cb8be57b15c21fd41f86f567298d584438e 100644 (file)
@@ -396,13 +396,13 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     const connectorId = commandPayload.connectorId;
     if (chargingStation.connectors.has(connectorId) === false) {
       logger.error(
-        `${chargingStation.logPrefix()} Trying to unlock a non existing connector Id ${connectorId.toString()}`
+        `${chargingStation.logPrefix()} Trying to unlock a non existing connector id ${connectorId.toString()}`
       );
       return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
     }
     if (connectorId === 0) {
       logger.error(
-        `${chargingStation.logPrefix()} Trying to unlock connector Id ${connectorId.toString()}`
+        `${chargingStation.logPrefix()} Trying to unlock connector id ${connectorId.toString()}`
       );
       return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
     }
@@ -543,7 +543,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     }
     if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
       logger.error(
-        `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${
+        `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector id ${
           commandPayload.connectorId
         }`
       );
@@ -599,7 +599,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     }
     if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
       logger.error(
-        `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector Id ${
+        `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector id ${
           commandPayload.connectorId
         }`
       );
@@ -649,7 +649,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     }
     if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
       logger.error(
-        `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${
+        `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector id ${
           commandPayload.connectorId
         }`
       );
@@ -724,7 +724,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     const connectorId: number = commandPayload.connectorId;
     if (chargingStation.connectors.has(connectorId) === false) {
       logger.error(
-        `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`
+        `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector id ${connectorId.toString()}`
       );
       return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
     }
@@ -926,7 +926,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       );
     }
     logger.warn(
-      `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector Id ${connectorId.toString()}, idTag '${idTag}', availability '${
+      `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id ${connectorId.toString()}, idTag '${idTag}', availability '${
         chargingStation.getConnectorStatus(connectorId)?.availability
       }', status '${chargingStation.getConnectorStatus(connectorId)?.status}'`
     );
index 36eecc577cdbfddd3ba1ea2484e23075f95809cb..f8c563c856d9acda14faeceb395173f9de384fc8 100644 (file)
@@ -410,7 +410,7 @@ export class OCPP16ResponseService extends OCPPResponseService {
       authorizeConnectorIdDefined &&
         (chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true);
       logger.debug(
-        `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' accepted${
+        `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' accepted${
           authorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''
         }`
       );
@@ -420,7 +420,7 @@ export class OCPP16ResponseService extends OCPPResponseService {
         delete chargingStation.getConnectorStatus(authorizeConnectorId)?.authorizeIdTag;
       }
       logger.debug(
-        `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' rejected with status '${
+        `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' rejected with status '${
           payload.idTagInfo.status
         }'${authorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''}`
       );
@@ -443,7 +443,7 @@ export class OCPP16ResponseService extends OCPPResponseService {
     }
     if (Utils.isNullOrUndefined(transactionConnectorId)) {
       logger.error(
-        `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector Id ${connectorId.toString()}`
+        `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector id ${connectorId.toString()}`
       );
       return;
     }
@@ -457,7 +457,7 @@ export class OCPP16ResponseService extends OCPPResponseService {
       logger.error(
         `${chargingStation.logPrefix()} Trying to start a transaction with a not local authorized idTag ${
           chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag
-        } on connector Id ${connectorId.toString()}`
+        } on connector id ${connectorId.toString()}`
       );
       await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
       return;
@@ -472,7 +472,7 @@ export class OCPP16ResponseService extends OCPPResponseService {
       logger.error(
         `${chargingStation.logPrefix()} Trying to start a transaction with a not authorized idTag ${
           chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag
-        } on connector Id ${connectorId.toString()}`
+        } on connector id ${connectorId.toString()}`
       );
       await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
       return;
@@ -486,7 +486,7 @@ export class OCPP16ResponseService extends OCPPResponseService {
           requestPayload.idTag
         } different from the authorize request one ${
           chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag
-        } on connector Id ${connectorId.toString()}`
+        } on connector id ${connectorId.toString()}`
       );
       await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
       return;
@@ -500,7 +500,7 @@ export class OCPP16ResponseService extends OCPPResponseService {
           requestPayload.idTag
         } different from the local authorized one ${
           chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag
-        } on connector Id ${connectorId.toString()}`
+        } on connector id ${connectorId.toString()}`
       );
       await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
       return;
index ea3dfe7ecd0d79efa02049a571a1c0dfcd6f2f59..b16d5f0d4a8970df1259fe9b9ef74850d178c7e8 100644 (file)
@@ -126,7 +126,7 @@ export class OCPPServiceUtils {
   ): boolean {
     if (connectorId < 0) {
       logger.error(
-        `${chargingStation.logPrefix()} ${ocppCommand} incoming request received with invalid connector Id ${connectorId}`
+        `${chargingStation.logPrefix()} ${ocppCommand} incoming request received with invalid connector id ${connectorId}`
       );
       return false;
     }
index 8060a937449cd5cd1a11c983d287f1d83bc5583b..4e04c84eeef1f94767501c8e3663ec322e2436aa 100644 (file)
@@ -6,6 +6,7 @@ import type {
   AutomaticTransactionGeneratorConfiguration,
   ChargingStationOcppConfiguration,
   ConnectorStatus,
+  EvseStatus,
   FirmwareStatus,
   IncomingRequestCommand,
   MessageTrigger,
@@ -112,5 +113,6 @@ export type ChargingStationTemplate = {
   messageTriggerSupport?: Record<MessageTrigger, boolean>;
   Configuration?: ChargingStationOcppConfiguration;
   AutomaticTransactionGenerator?: AutomaticTransactionGeneratorConfiguration;
+  Evses?: Record<string, EvseStatus>;
   Connectors: Record<string, ConnectorStatus>;
 };
index 8b060dd0bfc891b7ebcb06736ec8c6226fca0758..bdbe0422b039a83d753f37f5e656b624da9b10cf 100644 (file)
@@ -59,6 +59,7 @@ export {
   type ErrorCallback,
   type ErrorResponse,
   ErrorType,
+  type EvseStatus,
   FileType,
   FirmwareStatus,
   type FirmwareStatusNotificationRequest,
index 11a47d99a96412c4595e15f80a45d3deb82bb643..576dca54890222f91460d753bfedca304091aacc 100644 (file)
@@ -37,6 +37,7 @@ export * from './ChargingStationOcppConfiguration';
 export * from './ChargingStationTemplate';
 export * from './ChargingStationWorker';
 export * from './ConfigurationData';
+export type { EvseStatus } from './EvseStatus';
 export type { ConnectorStatus } from './ConnectorStatus';
 export type { EmptyObject } from './EmptyObject';
 export type { HandleErrorParams } from './Error';
index 12dfd6a765eebfb651263abd03d59acd97b73e55..c73c19e3660029ec2e4f39e07bf2dcb171ca38c6 100644 (file)
@@ -25,6 +25,11 @@ export enum BootReasonEnumType {
   Watchdog = 'Watchdog',
 }
 
+export enum OperationalStatusEnumType {
+  Operative = 'Operative',
+  Inoperative = 'Inoperative',
+}
+
 export enum OCPP20ConnectorStatusEnumType {
   Available = 'Available',
   Occupied = 'Occupied',
index ff238094f3e5186415d05e6e497ae7ec8fb4996f..72555e64d5006ced8ecef53a05cff62c5b449e9d 100644 (file)
@@ -20,6 +20,7 @@ import {
   OCPP20IncomingRequestCommand,
   OCPP20RequestCommand,
   type OCPP20StatusNotificationRequest,
+  OperationalStatusEnumType,
 } from '../internal';
 
 export const RequestCommand = {
@@ -83,8 +84,9 @@ export type IncomingRequestHandler = (
 
 export const AvailabilityType = {
   ...OCPP16AvailabilityType,
+  ...OperationalStatusEnumType,
 } as const;
-export type AvailabilityType = OCPP16AvailabilityType;
+export type AvailabilityType = OCPP16AvailabilityType | OperationalStatusEnumType;
 
 export const DiagnosticsStatus = {
   ...OCPP16DiagnosticsStatus,