Keep track of peak production, peak consumption, peak from grid, and peak to grid values to aid in solar panel and storage sizing.

This commit is contained in:
MarkBryanMilligan 2021-09-07 22:56:22 -05:00
parent d63f6df1fd
commit 0cfdaaa272
12 changed files with 209 additions and 26 deletions

View File

@ -33,6 +33,7 @@ public interface CurrentMonitorDao {
void updateSummaries(BreakerGroup _rootGroup, Set<Date> _daysToSummarize, TimeZone _tz); void updateSummaries(BreakerGroup _rootGroup, Set<Date> _daysToSummarize, TimeZone _tz);
void rebuildSummaries(int _accountId); void rebuildSummaries(int _accountId);
void rebuildSummariesAsync(int _accountId);
void rebuildSummaries(int _accountId, Date _start, Date _end); void rebuildSummaries(int _accountId, Date _start, Date _end);
String addPasswordResetKey(String _email); String addPasswordResetKey(String _email);

View File

@ -192,6 +192,11 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
} }
} }
@Override
public void rebuildSummariesAsync(int _accountId) {
executor.submit(()->rebuildSummaries(_accountId));
}
@Override @Override
public void rebuildSummaries(int _accountId) { public void rebuildSummaries(int _accountId) {
HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sort("minute")); HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sort("minute"));

View File

