use of io.openems.api.device.nature.ess.SymmetricEssNature in project openems by OpenEMS.
the class EssClusterNature method loadEss.
private void loadEss() {
Set<DeviceNature> natures = repo.getDeviceNatures();
JsonArray essIds;
try {
essIds = esss.value();
// remove old ess
for (SymmetricEssNature ess : this.essList) {
soc.removeChannel(ess.soc());
gridMode.removeChannel(ess.gridMode());
systemState.removeChannel(ess.systemState());
allowedCharge.removeChannel(ess.allowedCharge());
allowedDischarge.removeChannel(ess.allowedDischarge());
allowedApparent.removeChannel(ess.allowedApparent());
activePower.removeChannel(ess.activePower());
reactivePower.removeChannel(ess.reactivePower());
apparentPower.removeChannel(ess.apparentPower());
maxNominalPower.removeChannel(ess.maxNominalPower());
capacity.removeChannel(ess.capacity());
setWorkState.removeChannel(ess.setWorkState());
power.removeEss(ess);
}
essList.clear();
if (essIds != null && isInitialized) {
for (DeviceNature nature : natures) {
if (nature instanceof SymmetricEssNature) {
if (essIds.toString().contains(nature.id())) {
SymmetricEssNature ess = (SymmetricEssNature) nature;
essList.add(ess);
soc.addChannel(ess.soc());
gridMode.addChannel(ess.gridMode());
systemState.addChannel(ess.systemState());
allowedCharge.addChannel(ess.allowedCharge());
allowedDischarge.addChannel(ess.allowedDischarge());
allowedApparent.addChannel(ess.allowedApparent());
activePower.addChannel(ess.activePower());
reactivePower.addChannel(ess.reactivePower());
apparentPower.addChannel(ess.apparentPower());
maxNominalPower.addChannel(ess.maxNominalPower());
capacity.addChannel(ess.capacity());
setWorkState.addChannel(ess.setWorkState());
power.addEss(ess);
}
}
}
// capacity.channelUpdated(null, null);
}
} catch (InvalidValueException e) {
log.error("esss value is invalid!", e);
}
}
use of io.openems.api.device.nature.ess.SymmetricEssNature in project openems by OpenEMS.
the class SymmetricPowerClusterImpl method writePower.
@Override
protected void writePower() {
super.writePower();
if (dynamicLimitations.size() > 0) {
synchronized (this.ess) {
Point p = reduceToZero();
try {
setGeometry(p);
} catch (PowerException e1) {
}
long activePower = (long) p.getCoordinate().x;
long reactivePower = (long) p.getCoordinate().y;
long socSum = 0;
for (SymmetricEssNature ess : this.ess) {
socSum += ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0);
}
if (activePower > 0) {
/*
* Discharge
*/
// sort ess by useableSoc asc
Collections.sort(ess, (a, b) -> {
return (int) ((a.soc().valueOptional().orElse(0L) - a.minSoc().valueOptional().orElse(0)) - (b.soc().valueOptional().orElse(0L) - b.minSoc().valueOptional().orElse(0)));
});
for (int i = 0; i < ess.size(); i++) {
SymmetricEssNature ess = this.ess.get(i);
// calculate minimal power needed to fulfill the calculatedPower
long minP = activePower;
for (int j = i + 1; j < this.ess.size(); j++) {
if (this.ess.get(j).soc().valueOptional().orElse(0L) - this.ess.get(j).minSoc().valueOptional().orElse(0) > 0) {
minP -= this.ess.get(j).getPower().getMaxP().orElse(0L);
}
}
if (minP < 0) {
minP = 0;
}
// check maximal power to avoid larger charges then calculatedPower
long maxP = ess.getPower().getMaxP().orElse(0L);
if (activePower < maxP) {
maxP = activePower;
}
double diff = maxP - minP;
/*
* weight the range of possible power by the useableSoc
* if the useableSoc is negative the ess will be charged
*/
long power = (long) (Math.ceil(minP + diff / socSum * (ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0))));
PEqualLimitation limit = new PEqualLimitation(ess.getPower());
limit.setP(power);
try {
ess.getPower().applyLimitation(limit);
activePower -= power;
} catch (PowerException e) {
log.error("Failed to set activePower on " + ess.id());
}
}
} else {
/*
* Charge
*/
/*
* sort ess by 100 - useabelSoc
* 100 - 90 = 10
* 100 - 45 = 55
* 100 - (- 5) = 105
* => ess with negative useableSoc will be charged much more then one with positive useableSoc
*/
Collections.sort(this.ess, (a, b) -> {
return (int) ((100 - (a.soc().valueOptional().orElse(0L) - a.minSoc().valueOptional().orElse(0))) - (100 - (b.soc().valueOptional().orElse(0L) - b.minSoc().valueOptional().orElse(0))));
});
for (int i = 0; i < this.ess.size(); i++) {
SymmetricEssNature ess = this.ess.get(i);
// calculate minimal power needed to fulfill the calculatedPower
long minP = activePower;
for (int j = i + 1; j < this.ess.size(); j++) {
minP -= this.ess.get(j).getPower().getMinP().orElse(0L);
}
if (minP > 0) {
minP = 0;
}
// check maximal power to avoid larger charges then calculatedPower
long maxP = ess.getPower().getMinP().orElse(0L);
if (activePower > maxP) {
maxP = activePower;
}
double diff = maxP - minP;
// weight the range of possible power by the useableSoc
long power = (long) Math.floor(minP + diff / (this.ess.size() * 100 - socSum) * (100 - (ess.soc().valueOptional().orElse(0L) - ess.minSoc().valueOptional().orElse(0))));
PEqualLimitation limit = new PEqualLimitation(ess.getPower());
limit.setP(power);
try {
ess.getPower().applyLimitation(limit);
activePower -= power;
} catch (PowerException e) {
log.error("Failed to set activePower on " + ess.id());
}
}
}
// sort ess by maxNominalPower asc
Collections.sort(ess, (a, b) -> {
return (int) (a.maxNominalPower().valueOptional().orElse(0L) - b.maxNominalPower().valueOptional().orElse(0L));
});
if (reactivePower > 0) {
for (int i = 0; i < ess.size(); i++) {
SymmetricEssNature ess = this.ess.get(i);
// calculate minimal power needed to fulfill the calculatedPower
long minQ = reactivePower;
for (int j = i + 1; j < this.ess.size(); j++) {
if (this.ess.get(j).maxNominalPower().valueOptional().orElse(0L) > 0) {
minQ -= this.ess.get(j).getPower().getMaxQ().orElse(0L);
}
}
if (minQ < 0) {
minQ = 0;
}
// check maximal power to avoid larger charges then calculatedPower
long maxQ = ess.getPower().getMaxQ().orElse(0L);
if (reactivePower < maxQ) {
maxQ = reactivePower;
}
double diff = maxQ - minQ;
/*
* weight the range of possible power by the useableSoc
* if the useableSoc is negative the ess will be charged
*/
long power = (long) (Math.ceil(minQ + diff / getMaxApparentPower() * ess.maxNominalPower().valueOptional().orElse(0L)));
QEqualLimitation limit = new QEqualLimitation(ess.getPower());
limit.setQ(power);
try {
ess.getPower().applyLimitation(limit);
reactivePower -= power;
} catch (PowerException e) {
log.error("Failed to set reactivePower on " + ess.id());
}
}
} else {
for (int i = 0; i < this.ess.size(); i++) {
SymmetricEssNature ess = this.ess.get(i);
// calculate minimal power needed to fulfill the calculatedPower
long minQ = reactivePower;
for (int j = i + 1; j < this.ess.size(); j++) {
minQ -= this.ess.get(j).getPower().getMinQ().orElse(0L);
}
if (minQ > 0) {
minQ = 0;
}
// check maximal power to avoid larger charges then calculatedPower
long maxQ = ess.getPower().getMinQ().orElse(0L);
if (reactivePower > maxQ) {
maxQ = reactivePower;
}
double diff = maxQ - minQ;
// weight the range of possible power by the useableSoc
long power = (long) Math.floor(minQ + diff / getMaxApparentPower() * ess.maxNominalPower().valueOptional().orElse(0L));
QEqualLimitation limit = new QEqualLimitation(ess.getPower());
limit.setQ(power);
try {
ess.getPower().applyLimitation(limit);
reactivePower -= power;
} catch (PowerException e) {
log.error("Failed to set reactivePower on " + ess.id());
}
}
}
}
}
}
use of io.openems.api.device.nature.ess.SymmetricEssNature in project openems by OpenEMS.
the class SimulatorGridMeter method update.
@Override
protected void update() {
super.update();
long activePower = 0;
long activePowerL1 = 0;
long activePowerL2 = 0;
long activePowerL3 = 0;
if (activePowerLoad != null) {
factorL1 = SimulatorTools.addRandomDouble(factorL1, 0, 1, 0.01);
factorL2 = SimulatorTools.addRandomDouble(factorL2, 0, 1 - factorL1, 0.01);
factorL3 = 1 - factorL1 - factorL2;
long load = activePowerLoad.getLoad();
activePower = load;
activePowerL1 = (long) (load * factorL1);
activePowerL2 = (long) (load * factorL2);
activePowerL3 = (long) (load * factorL3);
}
long reactivePower = 0;
long reactivePowerL1 = 0;
long reactivePowerL2 = 0;
long reactivePowerL3 = 0;
if (reactivePowerLoad != null) {
reactivePower = reactivePowerLoad.getLoad();
reactivePowerL1 = reactivePower / 3;
reactivePowerL2 = reactivePower / 3;
reactivePowerL3 = reactivePower / 3;
}
for (EssNature entry : essNatures) {
if (entry instanceof SymmetricEssNature) {
SymmetricEssNature ess = (SymmetricEssNature) entry;
activePower -= ess.activePower().valueOptional().orElse(0L);
activePowerL1 -= ess.activePower().valueOptional().orElse(0L) / 3;
activePowerL2 -= ess.activePower().valueOptional().orElse(0L) / 3;
activePowerL3 -= ess.activePower().valueOptional().orElse(0L) / 3;
reactivePower -= ess.reactivePower().valueOptional().orElse(0L);
reactivePowerL1 -= ess.reactivePower().valueOptional().orElse(0L) / 3;
reactivePowerL2 -= ess.reactivePower().valueOptional().orElse(0L) / 3;
reactivePowerL3 -= ess.reactivePower().valueOptional().orElse(0L) / 3;
} else if (entry instanceof AsymmetricEssNature) {
AsymmetricEssNature ess = (AsymmetricEssNature) entry;
activePower -= ess.activePowerL1().valueOptional().orElse(0L) + ess.activePowerL2().valueOptional().orElse(0L) + ess.activePowerL3().valueOptional().orElse(0L);
activePowerL1 -= ess.activePowerL1().valueOptional().orElse(0L);
activePowerL2 -= ess.activePowerL2().valueOptional().orElse(0L);
activePowerL3 -= ess.activePowerL3().valueOptional().orElse(0L);
reactivePower -= ess.reactivePowerL1().valueOptional().orElse(0L) + ess.reactivePowerL2().valueOptional().orElse(0L) + ess.reactivePowerL3().valueOptional().orElse(0L);
reactivePowerL1 -= ess.reactivePowerL1().valueOptional().orElse(0L);
reactivePowerL2 -= ess.reactivePowerL2().valueOptional().orElse(0L);
reactivePowerL3 -= ess.reactivePowerL3().valueOptional().orElse(0L);
}
}
for (MeterNature entry : meterNatures) {
if (entry instanceof SymmetricMeterNature) {
SymmetricMeterNature meter = (SymmetricMeterNature) entry;
activePower -= meter.activePower().valueOptional().orElse(0L);
activePowerL1 -= meter.activePower().valueOptional().orElse(0L) / 3;
activePowerL2 -= meter.activePower().valueOptional().orElse(0L) / 3;
activePowerL3 -= meter.activePower().valueOptional().orElse(0L) / 3;
reactivePower -= meter.reactivePower().valueOptional().orElse(0L);
reactivePowerL1 -= meter.reactivePower().valueOptional().orElse(0L) / 3;
reactivePowerL2 -= meter.reactivePower().valueOptional().orElse(0L) / 3;
reactivePowerL3 -= meter.reactivePower().valueOptional().orElse(0L) / 3;
} else if (entry instanceof AsymmetricMeterNature) {
AsymmetricMeterNature meter = (AsymmetricMeterNature) entry;
activePower -= meter.activePowerL1().valueOptional().orElse(0L) + meter.activePowerL2().valueOptional().orElse(0L) + meter.activePowerL3().valueOptional().orElse(0L);
activePowerL1 -= meter.activePowerL1().valueOptional().orElse(0L);
activePowerL2 -= meter.activePowerL2().valueOptional().orElse(0L);
activePowerL3 -= meter.activePowerL3().valueOptional().orElse(0L);
reactivePower -= meter.reactivePowerL1().valueOptional().orElse(0L) + meter.reactivePowerL2().valueOptional().orElse(0L) + meter.reactivePowerL3().valueOptional().orElse(0L);
reactivePowerL1 -= meter.reactivePowerL1().valueOptional().orElse(0L);
reactivePowerL2 -= meter.reactivePowerL2().valueOptional().orElse(0L);
reactivePowerL3 -= meter.reactivePowerL3().valueOptional().orElse(0L);
}
}
if (isOffGrid(essNatures)) {
this.activePower.updateValue(null);
this.activePowerL1.updateValue(null);
this.activePowerL2.updateValue(null);
this.activePowerL3.updateValue(null);
this.reactivePower.updateValue(null);
this.reactivePowerL1.updateValue(null);
this.reactivePowerL2.updateValue(null);
this.reactivePowerL3.updateValue(null);
} else {
this.activePower.updateValue(activePower);
this.activePowerL1.updateValue(activePowerL1);
this.activePowerL2.updateValue(activePowerL2);
this.activePowerL3.updateValue(activePowerL3);
this.reactivePower.updateValue(reactivePower);
this.reactivePowerL1.updateValue(reactivePowerL1);
this.reactivePowerL2.updateValue(reactivePowerL2);
this.reactivePowerL3.updateValue(reactivePowerL3);
}
}
use of io.openems.api.device.nature.ess.SymmetricEssNature in project openems by OpenEMS.
the class Ess method toString.
@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append(//
ess.id() + " [" + "SOC:" + ess.soc().format() + "|");
if (ess instanceof SymmetricEssNature) {
SymmetricEssNature e = (SymmetricEssNature) ess;
b.append("L:" + e.activePower().format() + ";" + e.reactivePower().format());
}
if (ess instanceof AsymmetricEssNature && ess instanceof SymmetricEssNature) {
b.append("|");
}
if (ess instanceof AsymmetricEssNature) {
AsymmetricEssNature e = (AsymmetricEssNature) ess;
b.append(//
"L1:" + e.activePowerL1().format() + ";" + e.reactivePowerL1().format() + "|" + "L2:" + e.activePowerL2().format() + ";" + e.reactivePowerL2().format() + //
"|" + "L3:" + e.activePowerL3().format() + ";" + e.reactivePowerL3().format());
}
b.append(//
"|" + "Allowed:" + ess.allowedCharge().format() + ";" + ess.allowedDischarge().format());
b.append(//
"|" + "GridMode:" + ess.gridMode().labelOptional().orElse("unknown"));
List<ThingStateChannel> warningChannels = ess.getStateChannel().getWarningChannels().stream().filter(c -> c.isValuePresent() && c.getValue()).collect(Collectors.toList());
List<ThingStateChannel> faultChannels = ess.getStateChannel().getFaultChannels().stream().filter(c -> c.isValuePresent() && c.getValue()).collect(Collectors.toList());
if (warningChannels.size() > 0) {
b.append("|Warn:");
b.append(warningChannels.stream().map(c -> c.name()).collect(Collectors.joining(",")));
}
if (faultChannels.size() > 0) {
b.append("|Fault:");
b.append(faultChannels.stream().map(c -> c.name()).collect(Collectors.joining(",")));
}
b.append("]");
return b.toString();
}
Aggregations