Add AC/DC charging stations handling.
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 30 Oct 2020 19:58:57 +0000 (20:58 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 30 Oct 2020 19:58:57 +0000 (20:58 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/assets/station-templates/abb.station-template.json
src/assets/station-templates/keba.station-template.json
src/assets/station-templates/virtual-simple-atg.station-template.json
src/assets/station-templates/virtual-simple.station-template.json
src/assets/station-templates/virtual.station-template.json
src/charging-station/ChargingStation.js
src/utils/ElectricUtils.js

index 45da428fe5ebb7c83dd5c695785656eb9db865ef..55e5c174d60ab4ac1c89be6bf182ab1fff4471ee 100644 (file)
@@ -7,6 +7,7 @@
   "power": 50000,
   "powerSharedByConnectors": true,
   "powerUnit": "W",
+  "powerOutType": "DC",
   "numberOfConnectors": 2,
   "useConnectorId0": false,
   "randomConnectors": false,
index 5f217ebba98988feabdbc3a2e32e0f8c588f5614..97fd48ea00c9afe59abb7b6581d42961b06d30a5 100644 (file)
@@ -13,7 +13,7 @@
       {
         "key": "MeterValuesSampledData",
         "readonly": false,
-        "value": "Energy.Active.Import.Register"
+        "value": "Energy.Active.Import.Register,Current.Import,Voltage"
       },
       {
         "key": "MeterValueSampleInterval",
     "1": {
       "MeterValues": [
         {
-          "unit": "Wh",
-          "context": "Sample.Periodic"
+          "unit": "V",
+          "measurand": "Voltage"
+        },
+        {
+          "unit": "A",
+          "measurand": "Current.Import"
+        },
+        {
+          "unit": "Wh"
         }
       ]
     },
     "2": {
       "MeterValues": [
         {
-          "unit": "Wh",
-          "context": "Sample.Periodic"
+          "unit": "V",
+          "measurand": "Voltage"
+        },
+        {
+          "unit": "A",
+          "measurand": "Current.Import"
+        },
+        {
+          "unit": "Wh"
         }
       ]
     }
index e5b2d3a5edfdc2a44a6dcbe9894efcd501c04f04..eed0808a1e6cb67dfc04b1e72501a50fb34c6fc8 100644 (file)
@@ -5,6 +5,8 @@
   "chargePointVendor": "Ovomaltin",
   "power": 75000,
   "powerUnit": "W",
+  "powerSharedByConnectors": true,
+  "powerOutType": "DC",
   "numberOfConnectors": 3,
   "randomConnectors": false,
   "Configuration": {
index c10fc3c55085768fc0c6e75071623df7c05af479..4345dc4c49f2d84f459d43273496626f5cf181f8 100644 (file)
@@ -5,6 +5,8 @@
   "chargePointVendor": "Ovomaltin",
   "power": 50000,
   "powerUnit": "W",
+  "powerSharedByConnectors": true,
+  "powerOutType": "DC",
   "numberOfConnectors": 3,
   "randomConnectors": false,
   "Configuration": {
index b0b2cd509405a8fd638e350a9904b7fc5f517eb2..5082c774879411744240b6d858706259a8965bb2 100644 (file)
@@ -5,6 +5,8 @@
   "chargePointVendor": "Ovomaltin",
   "power": 50000,
   "powerUnit": "W",
+  "powerSharedByConnectors": true,
+  "powerOutType": "DC",
   "numberOfConnectors": 9,
   "randomConnectors": false,
   "Configuration": {
index 0e5ae1947ccc113a00d65d1f866fb152dd928c0c..6f9ec98eb0fe80b3e27a7580474895ff11e2ac31 100644 (file)
@@ -216,8 +216,25 @@ export default class ChargingStation {
     return this._connectors[0] ? Object.keys(this._connectors).length - 1 : Object.keys(this._connectors).length;
   }
 
-  _getVoltageLineToNeutral() {
-    return !Utils.isUndefined(this._stationInfo.voltageLineToNeutral) ? this._stationInfo.voltageLineToNeutral : 230;
+  _getVoltageOut() {
+    const errMsg = `${this._logPrefix()} Unknown ${this._getPowerOutType()} powerOutType in template file ${this._stationTemplateFile}, cannot define default voltage out`;
+    let defaultVoltageOut;
+    switch (this._getPowerOutType()) {
+      case 'AC':
+        defaultVoltageOut = 230;
+        break;
+      case 'DC':
+        defaultVoltageOut = 400;
+        break;
+      default:
+        logger.error(errMsg);
+        throw Error(errMsg);
+    }
+    return !Utils.isUndefined(this._stationInfo.voltageOut) ? Utils.convertToInt(this._stationInfo.voltageOut) : defaultVoltageOut;
+  }
+
+  _getPowerOutType() {
+    return !Utils.isUndefined(this._stationInfo.powerOutType) ? this._stationInfo.powerOutType : 'AC';
   }
 
   _getSupervisionURL() {
@@ -636,14 +653,14 @@ export default class ChargingStation {
             ...!Utils.isUndefined(meterValuesTemplate[index].context) && {context: meterValuesTemplate[index].context},
             measurand: meterValuesTemplate[index].measurand,
             ...!Utils.isUndefined(meterValuesTemplate[index].location) && {location: meterValuesTemplate[index].location},
-            ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: this._getVoltageLineToNeutral()},
+            ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: self._getVoltageOut()},
           });
-          for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
+          for (let phase = 1; self._getPowerOutType() === 'AC' && self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
             const voltageValue = sampledValues.sampledValue[sampledValues.sampledValue.length - 1].value;
             let phaseValue;
-            if (voltageValue >= 0 && voltageValue <= 240) {
+            if (voltageValue >= 0 && voltageValue <= 250) {
               phaseValue = `L${phase}-N`;
-            } else if (voltageValue > 240) {
+            } else if (voltageValue > 250) {
               phaseValue = `L${phase}-L${(phase + 1) % self._getNumberOfPhases() !== 0 ? (phase + 1) % self._getNumberOfPhases() : self._getNumberOfPhases()}`;
             }
             sampledValues.sampledValue.push({
@@ -651,7 +668,7 @@ export default class ChargingStation {
               ...!Utils.isUndefined(meterValuesTemplate[index].context) && {context: meterValuesTemplate[index].context},
               measurand: meterValuesTemplate[index].measurand,
               ...!Utils.isUndefined(meterValuesTemplate[index].location) && {location: meterValuesTemplate[index].location},
-              ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: this._getVoltageLineToNeutral()},
+              ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: self._getVoltageOut()},
               phase: phaseValue,
             });
           }
@@ -667,31 +684,43 @@ export default class ChargingStation {
             logger.error(errMsg);
             throw Error(errMsg);
           }
-          const maxAmpPerPhase = ElectricUtils.ampPerPhaseFromPower(self._getNumberOfPhases(), self._stationInfo.maxPower / self._stationInfo.powerDivider,
-              self._getVoltageLineToNeutral());
+          const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'}: Unknown ${self._getPowerOutType()} powerOutType in template file ${self._stationTemplateFile}, cannot calculate ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'} measurand value`;
           const currentMeasurandValues = {};
-          if (Utils.isUndefined(meterValuesTemplate[index].value)) {
-            currentMeasurandValues.L1 = Utils.getRandomFloatRounded(maxAmpPerPhase);
-            currentMeasurandValues.L2 = 0;
-            currentMeasurandValues.L3 = 0;
-            if (self._getNumberOfPhases() === 3) {
-              currentMeasurandValues.L2 = Utils.getRandomFloatRounded(maxAmpPerPhase);
-              currentMeasurandValues.L3 = Utils.getRandomFloatRounded(maxAmpPerPhase);
-            }
-            currentMeasurandValues.avg = Utils.roundTo((currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) / self._getNumberOfPhases(), 2);
+          let maxAmperage;
+          switch (self._getPowerOutType()) {
+            case 'AC':
+              maxAmperage = ElectricUtils.ampPerPhaseFromPower(self._getNumberOfPhases(), self._stationInfo.maxPower / self._stationInfo.powerDivider, self._getVoltageOut());
+              if (Utils.isUndefined(meterValuesTemplate[index].value)) {
+                currentMeasurandValues.L1 = Utils.getRandomFloatRounded(maxAmperage);
+                currentMeasurandValues.L2 = 0;
+                currentMeasurandValues.L3 = 0;
+                if (self._getNumberOfPhases() === 3) {
+                  currentMeasurandValues.L2 = Utils.getRandomFloatRounded(maxAmperage);
+                  currentMeasurandValues.L3 = Utils.getRandomFloatRounded(maxAmperage);
+                }
+                currentMeasurandValues.all = Utils.roundTo((currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) / self._getNumberOfPhases(), 2);
+              }
+              break;
+            case 'DC':
+              maxAmperage = ElectricUtils.ampTotalFromPower(self._stationInfo.maxPower / self._stationInfo.powerDivider, self._getVoltageOut());
+              currentMeasurandValues.all = Utils.getRandomFloatRounded(maxAmperage);
+              break;
+            default:
+              logger.error(errMsg);
+              throw Error(errMsg);
           }
           sampledValues.sampledValue.push({
             ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? {unit: meterValuesTemplate[index].unit} : {unit: 'A'},
             ...!Utils.isUndefined(meterValuesTemplate[index].context) && {context: meterValuesTemplate[index].context},
             measurand: meterValuesTemplate[index].measurand,
             ...!Utils.isUndefined(meterValuesTemplate[index].location) && {location: meterValuesTemplate[index].location},
-            ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: currentMeasurandValues.avg},
+            ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: currentMeasurandValues.all},
           });
           const sampledValuesIndex = sampledValues.sampledValue.length - 1;
-          if (sampledValues.sampledValue[sampledValuesIndex].value > maxAmpPerPhase || debug) {
-            logger.error(`${self._logPrefix()} MeterValues measurand ${sampledValues.sampledValue[sampledValuesIndex].measurand ? sampledValues.sampledValue[sampledValuesIndex].measurand : 'Energy.Active.Import.Register'}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${sampledValues.sampledValue[sampledValuesIndex].value}/${maxAmpPerPhase}`);
+          if (sampledValues.sampledValue[sampledValuesIndex].value > maxAmperage || debug) {
+            logger.error(`${self._logPrefix()} MeterValues measurand ${sampledValues.sampledValue[sampledValuesIndex].measurand ? sampledValues.sampledValue[sampledValuesIndex].measurand : 'Energy.Active.Import.Register'}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${sampledValues.sampledValue[sampledValuesIndex].value}/${maxAmperage}`);
           }
-          for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
+          for (let phase = 1; self._getPowerOutType() === 'AC' && self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
             const phaseValue = `L${phase}`;
             sampledValues.sampledValue.push({
               ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? {unit: meterValuesTemplate[index].unit} : {unit: 'A'},
index 4bbfc9b49a5db05aa301861fb4913ae35012949a..3d76b8dba34c1f6d728c3690240dfba21cdccf04 100644 (file)
@@ -1,4 +1,7 @@
-
+/**
+ * Targeted to AC related values calculation.
+ * To use for DC, always consider cosPhi = 1 and do not use per phase helpers
+ */
 export default class ElectricUtils {
   static ampTotal(nbOfPhases, Iph) {
     return nbOfPhases * Iph;