diff --git a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/Backup.java b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/Backup.java index 03f3a2a..69bdb65 100644 --- a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/Backup.java +++ b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/Backup.java @@ -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 energy = dao.getProxy().queryAll(BreakerGroupEnergy.class); + List 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 summary = dao.getProxy().queryAll(BreakerGroupSummary.class); + List summary = dao.getProxy().queryAll(EnergyTotal.class); t7.stop(); DebugTimer t8 = new DebugTimer("Save Summaries"); backupDao.getProxy().save(summary); diff --git a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/CurrentMonitorDao.java b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/CurrentMonitorDao.java index 3e8eec7..6b2203e 100644 --- a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/CurrentMonitorDao.java +++ b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/CurrentMonitorDao.java @@ -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 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 _daysToSummarize, TimeZone _tz); void rebuildSummaries(int _accountId); void rebuildSummariesAsync(int _accountId); void rebuildSummaries(int _accountId, Date _start, Date _end); diff --git a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/MongoCurrentMonitorDao.java b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/MongoCurrentMonitorDao.java index 6260a68..333694f 100644 --- a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/MongoCurrentMonitorDao.java +++ b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/MongoCurrentMonitorDao.java @@ -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 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 _summaries) { + proxy.save(_summaries); + proxy.save(CollectionUtils.transform(_summaries, ChargeTotal::new)); + } + @Override public List 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 _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 _daysToSummarize, TimeZone _tz) { Set monthsToSummarize = CollectionUtils.transformToSet(_daysToSummarize, _c -> DateUtils.getStartOfMonth(_c, _tz)); Set 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 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 groupEnergies = CollectionUtils.aggregate(proxy.query(BreakerGroupSummary.class, DaoQuery.in("_id", groupEnergyIds)), BreakerGroupSummary::getAllGroups); - Map> energies = CollectionUtils.transformToMultiMap(groupEnergies, BreakerGroupSummary::getGroupId); - BreakerGroupEnergy summary = BreakerGroupEnergy.summary(_rootGroup, energies, EnergyBlockViewMode.MONTH, month, _tz); - putBreakerGroupEnergy(summary); + List groupEnergies = CollectionUtils.aggregate(proxy.query(EnergyTotal.class, DaoQuery.in("_id", groupEnergyIds)), EnergyTotal::flatten); + Map> 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 groupEnergyIds = new ArrayList<>(); + List 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 groupEnergies = CollectionUtils.aggregate(proxy.query(BreakerGroupSummary.class, DaoQuery.in("_id", groupEnergyIds)), BreakerGroupSummary::getAllGroups); - Map> energies = CollectionUtils.transformToMultiMap(groupEnergies, BreakerGroupSummary::getGroupId); - BreakerGroupEnergy summary = BreakerGroupEnergy.summary(_rootGroup, energies, EnergyBlockViewMode.YEAR, year, _tz); - putBreakerGroupEnergy(summary); + List groupEnergies = CollectionUtils.aggregate(proxy.query(EnergyTotal.class, DaoQuery.in("_id", summaryIds)), EnergyTotal::flatten); + Map> energies = CollectionUtils.transformToMultiMap(groupEnergies, EnergyTotal::getGroupId); + EnergySummary summary = EnergySummary.summary(_rootGroup, energies, EnergyViewMode.YEAR, year, _tz); + putEnergySummary(summary); + } + List 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> 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 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 totals = proxy.query(EnergyTotal.class, DaoQuery.in("_id", groupEnergyIds)); + putChargeSummaries(_energySummary.toChargeSummaries(_config, totals)); + } + } + + private void updateChargeSummaries(BreakerConfig _config, Set _daysToSummarize, TimeZone _tz) { + if (CollectionUtils.isEmpty(_config.getBillingPlans())) + return; + Set yearsToSummarize = CollectionUtils.transformToSet(_daysToSummarize, _c -> DateUtils.getStartOfYear(_c, _tz)); + BreakerGroup rootGroup = _config.getRootGroup(); + for (BillingPlan plan : _config.getBillingPlans()) { + List summaries = new ArrayList<>(); + Set 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 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 monthTotals = CollectionUtils.aggregate(proxy.query(ChargeTotal.class, DaoQuery.in("_id", monthSummaryIds)), ChargeTotal::flatten); + Map> 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 summaries = new ArrayList<>(); + for (Date year : yearsToSummarize) { + Date yearStart = DateUtils.getStartOfYear(year, _tz); + Date yearEnd = DateUtils.addYears(yearStart, 1, _tz); + Date yearMonthStart = yearStart; + Set 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 flatTotals = CollectionUtils.aggregate(proxy.query(ChargeTotal.class, DaoQuery.in("_id", monthSummaryIds)), ChargeTotal::flatten); + Map> 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 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> charges = CollectionUtils.transformToMultiMap(yearTotals, ChargeTotal::getGroupId); + ChargeSummary summary = new ChargeSummary(rootGroup, plan, charges, EnergyViewMode.ALL, new Date(0), _tz); + putChargeSummary(summary); } - List 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> 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 _rates) { @@ -172,7 +266,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao { if (firstMinute == null) return; TimeZone tz = getTimeZoneForAccount(_accountId); - Map rates = CollectionUtils.transformToMap(_rates, _r->String.format("%d%d", DaoSerializer.toLong(_r.getBeginEffective()), DaoSerializer.toLong(_r.getEndEffective()))); + Map 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 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 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 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 dates = new HashSet<>(); + Date curDate = start; + while (curDate.before(end)) { + dates.add(curDate); + curDate = DateUtils.addDays(curDate, 1, tz); + } + List summaryIds = CollectionUtils.transform(dates, _dt->EnergySummary.toId(_config.getAccountId(), root.getId(), EnergyViewMode.DAY, _dt)); + DebugTimer t1 = new DebugTimer("Load Daily Energy Totals", logger); + Map totals = CollectionUtils.transformToMap(proxy.query(EnergyTotal.class, DaoQuery.in("_id", summaryIds)), EnergyTotal::getStart); + t1.stop(); + Map breakerGroupMeters = _config.getRootGroup().mapToMeters(); + List chargeSummaries = new ArrayList<>(); + DebugTimer t2 = new DebugTimer("Load Energy Summaries", logger); + List 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 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 changedRates = new ArrayList<>(_config.getBillingRates()); - changedRates.removeAll(CollectionUtils.makeNotNull(oldConfig.getBillingRates())); - if (!changedRates.isEmpty()) - rebuildSummaries(_config.getAccountId(), changedRates); + Map 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; diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingCurrency.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingCurrency.java index dbf1517..d92ca1c 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingCurrency.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingCurrency.java @@ -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(); + } } diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingPlan.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingPlan.java new file mode 100644 index 0000000..65fc8fb --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingPlan.java @@ -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 { + private int accountId; + private int planId; + private int billingDay; + private String name; + private List 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 getRates() { + return rates; + } + + public void setRates(List _rates) { + rates = _rates; + } + + @Override + public boolean isIdentical(BillingPlan _other) { + return accountId == _other.accountId && planId == _other.planId && billingDay == _other.billingDay && CollectionUtils.isEqual(rates, _other.rates); + } +} diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingRate.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingRate.java index 79f431c..927c4a6 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingRate.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingRate.java @@ -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); diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerConfig.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerConfig.java index 3fcf7a3..b9f5826 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerConfig.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerConfig.java @@ -20,6 +20,7 @@ public class BreakerConfig implements IIdentical { private List breakerHubs; private List breakerGroups; private List billingRates; + private List billingPlans; private int version; public BreakerConfig() { @@ -69,6 +70,14 @@ public class BreakerConfig implements IIdentical { breakerGroups = _breakerGroups; } + public List getBillingPlans() { + return billingPlans; + } + + public void setBillingPlans(List _billingPlans) { + billingPlans = _billingPlans; + } + public List getBillingRates() { return billingRates; } @@ -85,6 +94,10 @@ public class BreakerConfig implements IIdentical { version = _version; } + public BreakerGroup getRootGroup() { + return CollectionUtils.getFirst(breakerGroups); + } + public List getAllBreakers() { List allBreakers = new ArrayList<>(); for (BreakerGroup g : CollectionUtils.makeNotNull(breakerGroups)) { @@ -190,13 +203,13 @@ public class BreakerConfig implements IIdentical { 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 diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerGroup.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerGroup.java index 44dfd74..252e79e 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerGroup.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerGroup.java @@ -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 { return null; } + public Map mapToMeters() { + Map 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 _validPanels) { if (subGroups != null) subGroups.removeIf(_g->!_g.removeInvalidGroups(_validPanels)); diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerGroupEnergy.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerGroupEnergy.java index 4eec58c..8054fd7 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerGroupEnergy.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerGroupEnergy.java @@ -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 subGroups; - private List 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 subGroups; + private List 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 _power, EnergyBlockViewMode _viewMode, Date _start, BreakerGroupSummary _month, List _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 _rates, Map _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 _hubPower, BreakerGroupSummary _month, List _rates) { - Map breakers = CollectionUtils.transformToMap(_group.getAllBreakers(), Breaker::getKey); - Map 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 _breakers, Map _breakerKeyToGroup, List _hubPower, BreakerGroupSummary _month, List _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> 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 consumptionRates = CollectionUtils.filter(_rates, _r->_r.isApplicable(GridFlow.FROM, minute.getMeter(), monthkWh, minute.getMinute(), timezone)); - List 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 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 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 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> _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 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 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 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 _subGroups) { + subGroups = _subGroups; + } - public List getSubGroups() { - return subGroups; - } + public List getEnergyBlocks() { + return energyBlocks; + } - public EnergyBlockViewMode getViewMode() { - return viewMode; - } + public void setEnergyBlocks(List _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 _subGroups) { - subGroups = _subGroups; - } + public void setFromGrid(double _fromGrid) { + fromGrid = _fromGrid; + } - public List getEnergyBlocks() { - return energyBlocks; - } + public double getPeakToGrid() { + return peakToGrid; + } - public void setEnergyBlocks(List _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 _selectedBreakers) { - return joules(_selectedBreakers) / 3600; - } - - public double joules() { - return joules(null); - } - - public double joules(Set _selectedBreakers) { - return joules(_selectedBreakers, true); - } - - public double joules(Set _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 _selectedBreakers) { - return charge(_selectedBreakers, true); - } - - public double charge(Set _selectedBreakers, GridFlow _mode) { - return charge(_selectedBreakers, true, _mode); - } - - public double charge(Set _selectedBreakers, boolean _includeSubgroups) { - return charge(_selectedBreakers, _includeSubgroups, null); - } - - public double charge(Set _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 getAllGroups() { - Map groups = new TreeMap<>(); - getAllGroups(groups); - return new ArrayList<>(groups.values()); - } - - public void getAllGroups(Map _groups) { - _groups.put(getGroupId(), this); - for (BreakerGroupEnergy group : CollectionUtils.makeNotNull(subGroups)) { - group.getAllGroups(_groups); - } - } - - public List getAllEnergyBlocks() { - return getAllEnergyBlocks(null); - } - - public List getAllEnergyBlocks(Set _selectedGroups) { - return getAllEnergyBlocks(_selectedGroups, EnergyBlockType.ANY); - } - - public List getAllEnergyBlocks(Set _selectedGroups, EnergyBlockType _type) { - Map blocks = new TreeMap<>(); - getAllEnergyBlocks(_selectedGroups, blocks, _type); - return new ArrayList<>(blocks.values()); - } - - private void getAllEnergyBlocks(Set _selectedGroups, Map _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; + } } diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/ChargeSummary.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/ChargeSummary.java new file mode 100644 index 0000000..b29c8ca --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/ChargeSummary.java @@ -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 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> _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 getSubGroups() { + return subGroups; + } + + public void setSubGroups(List _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 _selectedBreakers) { + return charge(_selectedBreakers, true); + } + + public double charge(Set _selectedBreakers, GridFlow _mode) { + return charge(_selectedBreakers, true, _mode); + } + + public double charge(Set _selectedBreakers, boolean _includeSubgroups) { + return charge(_selectedBreakers, _includeSubgroups, null); + } + + public double charge(Set _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 _selectedGroups) { + return chargeBlocks(_selectedGroups, GridFlow.BOTH); + } + + public float[] chargeBlocks(Set _selectedGroups, GridFlow _flow) { + float[] blocks = new float[blockCount()]; + chargeBlocks(_selectedGroups, blocks, _flow); + return blocks; + } + + private void chargeBlocks(Set _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(); + } +} diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/ChargeTotal.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/ChargeTotal.java new file mode 100644 index 0000000..3e3f828 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/ChargeTotal.java @@ -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 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 getSubGroups() { + return subGroups; + } + + public void setSubGroups(List _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 flatten() { + List totals = new ArrayList<>(); + flatten(totals); + return totals; + } + + private void flatten(List _totals) { + _totals.add(this); + for (ChargeTotal total : CollectionUtils.makeNotNull(subGroups)) { + total.flatten(_totals); + } + } +} diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyBlock.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyBlock.java index c5fa4e6..3585a42 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyBlock.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyBlock.java @@ -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()); - } } diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergySummary.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergySummary.java new file mode 100644 index 0000000..a90f748 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergySummary.java @@ -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 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 _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 _hubPower) { + Map breakers = CollectionUtils.transformToMap(_group.getAllBreakers(), Breaker::getKey); + Map 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 _breakers, Map _breakerKeyToGroup, List _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> 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 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> _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 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 _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 _selectedBreakers) { + return joules(_selectedBreakers) / 3600; + } + + public double joules() { + return joules(null); + } + + public double joules(Set _selectedBreakers) { + return joules(_selectedBreakers, true, GridFlow.BOTH); + } + + public double joules(Set _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 _selectedBreakers) { + return flow(_selectedBreakers, true, GridFlow.BOTH); + } + + public double flow(Set _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 getAllGroups() { + Map groups = new TreeMap<>(); + getAllGroups(groups); + return new ArrayList<>(groups.values()); + } + + public void getAllGroups(Map _groups) { + _groups.put(getGroupId(), this); + for (EnergySummary group : CollectionUtils.makeNotNull(subGroups)) { + group.getAllGroups(_groups); + } + } + + public float[] energyBlocks() { + return energyBlocks(null); + } + + public float[] energyBlocks(Set _selectedGroups) { + return energyBlocks(_selectedGroups, GridFlow.BOTH); + } + + public float[] energyBlocks(Set _selectedGroups, GridFlow _flow) { + float[] blocks = new float[blockCount()]; + energyBlocks(_selectedGroups, blocks, _flow); + return blocks; + } + + private void energyBlocks(Set _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 toChargeSummaries(BreakerConfig _config, List _totals) { + Map 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 _rates, Map _breakerGroupMeters, MutableDouble _monthKwh) { + return toChargeSummaryForRates(_planId, CollectionUtils.filter(_rates, _r->_r.isApplicableForDay(start, timezone)), _breakerGroupMeters, _monthKwh); + } + + private ChargeSummary toChargeSummaryForRates(int _planId, List _rates, Map _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]; + } +} diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerGroupSummary.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyTotal.java similarity index 57% rename from currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerGroupSummary.java rename to currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyTotal.java index 9140444..4979b91 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerGroupSummary.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyTotal.java @@ -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 subGroups; + private List 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 getSubGroups() { + public List 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 _subGroups) { + public void setSubGroups(List _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 getAllGroups() { - Map 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 _groups) { - if (NullUtils.isNotEmpty(getGroupId())) - _groups.put(getGroupId(), this); - for (BreakerGroupSummary group : CollectionUtils.makeNotNull(subGroups)) { - group.getAllGroups(_groups); + public List flatten() { + List totals = new ArrayList<>(); + flatten(totals); + return totals; + } + + private void flatten(List _totals) { + _totals.add(this); + for (EnergyTotal total : CollectionUtils.makeNotNull(subGroups)) { + total.flatten(_totals); } } } diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyBlockViewMode.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyViewMode.java similarity index 73% rename from currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyBlockViewMode.java rename to currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyViewMode.java index 30ba90f..e552d0b 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyBlockViewMode.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergyViewMode.java @@ -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() { diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/HubPowerMinute.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/HubPowerMinute.java index 2eb3137..4b4fbf3 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/HubPowerMinute.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/HubPowerMinute.java @@ -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; diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BillingPlanSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BillingPlanSerializer.java new file mode 100644 index 0000000..39ae30b --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BillingPlanSerializer.java @@ -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 +{ + @Override + public Class getSupportedClass() + { + return BillingPlan.class; + } + + @Override + public List 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; + } +} \ No newline at end of file diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BillingRateSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BillingRateSerializer.java index ec1006b..66d98f8 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BillingRateSerializer.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BillingRateSerializer.java @@ -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 { 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 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")); diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerConfigSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerConfigSerializer.java index 7346bde..6cc4c72 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerConfigSerializer.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerConfigSerializer.java @@ -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 { @@ -88,7 +83,7 @@ public class BreakerGroupEnergySerializer extends AbstractDaoSerializer +{ + @Override + public Class getSupportedClass() + { + return ChargeSummary.class; + } + + @Override + public List 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; + } +} \ No newline at end of file diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/ChargeTotalSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/ChargeTotalSerializer.java new file mode 100644 index 0000000..97b13c7 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/ChargeTotalSerializer.java @@ -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 +{ + @Override + public Class getSupportedClass() + { + return ChargeTotal.class; + } + + @Override + public List 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; + } +} \ No newline at end of file diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/EnergySummarySerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/EnergySummarySerializer.java new file mode 100644 index 0000000..17d5d8a --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/EnergySummarySerializer.java @@ -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 +{ + @Override + public Class getSupportedClass() + { + return EnergySummary.class; + } + + @Override + public List 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; + } +} \ No newline at end of file diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerGroupSummarySerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/EnergyTotalSerializer.java similarity index 59% rename from currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerGroupSummarySerializer.java rename to currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/EnergyTotalSerializer.java index bc7599e..e0d23de 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerGroupSummarySerializer.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/EnergyTotalSerializer.java @@ -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 { +public class EnergyTotalSerializer extends AbstractDaoSerializer { @Override - public Class getSupportedClass() { - return BreakerGroupSummary.class; + public Class getSupportedClass() { + return EnergyTotal.class; } @Override @@ -23,19 +22,16 @@ public class BreakerGroupSummarySerializer extends AbstractDaoSerializer 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 rates = CollectionUtils.filter(config.getBillingRates(), _r->_r.isApplicableForDay(start, tz)); + Map 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)); + } } } diff --git a/rules/lantern-dataaccess-rules/src/main/java/com/lanternsoftware/dataaccess/rules/MongoRulesDataAccess.java b/rules/lantern-dataaccess-rules/src/main/java/com/lanternsoftware/dataaccess/rules/MongoRulesDataAccess.java index f4a46cd..7692e02 100644 --- a/rules/lantern-dataaccess-rules/src/main/java/com/lanternsoftware/dataaccess/rules/MongoRulesDataAccess.java +++ b/rules/lantern-dataaccess-rules/src/main/java/com/lanternsoftware/dataaccess/rules/MongoRulesDataAccess.java @@ -60,6 +60,7 @@ public class MongoRulesDataAccess implements RulesDataAccess { @Override public void putFcmDevice(FcmDevice _device) { + proxy.delete(FcmDevice.class, new DaoQuery("account_id", _device.getAccountId()).and("token", _device.getToken())); proxy.save(_device); } diff --git a/util/lantern-util-common/src/main/java/com/lanternsoftware/util/CollectionUtils.java b/util/lantern-util-common/src/main/java/com/lanternsoftware/util/CollectionUtils.java index 2890b30..1d4d7cd 100644 --- a/util/lantern-util-common/src/main/java/com/lanternsoftware/util/CollectionUtils.java +++ b/util/lantern-util-common/src/main/java/com/lanternsoftware/util/CollectionUtils.java @@ -955,4 +955,48 @@ public class CollectionUtils { return 0; return _arr.length; } + + public static byte[] toByteArray(float[] _floats) { + if ((_floats == null) || (_floats.length == 0)) + return null; + ByteBuffer bb = ByteBuffer.allocate(_floats.length * 4); + for (float f : _floats) { + bb.putFloat(f); + } + return bb.array(); + } + + public static float[] toFloatArray(byte[] _bytes) { + if ((_bytes == null) || (_bytes.length == 0)) + return null; + int offset = 0; + float[] floats = new float[_bytes.length/4]; + ByteBuffer bb = ByteBuffer.wrap(_bytes); + while (bb.hasRemaining()) { + floats[offset++] = bb.getFloat(); + } + return floats; + } + + public static byte[] toByteArray(double[] _doubles) { + if ((_doubles == null) || (_doubles.length == 0)) + return null; + ByteBuffer bb = ByteBuffer.allocate(_doubles.length * 8); + for (double d : _doubles) { + bb.putDouble(d); + } + return bb.array(); + } + + public static double[] toDoubleArray(byte[] _bytes) { + if ((_bytes == null) || (_bytes.length == 0)) + return null; + int offset = 0; + double[] doubles = new double[_bytes.length/8]; + ByteBuffer bb = ByteBuffer.wrap(_bytes); + while (bb.hasRemaining()) { + doubles[offset++] = bb.getDouble(); + } + return doubles; + } } diff --git a/util/lantern-util-common/src/main/java/com/lanternsoftware/util/DateUtils.java b/util/lantern-util-common/src/main/java/com/lanternsoftware/util/DateUtils.java index 815dc15..3b065b5 100644 --- a/util/lantern-util-common/src/main/java/com/lanternsoftware/util/DateUtils.java +++ b/util/lantern-util-common/src/main/java/com/lanternsoftware/util/DateUtils.java @@ -2,6 +2,8 @@ package com.lanternsoftware.util; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -224,6 +226,11 @@ public abstract class DateUtils { return String.format("%d years", iYears); } + public static int getDaysBetween(Date _start, Date _end, TimeZone _tz) { + ZoneId tz = _tz.toZoneId(); + return (int)ChronoUnit.DAYS.between(_start.toInstant().atZone(tz).toLocalDate(), _end.toInstant().atZone(tz).toLocalDate()); + } + public static int getMonthsBetween(Date _dtStart, Date _dtEnd) { Calendar calStart = getGMTCalendar(_dtStart.getTime()); Calendar calEnd = getGMTCalendar(_dtEnd.getTime()); diff --git a/util/lantern-util-common/src/main/java/com/lanternsoftware/util/mutable/MutableDouble.java b/util/lantern-util-common/src/main/java/com/lanternsoftware/util/mutable/MutableDouble.java new file mode 100644 index 0000000..1947621 --- /dev/null +++ b/util/lantern-util-common/src/main/java/com/lanternsoftware/util/mutable/MutableDouble.java @@ -0,0 +1,28 @@ +package com.lanternsoftware.util.mutable; + +public class MutableDouble { + private double value; + + public MutableDouble() { + } + + public MutableDouble(double _value) { + value = _value; + } + + public double getValue() { + return value; + } + + public void setValue(double _value) { + value = _value; + } + + public void add(double _value) { + value += _value; + } + + public void subtract(double _value) { + value -= _value; + } +} diff --git a/util/lantern-util-servlet/src/main/java/com/lanternsoftware/util/servlet/FreemarkerServlet.java b/util/lantern-util-servlet/src/main/java/com/lanternsoftware/util/servlet/FreemarkerServlet.java index 24fa490..ae9b34d 100644 --- a/util/lantern-util-servlet/src/main/java/com/lanternsoftware/util/servlet/FreemarkerServlet.java +++ b/util/lantern-util-servlet/src/main/java/com/lanternsoftware/util/servlet/FreemarkerServlet.java @@ -3,12 +3,14 @@ package com.lanternsoftware.util.servlet; import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.dao.DaoEntity; +import com.lanternsoftware.util.dao.DaoSerializer; import freemarker.template.Configuration; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.HashMap; import java.util.Map; public abstract class FreemarkerServlet extends LanternServlet { @@ -48,6 +50,16 @@ public abstract class FreemarkerServlet extends LanternServlet { protected static DaoEntity model(HttpServletRequest _req) { DaoEntity model = new DaoEntity("context", _req.getContextPath()); + String linkPrefix = ""; + String[] path = getPath(_req); + if (path.length > 1) { + StringBuilder prefix = new StringBuilder(); + for(int i=0; i _templateModel) { + ajaxRender(_rep, _template, _templateModel, null); + } + + protected void ajaxRender(HttpServletResponse _rep, String _templateName, Map _templateModel, Map _jsonRep) { + ajaxHtml(_rep, FreemarkerUtil.render(getFreemarkerConfig(), _templateName, _templateModel), _jsonRep); + + } + + protected static void ajaxHtml(HttpServletResponse _rep, String _html) { + ajaxHtml(_rep, _html, null); + } + + protected static void ajaxHtml(HttpServletResponse _rep, String _html, Map _model) { + if (_model == null) { + _model = new HashMap<>(); + } + _model.put("html", _html); + ajaxJson(_rep, _model); + } + + protected static void ajaxJson(HttpServletResponse _rep, Map _model) { + DaoEntity json = new DaoEntity(_model); + setResponseEntity(_rep, "application/json", DaoSerializer.toJson(json)); + } + + protected void ajaxRedirect(HttpServletResponse _rep, String _url) { + setResponseEntity(_rep, "application/json", DaoSerializer.toJson(new DaoEntity("redirect", _url))); + } + + protected void ajaxError(HttpServletResponse _rep, String _error) { + ajaxError(_rep, _error, null); + } + + protected void ajaxError(HttpServletResponse _rep, String _error, DaoEntity _model) { + if (_model == null) { + _model = new DaoEntity(); + } + _model.put("error", _error); + setResponseEntity(_rep, "application/json", DaoSerializer.toJson(_model, false, false)); + } } diff --git a/zwave/lantern-service-zwave/src/test/java/com/lanternsoftware/zwave/CreateConfig.java b/zwave/lantern-service-zwave/src/test/java/com/lanternsoftware/zwave/CreateConfig.java deleted file mode 100644 index aab2ea8..0000000 --- a/zwave/lantern-service-zwave/src/test/java/com/lanternsoftware/zwave/CreateConfig.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.lanternsoftware.zwave; - -import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao; -import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao; -import com.lanternsoftware.datamodel.zwave.Switch; -import com.lanternsoftware.datamodel.zwave.SwitchSchedule; -import com.lanternsoftware.datamodel.zwave.ZWaveConfig; -import com.lanternsoftware.util.CollectionUtils; -import com.lanternsoftware.util.LanternFiles; -import com.lanternsoftware.util.ResourceLoader; -import com.lanternsoftware.util.dao.DaoSerializer; -import com.lanternsoftware.util.dao.mongo.MongoConfig; -import com.lanternsoftware.zwave.dao.MongoZWaveDao; - -public class CreateConfig { - public static void main(String[] args) { - MongoZWaveDao dao = new MongoZWaveDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg")); - ZWaveConfig config = dao.getConfig(1); -// ZWaveConfig cconfig = DaoSerializer.parse(ResourceLoader.loadFile(LanternFiles.OPS_PATH + "config - christmas lights.dat"), ZWaveConfig.class); -// Switch c = CollectionUtils.filterOne(config.getSwitches(), _s->_s.getName().contains("hristm")); -// CollectionUtils.filterMod(config.getSwitches(), _s->!_s.getRoom().equals("Treehouse")); -// Switch treehouse = new Switch("Treehouse", "Interior", 14, true, true, null, 0); -// Switch to = new Switch("Treehouse", "Floods", 15, true, true, null, 0); -// Switch out = new Switch("Outside", "Repeater Outlet", 10, true, false, null, 0); -// config.getSwitches().add(out); - Switch c = CollectionUtils.filterOne(config.getSwitches(), _s->_s.getName().contains("Agitator")); - c.setName("Septic Aerator"); - dao.putConfig(config); -// if (c != null) { -// c.setNodeId(8); -// dao.putConfig(config); -// } -// ZWaveConfig config = DaoSerializer.parse(ResourceLoader.loadFile(LanternFiles.OPS_PATH + "config.dat"), ZWaveConfig.class); -// config.setAccountId(1); -// config.getSwitches().add(new Switch("Garage", "Septic Agitator", 12, 0, true, false, false, null, 0, Arrays.asList(new SwitchTransition(20)))); -// Switch thermo = CollectionUtils.filterOne(config.getSwitches(), _sw->_sw.getNodeId() == 0); -// if (thermo != null) -// thermo.setNodeId(100); -// config.getSwitches().add(new Switch("Basement", "Temperature", 101, true, false, "https://basement.lanternsoftware.com/thermometer/temp", 0)); - -/* ZWaveConfig config = new ZWaveConfig(); - List switches = new ArrayList<>(); - switches.add(new Switch("Basement", "Main", 3, true, true, null, 0)); - switches.add(new Switch("Basement", "Main", 5, false, true, null, 0)); - switches.add(new Switch("Basement", "Bar", 4, true, true, null, 0)); - switches.add(new Switch("Basement", "Bar", 6, false, true, null, 0)); - switches.add(new Switch("Master Bedroom", "Heater", 7, true, true, "https://thermometer.lanternsoftware.com/thermometer/temp", 0)); - switches.add(new Switch("Bruce's Room", "Heater", 8, true, true, "https://bruce.lanternsoftware.com/thermometer/temp", 0)); - switches.add(new Switch("Master Bedroom", "Heater", 9, false, true, "", 0)); - Switch out = new Switch("Outside", "Christmas Lights", 10, true, false, "", 0); - out.setSchedule(CollectionUtils.asArrayList( - new SwitchTransition(Calendar.FRIDAY, 12, 42, 0, 0), - new SwitchTransition(Calendar.FRIDAY, 12, 42, 10, 0xFF), - new SwitchTransition(Calendar.FRIDAY, 12, 42, 20, 0), - new SwitchTransition(Calendar.FRIDAY, 12, 42, 30, 0xFF), - new SwitchTransition(Calendar.FRIDAY, 12, 42, 40, 0), - new SwitchTransition(Calendar.FRIDAY, 12, 42, 50, 0xFF))); - switches.add(out); - config.setSwitches(switches); - */ -// config.setSwitches(switches); -// Switch sump = CollectionUtils.filterOne(config.getSwitches(), _c->_c.getNodeId() == 12); -// SwitchSchedule transition = CollectionUtils.getFirst(sump.getSchedule()); -// transition.setLevel(0xFF); -// transition.setMinutesPerHour(15); -// dao.putConfig(config); - dao.shutdown(); -// TimeZone tz = TimeZone.getTimeZone("America/Chicago"); -// Date next = transition.getNextTransition(tz); -// System.out.println("Next Transition: " + DateUtils.format(tz, next, "hh:mm:ssa")); - -// ResourceLoader.writeFile(LanternFiles.OPS_PATH + "config.dat", DaoSerializer.toJson(config)); - } -}