Add billing plans so different plans can be compared. Performance enhancements to charge calculations.

This commit is contained in:
MarkBryanMilligan
2021-10-18 15:46:25 -05:00
parent ecbf438082
commit 883cf7865d
35 changed files with 2123 additions and 813 deletions

View File

@@ -2,8 +2,8 @@ package com.lanternsoftware.dataaccess.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupEnergy;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupSummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergySummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyTotal;
import com.lanternsoftware.datamodel.currentmonitor.Sequence;
import com.lanternsoftware.datamodel.rules.Event;
import com.lanternsoftware.datamodel.rules.FcmDevice;
@@ -34,14 +34,14 @@ public class Backup {
t4.stop();
DebugTimer t5 = new DebugTimer("Query Energy");
List<BreakerGroupEnergy> energy = dao.getProxy().queryAll(BreakerGroupEnergy.class);
List<EnergySummary> energy = dao.getProxy().queryAll(EnergySummary.class);
t5.stop();
DebugTimer t6 = new DebugTimer("Save Energy");
backupDao.getProxy().save(energy);
t6.stop();
DebugTimer t7 = new DebugTimer("Query Summaries");
List<BreakerGroupSummary> summary = dao.getProxy().queryAll(BreakerGroupSummary.class);
List<EnergyTotal> summary = dao.getProxy().queryAll(EnergyTotal.class);
t7.stop();
DebugTimer t8 = new DebugTimer("Save Summaries");
backupDao.getProxy().save(summary);

View File

@@ -1,18 +1,16 @@
package com.lanternsoftware.dataaccess.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupEnergy;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.EnergyBlockViewMode;
import com.lanternsoftware.datamodel.currentmonitor.EnergySummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.util.dao.mongo.MongoProxy;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
public interface CurrentMonitorDao {
@@ -21,9 +19,10 @@ public interface CurrentMonitorDao {
void putBreakerPower(BreakerPower _current);
List<BreakerPower> getBreakerPowerForAccount(int _accountId);
BreakerPower getLatestBreakerPower(int _accountId, int _hub, int _port);
BreakerGroupEnergy getBreakerGroupEnergy(int _accountId, String _groupId, EnergyBlockViewMode _viewMode, Date _start);
byte[] getBreakerGroupEnergyBinary(int _accountId, String _groupId, EnergyBlockViewMode _viewMode, Date _start);
void putBreakerGroupEnergy(BreakerGroupEnergy _energy);
EnergySummary getEnergySummary(int _accountId, String _groupId, EnergyViewMode _viewMode, Date _start);
byte[] getEnergySummaryBinary(int _accountId, String _groupId, EnergyViewMode _viewMode, Date _start);
byte[] getChargeSummaryBinary(int _accountId, int _planId, String _groupId, EnergyViewMode _viewMode, Date _start);
void putEnergySummary(EnergySummary _energy);
void putHubPowerMinute(HubPowerMinute _power);
@@ -31,7 +30,6 @@ public interface CurrentMonitorDao {
BreakerConfig getMergedConfig(AuthCode _authCode);
void putConfig(BreakerConfig _config);
void updateSummaries(BreakerGroup _rootGroup, Set<Date> _daysToSummarize, TimeZone _tz);
void rebuildSummaries(int _accountId);
void rebuildSummariesAsync(int _accountId);
void rebuildSummaries(int _accountId, Date _start, Date _end);

View File

@@ -1,14 +1,17 @@
package com.lanternsoftware.dataaccess.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.datamodel.currentmonitor.BillingPlan;
import com.lanternsoftware.datamodel.currentmonitor.BillingRate;
import com.lanternsoftware.datamodel.currentmonitor.Breaker;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupEnergy;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupSummary;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.EnergyBlockViewMode;
import com.lanternsoftware.datamodel.currentmonitor.ChargeSummary;
import com.lanternsoftware.datamodel.currentmonitor.ChargeTotal;
import com.lanternsoftware.datamodel.currentmonitor.EnergySummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyTotal;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.datamodel.currentmonitor.Sequence;
import com.lanternsoftware.util.CollectionUtils;
@@ -23,6 +26,7 @@ import com.lanternsoftware.util.dao.DaoSort;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import com.lanternsoftware.util.dao.mongo.MongoProxy;
import com.lanternsoftware.util.mutable.MutableDouble;
import org.mindrot.jbcrypt.BCrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,6 +35,7 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -53,11 +58,13 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
proxy = new MongoProxy(_config);
proxy.ensureIndex(BreakerPower.class, DaoSort.sort("account_id").then("key"));
proxy.ensureIndex(HubPowerMinute.class, DaoSort.sort("account_id").then("minute"));
proxy.ensureIndex(BreakerGroupEnergy.class, DaoSort.sort("account_id").then("group_id").then("view_mode"));
proxy.ensureIndex(BreakerGroupSummary.class, DaoSort.sort("account_id").then("group_id").then("view_mode").then("start"));
proxy.ensureIndex(EnergySummary.class, DaoSort.sort("account_id").then("group_id").then("view_mode"));
proxy.ensureIndex(EnergyTotal.class, DaoSort.sort("account_id").then("group_id").then("view_mode").then("start"));
proxy.ensureIndex(ChargeSummary.class, DaoSort.sort("account_id").then("plan_id").then("group_id").then("view_mode"));
proxy.ensureIndex(ChargeTotal.class, DaoSort.sort("account_id").then("plan_id").then("group_id").then("view_mode").then("start"));
proxy.ensureIndex(DirtyMinute.class, DaoSort.sort("posted"));
for (DirtyMinute minute : proxy.queryAll(DirtyMinute.class)) {
updateSummaries(minute);
updateEnergySummaries(minute);
}
proxy.delete(DirtyMinute.class, new DaoQuery());
}
@@ -80,36 +87,49 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
proxy.save(_power);
DirtyMinute minute = new DirtyMinute(_power.getAccountId(), _power.getMinute(), new Date());
proxy.save(minute);
delayTimer.schedule(new TimerTask(){
delayTimer.schedule(new TimerTask() {
@Override
public void run() {
executor.submit(()->{
executor.submit(() -> {
if (proxy.queryOneAndDelete(DirtyMinute.class, new DaoQuery("_id", minute.getId())) != null)
updateSummaries(new DirtyMinute(_power.getAccountId(), _power.getMinute(), new Date()));
updateEnergySummaries(new DirtyMinute(_power.getAccountId(), _power.getMinute(), new Date()));
});
}
}, 10000);
}
private void updateSummaries(DirtyMinute _minute) {
private void updateEnergySummaries(DirtyMinute _minute) {
DebugTimer timer = new DebugTimer("Updating summaries", logger);
List<HubPowerMinute> minutes = proxy.query(HubPowerMinute.class, new DaoQuery("account_id", _minute.getAccountId()).and("minute", _minute.getMinute()));
TimeZone tz = getTimeZoneForAccount(_minute.getAccountId());
BreakerConfig config = getConfig(_minute.getAccountId());
BreakerGroup group = CollectionUtils.getFirst(config.getBreakerGroups());
Date day = DateUtils.getMidnightBefore(_minute.getMinuteAsDate(), tz);
BreakerGroupEnergy energy = getBreakerGroupEnergy(_minute.getAccountId(), group.getId(), EnergyBlockViewMode.DAY, day);
Date monthStart = DateUtils.getStartOfMonth(day, tz);
BreakerGroupSummary month = proxy.queryOne(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_minute.getAccountId(), group.getId(), EnergyBlockViewMode.MONTH, monthStart)));
DebugTimer t2 = new DebugTimer("Updating energy", logger);
EnergySummary energy = getEnergySummary(_minute.getAccountId(), group.getId(), EnergyViewMode.DAY, day);
if (energy == null)
energy = new BreakerGroupEnergy(group, minutes, EnergyBlockViewMode.DAY, day, month, config.getBillingRates(), tz);
energy = new EnergySummary(group, minutes, EnergyViewMode.DAY, day, tz);
else
energy.addEnergy(group, minutes, month, config.getBillingRates());
putBreakerGroupEnergy(energy);
updateSummaries(group, CollectionUtils.asHashSet(day), tz);
energy.addEnergy(group, minutes);
putEnergySummary(energy);
updateEnergySummaries(group, CollectionUtils.asHashSet(day), tz);
t2.stop();
DebugTimer t3 = new DebugTimer("Updating charges", logger);
updateChargeSummary(config, energy, tz);
updateChargeSummaries(config, CollectionUtils.asHashSet(energy.getStart()), tz);
t3.stop();
timer.stop();
}
private void putChargeSummary(ChargeSummary _summary) {
putChargeSummaries(CollectionUtils.asArrayList(_summary));
}
private void putChargeSummaries(Collection<ChargeSummary> _summaries) {
proxy.save(_summaries);
proxy.save(CollectionUtils.transform(_summaries, ChargeTotal::new));
}
@Override
public List<BreakerPower> getBreakerPowerForAccount(int _accountId) {
return proxy.query(BreakerPower.class, new DaoQuery("account_id", _accountId).andGt("read_time", DateUtils.minutesFromNow(-1).getTime()));
@@ -121,17 +141,21 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
}
@Override
public BreakerGroupEnergy getBreakerGroupEnergy(int _accountId, String _groupId, EnergyBlockViewMode _viewMode, Date _start) {
return proxy.queryOne(BreakerGroupEnergy.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, _groupId, _viewMode, _start)));
public EnergySummary getEnergySummary(int _accountId, String _groupId, EnergyViewMode _viewMode, Date _start) {
return proxy.queryOne(EnergySummary.class, new DaoQuery("_id", EnergySummary.toId(_accountId, _groupId, _viewMode, _start)));
}
@Override
public byte[] getBreakerGroupEnergyBinary(int _accountId, String _groupId, EnergyBlockViewMode _viewMode, Date _start) {
return DaoSerializer.toZipBson(proxy.queryForEntity(BreakerGroupEnergy.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, _groupId, _viewMode, _start))));
public byte[] getEnergySummaryBinary(int _accountId, String _groupId, EnergyViewMode _viewMode, Date _start) {
return DaoSerializer.toZipBson(proxy.queryForEntity(EnergySummary.class, new DaoQuery("_id", EnergySummary.toId(_accountId, _groupId, _viewMode, _start))));
}
@Override
public void updateSummaries(BreakerGroup _rootGroup, Set<Date> _daysToSummarize, TimeZone _tz) {
public byte[] getChargeSummaryBinary(int _accountId, int _planId, String _groupId, EnergyViewMode _viewMode, Date _start) {
return DaoSerializer.toZipBson(proxy.queryForEntity(ChargeSummary.class, new DaoQuery("_id", ChargeSummary.toId(_accountId, _planId, _groupId, _viewMode, _start))));
}
private void updateEnergySummaries(BreakerGroup _rootGroup, Set<Date> _daysToSummarize, TimeZone _tz) {
Set<Date> monthsToSummarize = CollectionUtils.transformToSet(_daysToSummarize, _c -> DateUtils.getStartOfMonth(_c, _tz));
Set<Date> yearsToSummarize = CollectionUtils.transformToSet(monthsToSummarize, _c -> DateUtils.getStartOfYear(_c, _tz));
for (Date month : monthsToSummarize) {
@@ -139,31 +163,101 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
Calendar end = DateUtils.getEndOfMonthCal(month, _tz);
List<String> groupEnergyIds = new ArrayList<>();
while (calDayStart.before(end)) {
groupEnergyIds.add(BreakerGroupEnergy.toId(_rootGroup.getAccountId(), _rootGroup.getId(), EnergyBlockViewMode.DAY, calDayStart.getTime()));
groupEnergyIds.add(EnergySummary.toId(_rootGroup.getAccountId(), _rootGroup.getId(), EnergyViewMode.DAY, calDayStart.getTime()));
calDayStart.add(Calendar.DAY_OF_YEAR, 1);
}
List<BreakerGroupSummary> groupEnergies = CollectionUtils.aggregate(proxy.query(BreakerGroupSummary.class, DaoQuery.in("_id", groupEnergyIds)), BreakerGroupSummary::getAllGroups);
Map<String, List<BreakerGroupSummary>> energies = CollectionUtils.transformToMultiMap(groupEnergies, BreakerGroupSummary::getGroupId);
BreakerGroupEnergy summary = BreakerGroupEnergy.summary(_rootGroup, energies, EnergyBlockViewMode.MONTH, month, _tz);
putBreakerGroupEnergy(summary);
List<EnergyTotal> groupEnergies = CollectionUtils.aggregate(proxy.query(EnergyTotal.class, DaoQuery.in("_id", groupEnergyIds)), EnergyTotal::flatten);
Map<String, List<EnergyTotal>> energies = CollectionUtils.transformToMultiMap(groupEnergies, EnergyTotal::getGroupId);
EnergySummary summary = EnergySummary.summary(_rootGroup, energies, EnergyViewMode.MONTH, month, _tz);
putEnergySummary(summary);
}
for (Date year : yearsToSummarize) {
Calendar calMonthStart = DateUtils.toCalendar(year, _tz);
Calendar end = DateUtils.getEndOfYearCal(year, _tz);
List<String> groupEnergyIds = new ArrayList<>();
List<String> summaryIds = new ArrayList<>();
while (calMonthStart.before(end)) {
groupEnergyIds.add(BreakerGroupEnergy.toId(_rootGroup.getAccountId(), _rootGroup.getId(), EnergyBlockViewMode.MONTH, calMonthStart.getTime()));
summaryIds.add(EnergySummary.toId(_rootGroup.getAccountId(), _rootGroup.getId(), EnergyViewMode.MONTH, calMonthStart.getTime()));
calMonthStart.add(Calendar.MONTH, 1);
}
List<BreakerGroupSummary> groupEnergies = CollectionUtils.aggregate(proxy.query(BreakerGroupSummary.class, DaoQuery.in("_id", groupEnergyIds)), BreakerGroupSummary::getAllGroups);
Map<String, List<BreakerGroupSummary>> energies = CollectionUtils.transformToMultiMap(groupEnergies, BreakerGroupSummary::getGroupId);
BreakerGroupEnergy summary = BreakerGroupEnergy.summary(_rootGroup, energies, EnergyBlockViewMode.YEAR, year, _tz);
putBreakerGroupEnergy(summary);
List<EnergyTotal> groupEnergies = CollectionUtils.aggregate(proxy.query(EnergyTotal.class, DaoQuery.in("_id", summaryIds)), EnergyTotal::flatten);
Map<String, List<EnergyTotal>> energies = CollectionUtils.transformToMultiMap(groupEnergies, EnergyTotal::getGroupId);
EnergySummary summary = EnergySummary.summary(_rootGroup, energies, EnergyViewMode.YEAR, year, _tz);
putEnergySummary(summary);
}
List<EnergyTotal> groupEnergies = CollectionUtils.aggregate(proxy.query(EnergyTotal.class, new DaoQuery("account_id", _rootGroup.getAccountId()).and("group_id", _rootGroup.getId()).and("view_mode", EnergyViewMode.YEAR.name())), EnergyTotal::flatten);
Map<String, List<EnergyTotal>> energies = CollectionUtils.transformToMultiMap(groupEnergies, EnergyTotal::getGroupId);
EnergySummary summary = EnergySummary.summary(_rootGroup, energies, EnergyViewMode.ALL, new Date(0), _tz);
putEnergySummary(summary);
}
private void updateChargeSummary(BreakerConfig _config, EnergySummary _energySummary, TimeZone _tz) {
Date lookback = null;
for (BillingPlan p : CollectionUtils.makeNotNull(_config.getBillingPlans())) {
Date cycleStart = p.getBillingCycleStart(_energySummary.getStart(), _tz);
if (cycleStart.after(_energySummary.getStart()))
cycleStart = DateUtils.addMonths(cycleStart, -1, _tz);
if ((lookback == null) || cycleStart.before(lookback))
lookback = cycleStart;
}
if (lookback != null) {
List<String> groupEnergyIds = new ArrayList<>();
while (lookback.before(_energySummary.getStart())) {
groupEnergyIds.add(EnergySummary.toId(_config.getAccountId(), _energySummary.getGroupId(), EnergyViewMode.DAY, lookback));
lookback = DateUtils.addDays(lookback, 1, _tz);
}
List<EnergyTotal> totals = proxy.query(EnergyTotal.class, DaoQuery.in("_id", groupEnergyIds));
putChargeSummaries(_energySummary.toChargeSummaries(_config, totals));
}
}
private void updateChargeSummaries(BreakerConfig _config, Set<Date> _daysToSummarize, TimeZone _tz) {
if (CollectionUtils.isEmpty(_config.getBillingPlans()))
return;
Set<Date> yearsToSummarize = CollectionUtils.transformToSet(_daysToSummarize, _c -> DateUtils.getStartOfYear(_c, _tz));
BreakerGroup rootGroup = _config.getRootGroup();
for (BillingPlan plan : _config.getBillingPlans()) {
List<ChargeSummary> summaries = new ArrayList<>();
Set<Date> monthsToSummarize = CollectionUtils.transformToSet(_daysToSummarize, _c -> plan.getBillingCycleStart(_c, _tz));
for (Date month : monthsToSummarize) {
Calendar monthDayStart = DateUtils.toCalendar(month, _tz);
Calendar monthEnd = DateUtils.toCalendar(plan.getBillingCycleEnd(month, _tz), _tz);
Set<String> monthSummaryIds = new HashSet<>();
while (monthDayStart.before(monthEnd)) {
monthSummaryIds.add(ChargeSummary.toId(rootGroup.getAccountId(), plan.getPlanId(), rootGroup.getId(), EnergyViewMode.DAY, monthDayStart.getTime()));
monthDayStart.add(Calendar.DAY_OF_YEAR, 1);
}
List<ChargeTotal> monthTotals = CollectionUtils.aggregate(proxy.query(ChargeTotal.class, DaoQuery.in("_id", monthSummaryIds)), ChargeTotal::flatten);
Map<String, List<ChargeTotal>> monthCharges = CollectionUtils.transformToMultiMap(monthTotals, ChargeTotal::getGroupId);
summaries.add(new ChargeSummary(rootGroup, plan, monthCharges, EnergyViewMode.MONTH, month, _tz));
}
putChargeSummaries(summaries);
}
for (BillingPlan plan : _config.getBillingPlans()) {
List<ChargeSummary> summaries = new ArrayList<>();
for (Date year : yearsToSummarize) {
Date yearStart = DateUtils.getStartOfYear(year, _tz);
Date yearEnd = DateUtils.addYears(yearStart, 1, _tz);
Date yearMonthStart = yearStart;
Set<String> monthSummaryIds = new HashSet<>();
Date loopEnd = DateUtils.addDays(yearEnd, 1, _tz);
while (yearMonthStart.before(loopEnd)) {
Date billingStart = plan.getBillingCycleStart(yearMonthStart, _tz);
if (DateUtils.isBetween(billingStart, yearStart, yearEnd))
monthSummaryIds.add(ChargeSummary.toId(rootGroup.getAccountId(), plan.getPlanId(), rootGroup.getId(), EnergyViewMode.MONTH, billingStart));
yearMonthStart = DateUtils.addMonths(yearMonthStart, 1, _tz);
}
List<ChargeTotal> flatTotals = CollectionUtils.aggregate(proxy.query(ChargeTotal.class, DaoQuery.in("_id", monthSummaryIds)), ChargeTotal::flatten);
Map<String, List<ChargeTotal>> yearCharges = CollectionUtils.transformToMultiMap(flatTotals, ChargeTotal::getGroupId);
summaries.add(new ChargeSummary(rootGroup, plan, yearCharges, EnergyViewMode.YEAR, yearStart, _tz));
}
putChargeSummaries(summaries);
}
for (BillingPlan plan : _config.getBillingPlans()) {
List<ChargeTotal> yearTotals = CollectionUtils.aggregate(proxy.query(ChargeTotal.class, new DaoQuery("account_id", rootGroup.getAccountId()).and("plan_id", plan.getPlanId()).and("group_id", rootGroup.getId()).and("view_mode", EnergyViewMode.YEAR.name())), ChargeTotal::flatten);
Map<String, List<ChargeTotal>> charges = CollectionUtils.transformToMultiMap(yearTotals, ChargeTotal::getGroupId);
ChargeSummary summary = new ChargeSummary(rootGroup, plan, charges, EnergyViewMode.ALL, new Date(0), _tz);
putChargeSummary(summary);
}
List<BreakerGroupSummary> groupEnergies = CollectionUtils.aggregate(proxy.query(BreakerGroupSummary.class, new DaoQuery("account_id", _rootGroup.getAccountId()).and("group_id", _rootGroup.getId()).and("view_mode", EnergyBlockViewMode.YEAR.name())), BreakerGroupSummary::getAllGroups);
Map<String, List<BreakerGroupSummary>> energies = CollectionUtils.transformToMultiMap(groupEnergies, BreakerGroupSummary::getGroupId);
BreakerGroupEnergy summary = BreakerGroupEnergy.summary(_rootGroup, energies, EnergyBlockViewMode.ALL, new Date(0), _tz);
putBreakerGroupEnergy(summary);
}
private void rebuildSummaries(int _accountId, Collection<BillingRate> _rates) {
@@ -172,7 +266,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
if (firstMinute == null)
return;
TimeZone tz = getTimeZoneForAccount(_accountId);
Map<String, BillingRate> rates = CollectionUtils.transformToMap(_rates, _r->String.format("%d%d", DaoSerializer.toLong(_r.getBeginEffective()), DaoSerializer.toLong(_r.getEndEffective())));
Map<String, BillingRate> rates = CollectionUtils.transformToMap(_rates, _r -> String.format("%d%d", DaoSerializer.toLong(_r.getBeginEffective()), DaoSerializer.toLong(_r.getEndEffective())));
for (BillingRate rate : rates.values()) {
Date start = rate.getBeginEffective();
Date end = rate.getEndEffective();
@@ -194,7 +288,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
@Override
public void rebuildSummariesAsync(int _accountId) {
executor.submit(()->rebuildSummaries(_accountId));
executor.submit(() -> rebuildSummaries(_accountId));
}
@Override
@@ -210,35 +304,88 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
BreakerConfig config = getConfig(_accountId);
TimeZone tz = getTimeZoneForAccount(_accountId);
Date start = DateUtils.getMidnightBefore(_start, tz);
Date monthStart = DateUtils.getStartOfMonth(_start, tz);
BreakerGroup root = CollectionUtils.getFirst(config.getBreakerGroups());
if (root == null)
return;
proxy.delete(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, root.getId(), EnergyBlockViewMode.MONTH, monthStart)));
Set<Date> dates = new HashSet<>();
while (start.before(_end)) {
Date dayEnd = DateUtils.getMidnightAfter(start, tz);
DebugTimer timer = new DebugTimer("Time to rebuild one day");
DebugTimer t1 = new DebugTimer("Loading hub power for day, account: " + _accountId + " day: " + DateUtils.format("MM/dd/yyyy", tz, start));
List<HubPowerMinute> minutes = proxy.query(HubPowerMinute.class, new DaoQuery("account_id", _accountId).andBetweenInclusiveExclusive("minute", (int) (start.getTime() / 60000), (int) (dayEnd.getTime() / 60000)));
t1.stop();
monthStart = DateUtils.getStartOfMonth(start, tz);
BreakerGroupSummary month = null;
if (monthStart.equals(start))
proxy.delete(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, root.getId(), EnergyBlockViewMode.MONTH, monthStart)));
else
month = proxy.queryOne(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, root.getId(), EnergyBlockViewMode.MONTH, monthStart)));
BreakerGroupEnergy energy = new BreakerGroupEnergy(root, minutes, EnergyBlockViewMode.DAY, start, month, config.getBillingRates(), tz);
timer.stop();
putBreakerGroupEnergy(energy);
updateSummaries(root, CollectionUtils.asHashSet(start), tz);
DebugTimer timer = new DebugTimer("Time to rebuild one day", logger);
DebugTimer t1 = new DebugTimer("Loading hub power for day, account: " + _accountId + " day: " + DateUtils.format("MM/dd/yyyy", tz, start), logger);
List<HubPowerMinute> minutes = proxy.query(HubPowerMinute.class, new DaoQuery("account_id", _accountId).andBetweenInclusiveExclusive("minute", (int) (start.getTime() / 60000), (int) (dayEnd.getTime() / 60000)));
t1.stop();
if (!minutes.isEmpty()) {
DebugTimer t2 = new DebugTimer("In memory rebuild", logger);
EnergySummary energy = new EnergySummary(root, minutes, EnergyViewMode.DAY, start, tz);
t2.stop();
timer.stop();
putEnergySummary(energy);
DebugTimer t3 = new DebugTimer("Updating charges", logger);
updateChargeSummary(config, energy, tz);
t3.stop();
}
dates.add(start);
start = DateUtils.addDays(start, 1, tz);
}
DebugTimer t4 = new DebugTimer("Updating month/year/lifetime energy summaries", logger);
updateEnergySummaries(root, dates, tz);
t4.stop();
DebugTimer t5 = new DebugTimer("Updating month/year/lifetime charge summaries", logger);
updateChargeSummaries(config, dates, tz);
t5.stop();
}
public void rebuildChargeSummaries(BreakerConfig _config, BillingPlan _plan) {
TimeZone tz = getTimeZoneForAccount(_config.getAccountId());
HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _config.getAccountId()), DaoSort.sort("minute"));
if (firstMinute == null)
return;
Date start = DateUtils.getMidnightBefore(firstMinute.getMinuteAsDate(), tz);
Date end = DateUtils.getMidnightAfter(new Date(), tz);
BreakerGroup root = CollectionUtils.getFirst(_config.getBreakerGroups());
if (root == null)
return;
Set<Date> dates = new HashSet<>();
Date curDate = start;
while (curDate.before(end)) {
dates.add(curDate);
curDate = DateUtils.addDays(curDate, 1, tz);
}
List<String> summaryIds = CollectionUtils.transform(dates, _dt->EnergySummary.toId(_config.getAccountId(), root.getId(), EnergyViewMode.DAY, _dt));
DebugTimer t1 = new DebugTimer("Load Daily Energy Totals", logger);
Map<Date, EnergyTotal> totals = CollectionUtils.transformToMap(proxy.query(EnergyTotal.class, DaoQuery.in("_id", summaryIds)), EnergyTotal::getStart);
t1.stop();
Map<String, Integer> breakerGroupMeters = _config.getRootGroup().mapToMeters();
List<ChargeSummary> chargeSummaries = new ArrayList<>();
DebugTimer t2 = new DebugTimer("Load Energy Summaries", logger);
List<EnergySummary> energySummaries = proxy.query(EnergySummary.class, DaoQuery.in("_id", summaryIds));
t2.stop();
DebugTimer t3 = new DebugTimer("Rebuild Charges Summaries", logger);
for (EnergySummary energy : energySummaries) {
Date cycleStart = _plan.getBillingCycleStart(energy.getStart(), tz);
double monthKwh = 0.0;
while (cycleStart.before(energy.getStart())) {
EnergyTotal total = totals.get(cycleStart);
if (total != null)
monthKwh += total.totalJoules();
cycleStart = DateUtils.addDays(cycleStart, 1, tz);
}
monthKwh /= 3600000.0;
chargeSummaries.add(energy.toChargeSummary(_plan.getPlanId(), _plan.getRates(), breakerGroupMeters, new MutableDouble(monthKwh)));
}
t3.stop();
DebugTimer t4 = new DebugTimer("Persist Charge Summaries", logger);
putChargeSummaries(chargeSummaries);
t4.stop();
DebugTimer t5 = new DebugTimer("Updating month/year/lifetime charge summaries", logger);
updateChargeSummaries(_config, dates, tz);
t5.stop();
}
@Override
public void putBreakerGroupEnergy(BreakerGroupEnergy _energy) {
public void putEnergySummary(EnergySummary _energy) {
proxy.save(_energy);
proxy.save(new BreakerGroupSummary(_energy));
proxy.save(new EnergyTotal(_energy));
}
@Override
@@ -250,6 +397,8 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
public BreakerConfig getMergedConfig(AuthCode _authCode) {
if (_authCode == null)
return null;
if (CollectionUtils.size(_authCode.getAllAccountIds()) == 1)
return getConfig(_authCode.getAccountId());
List<BreakerConfig> configs = CollectionUtils.transform(_authCode.getAllAccountIds(), this::getConfig, true);
BreakerConfig config = new BreakerConfig();
config.setAccountId(_authCode.getAccountId());
@@ -257,6 +406,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
config.setBreakerGroups(CollectionUtils.aggregate(configs, BreakerConfig::getBreakerGroups));
config.setPanels(CollectionUtils.aggregate(configs, BreakerConfig::getPanels));
config.setMeters(CollectionUtils.aggregate(configs, BreakerConfig::getMeters));
config.setBillingPlans(CollectionUtils.aggregate(configs, BreakerConfig::getBillingPlans));
config.setBillingRates(CollectionUtils.aggregate(configs, BreakerConfig::getBillingRates));
return config;
}
@@ -274,10 +424,12 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
oldEntity.put("archive_date", DaoSerializer.toLong(new Date()));
proxy.saveEntity("config_archive", oldEntity);
executor.submit(() -> {
List<BillingRate> changedRates = new ArrayList<>(_config.getBillingRates());
changedRates.removeAll(CollectionUtils.makeNotNull(oldConfig.getBillingRates()));
if (!changedRates.isEmpty())
rebuildSummaries(_config.getAccountId(), changedRates);
Map<Integer, BillingPlan> oldPlans = CollectionUtils.transformToMap(oldConfig.getBillingPlans(), BillingPlan::getPlanId);
for (BillingPlan plan : CollectionUtils.makeNotNull(_config.getBillingPlans())) {
BillingPlan oldPlan = oldPlans.get(plan.getPlanId());
if ((oldPlan == null) || !oldPlan.isIdentical(plan))
rebuildChargeSummaries(_config, plan);
}
});
}
}
@@ -336,8 +488,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
_account.setPassword(account.getPassword());
else
_account.setPassword(BCrypt.hashpw(_account.getPassword(), BCrypt.gensalt(BCRYPT_ROUNDS)));
}
else if (NullUtils.isNotEmpty(_account.getPassword())) {
} else if (NullUtils.isNotEmpty(_account.getPassword())) {
_account.setPassword(BCrypt.hashpw(_account.getPassword(), BCrypt.gensalt(BCRYPT_ROUNDS)));
}
if (_account.getId() == 0)
@@ -363,8 +514,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
try {
if (NullUtils.isNotEmpty(timezone))
tz = TimeZone.getTimeZone(timezone);
}
catch (Exception _e) {
} catch (Exception _e) {
logger.error("TimeZone not configured correctly for account {}", _accountId);
}
return tz == null ? TimeZone.getTimeZone("America/Chicago") : tz;