@ -14,7 +14,6 @@ import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.TreeMap; import java.util.TreeMap;
@ -30,6 +29,10 @@ public class BreakerGroupEnergy {
private List<EnergyBlock> energyBlocks; private List<EnergyBlock> energyBlocks;
private double toGrid; private double toGrid;
private double fromGrid; private double fromGrid;
private double peakToGrid;
private double peakFromGrid;
private double peakConsumption;
private double peakProduction;
private TimeZone timezone; private TimeZone timezone;
public BreakerGroupEnergy() { public BreakerGroupEnergy() {
@ -65,7 +68,7 @@ public class BreakerGroupEnergy {
resetEnergy(minute); resetEnergy(minute);
} }
int idx; int idx;
Map<MeterMinute, MeterMinuteValues> meters = new HashMap<>(); Map<Integer, Map<Integer, MeterMinute>> minutes = new TreeMap<>();
for (HubPowerMinute hubPower : _hubPower) { for (HubPowerMinute hubPower : _hubPower) {
Date minute = hubPower.getMinuteAsDate(); Date minute = hubPower.getMinuteAsDate();
for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) { for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) {
@ -75,7 +78,7 @@ public class BreakerGroupEnergy {
BreakerGroup group = _breakerKeyToGroup.get(breaker.breakerKey()); BreakerGroup group = _breakerKeyToGroup.get(breaker.breakerKey());
if (group == null) if (group == null)
continue; continue;
MeterMinuteValues meter = meters.computeIfAbsent(new MeterMinute(b.getMeter(), minute), _p->new MeterMinuteValues()); MeterMinute meter = minutes.computeIfAbsent(hubPower.getMinute(), _p->new TreeMap<>()).computeIfAbsent(b.getMeter(), _m->new MeterMinute(b.getMeter(), hubPower.getMinuteAsDate()));
idx = 0; idx = 0;
EnergyBlock block = getBlock(group.getId(), minute); EnergyBlock block = getBlock(group.getId(), minute);
if (block != null) { if (block != null) {
@ -94,27 +97,64 @@ public class BreakerGroupEnergy {
} }
double monthFromGrid = _month == null ? 0.0 : _month.getFromGrid(); double monthFromGrid = _month == null ? 0.0 : _month.getFromGrid();
double secondFromGrid; double secondFromGrid;
for (Map.Entry<MeterMinute, MeterMinuteValues> meter : meters.entrySet()) { for (MeterMinute minute : CollectionUtils.aggregate(minutes.values(), Map::values)) {
double monthkWh = monthFromGrid/3600000; double monthkWh = monthFromGrid/3600000;
List<BillingRate> consumptionRates = CollectionUtils.filter(_rates, _r->_r.isApplicable(GridFlow.FROM, meter.getKey().meter, monthkWh, meter.getKey().minute, timezone)); List<BillingRate> consumptionRates = CollectionUtils.filter(_rates, _r->_r.isApplicable(GridFlow.FROM, minute.getMeter(), monthkWh, minute.getMinute(), timezone));
List<BillingRate> productionRates = CollectionUtils.filter(_rates, _r->_r.isApplicable(GridFlow.TO, meter.getKey().meter, monthkWh, meter.getKey().minute, timezone)); List<BillingRate> productionRates = CollectionUtils.filter(_rates, _r->_r.isApplicable(GridFlow.TO, minute.getMeter(), monthkWh, minute.getMinute(), timezone));
for (int i = 0; i < 60; i++) { for (int i = 0; i < 60; i++) {
secondFromGrid = meter.getValue().usage[i] - meter.getValue().solar[i]; if (minute.usage[i] > peakConsumption)
peakConsumption = minute.usage[i];
if (minute.solar[i] > peakProduction)
peakProduction = minute.solar[i];
secondFromGrid = minute.usage[i] - minute.solar[i];
monthFromGrid += secondFromGrid; monthFromGrid += secondFromGrid;
if (secondFromGrid > 0) { if (secondFromGrid > 0) {
fromGrid += secondFromGrid; fromGrid += secondFromGrid;
if (secondFromGrid > peakFromGrid)
peakFromGrid = secondFromGrid;
for (BillingRate rate : consumptionRates) { for (BillingRate rate : consumptionRates) {
meter.getValue().charges[i] += rate.apply(secondFromGrid/3600000); minute.charges[i] += rate.apply(secondFromGrid/3600000);
} }
} }
else { else {
toGrid -= secondFromGrid; secondFromGrid = -secondFromGrid;
toGrid += secondFromGrid;
if (secondFromGrid > peakToGrid)
peakToGrid = secondFromGrid;
for (BillingRate rate : productionRates) { for (BillingRate rate : productionRates) {
meter.getValue().charges[i] += rate.apply(secondFromGrid/3600000); minute.charges[i] -= rate.apply(secondFromGrid/3600000);
} }
} }
} }
} }
double curConsumption;
double curProduction;
double curToGrid;
double curFromGrid;
for (Map<Integer, MeterMinute> meters : minutes.values()) {
for (int i=0; i < 60; i++) {
curConsumption = 0;
curProduction = 0;
curToGrid = 0;
curFromGrid = 0;
for (MeterMinute meterValues : meters.values()) {
curConsumption += meterValues.usage[i];
curProduction += meterValues.solar[i];
if (meterValues.solar[i] > meterValues.usage[i])
curToGrid += meterValues.solar[i] - meterValues.usage[i];
else
curFromGrid += meterValues.usage[i] - meterValues.solar[i];
}
if (curConsumption > peakConsumption)
peakConsumption = curConsumption;
if (curProduction > peakProduction)
peakProduction = curProduction;
if (curToGrid > peakToGrid)
peakToGrid = curToGrid;
if (curFromGrid > peakFromGrid)
peakFromGrid = curFromGrid;
}
}
for (HubPowerMinute hubPower : _hubPower) { for (HubPowerMinute hubPower : _hubPower) {
Date minute = hubPower.getMinuteAsDate(); Date minute = hubPower.getMinuteAsDate();
for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) { for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) {
@ -124,7 +164,7 @@ public class BreakerGroupEnergy {
BreakerGroup group = _breakerKeyToGroup.get(breaker.breakerKey()); BreakerGroup group = _breakerKeyToGroup.get(breaker.breakerKey());
if (group == null) if (group == null)
continue; continue;
MeterMinuteValues meter = meters.get(new MeterMinute(b.getMeter(), minute)); MeterMinute meter = minutes.get(hubPower.getMinute()).get(b.getMeter());
idx = 0; idx = 0;
double charge = 0.0; double charge = 0.0;
for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) { for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) {
@ -190,6 +230,14 @@ public class BreakerGroupEnergy {
block.addCharge(curEnergy.getCharge()); block.addCharge(curEnergy.getCharge());
energy.setToGrid(energy.getToGrid()+curEnergy.getToGrid()); energy.setToGrid(energy.getToGrid()+curEnergy.getToGrid());
energy.setFromGrid(energy.getFromGrid()+curEnergy.getFromGrid()); energy.setFromGrid(energy.getFromGrid()+curEnergy.getFromGrid());
if (curEnergy.getPeakFromGrid() > energy.getPeakFromGrid())
energy.setPeakFromGrid(curEnergy.getPeakFromGrid());
if (curEnergy.getPeakToGrid() > energy.getPeakToGrid())
energy.setPeakToGrid(curEnergy.getPeakToGrid());
if (curEnergy.getPeakConsumption() > energy.getPeakConsumption())
energy.setPeakConsumption(curEnergy.getPeakConsumption());
if (curEnergy.getPeakProduction() > energy.getPeakProduction())
energy.setPeakProduction(curEnergy.getPeakProduction());
} }
return energy; return energy;
} }
@ -304,6 +352,38 @@ public class BreakerGroupEnergy {
fromGrid = _fromGrid; fromGrid = _fromGrid;
} }
public double getPeakToGrid() {
return peakToGrid;
}
public void setPeakToGrid(double _peakToGrid) {
peakToGrid = _peakToGrid;
}
public double getPeakFromGrid() {
return peakFromGrid;
}
public void setPeakFromGrid(double _peakFromGrid) {
peakFromGrid = _peakFromGrid;
}
public double getPeakConsumption() {
return peakConsumption;
}
public void setPeakConsumption(double _peakConsumption) {
peakConsumption = _peakConsumption;
}
public double getPeakProduction() {
return peakProduction;
}
public void setPeakProduction(double _peakProduction) {
peakProduction = _peakProduction;
}
public TimeZone getTimeZone() { public TimeZone getTimeZone() {
return timezone; return timezone;
} }
@ -430,21 +510,14 @@ public class BreakerGroupEnergy {
minute = _minute; minute = _minute;
} }
@Override public int getMeter() {
public boolean equals(Object _o) { return meter;
if (this == _o) return true;
if (_o == null || getClass() != _o.getClass()) return false;
MeterMinute that = (MeterMinute) _o;
return meter == that.meter && minute.equals(that.minute);
} }
@Override public Date getMinute() {
public int hashCode() { return minute;
return Objects.hash(meter, minute);
} }
}
private static class MeterMinuteValues {
public double[] usage = new double[60]; public double[] usage = new double[60];
public double[] solar = new double[60]; public double[] solar = new double[60];
public double[] charges = new double[60]; public double[] charges = new double[60];

View File

@ -24,6 +24,10 @@ public class BreakerGroupSummary {
private double charge; private double charge;
private double toGrid; private double toGrid;
private double fromGrid; private double fromGrid;
private double peakToGrid;
private double peakFromGrid;
private double peakConsumption;
private double peakProduction;
public BreakerGroupSummary() { public BreakerGroupSummary() {
} }
@ -39,6 +43,10 @@ public class BreakerGroupSummary {
charge = _energy.charge(null, false); charge = _energy.charge(null, false);
toGrid = _energy.getToGrid(); toGrid = _energy.getToGrid();
fromGrid = _energy.getFromGrid(); fromGrid = _energy.getFromGrid();
peakToGrid = _energy.getPeakToGrid();
peakFromGrid = _energy.getPeakFromGrid();
peakConsumption = _energy.getPeakConsumption();
peakProduction = _energy.getPeakProduction();
} }
public String getId() { public String getId() {
@ -133,6 +141,38 @@ public class BreakerGroupSummary {
fromGrid = _fromGrid; fromGrid = _fromGrid;
} }
public double getPeakConsumption() {
return peakConsumption;
}
public void setPeakConsumption(double _peakConsumption) {
peakConsumption = _peakConsumption;
}
public double getPeakProduction() {
return peakProduction;
}
public void setPeakProduction(double _peakProduction) {
peakProduction = _peakProduction;
}
public double getPeakToGrid() {
return peakToGrid;
}
public void setPeakToGrid(double _peakToGrid) {
peakToGrid = _peakToGrid;
}
public double getPeakFromGrid() {
return peakFromGrid;
}
public void setPeakFromGrid(double _peakFromGrid) {
peakFromGrid = _peakFromGrid;
}
public List<BreakerGroupSummary> getAllGroups() { public List<BreakerGroupSummary> getAllGroups() {
Map<String, BreakerGroupSummary> groups = new TreeMap<>(); Map<String, BreakerGroupSummary> groups = new TreeMap<>();
getAllGroups(groups); getAllGroups(groups);

View File

@ -74,6 +74,10 @@ public class BreakerGroupEnergySerializer extends AbstractDaoSerializer<BreakerG
} }
d.put("to_grid", _o.getToGrid()); d.put("to_grid", _o.getToGrid());
d.put("from_grid", _o.getFromGrid()); d.put("from_grid", _o.getFromGrid());
d.put("peak_to_grid", _o.getPeakToGrid());
d.put("peak_from_grid", _o.getPeakFromGrid());
d.put("peak_production", _o.getPeakProduction());
d.put("peak_consumption", _o.getPeakConsumption());
return d; return d;
} }
@ -110,6 +114,10 @@ public class BreakerGroupEnergySerializer extends AbstractDaoSerializer<BreakerG
} }
o.setToGrid(DaoSerializer.getDouble(_d, "to_grid")); o.setToGrid(DaoSerializer.getDouble(_d, "to_grid"));
o.setFromGrid(DaoSerializer.getDouble(_d, "from_grid")); o.setFromGrid(DaoSerializer.getDouble(_d, "from_grid"));
o.setPeakToGrid(DaoSerializer.getDouble(_d, "peak_to_grid"));
o.setPeakFromGrid(DaoSerializer.getDouble(_d, "peak_from_grid"));
o.setPeakProduction(DaoSerializer.getDouble(_d, "peak_production"));
o.setPeakConsumption(DaoSerializer.getDouble(_d, "peak_consumption"));
return o; return o;
} }
} }

