refactor: validate station information at CS init
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Wed, 24 Jul 2024 23:14:16 +0000 (01:14 +0200)
committerJérôme Benoit <jerome.benoit@piment-noir.org>
Wed, 24 Jul 2024 23:14:16 +0000 (01:14 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
src/charging-station/AutomaticTransactionGenerator.ts
src/charging-station/ChargingStation.ts
src/charging-station/Helpers.ts
src/charging-station/index.ts
src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts

index 58956ec8b2bc7ee4fff3053a32f25e85eb790499..3cfe3253c783aeb440c75f4615cf47f70b4871b1 100644 (file)
@@ -28,7 +28,7 @@ import {
   sleep,
 } from '../utils/index.js'
 import type { ChargingStation } from './ChargingStation.js'
-import { checkChargingStation } from './Helpers.js'
+import { checkChargingStationState } from './Helpers.js'
 import { IdTagsCache } from './IdTagsCache.js'
 import { isIdTagAuthorized } from './ocpp/index.js'
 
@@ -74,7 +74,7 @@ export class AutomaticTransactionGenerator {
   }
 
   public start (stopAbsoluteDuration?: boolean): void {
-    if (!checkChargingStation(this.chargingStation, this.logPrefix())) {
+    if (!checkChargingStationState(this.chargingStation, this.logPrefix())) {
       return
     }
     if (this.started) {
@@ -107,7 +107,7 @@ export class AutomaticTransactionGenerator {
   }
 
   public startConnector (connectorId: number, stopAbsoluteDuration?: boolean): void {
-    if (!checkChargingStation(this.chargingStation, this.logPrefix(connectorId))) {
+    if (!checkChargingStationState(this.chargingStation, this.logPrefix(connectorId))) {
       return
     }
     if (!this.connectorsStatus.has(connectorId)) {
index 6af98a50b5d6ef59548b36d3b6d86b35c0c30804..1f35092c6b99397a98324bc9539fa99ddfa9ea0a 100644 (file)
@@ -111,7 +111,7 @@ import {
 import {
   buildConnectorsMap,
   buildTemplateName,
-  checkChargingStation,
+  checkChargingStationState,
   checkConfiguration,
   checkConnectorsConfiguration,
   checkStationInfoConnectorStatus,
@@ -136,6 +136,7 @@ import {
   propagateSerialNumber,
   setChargingStationOptions,
   stationTemplateToStationInfo,
+  validateStationInfo,
   warnTemplateKeysDeprecation,
 } from './Helpers.js'
 import { IdTagsCache } from './IdTagsCache.js'
@@ -827,7 +828,7 @@ export class ChargingStation extends EventEmitter {
       ...options,
     }
     params = { ...{ closeOpened: false, terminateOpened: false }, ...params }
-    if (!checkChargingStation(this, this.logPrefix())) {
+    if (!checkChargingStationState(this, this.logPrefix())) {
       return
     }
     if (this.stationInfo?.supervisionUser != null && this.stationInfo.supervisionPassword != null) {
@@ -1304,6 +1305,7 @@ export class ChargingStation extends EventEmitter {
       this.initializeConnectorsOrEvsesFromTemplate(stationTemplate)
     }
     this.stationInfo = this.getStationInfo(options)
+    validateStationInfo(this)
     if (
       this.stationInfo.firmwareStatus === FirmwareStatus.Installing &&
       isNotEmptyString(this.stationInfo.firmwareVersionPattern) &&
index 315a51b9f89069561abdf3709930e60de62ff9bd..656e77be432a2c3d212d597daa63d36a7671e8d7 100644 (file)
@@ -175,7 +175,44 @@ export const getHashId = (index: number, stationTemplate: ChargingStationTemplat
     .digest('hex')
 }
 
-export const checkChargingStation = (
+export const validateStationInfo = (chargingStation: ChargingStation): void => {
+  if (isEmpty(chargingStation.stationInfo)) {
+    throw new BaseError('Missing charging station information')
+  }
+  if (isEmpty(chargingStation.stationInfo?.hashId.trim())) {
+    throw new BaseError('Missing hashId in stationInfo properties')
+  }
+  if (isEmpty(chargingStation.stationInfo?.templateIndex)) {
+    throw new BaseError('Missing templateIndex in stationInfo properties')
+  }
+  if (isEmpty(chargingStation.stationInfo?.templateName.trim())) {
+    throw new BaseError('Missing templateName in stationInfo properties')
+  }
+  if (isEmpty(chargingStation.stationInfo?.chargingStationId?.trim())) {
+    throw new BaseError('Missing chargingStationId in stationInfo properties')
+  }
+  if (isEmpty(chargingStation.stationInfo?.maximumPower)) {
+    throw new BaseError('Missing maximumPower in stationInfo properties')
+  }
+  if (chargingStation.stationInfo?.maximumPower != null && chargingStation.stationInfo.maximumPower <= 0) {
+    throw new RangeError('Invalid maximumPower value in stationInfo properties')
+  }
+  if (isEmpty(chargingStation.stationInfo?.maximumAmperage)) {
+    throw new BaseError('Missing maximumAmperage in stationInfo properties')
+  }
+  if (chargingStation.stationInfo?.maximumAmperage != null && chargingStation.stationInfo.maximumAmperage <= 0) {
+    throw new RangeError('Invalid maximumAmperage value in stationInfo properties')
+  }
+  switch (chargingStation.stationInfo?.ocppVersion) {
+    case OCPPVersion.VERSION_20:
+    case OCPPVersion.VERSION_201:
+      if (chargingStation.evses.size === 0) {
+        throw new BaseError('OCPP 2.0 or superior requires at least one EVSE defined in the charging station template/configuration')
+      }
+  }
+}
+
+export const checkChargingStationState = (
   chargingStation: ChargingStation,
   logPrefix: string
 ): boolean => {
index 9d9d44faa4f42b4c73f51e4c4165b6ecb96519f2..747e42eb735cd8e404b029b882ee366734c894a9 100644 (file)
@@ -7,7 +7,7 @@ export {
 } from './ConfigurationKeyUtils.js'
 export {
   canProceedChargingProfile,
-  checkChargingStation,
+  checkChargingStationState,
   getConnectorChargingProfiles,
   getIdTagsFile,
   hasFeatureProfile,
index d114fe6c00241c429774d959625db4b785cecd01..409fbbaba02d2ad9c1eb835c30654053556a0c3d 100644 (file)
@@ -21,7 +21,7 @@ import { create } from 'tar'
 import {
   canProceedChargingProfile,
   type ChargingStation,
-  checkChargingStation,
+  checkChargingStationState,
   getConfigurationKey,
   getConnectorChargingProfiles,
   prepareChargingProfileKind,
@@ -1353,7 +1353,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     maxDelay = 30,
     minDelay = 15
   ): Promise<void> {
-    if (!checkChargingStation(chargingStation, chargingStation.logPrefix())) {
+    if (!checkChargingStationState(chargingStation, chargingStation.logPrefix())) {
       return
     }
     if (chargingStation.hasEvses) {
@@ -1464,7 +1464,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       }
     } while (transactionsStarted)
     !wasTransactionsStarted && (await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay))))
-    if (!checkChargingStation(chargingStation, chargingStation.logPrefix())) {
+    if (!checkChargingStationState(chargingStation, chargingStation.logPrefix())) {
       return
     }
     await chargingStation.ocppRequestService.requestHandler<