View File

@@ -23,4 +23,14 @@ public enum BillingCurrency {
return "-" + symbol + _value.abs().setScale(2, RoundingMode.HALF_EVEN);
return symbol + _value.setScale(2, RoundingMode.HALF_EVEN);
}
public String formatValue(double _value) {
return formatValue(BigDecimal.valueOf(_value));
}
public String formatValue(BigDecimal _value) {
if (_value.compareTo(BigDecimal.ZERO) < 0)
return "-" + _value.abs().setScale(2, RoundingMode.HALF_EVEN);
return _value.setScale(2, RoundingMode.HALF_EVEN).toString();
}
}

View File

@@ -0,0 +1,86 @@
package com.lanternsoftware.datamodel.currentmonitor;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.IIdentical;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
@DBSerializable
public class BillingPlan implements IIdentical<BillingPlan> {
private int accountId;
private int planId;
private int billingDay;
private String name;
private List<BillingRate> rates;
public int getAccountId() {
return accountId;
}
public void setAccountId(int _accountId) {
accountId = _accountId;
}
public int getPlanId() {
return planId;
}
public void setPlanId(int _planId) {
planId = _planId;
}
public int getBillingDay() {
return billingDay;
}
public void setBillingDay(int _billingDay) {
billingDay = _billingDay;
}
public Date getBillingCycleStart(Date _for, TimeZone _tz) {
Calendar cal = DateUtils.getStartOfMonthCal(_for, _tz);
if (billingDay < 100) {
cal.set(Calendar.DAY_OF_MONTH, (billingDay == 0) ? 1 : billingDay);
if (cal.getTime().after(_for))
cal.add(Calendar.MONTH, -1);
return cal.getTime();
}
int dayOfWeek = (billingDay-101)%7 + 1;
int weekOfMonth = (billingDay-101)/7;
int dayOfMonthOffset = dayOfWeek-cal.get(Calendar.DAY_OF_WEEK);
if (dayOfMonthOffset < 0)
dayOfMonthOffset += 7;
cal.set(Calendar.DAY_OF_MONTH, dayOfMonthOffset+(7*weekOfMonth)+1);
return cal.getTime();
}
public Date getBillingCycleEnd(Date _for, TimeZone _tz) {
return getBillingCycleStart(DateUtils.addMonths(_for, 1, _tz), _tz);
}
public String getName() {
return name;
}
public void setName(String _name) {
name = _name;
}
public List<BillingRate> getRates() {
return rates;
}
public void setRates(List<BillingRate> _rates) {
rates = _rates;
}
@Override
public boolean isIdentical(BillingPlan _other) {
return accountId == _other.accountId && planId == _other.planId && billingDay == _other.billingDay && CollectionUtils.isEqual(rates, _other.rates);
}
}