View File

@ -36,6 +36,10 @@ public class BreakerGroupSummarySerializer extends AbstractDaoSerializer<Breaker
d.put("charge", _o.getCharge()); d.put("charge", _o.getCharge());
d.put("to_grid", _o.getToGrid()); d.put("to_grid", _o.getToGrid());
d.put("from_grid", _o.getFromGrid()); d.put("from_grid", _o.getFromGrid());
d.put("peak_to_grid", _o.getPeakToGrid());
d.put("peak_from_grid", _o.getPeakFromGrid());
d.put("peak_production", _o.getPeakProduction());
d.put("peak_consumption", _o.getPeakConsumption());
return d; return d;
} }
@ -52,6 +56,10 @@ public class BreakerGroupSummarySerializer extends AbstractDaoSerializer<Breaker
o.setCharge(DaoSerializer.getDouble(_d, "charge")); o.setCharge(DaoSerializer.getDouble(_d, "charge"));
o.setToGrid(DaoSerializer.getDouble(_d, "to_grid")); o.setToGrid(DaoSerializer.getDouble(_d, "to_grid"));
o.setFromGrid(DaoSerializer.getDouble(_d, "from_grid")); o.setFromGrid(DaoSerializer.getDouble(_d, "from_grid"));
o.setPeakToGrid(DaoSerializer.getDouble(_d, "peak_to_grid"));
o.setPeakFromGrid(DaoSerializer.getDouble(_d, "peak_from_grid"));
o.setPeakProduction(DaoSerializer.getDouble(_d, "peak_production"));
o.setPeakConsumption(DaoSerializer.getDouble(_d, "peak_consumption"));
return o; return o;
} }
} }

