From b2acff85739c6e7002d7c469fd012eb165237d89 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Fri, 30 Oct 2020 20:58:57 +0100 Subject: [PATCH] Add AC/DC charging stations handling. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .../abb.station-template.json | 1 + .../keba.station-template.json | 24 ++++-- .../virtual-simple-atg.station-template.json | 2 + .../virtual-simple.station-template.json | 2 + .../virtual.station-template.json | 2 + src/charging-station/ChargingStation.js | 73 +++++++++++++------ src/utils/ElectricUtils.js | 5 +- 7 files changed, 81 insertions(+), 28 deletions(-) diff --git a/src/assets/station-templates/abb.station-template.json b/src/assets/station-templates/abb.station-template.json index 45da428f..55e5c174 100644 --- a/src/assets/station-templates/abb.station-template.json +++ b/src/assets/station-templates/abb.station-template.json @@ -7,6 +7,7 @@ "power": 50000, "powerSharedByConnectors": true, "powerUnit": "W", + "powerOutType": "DC", "numberOfConnectors": 2, "useConnectorId0": false, "randomConnectors": false, diff --git a/src/assets/station-templates/keba.station-template.json b/src/assets/station-templates/keba.station-template.json index 5f217ebb..97fd48ea 100644 --- a/src/assets/station-templates/keba.station-template.json +++ b/src/assets/station-templates/keba.station-template.json @@ -13,7 +13,7 @@ { "key": "MeterValuesSampledData", "readonly": false, - "value": "Energy.Active.Import.Register" + "value": "Energy.Active.Import.Register,Current.Import,Voltage" }, { "key": "MeterValueSampleInterval", @@ -51,16 +51,30 @@ "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" } ] } diff --git a/src/assets/station-templates/virtual-simple-atg.station-template.json b/src/assets/station-templates/virtual-simple-atg.station-template.json index e5b2d3a5..eed0808a 100644 --- a/src/assets/station-templates/virtual-simple-atg.station-template.json +++ b/src/assets/station-templates/virtual-simple-atg.station-template.json @@ -5,6 +5,8 @@ "chargePointVendor": "Ovomaltin", "power": 75000, "powerUnit": "W", + "powerSharedByConnectors": true, + "powerOutType": "DC", "numberOfConnectors": 3, "randomConnectors": false, "Configuration": { diff --git a/src/assets/station-templates/virtual-simple.station-template.json b/src/assets/station-templates/virtual-simple.station-template.json index c10fc3c5..4345dc4c 100644 --- a/src/assets/station-templates/virtual-simple.station-template.json +++ b/src/assets/station-templates/virtual-simple.station-template.json @@ -5,6 +5,8 @@ "chargePointVendor": "Ovomaltin", "power": 50000, "powerUnit": "W", + "powerSharedByConnectors": true, + "powerOutType": "DC", "numberOfConnectors": 3, "randomConnectors": false, "Configuration": { diff --git a/src/assets/station-templates/virtual.station-template.json b/src/assets/station-templates/virtual.station-template.json index b0b2cd50..5082c774 100644 --- a/src/assets/station-templates/virtual.station-template.json +++ b/src/assets/station-templates/virtual.station-template.json @@ -5,6 +5,8 @@ "chargePointVendor": "Ovomaltin", "power": 50000, "powerUnit": "W", + "powerSharedByConnectors": true, + "powerOutType": "DC", "numberOfConnectors": 9, "randomConnectors": false, "Configuration": { diff --git a/src/charging-station/ChargingStation.js b/src/charging-station/ChargingStation.js index 0e5ae194..6f9ec98e 100644 --- a/src/charging-station/ChargingStation.js +++ b/src/charging-station/ChargingStation.js @@ -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'}, diff --git a/src/utils/ElectricUtils.js b/src/utils/ElectricUtils.js index 4bbfc9b4..3d76b8db 100644 --- a/src/utils/ElectricUtils.js +++ b/src/utils/ElectricUtils.js @@ -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; -- 2.34.1