View File

@@ -3,7 +3,6 @@ package com.lanternsoftware.datamodel.currentmonitor;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.TimeZone;
@@ -11,7 +10,6 @@ import java.util.TimeZone;
@DBSerializable
public class BillingRate {
private int meter;
private int dayBillingCycleStart;
private GridFlow flow;
private double rate;
private BillingCurrency currency;
@@ -31,14 +29,6 @@ public class BillingRate {
meter = _meter;
}
public int getDayBillingCycleStart() {
return dayBillingCycleStart;
}
public void setDayBillingCycleStart(int _dayBillingCycleStart) {
dayBillingCycleStart = _dayBillingCycleStart;
}
public GridFlow getFlow() {
return flow;
}
@@ -119,7 +109,7 @@ public class BillingRate {
recursAnnually = _recursAnnually;
}
public boolean isApplicable(GridFlow _mode, int _meter, double _monthKWh, Date _time, TimeZone _tz) {
public boolean isApplicable(GridFlow _mode, int _meter, double _monthKWh, int _secondsIntoDay) {
if ((flow != GridFlow.BOTH) && (flow != _mode))
return false;
if ((meter != -1) && (_meter != meter))
@@ -128,6 +118,14 @@ public class BillingRate {
return false;
if ((monthKWhEnd > 0) && (_monthKWh >= monthKWhEnd))
return false;
if ((timeOfDayStart == 0) && (timeOfDayEnd == 0))
return true;
if ((timeOfDayStart > 0) && (_secondsIntoDay < timeOfDayStart))
return false;
return (timeOfDayEnd == 0) || (_secondsIntoDay < timeOfDayEnd);
}
public boolean isApplicableForDay(Date _time, TimeZone _tz) {
if ((beginEffective != null) && (endEffective != null) && recursAnnually) {
Date begin = beginEffective;
Date end = endEffective;
@@ -148,13 +146,7 @@ public class BillingRate {
if ((endEffective != null) && endEffective.before(_time))
return false;
}
if ((timeOfDayStart == 0) && (timeOfDayEnd == 0))
return true;
Calendar midnight = DateUtils.getMidnightBeforeCal(_time, _tz);
int timeOfDay = (int)((_time.getTime() - midnight.getTimeInMillis()) / 1000);
if ((timeOfDayStart > 0) && (timeOfDay < timeOfDayStart))
return false;
return (timeOfDayEnd == 0) || (timeOfDay < timeOfDayEnd);
return true;
}
public double apply(double _kWh) {
@@ -166,18 +158,17 @@ public class BillingRate {
if (this == _o) return true;
if (_o == null || getClass() != _o.getClass()) return false;
BillingRate that = (BillingRate) _o;
return meter == that.meter && dayBillingCycleStart == that.dayBillingCycleStart && Double.compare(that.rate, rate) == 0 && timeOfDayStart == that.timeOfDayStart && timeOfDayEnd == that.timeOfDayEnd && Double.compare(that.monthKWhStart, monthKWhStart) == 0 && Double.compare(that.monthKWhEnd, monthKWhEnd) == 0 && recursAnnually == that.recursAnnually && flow == that.flow && currency == that.currency && Objects.equals(beginEffective, that.beginEffective) && Objects.equals(endEffective, that.endEffective);
return meter == that.meter && Double.compare(that.rate, rate) == 0 && timeOfDayStart == that.timeOfDayStart && timeOfDayEnd == that.timeOfDayEnd && Double.compare(that.monthKWhStart, monthKWhStart) == 0 && Double.compare(that.monthKWhEnd, monthKWhEnd) == 0 && recursAnnually == that.recursAnnually && flow == that.flow && currency == that.currency && Objects.equals(beginEffective, that.beginEffective) && Objects.equals(endEffective, that.endEffective);
}
@Override
public int hashCode() {
return Objects.hash(meter, dayBillingCycleStart, flow, rate, currency, timeOfDayStart, timeOfDayEnd, monthKWhStart, monthKWhEnd, beginEffective, endEffective, recursAnnually);
return Objects.hash(meter, flow, rate, currency, timeOfDayStart, timeOfDayEnd, monthKWhStart, monthKWhEnd, beginEffective, endEffective, recursAnnually);
}
public BillingRate duplicate() {
BillingRate r = new BillingRate();
r.setMeter(meter);
r.setDayBillingCycleStart(dayBillingCycleStart);
r.setFlow(flow);
r.setRate(rate);
r.setCurrency(currency);

View File

@@ -20,6 +20,7 @@ public class BreakerConfig implements IIdentical<BreakerConfig> {
private List<BreakerHub> breakerHubs;
private List<BreakerGroup> breakerGroups;
private List<BillingRate> billingRates;
private List<BillingPlan> billingPlans;
private int version;
public BreakerConfig() {
@@ -69,6 +70,14 @@ public class BreakerConfig implements IIdentical<BreakerConfig> {
breakerGroups = _breakerGroups;
}
public List<BillingPlan> getBillingPlans() {
return billingPlans;
}
public void setBillingPlans(List<BillingPlan> _billingPlans) {
billingPlans = _billingPlans;
}
public List<BillingRate> getBillingRates() {
return billingRates;
}
@@ -85,6 +94,10 @@ public class BreakerConfig implements IIdentical<BreakerConfig> {
version = _version;
}
public BreakerGroup getRootGroup() {
return CollectionUtils.getFirst(breakerGroups);
}
public List<Breaker> getAllBreakers() {
List<Breaker> allBreakers = new ArrayList<>();
for (BreakerGroup g : CollectionUtils.makeNotNull(breakerGroups)) {
@@ -190,13 +203,13 @@ public class BreakerConfig implements IIdentical<BreakerConfig> {
if (this == _o) return true;
if (_o == null || getClass() != _o.getClass()) return false;
BreakerConfig that = (BreakerConfig) _o;
return accountId == that.accountId && CollectionUtils.isEqual(meters, that.meters) && CollectionUtils.isEqual(panels, that.panels) && CollectionUtils.isEqual(breakerHubs, that.breakerHubs) && CollectionUtils.isEqual(breakerGroups, that.breakerGroups) && CollectionUtils.isEqual(billingRates, that.billingRates);
return accountId == that.accountId && CollectionUtils.isEqual(meters, that.meters) && CollectionUtils.isEqual(panels, that.panels) && CollectionUtils.isEqual(breakerHubs, that.breakerHubs) && CollectionUtils.isEqual(breakerGroups, that.breakerGroups) && CollectionUtils.isEqual(billingPlans, that.billingPlans);
}
@Override
public boolean isIdentical(BreakerConfig _o) {
if (this == _o) return true;
return accountId == _o.accountId && CollectionUtils.isIdentical(meters, _o.meters) && CollectionUtils.isIdentical(panels, _o.panels) && CollectionUtils.isIdentical(breakerHubs, _o.breakerHubs) && CollectionUtils.isIdentical(breakerGroups, _o.breakerGroups) && CollectionUtils.isEqual(billingRates, _o.billingRates);
return accountId == _o.accountId && CollectionUtils.isIdentical(meters, _o.meters) && CollectionUtils.isIdentical(panels, _o.panels) && CollectionUtils.isIdentical(breakerHubs, _o.breakerHubs) && CollectionUtils.isIdentical(breakerGroups, _o.breakerGroups) && CollectionUtils.isEqual(billingPlans, _o.billingPlans);
}
@Override

View File

@@ -8,7 +8,9 @@ import com.lanternsoftware.util.dao.annotations.DBSerializable;
import com.lanternsoftware.util.dao.annotations.PrimaryKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -170,6 +172,16 @@ public class BreakerGroup implements IIdentical<BreakerGroup> {
return null;
}
public Map<String, Integer> mapToMeters() {
Map<String, Integer> groups = new HashMap<>();
for (BreakerGroup group : getAllBreakerGroups()) {
Breaker b = CollectionUtils.getFirst(group.getBreakers());
if (b != null)
groups.put(group.getId(), b.getMeter());
}
return groups;
}
public boolean removeInvalidGroups(Set<Integer> _validPanels) {
if (subGroups != null)
subGroups.removeIf(_g->!_g.removeInvalidGroups(_validPanels));

View File

@@ -2,524 +2,220 @@ package com.lanternsoftware.datamodel.currentmonitor;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import com.lanternsoftware.util.mutable.MutableDouble;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
@DBSerializable(autogen = false)
public class BreakerGroupEnergy {
private int accountId;
private String groupId;
private String groupName;
private EnergyBlockViewMode viewMode;
private Date start;
private List<BreakerGroupEnergy> subGroups;
private List<EnergyBlock> energyBlocks;
private double toGrid;
private double fromGrid;
private double peakToGrid;
private double peakFromGrid;
private double peakConsumption;
private double peakProduction;
private TimeZone timezone;
private int accountId;
private String groupId;
private String groupName;
private EnergyViewMode viewMode;
private Date start;
private List<BreakerGroupEnergy> subGroups;
private List<EnergyBlock> energyBlocks;
private double toGrid;
private double fromGrid;
private double peakToGrid;
private double peakFromGrid;
private double peakConsumption;
private double peakProduction;
private TimeZone timezone;
public BreakerGroupEnergy() {
}
public BreakerGroupEnergy() {
}
public BreakerGroupEnergy(BreakerGroup _group, List<HubPowerMinute> _power, EnergyBlockViewMode _viewMode, Date _start, BreakerGroupSummary _month, List<BillingRate> _rates, TimeZone _timezone) {
groupId = _group.getId();
groupName = _group.getName();
viewMode = _viewMode;
start = _start;
accountId = _group.getAccountId();
timezone = _timezone;
subGroups = CollectionUtils.transform(_group.getSubGroups(), _g -> new BreakerGroupEnergy(_g, null, _viewMode, _start, _month, _rates, timezone));
addEnergy(_group, _power, _month, _rates);
}
public BreakerGroupEnergy(EnergySummary _summary, List<BillingRate> _rates, Map<String, Integer> _breakerGroupMeters) {
groupId = _summary.getGroupId();
groupName = _summary.getGroupName();
viewMode = _summary.getViewMode();
start = _summary.getStart();
accountId = _summary.getAccountId();
timezone = _summary.getTimeZone();
peakToGrid = _summary.getPeakToGrid();
peakFromGrid = _summary.getPeakFromGrid();
peakConsumption = _summary.getPeakConsumption();
peakProduction = _summary.getPeakProduction();
Date readTime = start;
if (_summary.getEnergy() != null) {
for (float joules : _summary.getEnergy()) {
addEnergy(readTime, joules);
readTime = viewMode.incrementBlock(readTime, timezone);
}
}
readTime = start;
ChargeSummary charges = _summary.toChargeSummary(0, _rates, _breakerGroupMeters, new MutableDouble(0.0));
for (double charge : charges.chargeBlocks(CollectionUtils.asHashSet(groupId), GridFlow.BOTH)) {
addCharge(readTime, charge);
readTime = viewMode.incrementBlock(readTime, timezone);
}
subGroups = CollectionUtils.transform(_summary.getSubGroups(), _e->new BreakerGroupEnergy(_e, _rates, _breakerGroupMeters));
}
public void addEnergy(BreakerGroup _group, List<HubPowerMinute> _hubPower, BreakerGroupSummary _month, List<BillingRate> _rates) {
Map<String, Breaker> breakers = CollectionUtils.transformToMap(_group.getAllBreakers(), Breaker::getKey);
Map<String, BreakerGroup> breakerKeyToGroup = new HashMap<>();
for (BreakerGroup group : _group.getAllBreakerGroups()) {
for (Breaker b : group.getAllBreakers()) {
breakerKeyToGroup.put(b.getKey(), group);
}
}
addEnergy(breakers, breakerKeyToGroup, _hubPower, _month, _rates);
}
private void addEnergy(Date _readTime, double _joules) {
EnergyBlock block = getBlock(_readTime);
if (block != null)
block.addJoules(_joules);
}
public void addEnergy(Map<String, Breaker> _breakers, Map<String, BreakerGroup> _breakerKeyToGroup, List<HubPowerMinute> _hubPower, BreakerGroupSummary _month, List<BillingRate> _rates) {
if (CollectionUtils.isEmpty(_hubPower) || CollectionUtils.anyQualify(_hubPower, _p->_p.getAccountId() != accountId))
return;
_hubPower.sort(Comparator.comparing(HubPowerMinute::getMinute));
for (Date minute : CollectionUtils.transformToSet(_hubPower, HubPowerMinute::getMinuteAsDate)) {
resetEnergy(minute);
}
int idx;
Map<Integer, Map<Integer, MeterMinute>> minutes = new TreeMap<>();
for (HubPowerMinute hubPower : _hubPower) {
Date minute = hubPower.getMinuteAsDate();
for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) {
Breaker b = _breakers.get(breaker.breakerKey());
if (b == null)
continue;
BreakerGroup group = _breakerKeyToGroup.get(breaker.breakerKey());
if (group == null)
continue;
MeterMinute meter = minutes.computeIfAbsent(hubPower.getMinute(), _p->new TreeMap<>()).computeIfAbsent(b.getMeter(), _m->new MeterMinute(b.getMeter(), hubPower.getMinuteAsDate()));
idx = 0;
EnergyBlock block = getBlock(group.getId(), minute);
if (block != null) {
for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) {
if (idx >= 60)
break;
if (power > 0)
meter.usage[idx] += power;
else
meter.solar[idx] -= power;
block.addJoules(power);
idx++;
}
}
}
}
double monthFromGrid = _month == null ? 0.0 : _month.getFromGrid();
double secondFromGrid;
for (MeterMinute minute : CollectionUtils.aggregate(minutes.values(), Map::values)) {
double monthkWh = monthFromGrid/3600000;
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, minute.getMeter(), monthkWh, minute.getMinute(), timezone));
for (int i = 0; i < 60; 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;
if (secondFromGrid > 0) {
fromGrid += secondFromGrid;
if (secondFromGrid > peakFromGrid)
peakFromGrid = secondFromGrid;
for (BillingRate rate : consumptionRates) {
minute.charges[i] += rate.apply(secondFromGrid/3600000);
}
}
else {
secondFromGrid = -secondFromGrid;
toGrid += secondFromGrid;
if (secondFromGrid > peakToGrid)
peakToGrid = secondFromGrid;
for (BillingRate rate : productionRates) {
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) {
Date minute = hubPower.getMinuteAsDate();
for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) {
Breaker b = _breakers.get(breaker.breakerKey());
if (b == null)
continue;
BreakerGroup group = _breakerKeyToGroup.get(breaker.breakerKey());
if (group == null)
continue;
MeterMinute meter = minutes.get(hubPower.getMinute()).get(b.getMeter());
idx = 0;
double charge = 0.0;
for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) {
if (b.getPolarity() == BreakerPolarity.SOLAR) {
if (meter.charges[idx] < 0.0)
charge -= meter.charges[idx] * (power/meter.solar[idx]);
}
else if (meter.charges[idx] > 0.0)
charge += meter.charges[idx] * (power/meter.usage[idx]);
idx++;
}
addCharge(group.getId(), minute, charge);
}
}
}
private void addCharge(Date _readTime, double _charge) {
EnergyBlock block = getBlock(_readTime);
if (block != null)
block.addCharge(_charge);
}
public void resetEnergy(Date _readTime) {
EnergyBlock block = getBlock(_readTime, false);
if (block != null)
block.setJoules(0);
for (BreakerGroupEnergy subGroup : CollectionUtils.makeNotNull(subGroups)) {
subGroup.resetEnergy(_readTime);
}
}
private EnergyBlock getBlock(Date _readTime) {
int size = CollectionUtils.size(energyBlocks);
int idx = viewMode.blockIndex(start, _readTime, timezone);
if (idx >= size) {
if (energyBlocks == null)
energyBlocks = new ArrayList<>(viewMode.initBlockCount());
LinkedList<EnergyBlock> newBlocks = new LinkedList<>();
Date end = viewMode.toBlockEnd(_readTime, timezone);
while (idx >= size) {
Date start = viewMode.decrementBlock(end, timezone);
newBlocks.add(new EnergyBlock(start, end, 0));
end = start;
size++;
}
Iterator<EnergyBlock> iter = newBlocks.descendingIterator();
while (iter.hasNext()) {
energyBlocks.add(iter.next());
}
}
return CollectionUtils.get(energyBlocks, idx);
}
private EnergyBlock getBlock(String _groupId, Date _readTime) {
if (NullUtils.isEqual(groupId, _groupId))
return getBlock(_readTime);
else {
for (BreakerGroupEnergy subGroup : CollectionUtils.makeNotNull(subGroups)) {
EnergyBlock block = subGroup.getBlock(_groupId, _readTime);
if (block != null)
return block;
}
return null;
}
}
public String getId() {
return toId(accountId, groupId, viewMode, start);
}
private void addEnergy(String _groupId, Date _readTime, double _joules) {
EnergyBlock block = getBlock(_groupId, _readTime);
if (block != null)
block.addJoules(_joules);
}
public static String toId(int _accountId, String _groupId, EnergyViewMode _viewMode, Date _start) {
return _accountId + "-" + _groupId + "-" + DaoSerializer.toEnumName(_viewMode) + "-" + _start.getTime();
}
private void addCharge(String _groupId, Date _readTime, double _charge) {
EnergyBlock block = getBlock(_groupId, _readTime);
if (block != null)
block.addCharge(_charge);
}
public int getAccountId() {
return accountId;
}
public static BreakerGroupEnergy summary(BreakerGroup _group, Map<String, List<BreakerGroupSummary>> _energies, EnergyBlockViewMode _viewMode, Date _start, TimeZone _tz) {
BreakerGroupEnergy energy = new BreakerGroupEnergy();
energy.setGroupId(_group.getId());
energy.setGroupName(_group.getName());
energy.setAccountId(_group.getAccountId());
energy.setViewMode(_viewMode);
energy.setStart(_start);
energy.setTimeZone(_tz);
energy.setSubGroups(CollectionUtils.transform(_group.getSubGroups(), _g -> BreakerGroupEnergy.summary(_g, _energies, _viewMode, _start, _tz)));
for (BreakerGroupSummary curEnergy : CollectionUtils.makeNotNull(_energies.get(_group.getId()))) {
EnergyBlock block = energy.getBlock(curEnergy.getStart());
block.addJoules(curEnergy.getJoules());
block.addCharge(curEnergy.getCharge());
energy.setToGrid(energy.getToGrid()+curEnergy.getToGrid());
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;
}
public void setAccountId(int _accountId) {
accountId = _accountId;
}
private EnergyBlock getBlock(Date _readTime) {
return getBlock(_readTime, true);
}
public String getGroupId() {
return groupId;
}
private EnergyBlock getBlock(Date _readTime, boolean _add) {
int size = CollectionUtils.size(energyBlocks);
int idx = viewMode.blockIndex(start, _readTime, timezone);
if (_add && (idx >= size)) {
if (energyBlocks == null)
energyBlocks = new ArrayList<>(viewMode.initBlockCount());
LinkedList<EnergyBlock> newBlocks = new LinkedList<>();
Date end = viewMode.toBlockEnd(_readTime, timezone);
while (idx >= size) {
Date start = viewMode.decrementBlock(end, timezone);
newBlocks.add(new EnergyBlock(start, end, 0));
end = start;
size++;
}
Iterator<EnergyBlock> iter = newBlocks.descendingIterator();
while (iter.hasNext()) {
energyBlocks.add(iter.next());
}
}
return CollectionUtils.get(energyBlocks, idx);
}
public void setGroupId(String _groupId) {
groupId = _groupId;
}
public String getId() {
return toId(accountId, groupId, viewMode, start);
}
public String getGroupName() {
return groupName;
}
public static String toId(int _accountId, String _groupId, EnergyBlockViewMode _viewMode, Date _start) {
return _accountId + "-" + _groupId + "-" + DaoSerializer.toEnumName(_viewMode) + "-" + _start.getTime();
}
public void setGroupName(String _groupName) {
groupName = _groupName;
}
public int getAccountId() {
return accountId;
}
public BreakerGroupEnergy getSubGroup(String _groupId) {
return CollectionUtils.filterOne(subGroups, _g -> _groupId.equals(_g.getGroupId()));
}
public void setAccountId(int _accountId) {
accountId = _accountId;
}
public List<BreakerGroupEnergy> getSubGroups() {
return subGroups;
}
public String getGroupId() {
return groupId;
}
public EnergyViewMode getViewMode() {
return viewMode;
}
public void setGroupId(String _groupId) {
groupId = _groupId;
}
public void setViewMode(EnergyViewMode _viewMode) {
viewMode = _viewMode;
}
public String getGroupName() {
return groupName;
}
public Date getStart() {
return start;
}
public void setGroupName(String _groupName) {
groupName = _groupName;
}
public void setStart(Date _start) {
start = _start;
}
public BreakerGroupEnergy getSubGroup(String _groupId) {
return CollectionUtils.filterOne(subGroups, _g->_groupId.equals(_g.getGroupId()));
}
public void setSubGroups(List<BreakerGroupEnergy> _subGroups) {
subGroups = _subGroups;
}
public List<BreakerGroupEnergy> getSubGroups() {
return subGroups;
}
public List<EnergyBlock> getEnergyBlocks() {
return energyBlocks;
}
public EnergyBlockViewMode getViewMode() {
return viewMode;
}
public void setEnergyBlocks(List<EnergyBlock> _energyBlocks) {
energyBlocks = _energyBlocks;
}
public void setViewMode(EnergyBlockViewMode _viewMode) {
viewMode = _viewMode;
}
public double getToGrid() {
return toGrid;
}
public Date getStart() {
return start;
}
public void setToGrid(double _toGrid) {
toGrid = _toGrid;
}
public void setStart(Date _start) {
start = _start;
}
public double getFromGrid() {
return fromGrid;
}
public void setSubGroups(List<BreakerGroupEnergy> _subGroups) {
subGroups = _subGroups;
}
public void setFromGrid(double _fromGrid) {
fromGrid = _fromGrid;
}
public List<EnergyBlock> getEnergyBlocks() {
return energyBlocks;
}
public double getPeakToGrid() {
return peakToGrid;
}
public void setEnergyBlocks(List<EnergyBlock> _energyBlocks) {
energyBlocks = _energyBlocks;
}
public void setPeakToGrid(double _peakToGrid) {
peakToGrid = _peakToGrid;
}
public double getToGrid() {
return toGrid;
}
public double getPeakFromGrid() {
return peakFromGrid;
}
public void setToGrid(double _toGrid) {
toGrid = _toGrid;
}
public void setPeakFromGrid(double _peakFromGrid) {
peakFromGrid = _peakFromGrid;
}
public double getFromGrid() {
return fromGrid;
}
public double getPeakConsumption() {
return peakConsumption;
}
public void setFromGrid(double _fromGrid) {
fromGrid = _fromGrid;
}
public void setPeakConsumption(double _peakConsumption) {
peakConsumption = _peakConsumption;
}
public double getPeakToGrid() {
return peakToGrid;
}
public double getPeakProduction() {
return peakProduction;
}
public void setPeakToGrid(double _peakToGrid) {
peakToGrid = _peakToGrid;
}
public void setPeakProduction(double _peakProduction) {
peakProduction = _peakProduction;
}
public double getPeakFromGrid() {
return peakFromGrid;
}
public TimeZone getTimeZone() {
return timezone;
}
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() {
return timezone;
}
public void setTimeZone(TimeZone _timezone) {
timezone = _timezone;
}
public double wattHours() {
return joules() / 3600;
}
public double wattHours(Set<String> _selectedBreakers) {
return joules(_selectedBreakers) / 3600;
}
public double joules() {
return joules(null);
}
public double joules(Set<String> _selectedBreakers) {
return joules(_selectedBreakers, true);
}
public double joules(Set<String> _selectedBreakers, boolean _includeSubgroups) {
double joules = 0.0;
if (_includeSubgroups) {
for (BreakerGroupEnergy group : CollectionUtils.makeNotNull(subGroups)) {
joules += group.joules(_selectedBreakers);
}
}
if ((energyBlocks != null) && ((_selectedBreakers == null) || _selectedBreakers.contains(getGroupId()))) {
for (EnergyBlock energy : energyBlocks) {
joules += energy.getJoules();
}
}
return joules;
}
public double charge() {
return charge(null);
}
public double charge(Set<String> _selectedBreakers) {
return charge(_selectedBreakers, true);
}
public double charge(Set<String> _selectedBreakers, GridFlow _mode) {
return charge(_selectedBreakers, true, _mode);
}
public double charge(Set<String> _selectedBreakers, boolean _includeSubgroups) {
return charge(_selectedBreakers, _includeSubgroups, null);
}
public double charge(Set<String> _selectedBreakers, boolean _includeSubgroups, GridFlow _mode) {
double charge = 0.0;
if (_includeSubgroups) {
for (BreakerGroupEnergy group : CollectionUtils.makeNotNull(subGroups)) {
charge += group.charge(_selectedBreakers, true, _mode);
}
}
if ((energyBlocks != null) && ((_selectedBreakers == null) || _selectedBreakers.contains(getGroupId()))) {
for (EnergyBlock energy : energyBlocks) {
if ((_mode == null) || ((_mode == GridFlow.TO) && energy.getCharge() < 0.0) || (_mode == GridFlow.FROM && energy.getCharge() > 0.0))
charge += energy.getCharge();
}
}
return charge;
}
public List<BreakerGroupEnergy> getAllGroups() {
Map<String, BreakerGroupEnergy> groups = new TreeMap<>();
getAllGroups(groups);
return new ArrayList<>(groups.values());
}
public void getAllGroups(Map<String, BreakerGroupEnergy> _groups) {
_groups.put(getGroupId(), this);
for (BreakerGroupEnergy group : CollectionUtils.makeNotNull(subGroups)) {
group.getAllGroups(_groups);
}
}
public List<EnergyBlock> getAllEnergyBlocks() {
return getAllEnergyBlocks(null);
}
public List<EnergyBlock> getAllEnergyBlocks(Set<String> _selectedGroups) {
return getAllEnergyBlocks(_selectedGroups, EnergyBlockType.ANY);
}
public List<EnergyBlock> getAllEnergyBlocks(Set<String> _selectedGroups, EnergyBlockType _type) {
Map<Long, EnergyBlock> blocks = new TreeMap<>();
getAllEnergyBlocks(_selectedGroups, blocks, _type);
return new ArrayList<>(blocks.values());
}
private void getAllEnergyBlocks(Set<String> _selectedGroups, Map<Long, EnergyBlock> _energyBlocks, EnergyBlockType _type) {
if ((energyBlocks != null) && ((_selectedGroups == null) || _selectedGroups.contains(getGroupId()))) {
for (EnergyBlock block : energyBlocks) {
if ((_type == EnergyBlockType.ANY) || ((_type == EnergyBlockType.POSITIVE) && block.getJoules() >= 0.0) || ((_type == EnergyBlockType.NEGATIVE) && block.getJoules() <= 0.0)) {
EnergyBlock b = _energyBlocks.get(block.getStart().getTime());
if (b == null) {
b = new EnergyBlock(block.getStart(), block.getEnd(), block.getJoules());
_energyBlocks.put(block.getStart().getTime(), b);
} else
b.addJoules(block.getJoules());
b.addCharge(block.getCharge());
}
}
}
for (BreakerGroupEnergy group : CollectionUtils.makeNotNull(subGroups)) {
group.getAllEnergyBlocks(_selectedGroups, _energyBlocks, _type);
}
}
private static class MeterMinute {
private final int meter;
private final Date minute;
public MeterMinute(int _meter, Date _minute) {
meter = _meter;
minute = _minute;
}
public int getMeter() {
return meter;
}
public Date getMinute() {
return minute;
}
public double[] usage = new double[60];
public double[] solar = new double[60];
public double[] charges = new double[60];
}
public void setTimeZone(TimeZone _timezone) {
timezone = _timezone;
}
}

View File

@@ -0,0 +1,285 @@
package com.lanternsoftware.datamodel.currentmonitor;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
@DBSerializable(autogen = false)
public class ChargeSummary {
private int accountId;
private int planId;
private String groupId;
private String groupName;
private EnergyViewMode viewMode;
private Date start;
private List<ChargeSummary> subGroups;
private TimeZone timezone;
private double[] charges;
private double totalUsageJoules;
private double totalSolarJoules;
private double fromGridJoules;
private double toGridJoules;
private double peakToGrid;
private double peakFromGrid;
private double peakConsumption;
private double peakProduction;
public ChargeSummary() {
}
public ChargeSummary(int _accountId, int _planId, String _groupId, String _groupName, EnergyViewMode _viewMode, Date _start, TimeZone _timezone) {
accountId = _accountId;
planId = _planId;
groupId = _groupId;
groupName = _groupName;
viewMode = _viewMode;
start = _start;
timezone = _timezone;
}
public ChargeSummary(BreakerGroup _group, BillingPlan _plan, Map<String, List<ChargeTotal>> _charges, EnergyViewMode _viewMode, Date _start, TimeZone _timezone) {
this(_group.getAccountId(), _plan.getPlanId(), _group.getId(), _group.getName(), _viewMode, _start, _timezone);
subGroups = CollectionUtils.transform(_group.getSubGroups(), _g -> new ChargeSummary(_g, _plan, _charges, _viewMode, _start, _timezone));
if (_viewMode == EnergyViewMode.MONTH) {
charges = new double[DateUtils.getDaysBetween(_start, _plan.getBillingCycleEnd(_start, _timezone), _timezone)];
}
else
charges = new double[_viewMode.blockCount(_start, _timezone)];
for (ChargeTotal charge : CollectionUtils.makeNotNull(_charges.get(_group.getId()))) {
int idx;
if (_viewMode == EnergyViewMode.MONTH)
idx = DateUtils.getDaysBetween(_start, charge.getStart(), _timezone);
else
idx = viewMode.blockIndex(start, charge.getStart(), timezone);
if (idx < charges.length)
charges[idx] += charge.getCharge();
}
totalUsageJoules = CollectionUtils.sum(CollectionUtils.transform(_charges.get(_group.getId()), ChargeTotal::getTotalUsageJoules));
totalSolarJoules = CollectionUtils.sum(CollectionUtils.transform(_charges.get(_group.getId()), ChargeTotal::getTotalSolarJoules));
fromGridJoules = CollectionUtils.sum(CollectionUtils.transform(_charges.get(_group.getId()), ChargeTotal::getFromGridJoules));
toGridJoules = CollectionUtils.sum(CollectionUtils.transform(_charges.get(_group.getId()), ChargeTotal::getToGridJoules));
peakToGrid = DaoSerializer.toDouble(CollectionUtils.getLargest(CollectionUtils.transform(_charges.get(_group.getId()), ChargeTotal::getPeakToGrid)));
peakFromGrid = DaoSerializer.toDouble(CollectionUtils.getLargest(CollectionUtils.transform(_charges.get(_group.getId()), ChargeTotal::getPeakFromGrid)));
peakConsumption = DaoSerializer.toDouble(CollectionUtils.getLargest(CollectionUtils.transform(_charges.get(_group.getId()), ChargeTotal::getPeakConsumption)));
peakProduction = DaoSerializer.toDouble(CollectionUtils.getLargest(CollectionUtils.transform(_charges.get(_group.getId()), ChargeTotal::getPeakProduction)));
}
public String getId() {
return toId(accountId, planId, groupId, viewMode, start);
}
public int getAccountId() {
return accountId;
}
public void setAccountId(int _accountId) {
accountId = _accountId;
}
public int getPlanId() {
return planId;
}
public void setPlanId(int _planId) {
planId = _planId;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String _groupId) {
groupId = _groupId;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String _groupName) {
groupName = _groupName;
}
public EnergyViewMode getViewMode() {
return viewMode;
}
public void setViewMode(EnergyViewMode _viewMode) {
viewMode = _viewMode;
}
public Date getStart() {
return start;
}
public void setStart(Date _start) {
start = _start;
}
public List<ChargeSummary> getSubGroups() {
return subGroups;
}
public void setSubGroups(List<ChargeSummary> _subGroups) {
subGroups = _subGroups;
}
public double[] getCharges() {
return charges;
}
public void setCharges(double[] _charges) {
charges = _charges;
}
public TimeZone getTimezone() {
return timezone;
}
public void setTimezone(TimeZone _timezone) {
timezone = _timezone;
}
public double getTotalUsageJoules() {
return totalUsageJoules;
}
public void setTotalUsageJoules(double _totalUsageJoules) {
totalUsageJoules = _totalUsageJoules;
}
public double getTotalSolarJoules() {
return totalSolarJoules;
}
public void setTotalSolarJoules(double _totalSolarJoules) {
totalSolarJoules = _totalSolarJoules;
}
public double getFromGridJoules() {
return fromGridJoules;
}
public void setFromGridJoules(double _fromGridJoules) {
fromGridJoules = _fromGridJoules;
}
public double getToGridJoules() {
return toGridJoules;
}
public void setToGridJoules(double _toGridJoules) {
toGridJoules = _toGridJoules;
}
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 double charge() {
return charge(null);
}
public double charge(Set<String> _selectedBreakers) {
return charge(_selectedBreakers, true);
}
public double charge(Set<String> _selectedBreakers, GridFlow _mode) {
return charge(_selectedBreakers, true, _mode);
}
public double charge(Set<String> _selectedBreakers, boolean _includeSubgroups) {
return charge(_selectedBreakers, _includeSubgroups, null);
}
public double charge(Set<String> _selectedBreakers, boolean _includeSubgroups, GridFlow _mode) {
double charge = 0.0;
if (_includeSubgroups) {
for (ChargeSummary group : CollectionUtils.makeNotNull(subGroups)) {
charge += group.charge(_selectedBreakers, true, _mode);
}
}
if ((charges != null) && ((_selectedBreakers == null) || _selectedBreakers.contains(getGroupId()))) {
for (double c : charges) {
if ((_mode == null) || ((_mode == GridFlow.TO) && c < 0.0) || (_mode == GridFlow.FROM && c > 0.0))
charge += c;
}
}
return charge;
}
public float[] chargeBlocks() {
return chargeBlocks(null);
}
public float[] chargeBlocks(Set<String> _selectedGroups) {
return chargeBlocks(_selectedGroups, GridFlow.BOTH);
}
public float[] chargeBlocks(Set<String> _selectedGroups, GridFlow _flow) {
float[] blocks = new float[blockCount()];
chargeBlocks(_selectedGroups, blocks, _flow);
return blocks;
}
private void chargeBlocks(Set<String> _selectedGroups, float[] _chargeBlocks, GridFlow _flow) {
if ((charges != null) && ((_selectedGroups == null) || _selectedGroups.contains(getGroupId()))) {
for (int i = 0; i < charges.length; i++) {
if ((_flow == GridFlow.BOTH) || ((_flow == GridFlow.FROM) && charges[i] >= 0.0) || ((_flow == GridFlow.TO) && charges[i] <= 0.0))
_chargeBlocks[i] += charges[i];
}
}
for (ChargeSummary group : CollectionUtils.makeNotNull(subGroups)) {
group.chargeBlocks(_selectedGroups, _chargeBlocks, _flow);
}
}
private int blockCount() {
int blocks = 0;
for (ChargeSummary s : CollectionUtils.makeNotNull(subGroups)) {
blocks = Math.max(blocks, s.blockCount());
}
if ((charges != null) && (charges.length > blocks))
return charges.length;
return blocks;
}
public static String toId(int _accountId, int _planId, String _groupId, EnergyViewMode _viewMode, Date _start) {
return _accountId + "-" + _planId + "-" + _groupId + "-" + DaoSerializer.toEnumName(_viewMode) + "-" + _start.getTime();
}
}

View File

@@ -0,0 +1,199 @@
package com.lanternsoftware.datamodel.currentmonitor;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@DBSerializable(autogen = false)
public class ChargeTotal {
private int accountId;
private String groupId;
private int planId;
private EnergyViewMode viewMode;
private Date start;
private double charge;
private List<ChargeTotal> subGroups;
private double totalUsageJoules;
private double totalSolarJoules;
private double fromGridJoules;
private double toGridJoules;
private double peakToGrid;
private double peakFromGrid;
private double peakConsumption;
private double peakProduction;
public ChargeTotal() {
}
public ChargeTotal(ChargeSummary _summary) {
accountId = _summary.getAccountId();
groupId = _summary.getGroupId();
planId = _summary.getPlanId();
viewMode = _summary.getViewMode();
start = _summary.getStart();
if (_summary.getCharges() != null) {
for (double c : _summary.getCharges()) {
charge += c;
}
}
subGroups = CollectionUtils.transform(_summary.getSubGroups(), ChargeTotal::new);
totalUsageJoules = _summary.getTotalUsageJoules();
totalSolarJoules = _summary.getTotalSolarJoules();
fromGridJoules = _summary.getFromGridJoules();
toGridJoules = _summary.getToGridJoules();
peakToGrid = _summary.getPeakToGrid();
peakFromGrid = _summary.getPeakFromGrid();
peakConsumption = _summary.getPeakConsumption();
peakProduction = _summary.getPeakProduction();
}
public String getId() {
return ChargeSummary.toId(accountId, planId, groupId, viewMode, start);
}
public int getAccountId() {
return accountId;
}
public void setAccountId(int _accountId) {
accountId = _accountId;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String _groupId) {
groupId = _groupId;
}
public int getPlanId() {
return planId;
}
public void setPlanId(int _planId) {
planId = _planId;
}
public EnergyViewMode getViewMode() {
return viewMode;
}
public void setViewMode(EnergyViewMode _viewMode) {
viewMode = _viewMode;
}
public Date getStart() {
return start;
}
public void setStart(Date _start) {
start = _start;
}
public double getCharge() {
return charge;
}
public void setCharge(double _charge) {
charge = _charge;
}
public List<ChargeTotal> getSubGroups() {
return subGroups;
}
public void setSubGroups(List<ChargeTotal> _subGroups) {
subGroups = _subGroups;
}
public double getTotalUsageJoules() {
return totalUsageJoules;
}
public void setTotalUsageJoules(double _totalUsageJoules) {
totalUsageJoules = _totalUsageJoules;
}
public double getTotalSolarJoules() {
return totalSolarJoules;
}
public void setTotalSolarJoules(double _totalSolarJoules) {
totalSolarJoules = _totalSolarJoules;
}
public double getFromGridJoules() {
return fromGridJoules;
}
public void setFromGridJoules(double _fromGridJoules) {
fromGridJoules = _fromGridJoules;
}
public double getToGridJoules() {
return toGridJoules;
}
public void setToGridJoules(double _toGridJoules) {
toGridJoules = _toGridJoules;
}
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 double chargeTotal() {
double retCharge = charge;
for (ChargeTotal t : CollectionUtils.makeNotNull(subGroups)) {
retCharge += t.chargeTotal();
}
return retCharge;
}
public List<ChargeTotal> flatten() {
List<ChargeTotal> totals = new ArrayList<>();
flatten(totals);
return totals;
}
private void flatten(List<ChargeTotal> _totals) {
_totals.add(this);
for (ChargeTotal total : CollectionUtils.makeNotNull(subGroups)) {
total.flatten(_totals);
}
}
}

View File

@@ -56,6 +56,7 @@ public class EnergyBlock {
public void setCharge(double _charge) {
charge = _charge;
}
public void addCharge(double _charge) {
charge += _charge;
}
@@ -63,10 +64,4 @@ public class EnergyBlock {
public double wattHours() {
return joules / 3600;
}
public double getAveragePower() {
if ((end == null) || (start == null))
return 0;
return 1000*joules/(end.getTime()-start.getTime());
}
}

View File

@@ -0,0 +1,503 @@
package com.lanternsoftware.datamodel.currentmonitor;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import com.lanternsoftware.util.mutable.MutableDouble;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
@DBSerializable(autogen = false)
public class EnergySummary {
private int accountId;
private String groupId;
private String groupName;
private EnergyViewMode viewMode;
private Date start;
private List<EnergySummary> subGroups;
private float[] energy;
private float[] gridEnergy;
private double peakToGrid;
private double peakFromGrid;
private double peakConsumption;
private double peakProduction;
private TimeZone timezone;
public EnergySummary() {
}
public EnergySummary(BreakerGroup _group, List<HubPowerMinute> _power, EnergyViewMode _viewMode, Date _start, TimeZone _timezone) {
groupId = _group.getId();
groupName = _group.getName();
viewMode = _viewMode;
start = _start;
accountId = _group.getAccountId();
timezone = _timezone;
subGroups = CollectionUtils.transform(_group.getSubGroups(), _g -> new EnergySummary(_g, null, _viewMode, _start, timezone));
if (_power != null)
addEnergy(_group, _power);
}
public void addEnergy(BreakerGroup _group, List<HubPowerMinute> _hubPower) {
Map<String, Breaker> breakers = CollectionUtils.transformToMap(_group.getAllBreakers(), Breaker::getKey);
Map<String, BreakerGroup> breakerKeyToGroup = new HashMap<>();
for (BreakerGroup group : _group.getAllBreakerGroups()) {
for (Breaker b : CollectionUtils.makeNotNull(group.getBreakers())) {
breakerKeyToGroup.put(b.getKey(), group);
}
}
addEnergy(breakers, breakerKeyToGroup, _hubPower);
}
public void addEnergy(Map<String, Breaker> _breakers, Map<String, BreakerGroup> _breakerKeyToGroup, List<HubPowerMinute> _hubPower) {
if (CollectionUtils.isEmpty(_hubPower) || CollectionUtils.anyQualify(_hubPower, _p -> _p.getAccountId() != accountId))
return;
_hubPower.sort(Comparator.comparing(HubPowerMinute::getMinute));
for (Date minute : CollectionUtils.transformToSet(_hubPower, HubPowerMinute::getMinuteAsDate)) {
resetEnergy(minute);
}
int idx;
Map<Integer, Map<Integer, MeterMinute>> minutes = new HashMap<>();
for (HubPowerMinute hubPower : _hubPower) {
Date minute = hubPower.getMinuteAsDate();
for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) {
String key = breaker.breakerKey();
Breaker b = _breakers.get(key);
if (b == null)
continue;
BreakerGroup group = _breakerKeyToGroup.get(key);
if (group == null)
continue;
MeterMinute meter = minutes.computeIfAbsent(hubPower.getMinute(), _p -> new HashMap<>()).computeIfAbsent(b.getMeter(), _m -> new MeterMinute(b.getMeter(), minute));
idx = 0;
for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) {
if (idx >= 60)
break;
if (power > 0)
meter.usage[idx] += power;
else
meter.solar[idx] -= power;
addEnergy(group.getId(), minute, power);
idx++;
}
}
}
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 minute : meters.values()) {
curConsumption += minute.usage[i];
curProduction += minute.solar[i];
minute.flow[i] = minute.usage[i] - minute.solar[i];
if (minute.flow[i] > 0)
curFromGrid += minute.flow[i];
else
curToGrid -= minute.flow[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) {
Date minute = hubPower.getMinuteAsDate();
for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) {
String key = breaker.breakerKey();
Breaker b = _breakers.get(key);
if (b == null)
continue;
BreakerGroup group = _breakerKeyToGroup.get(key);
if (group == null)
continue;
MeterMinute meter = minutes.get(hubPower.getMinute()).get(b.getMeter());
idx = 0;
double flow = 0.0;
for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) {
if ((b.getPolarity() == BreakerPolarity.SOLAR) && (meter.flow[idx] < 0.0))
flow -= meter.flow[idx] * (power / meter.solar[idx]);
else if ((b.getPolarity() != BreakerPolarity.SOLAR) && (meter.flow[idx] > 0.0))
flow += meter.flow[idx] * (power / meter.usage[idx]);
idx++;
}
addFlow(group.getId(), minute, flow);
}
}
}
public void resetEnergy(Date _readTime) {
if (energy == null)
return;
int idx = viewMode.blockIndex(start, _readTime, timezone);
if (idx < energy.length)
energy[idx] = 0f;
for (EnergySummary subGroup : CollectionUtils.makeNotNull(subGroups)) {
subGroup.resetEnergy(_readTime);
}
}
public static EnergySummary summary(BreakerGroup _group, Map<String, List<EnergyTotal>> _energies, EnergyViewMode _viewMode, Date _start, TimeZone _tz) {
EnergySummary energy = new EnergySummary();
energy.setGroupId(_group.getId());
energy.setGroupName(_group.getName());
energy.setAccountId(_group.getAccountId());
energy.setViewMode(_viewMode);
energy.setStart(_start);
energy.setTimeZone(_tz);
energy.setSubGroups(CollectionUtils.transform(_group.getSubGroups(), _g -> EnergySummary.summary(_g, _energies, _viewMode, _start, _tz)));
for (EnergyTotal curEnergy : CollectionUtils.makeNotNull(_energies.get(_group.getId()))) {
energy.addEnergy(curEnergy.getStart(), curEnergy.getJoules());
energy.addFlow(curEnergy.getStart(), curEnergy.getFlow());
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;
}
private boolean addEnergy(String _groupId, Date _readTime, double _joules) {
if (NullUtils.isEqual(groupId, _groupId)) {
addEnergy(_readTime, _joules);
return true;
} else {
for (EnergySummary subGroup : CollectionUtils.makeNotNull(subGroups)) {
if (subGroup.addEnergy(_groupId, _readTime, _joules))
return true;
}
}
return false;
}
private void addEnergy(Date _readTime, double _joules) {
if (energy == null)
energy = new float[blockCount()];
int idx = viewMode.blockIndex(start, _readTime, timezone);
if (idx < energy.length)
energy[idx] += _joules;
}
private boolean addFlow(String _groupId, Date _readTime, double _joules) {
if (NullUtils.isEqual(groupId, _groupId)) {
addFlow(_readTime, _joules);
return true;
} else {
for (EnergySummary subGroup : CollectionUtils.makeNotNull(subGroups)) {
if (subGroup.addFlow(_groupId, _readTime, _joules))
return true;
}
}
return false;
}
private void addFlow(Date _readTime, double _joules) {
if (gridEnergy == null)
gridEnergy = new float[blockCount()];
int idx = viewMode.blockIndex(start, _readTime, timezone);
if (idx < gridEnergy.length)
gridEnergy[idx] += _joules;
}
private int blockCount() {
return viewMode.blockCount(start, timezone);
}
public String getId() {
return toId(accountId, groupId, viewMode, start);
}
public static String toId(int _accountId, String _groupId, EnergyViewMode _viewMode, Date _start) {
return _accountId + "-" + _groupId + "-" + DaoSerializer.toEnumName(_viewMode) + "-" + _start.getTime();
}
public int getAccountId() {
return accountId;
}
public void setAccountId(int _accountId) {
accountId = _accountId;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String _groupId) {
groupId = _groupId;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String _groupName) {
groupName = _groupName;
}
public EnergySummary getSubGroup(String _groupId) {
return CollectionUtils.filterOne(subGroups, _g -> _groupId.equals(_g.getGroupId()));
}
public List<EnergySummary> getSubGroups() {
return subGroups;
}
public EnergyViewMode getViewMode() {
return viewMode;
}
public void setViewMode(EnergyViewMode _viewMode) {
viewMode = _viewMode;
}
public Date getStart() {
return start;
}
public void setStart(Date _start) {
start = _start;
}
public void setSubGroups(List<EnergySummary> _subGroups) {
subGroups = _subGroups;
}
public float[] getEnergy() {
return energy;
}
public void setEnergy(float[] _energy) {
energy = _energy;
}
public float[] getGridEnergy() {
return gridEnergy;
}
public void setGridEnergy(float[] _gridEnergy) {
gridEnergy = _gridEnergy;
}
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() {
return timezone;
}
public void setTimeZone(TimeZone _timezone) {
timezone = _timezone;
}
public double wattHours() {
return joules() / 3600;
}
public double wattHours(Set<String> _selectedBreakers) {
return joules(_selectedBreakers) / 3600;
}
public double joules() {
return joules(null);
}
public double joules(Set<String> _selectedBreakers) {
return joules(_selectedBreakers, true, GridFlow.BOTH);
}
public double joules(Set<String> _selectedBreakers, boolean _includeSubgroups, GridFlow _mode) {
double joules = 0.0;
if (_includeSubgroups) {
for (EnergySummary group : CollectionUtils.makeNotNull(subGroups)) {
joules += group.joules(_selectedBreakers, true, _mode);
}
}
if ((energy != null) && ((_selectedBreakers == null) || _selectedBreakers.contains(getGroupId()))) {
for (float block : energy) {
if ((_mode == GridFlow.BOTH) || ((_mode == GridFlow.FROM) && (block > 0f)) || ((_mode == GridFlow.TO) && (block < 0f)))
joules += block;
}
}
return joules;
}
public double flow() {
return flow(null);
}
public double flow(Set<String> _selectedBreakers) {
return flow(_selectedBreakers, true, GridFlow.BOTH);
}
public double flow(Set<String> _selectedBreakers, boolean _includeSubgroups, GridFlow _mode) {
double flow = 0.0;
if (_includeSubgroups) {
for (EnergySummary group : CollectionUtils.makeNotNull(subGroups)) {
flow += group.flow(_selectedBreakers, true, _mode);
}
}
if ((gridEnergy != null) && ((_selectedBreakers == null) || _selectedBreakers.contains(getGroupId()))) {
for (float block : gridEnergy) {
if ((_mode == GridFlow.BOTH) || ((_mode == GridFlow.FROM) && (block > 0f)) || ((_mode == GridFlow.TO) && (block < 0f)))
flow += block;
}
}
return flow;
}
public List<EnergySummary> getAllGroups() {
Map<String, EnergySummary> groups = new TreeMap<>();
getAllGroups(groups);
return new ArrayList<>(groups.values());
}
public void getAllGroups(Map<String, EnergySummary> _groups) {
_groups.put(getGroupId(), this);
for (EnergySummary group : CollectionUtils.makeNotNull(subGroups)) {
group.getAllGroups(_groups);
}
}
public float[] energyBlocks() {
return energyBlocks(null);
}
public float[] energyBlocks(Set<String> _selectedGroups) {
return energyBlocks(_selectedGroups, GridFlow.BOTH);
}
public float[] energyBlocks(Set<String> _selectedGroups, GridFlow _flow) {
float[] blocks = new float[blockCount()];
energyBlocks(_selectedGroups, blocks, _flow);
return blocks;
}
private void energyBlocks(Set<String> _selectedGroups, float[] _energyBlocks, GridFlow _flow) {
if ((energy != null) && ((_selectedGroups == null) || _selectedGroups.contains(getGroupId()))) {
for (int i = 0; i < energy.length; i++) {
if ((_flow == GridFlow.BOTH) || ((_flow == GridFlow.FROM) && energy[i] >= 0.0) || ((_flow == GridFlow.TO) && energy[i] <= 0.0))
_energyBlocks[i] += energy[i];
}
}
for (EnergySummary group : CollectionUtils.makeNotNull(subGroups)) {
group.energyBlocks(_selectedGroups, _energyBlocks, _flow);
}
}
public List<ChargeSummary> toChargeSummaries(BreakerConfig _config, List<EnergyTotal> _totals) {
Map<String, Integer> breakerGroupMeters = _config.getRootGroup().mapToMeters();
return CollectionUtils.transform(_config.getBillingPlans(), _p->{
double monthKwh = CollectionUtils.sum(CollectionUtils.transform(CollectionUtils.filter(_totals, _t->_t.getStart().getTime() >= _p.getBillingCycleStart(start, timezone).getTime()), EnergyTotal::totalJoules))/3600000.0;
return toChargeSummary(_p.getPlanId(), _p.getRates(), breakerGroupMeters, new MutableDouble(monthKwh));
});
}
public ChargeSummary toChargeSummary(int _planId, List<BillingRate> _rates, Map<String, Integer> _breakerGroupMeters, MutableDouble _monthKwh) {
return toChargeSummaryForRates(_planId, CollectionUtils.filter(_rates, _r->_r.isApplicableForDay(start, timezone)), _breakerGroupMeters, _monthKwh);
}
private ChargeSummary toChargeSummaryForRates(int _planId, List<BillingRate> _rates, Map<String, Integer> _breakerGroupMeters, MutableDouble _monthKwh) {
ChargeSummary summary = new ChargeSummary(accountId, _planId, groupId, groupName, viewMode, start, timezone);
if (gridEnergy != null) {
double[] charges = new double[gridEnergy.length];
for (int i = 0; i < gridEnergy.length; i++) {
_monthKwh.add(gridEnergy[i]/3600000.0);
for (BillingRate rate : _rates) {
if (gridEnergy[i] > 0f) {
if (rate.isApplicable(GridFlow.FROM, DaoSerializer.toInteger(_breakerGroupMeters.get(groupId)), _monthKwh.getValue(), i*60))
charges[i] += rate.apply(((double) gridEnergy[i]) / 3600000.0);
} else if (rate.isApplicable(GridFlow.TO, DaoSerializer.toInteger(_breakerGroupMeters.get(groupId)), _monthKwh.getValue(), i*60))
charges[i] += rate.apply(((double) gridEnergy[i]) / 3600000.0);
}
}
summary.setCharges(charges);
}
summary.setSubGroups(CollectionUtils.transform(subGroups, _s->_s.toChargeSummary(_planId, _rates, _breakerGroupMeters, _monthKwh)));
summary.setTotalUsageJoules(joules(null, true, GridFlow.FROM));
summary.setTotalSolarJoules(Math.abs(joules(null, true, GridFlow.TO)));
summary.setFromGridJoules(flow(null, true, GridFlow.FROM));
summary.setToGridJoules(Math.abs(flow(null, true, GridFlow.TO)));
summary.setPeakConsumption(peakConsumption);
summary.setPeakProduction(peakProduction);
summary.setPeakFromGrid(peakFromGrid);
summary.setPeakToGrid(peakToGrid);
return summary;
}
private static class MeterMinute {
private final int meter;
private final Date minute;
public MeterMinute(int _meter, Date _minute) {
meter = _meter;
minute = _minute;
}
public int getMeter() {
return meter;
}
public Date getMinute() {
return minute;
}
public double[] usage = new double[60];
public double[] solar = new double[60];
public double[] flow = new double[60];
}
}

View File

@@ -2,47 +2,38 @@ package com.lanternsoftware.datamodel.currentmonitor;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@DBSerializable(autogen = false)
public class BreakerGroupSummary {
public class EnergyTotal {
private int accountId;
private String groupId;
private String groupName;
private EnergyBlockViewMode viewMode;
private EnergyViewMode viewMode;
private Date start;
private List<BreakerGroupSummary> subGroups;
private List<EnergyTotal> subGroups;
private double joules;
private double charge;
private double toGrid;
private double fromGrid;
private double flow;
private double peakToGrid;
private double peakFromGrid;
private double peakConsumption;
private double peakProduction;
public BreakerGroupSummary() {
public EnergyTotal() {
}
public BreakerGroupSummary(BreakerGroupEnergy _energy) {
public EnergyTotal(EnergySummary _energy) {
accountId = _energy.getAccountId();
groupId = _energy.getGroupId();
groupName = _energy.getGroupName();
viewMode = _energy.getViewMode();
start = _energy.getStart();
subGroups = CollectionUtils.transform(_energy.getSubGroups(), BreakerGroupSummary::new);
joules = _energy.joules(null, false);
charge = _energy.charge(null, false);
toGrid = _energy.getToGrid();
fromGrid = _energy.getFromGrid();
subGroups = CollectionUtils.transform(_energy.getSubGroups(), EnergyTotal::new);
joules = _energy.joules(null, false, GridFlow.BOTH);
flow = _energy.flow(null, false, GridFlow.BOTH);
peakToGrid = _energy.getPeakToGrid();
peakFromGrid = _energy.getPeakFromGrid();
peakConsumption = _energy.getPeakConsumption();
@@ -53,7 +44,7 @@ public class BreakerGroupSummary {
return toId(accountId, groupId, viewMode, start);
}
public static String toId(int _accountId, String _groupId, EnergyBlockViewMode _viewMode, Date _start) {
public static String toId(int _accountId, String _groupId, EnergyViewMode _viewMode, Date _start) {
return _accountId + "-" + _groupId + "-" + DaoSerializer.toEnumName(_viewMode) + "-" + _start.getTime();
}
@@ -73,27 +64,19 @@ public class BreakerGroupSummary {
groupId = _groupId;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String _groupName) {
groupName = _groupName;
}
public BreakerGroupSummary getSubGroup(String _groupId) {
public EnergyTotal getSubGroup(String _groupId) {
return CollectionUtils.filterOne(subGroups, _g->_groupId.equals(_g.getGroupId()));
}
public List<BreakerGroupSummary> getSubGroups() {
public List<EnergyTotal> getSubGroups() {
return subGroups;
}
public EnergyBlockViewMode getViewMode() {
public EnergyViewMode getViewMode() {
return viewMode;
}
public void setViewMode(EnergyBlockViewMode _viewMode) {
public void setViewMode(EnergyViewMode _viewMode) {
viewMode = _viewMode;
}
@@ -105,7 +88,7 @@ public class BreakerGroupSummary {
start = _start;
}
public void setSubGroups(List<BreakerGroupSummary> _subGroups) {
public void setSubGroups(List<EnergyTotal> _subGroups) {
subGroups = _subGroups;
}
@@ -117,28 +100,12 @@ public class BreakerGroupSummary {
joules = _joules;
}
public double getCharge() {
return charge;
public double getFlow() {
return flow;
}
public void setCharge(double _charge) {
charge = _charge;
}
public double getToGrid() {
return toGrid;
}
public void setToGrid(double _toGrid) {
toGrid = _toGrid;
}
public double getFromGrid() {
return fromGrid;
}
public void setFromGrid(double _fromGrid) {
fromGrid = _fromGrid;
public void setFlow(double _flow) {
flow = _flow;
}
public double getPeakConsumption() {
@@ -173,17 +140,24 @@ public class BreakerGroupSummary {
peakFromGrid = _peakFromGrid;
}
public List<BreakerGroupSummary> getAllGroups() {
Map<String, BreakerGroupSummary> groups = new TreeMap<>();
getAllGroups(groups);
return new ArrayList<>(groups.values());
public double totalJoules() {
double total = joules;
for (EnergyTotal t : CollectionUtils.makeNotNull(subGroups)) {
total += t.totalJoules();
}
return total;
}
public void getAllGroups(Map<String, BreakerGroupSummary> _groups) {
if (NullUtils.isNotEmpty(getGroupId()))
_groups.put(getGroupId(), this);
for (BreakerGroupSummary group : CollectionUtils.makeNotNull(subGroups)) {
group.getAllGroups(_groups);
public List<EnergyTotal> flatten() {
List<EnergyTotal> totals = new ArrayList<>();
flatten(totals);
return totals;
}
private void flatten(List<EnergyTotal> _totals) {
_totals.add(this);
for (EnergyTotal total : CollectionUtils.makeNotNull(subGroups)) {
total.flatten(_totals);
}
}
}

View File

@@ -7,7 +7,7 @@ import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public enum EnergyBlockViewMode {
public enum EnergyViewMode {
DAY,
MONTH,
YEAR,
@@ -54,59 +54,58 @@ public enum EnergyBlockViewMode {
}
public Date incrementBlock(Date _dt, TimeZone _tz) {
Calendar cal = DateUtils.toCalendar(_dt, _tz);
if (this == DAY)
cal.add(Calendar.MINUTE, 1);
else if (this == MONTH)
cal.add(Calendar.DAY_OF_YEAR, 1);
return DateUtils.addMinutes(_dt, 1);
if (this == MONTH)
return DateUtils.addDays(_dt, 1, _tz);
if (this == YEAR)
cal.add(Calendar.MONTH, 1);
return cal.getTime();
return DateUtils.addMonths(_dt, 1, _tz);
return _dt;
}
public Date decrementBlock(Date _dt, TimeZone _tz) {
Calendar cal = DateUtils.toCalendar(_dt, _tz);
if (this == DAY)
cal.add(Calendar.MINUTE, -1);
else if (this == MONTH)
cal.add(Calendar.DAY_OF_YEAR, -1);
return DateUtils.addMinutes(_dt, -1);
if (this == MONTH)
return DateUtils.addDays(_dt, -1, _tz);
if (this == YEAR)
cal.add(Calendar.MONTH, -1);
return cal.getTime();
return DateUtils.addMonths(_dt, -1, _tz);
return _dt;
}
public Date incrementView(Date _dt, TimeZone _tz) {
Calendar cal = DateUtils.toCalendar(_dt, _tz);
if (this == DAY)
cal.add(Calendar.DAY_OF_YEAR, 1);
else if (this == MONTH)
cal.add(Calendar.MONTH, 1);
return DateUtils.addDays(_dt, 1, _tz);
if (this == MONTH)
return DateUtils.addMonths(_dt, 1, _tz);
if (this == YEAR)
cal.add(Calendar.YEAR, 1);
return cal.getTime();
return DateUtils.addYears(_dt, 1, _tz);
return _dt;
}
public Date decrementView(Date _dt, TimeZone _tz) {
Calendar cal = DateUtils.toCalendar(_dt, _tz);
if (this == DAY)
cal.add(Calendar.DAY_OF_YEAR, -1);
else if (this == MONTH)
cal.add(Calendar.MONTH, -1);
return DateUtils.addDays(_dt, -1, _tz);
if (this == MONTH)
return DateUtils.addMonths(_dt, -1, _tz);
if (this == YEAR)
cal.add(Calendar.YEAR, -1);
return cal.getTime();
return DateUtils.addYears(_dt, -1, _tz);
return _dt;
}
public int blockCount(Date _start, TimeZone _tz) {
if (this == ALL)
return 1;
Date end = toEnd(_start, _tz);
int blockCnt = 0;
while (_start.before(end)) {
blockCnt++;
_start = toBlockEnd(_start, _tz);
if (this == YEAR)
return 12;
if (this == MONTH) {
Calendar end = DateUtils.getEndOfMonthCal(_start, _tz);
end.add(Calendar.MINUTE, -2);
return end.get(Calendar.DAY_OF_MONTH);
}
return blockCnt;
if (this == DAY) {
Date end = DateUtils.getMidnightAfter(_start, _tz);
return (int)((end.getTime() - _start.getTime()) / 60000);
}
return 1;
}
public int initBlockCount() {

View File

@@ -5,7 +5,7 @@ import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.Date;
import java.util.List;
@DBSerializable
@DBSerializable(autogen = false)
public class HubPowerMinute {
private int accountId;
private int hub;

View File

@@ -0,0 +1,48 @@
package com.lanternsoftware.datamodel.currentmonitor.dao;
import com.lanternsoftware.datamodel.currentmonitor.BillingPlan;
import com.lanternsoftware.datamodel.currentmonitor.BillingRate;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
public class BillingPlanSerializer extends AbstractDaoSerializer<BillingPlan>
{
@Override
public Class<BillingPlan> getSupportedClass()
{
return BillingPlan.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(BillingPlan _o)
{
DaoEntity d = new DaoEntity();
d.put("account_id", _o.getAccountId());
d.put("plan_id", _o.getPlanId());
d.put("billing_day", _o.getBillingDay());
d.put("name", _o.getName());
d.put("rates", DaoSerializer.toDaoEntities(_o.getRates(), DaoProxyType.MONGO));
return d;
}
@Override
public BillingPlan fromDaoEntity(DaoEntity _d)
{
BillingPlan o = new BillingPlan();
o.setAccountId(DaoSerializer.getInteger(_d, "account_id"));
o.setPlanId(DaoSerializer.getInteger(_d, "plan_id"));
o.setBillingDay(DaoSerializer.getInteger(_d, "billing_day"));
o.setName(DaoSerializer.getString(_d, "name"));
o.setRates(DaoSerializer.getList(_d, "rates", BillingRate.class));
return o;
}
}

View File

@@ -1,12 +1,13 @@
package com.lanternsoftware.datamodel.currentmonitor.dao;
import com.lanternsoftware.datamodel.currentmonitor.BillingCurrency;
import com.lanternsoftware.datamodel.currentmonitor.GridFlow;
import com.lanternsoftware.datamodel.currentmonitor.BillingRate;
import com.lanternsoftware.datamodel.currentmonitor.GridFlow;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
@@ -28,10 +29,10 @@ public class BillingRateSerializer extends AbstractDaoSerializer<BillingRate>
{
DaoEntity d = new DaoEntity();
d.put("meter", _o.getMeter());
d.put("day_billing_cycle_start", _o.getDayBillingCycleStart());
d.put("flow", DaoSerializer.toEnumName(_o.getFlow()));
d.put("rate", _o.getRate());
d.put("unit", DaoSerializer.toEnumName(_o.getCurrency()));
d.put("unit", DaoSerializer.toEnumName(_o.getCurrency())); //TODO: Remove post migration
d.put("currency", DaoSerializer.toEnumName(_o.getCurrency()));
d.put("time_of_day_start", _o.getTimeOfDayStart());
d.put("time_of_day_end", _o.getTimeOfDayEnd());
d.put("month_kwh_start", _o.getMonthKWhStart());
@@ -47,10 +48,11 @@ public class BillingRateSerializer extends AbstractDaoSerializer<BillingRate>
{
BillingRate o = new BillingRate();
o.setMeter(DaoSerializer.getInteger(_d, "meter"));
o.setDayBillingCycleStart(DaoSerializer.getInteger(_d, "day_billing_cycle_start"));
o.setFlow(DaoSerializer.getEnum(_d, "flow", GridFlow.class, GridFlow.BOTH));
o.setFlow(DaoSerializer.getEnum(_d, "flow", GridFlow.class));
o.setRate(DaoSerializer.getDouble(_d, "rate"));
o.setCurrency(DaoSerializer.getEnum(_d, "unit", BillingCurrency.class));
o.setCurrency(DaoSerializer.getEnum(_d, "currency", BillingCurrency.class));
if (o.getCurrency() == null)
o.setCurrency(DaoSerializer.getEnum(_d, "unit", BillingCurrency.class));
o.setTimeOfDayStart(DaoSerializer.getInteger(_d, "time_of_day_start"));
o.setTimeOfDayEnd(DaoSerializer.getInteger(_d, "time_of_day_end"));
o.setMonthKWhStart(DaoSerializer.getDouble(_d, "month_kwh_start"));

View File

@@ -1,5 +1,6 @@
package com.lanternsoftware.datamodel.currentmonitor.dao;
import com.lanternsoftware.datamodel.currentmonitor.BillingPlan;
import com.lanternsoftware.datamodel.currentmonitor.BillingRate;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup;
@@ -36,6 +37,7 @@ public class BreakerConfigSerializer extends AbstractDaoSerializer<BreakerConfig
d.put("panels", DaoSerializer.toDaoEntities(_o.getPanels(), DaoProxyType.MONGO));
d.put("breaker_hubs", DaoSerializer.toDaoEntities(_o.getBreakerHubs(), DaoProxyType.MONGO));
d.put("breaker_groups", DaoSerializer.toDaoEntities(_o.getBreakerGroups(), DaoProxyType.MONGO));
d.put("billing_plans", DaoSerializer.toDaoEntities(_o.getBillingPlans(), DaoProxyType.MONGO));
d.put("billing_rates", DaoSerializer.toDaoEntities(_o.getBillingRates(), DaoProxyType.MONGO));
d.put("version", _o.getVersion());
return d;
@@ -50,6 +52,7 @@ public class BreakerConfigSerializer extends AbstractDaoSerializer<BreakerConfig
o.setPanels(DaoSerializer.getList(_d, "panels", BreakerPanel.class));
o.setBreakerHubs(DaoSerializer.getList(_d, "breaker_hubs", BreakerHub.class));
o.setBreakerGroups(DaoSerializer.getList(_d, "breaker_groups", BreakerGroup.class));
o.setBillingPlans(DaoSerializer.getList(_d, "billing_plans", BillingPlan.class));
o.setBillingRates(DaoSerializer.getList(_d, "billing_rates", BillingRate.class));
o.setVersion(DaoSerializer.getInteger(_d, "version"));
return o;

View File

@@ -2,7 +2,7 @@ package com.lanternsoftware.datamodel.currentmonitor.dao;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupEnergy;
import com.lanternsoftware.datamodel.currentmonitor.EnergyBlock;
import com.lanternsoftware.datamodel.currentmonitor.EnergyBlockViewMode;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
@@ -11,12 +11,7 @@ import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.*;
public class BreakerGroupEnergySerializer extends AbstractDaoSerializer<BreakerGroupEnergy>
{
@@ -88,7 +83,7 @@ public class BreakerGroupEnergySerializer extends AbstractDaoSerializer<BreakerG
o.setGroupId(DaoSerializer.getString(_d, "group_id"));
o.setAccountId(DaoSerializer.getInteger(_d, "account_id"));
o.setGroupName(DaoSerializer.getString(_d, "group_name"));
o.setViewMode(DaoSerializer.getEnum(_d, "view_mode", EnergyBlockViewMode.class));
o.setViewMode(DaoSerializer.getEnum(_d, "view_mode", EnergyViewMode.class));
o.setStart(DaoSerializer.getDate(_d, "start"));
o.setSubGroups(DaoSerializer.getList(_d, "sub_groups", BreakerGroupEnergy.class));
o.setTimeZone(DateUtils.fromTimeZoneId(DaoSerializer.getString(_d, "timezone")));

View File

@@ -0,0 +1,78 @@
package com.lanternsoftware.datamodel.currentmonitor.dao;
import com.lanternsoftware.datamodel.currentmonitor.ChargeSummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
public class ChargeSummarySerializer extends AbstractDaoSerializer<ChargeSummary>
{
@Override
public Class<ChargeSummary> getSupportedClass()
{
return ChargeSummary.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(ChargeSummary _o)
{
DaoEntity d = new DaoEntity();
d.put("_id", _o.getId());
d.put("account_id", _o.getAccountId());
d.put("plan_id", _o.getPlanId());
d.put("group_id", _o.getGroupId());
d.put("group_name", _o.getGroupName());
d.put("view_mode", DaoSerializer.toEnumName(_o.getViewMode()));
d.put("start", DaoSerializer.toLong(_o.getStart()));
d.put("sub_groups", DaoSerializer.toDaoEntities(_o.getSubGroups(), DaoProxyType.MONGO));
TimeZone tz = DateUtils.defaultTimeZone(_o.getTimezone());
d.put("timezone", tz.getID());
if (_o.getCharges() != null)
d.put("charges", CollectionUtils.toByteArray(_o.getCharges()));
d.put("total_usage_joules", _o.getTotalUsageJoules());
d.put("total_solar_joules", _o.getTotalSolarJoules());
d.put("from_grid_joules", _o.getFromGridJoules());
d.put("to_grid_joules", _o.getToGridJoules());
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;
}
@Override
public ChargeSummary fromDaoEntity(DaoEntity _d)
{
ChargeSummary o = new ChargeSummary();
o.setAccountId(DaoSerializer.getInteger(_d, "account_id"));
o.setPlanId(DaoSerializer.getInteger(_d, "plan_id"));
o.setGroupId(DaoSerializer.getString(_d, "group_id"));
o.setGroupName(DaoSerializer.getString(_d, "group_name"));
o.setViewMode(DaoSerializer.getEnum(_d, "view_mode", EnergyViewMode.class));
o.setStart(DaoSerializer.getDate(_d, "start"));
o.setSubGroups(DaoSerializer.getList(_d, "sub_groups", ChargeSummary.class));
o.setTimezone(DateUtils.fromTimeZoneId(DaoSerializer.getString(_d, "timezone")));
o.setCharges(CollectionUtils.toDoubleArray(DaoSerializer.getByteArray(_d, "charges")));
o.setTotalUsageJoules(DaoSerializer.getDouble(_d, "total_usage_joules"));
o.setTotalSolarJoules(DaoSerializer.getDouble(_d, "total_solar_joules"));
o.setFromGridJoules(DaoSerializer.getDouble(_d, "from_grid_joules"));
o.setToGridJoules(DaoSerializer.getDouble(_d, "to_grid_joules"));
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;
}
}

View File

@@ -0,0 +1,69 @@
package com.lanternsoftware.datamodel.currentmonitor.dao;
import com.lanternsoftware.datamodel.currentmonitor.ChargeTotal;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
public class ChargeTotalSerializer extends AbstractDaoSerializer<ChargeTotal>
{
@Override
public Class<ChargeTotal> getSupportedClass()
{
return ChargeTotal.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(ChargeTotal _o)
{
DaoEntity d = new DaoEntity();
d.put("_id", _o.getId());
d.put("account_id", _o.getAccountId());
d.put("group_id", _o.getGroupId());
d.put("plan_id", _o.getPlanId());
d.put("view_mode", DaoSerializer.toEnumName(_o.getViewMode()));
d.put("start", DaoSerializer.toLong(_o.getStart()));
d.put("charge", _o.getCharge());
d.put("sub_groups", DaoSerializer.toDaoEntities(_o.getSubGroups(), DaoProxyType.MONGO));
d.put("total_usage_joules", _o.getTotalUsageJoules());
d.put("total_solar_joules", _o.getTotalSolarJoules());
d.put("from_grid_joules", _o.getFromGridJoules());
d.put("to_grid_joules", _o.getToGridJoules());
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;
}
@Override
public ChargeTotal fromDaoEntity(DaoEntity _d)
{
ChargeTotal o = new ChargeTotal();
o.setAccountId(DaoSerializer.getInteger(_d, "account_id"));
o.setGroupId(DaoSerializer.getString(_d, "group_id"));
o.setPlanId(DaoSerializer.getInteger(_d, "plan_id"));
o.setViewMode(DaoSerializer.getEnum(_d, "view_mode", EnergyViewMode.class));
o.setStart(DaoSerializer.getDate(_d, "start"));
o.setCharge(DaoSerializer.getDouble(_d, "charge"));
o.setSubGroups(DaoSerializer.getList(_d, "sub_groups", ChargeTotal.class));
o.setTotalUsageJoules(DaoSerializer.getDouble(_d, "total_usage_joules"));
o.setTotalSolarJoules(DaoSerializer.getDouble(_d, "total_solar_joules"));
o.setFromGridJoules(DaoSerializer.getDouble(_d, "from_grid_joules"));
o.setToGridJoules(DaoSerializer.getDouble(_d, "to_grid_joules"));
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;
}
}

View File

@@ -0,0 +1,73 @@
package com.lanternsoftware.datamodel.currentmonitor.dao;
import com.lanternsoftware.datamodel.currentmonitor.EnergySummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
public class EnergySummarySerializer extends AbstractDaoSerializer<EnergySummary>
{
@Override
public Class<EnergySummary> getSupportedClass()
{
return EnergySummary.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(EnergySummary _o)
{
DaoEntity d = new DaoEntity();
d.put("_id", _o.getId());
d.put("account_id", _o.getAccountId());
d.put("group_id", _o.getGroupId());
d.put("group_name", _o.getGroupName());
d.put("view_mode", DaoSerializer.toEnumName(_o.getViewMode()));
d.put("start", DaoSerializer.toLong(_o.getStart()));
d.put("sub_groups", DaoSerializer.toDaoEntities(_o.getSubGroups(), DaoProxyType.MONGO));
TimeZone tz = DateUtils.defaultTimeZone(_o.getTimeZone());
d.put("timezone", tz.getID());
if (_o.getEnergy() != null)
d.put("energy", CollectionUtils.toByteArray(_o.getEnergy()));
if (_o.getGridEnergy() != null)
d.put("grid_energy", CollectionUtils.toByteArray(_o.getGridEnergy()));
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;
}
@Override
public EnergySummary fromDaoEntity(DaoEntity _d)
{
EnergySummary o = new EnergySummary();
o.setGroupId(DaoSerializer.getString(_d, "group_id"));
o.setAccountId(DaoSerializer.getInteger(_d, "account_id"));
o.setGroupName(DaoSerializer.getString(_d, "group_name"));
o.setViewMode(DaoSerializer.getEnum(_d, "view_mode", EnergyViewMode.class));
o.setStart(DaoSerializer.getDate(_d, "start"));
o.setSubGroups(DaoSerializer.getList(_d, "sub_groups", EnergySummary.class));
o.setTimeZone(DateUtils.fromTimeZoneId(DaoSerializer.getString(_d, "timezone")));
o.setEnergy(CollectionUtils.toFloatArray(DaoSerializer.getByteArray(_d, "energy")));
o.setGridEnergy(CollectionUtils.toFloatArray(DaoSerializer.getByteArray(_d, "grid_energy")));
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;
}
}

View File

@@ -1,7 +1,7 @@
package com.lanternsoftware.datamodel.currentmonitor.dao;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupSummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyBlockViewMode;
import com.lanternsoftware.datamodel.currentmonitor.EnergyTotal;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
@@ -9,12 +9,11 @@ import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
public class BreakerGroupSummarySerializer extends AbstractDaoSerializer<BreakerGroupSummary> {
public class EnergyTotalSerializer extends AbstractDaoSerializer<EnergyTotal> {
@Override
public Class<BreakerGroupSummary> getSupportedClass() {
return BreakerGroupSummary.class;
public Class<EnergyTotal> getSupportedClass() {
return EnergyTotal.class;
}
@Override
@@ -23,19 +22,16 @@ public class BreakerGroupSummarySerializer extends AbstractDaoSerializer<Breaker
}
@Override
public DaoEntity toDaoEntity(BreakerGroupSummary _o) {
public DaoEntity toDaoEntity(EnergyTotal _o) {
DaoEntity d = new DaoEntity();
d.put("_id", _o.getId());
d.put("account_id", _o.getAccountId());
d.put("group_id", _o.getGroupId());
d.put("group_name", _o.getGroupName());
d.put("view_mode", DaoSerializer.toEnumName(_o.getViewMode()));
d.put("start", DaoSerializer.toLong(_o.getStart()));
d.put("sub_groups", DaoSerializer.toDaoEntities(_o.getSubGroups(), DaoProxyType.MONGO));
d.put("joules", _o.getJoules());
d.put("charge", _o.getCharge());
d.put("to_grid", _o.getToGrid());
d.put("from_grid", _o.getFromGrid());
d.put("flow", _o.getFlow());
d.put("peak_to_grid", _o.getPeakToGrid());
d.put("peak_from_grid", _o.getPeakFromGrid());
d.put("peak_production", _o.getPeakProduction());
@@ -44,18 +40,15 @@ public class BreakerGroupSummarySerializer extends AbstractDaoSerializer<Breaker
}
@Override
public BreakerGroupSummary fromDaoEntity(DaoEntity _d) {
BreakerGroupSummary o = new BreakerGroupSummary();
public EnergyTotal fromDaoEntity(DaoEntity _d) {
EnergyTotal o = new EnergyTotal();
o.setGroupId(DaoSerializer.getString(_d, "group_id"));
o.setAccountId(DaoSerializer.getInteger(_d, "account_id"));
o.setGroupName(DaoSerializer.getString(_d, "group_name"));
o.setViewMode(DaoSerializer.getEnum(_d, "view_mode", EnergyBlockViewMode.class));
o.setViewMode(DaoSerializer.getEnum(_d, "view_mode", EnergyViewMode.class));
o.setStart(DaoSerializer.getDate(_d, "start"));
o.setSubGroups(DaoSerializer.getList(_d, "sub_groups", BreakerGroupSummary.class));
o.setSubGroups(DaoSerializer.getList(_d, "sub_groups", EnergyTotal.class));
o.setJoules(DaoSerializer.getDouble(_d, "joules"));
o.setCharge(DaoSerializer.getDouble(_d, "charge"));
o.setToGrid(DaoSerializer.getDouble(_d, "to_grid"));
o.setFromGrid(DaoSerializer.getDouble(_d, "from_grid"));
o.setFlow(DaoSerializer.getDouble(_d, "flow"));
o.setPeakToGrid(DaoSerializer.getDouble(_d, "peak_to_grid"));
o.setPeakFromGrid(DaoSerializer.getDouble(_d, "peak_from_grid"));
o.setPeakProduction(DaoSerializer.getDouble(_d, "peak_production"));

View File

@@ -26,6 +26,7 @@ public class HubPowerMinuteSerializer extends AbstractDaoSerializer<HubPowerMinu
public DaoEntity toDaoEntity(HubPowerMinute _o)
{
DaoEntity d = new DaoEntity();
d.put("_id", _o.getId());
d.put("account_id", _o.getAccountId());
d.put("hub", _o.getHub());
d.put("minute", _o.getMinute());

View File

@@ -1,15 +1,19 @@
com.lanternsoftware.datamodel.currentmonitor.dao.AccountSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BillingPlanSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BillingRateSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerConfigSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerGroupEnergySerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerGroupSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerGroupSummarySerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerHubSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerPanelSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerPowerMinuteSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerPowerSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.ChargeSummarySerializer
com.lanternsoftware.datamodel.currentmonitor.dao.ChargeTotalSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.EnergyBlockSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.EnergySummarySerializer
com.lanternsoftware.datamodel.currentmonitor.dao.EnergyTotalSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.HubPowerMinuteSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.MeterSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.NetworkStatusSerializer

View File

@@ -0,0 +1,38 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
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;
import javax.ws.rs.core.MediaType;
import java.util.Date;
@WebServlet("/charge/*")
public class ChargeServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
String[] path = path(_req);
if (path.length < 3) {
_rep.setStatus(400);
return;
}
int accountId = DaoSerializer.toInteger(CollectionUtils.getFirst(_authCode.getAllAccountIds()));
if (accountId == 0) {
_rep.setStatus(404);
return;
}
EnergyViewMode viewMode = NullUtils.toEnum(EnergyViewMode.class, path[2], EnergyViewMode.DAY);
Date start = new Date(NullUtils.toLong(path[3]));
byte[] charges = Globals.dao.getChargeSummaryBinary(accountId, DaoSerializer.toInteger(path[0]), path[1], viewMode, start);
if (charges == null)
_rep.setStatus(404);
else
setResponseEntity(_rep, 200, MediaType.APPLICATION_OCTET_STREAM, charges);
}
}

View File

@@ -0,0 +1,38 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
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;
import javax.ws.rs.core.MediaType;
import java.util.Date;
@WebServlet("/energy/*")
public class EnergyServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
String[] path = path(_req);
if (path.length < 3) {
_rep.setStatus(400);
return;
}
int accountId = DaoSerializer.toInteger(CollectionUtils.getFirst(_authCode.getAllAccountIds()));
if (accountId == 0) {
_rep.setStatus(404);
return;
}
EnergyViewMode viewMode = NullUtils.toEnum(EnergyViewMode.class, path[1], EnergyViewMode.DAY);
Date start = new Date(NullUtils.toLong(path[2]));
byte[] energy = Globals.dao.getEnergySummaryBinary(accountId, path[0], viewMode, start);
if (energy == null)
_rep.setStatus(404);
else
setResponseEntity(_rep, 200, MediaType.APPLICATION_OCTET_STREAM, energy);
}
}

View File

@@ -1,18 +1,18 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupEnergy;
import com.lanternsoftware.datamodel.currentmonitor.EnergyBlockViewMode;
import com.lanternsoftware.datamodel.currentmonitor.*;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.NullUtils;
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;
import javax.ws.rs.core.MediaType;
import java.util.Date;
import java.util.List;
import java.util.*;
@WebServlet("/energy/group/*")
public class GroupEnergyServlet extends SecureServlet {
@@ -23,34 +23,31 @@ public class GroupEnergyServlet extends SecureServlet {
_rep.setStatus(400);
return;
}
EnergyBlockViewMode viewMode = NullUtils.toEnum(EnergyBlockViewMode.class, path[1], EnergyBlockViewMode.DAY);
Date start = new Date(NullUtils.toLong(path[2]));
if ((CollectionUtils.size(_authCode.getAllAccountIds()) == 1) && NullUtils.isEqual(CollectionUtils.get(path, 3), "bin")) {
byte[] energy = Globals.dao.getBreakerGroupEnergyBinary(CollectionUtils.getFirst(_authCode.getAllAccountIds()), path[0], viewMode, start);
if (energy == null)
_rep.setStatus(404);
else
setResponseEntity(_rep, 200, MediaType.APPLICATION_OCTET_STREAM, energy);
}
List<BreakerGroupEnergy> energies = CollectionUtils.transform(_authCode.getAllAccountIds(), _id->Globals.dao.getBreakerGroupEnergy(_id, path[0], viewMode, start), true);
if (CollectionUtils.isNotEmpty(energies)) {
BreakerGroupEnergy energy;
if (energies.size() > 1) {
energy = new BreakerGroupEnergy();
energy.setAccountId(_authCode.getAccountId());
energy.setGroupId("Sites");
energy.setGroupName("Sites");
energy.setStart(start);
energy.setViewMode(viewMode);
energy.setSubGroups(CollectionUtils.asArrayList(energies));
}
else
energy = CollectionUtils.getFirst(energies);
if (NullUtils.isEqual(CollectionUtils.get(path, 3), "bin"))
zipBsonResponse(_rep, energy);
else
jsonResponse(_rep, energy);
} else
int accountId = DaoSerializer.toInteger(CollectionUtils.getFirst(_authCode.getAllAccountIds()));
if (accountId == 0) {
_rep.setStatus(404);
return;
}
EnergyViewMode viewMode = NullUtils.toEnum(EnergyViewMode.class, path[1], EnergyViewMode.DAY);
Date start = new Date(NullUtils.toLong(path[2]));
EnergySummary summary = Globals.dao.getEnergySummary(accountId, path[0], viewMode, start);
if (summary == null)
_rep.setStatus(404);
else {
BreakerConfig config = Globals.dao.getConfig(accountId);
Account acct = Globals.dao.getAccount(accountId);
TimeZone tz = DateUtils.fromTimeZoneId(acct.getTimezone(), "America/Chicago");
List<BillingRate> rates = CollectionUtils.filter(config.getBillingRates(), _r->_r.isApplicableForDay(start, tz));
Map<String, Integer> breakerGroupMeters = new HashMap<>();
for (BreakerGroup group : config.getAllBreakerGroups()) {
Breaker b = CollectionUtils.getFirst(group.getBreakers());
if (b != null)
breakerGroupMeters.put(group.getId(), b.getMeter());
}
BreakerGroupEnergy energy = new BreakerGroupEnergy(summary, rates, breakerGroupMeters);
energy.setToGrid(-summary.flow(null, true, GridFlow.TO));
energy.setFromGrid(summary.flow(null, true, GridFlow.FROM));
setResponseEntity(_rep, 200, MediaType.APPLICATION_OCTET_STREAM, DaoSerializer.toZipBson(energy));
}
}
}