View File

@ -35,6 +35,12 @@
<artifactId>lantern-util-servlet</artifactId> <artifactId>lantern-util-servlet</artifactId>
<version>1.0.0</version> <version>1.0.0</version>
</dependency> </dependency>
<dependency>
<groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-http</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>com.lanternsoftware.rules</groupId> <groupId>com.lanternsoftware.rules</groupId>
<artifactId>lantern-service-rules</artifactId> <artifactId>lantern-service-rules</artifactId>

View File

@ -0,0 +1,26 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.auth.AuthCode;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/rebuildSummaries")
public class RebuildSummariesServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
if (_authCode.getAccountId() == 100) {
for (String sId : Globals.dao.getProxy().queryForField(Account.class, null, "_id")) {
int id = DaoSerializer.toInteger(sId);
if (id != 0)
Globals.dao.rebuildSummariesAsync(id);
}
}
else
_rep.setStatus(401);
}
}

View File

@ -1,10 +1,10 @@
package com.lanternsoftware.currentmonitor; package com.lanternsoftware.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.AuthCode;
import com.lanternsoftware.util.LanternFiles; import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.ResourceLoader; import com.lanternsoftware.util.ResourceLoader;
import com.lanternsoftware.util.cryptography.AESTool; import com.lanternsoftware.util.cryptography.AESTool;
import com.lanternsoftware.util.dao.DaoSerializer; import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.auth.AuthCode;
public class CreateAuthCode { public class CreateAuthCode {
private static final AESTool aes = new AESTool(ResourceLoader.loadFile(LanternFiles.OPS_PATH + "authKey.dat")); private static final AESTool aes = new AESTool(ResourceLoader.loadFile(LanternFiles.OPS_PATH + "authKey.dat"));

View File

@ -0,0 +1,16 @@
package com.lanternsoftware.currentmonitor;
import com.lanternsoftware.util.http.HttpPool;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
public class RebuildSummariesRemote {
public static void main(String[] args) {
HttpPool pool = new HttpPool(10, 10, 10000, 10000, 10000);
HttpGet r = new HttpGet("https://lanternpowermonitor.com/currentmonitor/rebuildSummaries");
r.addHeader("auth_code", "<redacted>");
CloseableHttpResponse resp = pool.execute(r);
System.out.println(resp.getStatusLine().getStatusCode());
pool.shutdown();
}
}

View File

@ -266,7 +266,7 @@ public class CollectionUtils {
public static <T, V> List<V> aggregate(Collection<T> _coll, IAggregator<T, V> _aggregator) { public static <T, V> List<V> aggregate(Collection<T> _coll, IAggregator<T, V> _aggregator) {
List<V> list = new ArrayList<>(); List<V> list = new ArrayList<>();
for (T t : makeNotNull(_coll)) { for (T t : makeNotNull(_coll)) {
List<V> vs = _aggregator.aggregate(t); Collection<V> vs = _aggregator.aggregate(t);
if (vs != null) if (vs != null)
list.addAll(vs); list.addAll(vs);
} }

View File

@ -1,7 +1,7 @@
package com.lanternsoftware.util; package com.lanternsoftware.util;
import java.util.List; import java.util.Collection;
public interface IAggregator<T, V> { public interface IAggregator<T, V> {
List<V> aggregate(T _t); Collection<V> aggregate(T _t);
} }