diff --git a/currentmonitor/lantern-dataaccess-currentmonitor/pom.xml b/currentmonitor/lantern-dataaccess-currentmonitor/pom.xml index 11cc06a..80a13dc 100644 --- a/currentmonitor/lantern-dataaccess-currentmonitor/pom.xml +++ b/currentmonitor/lantern-dataaccess-currentmonitor/pom.xml @@ -17,6 +17,11 @@ lantern-datamodel-currentmonitor 1.0.0 + + com.lanternsoftware.rules + lantern-datamodel-rules + 1.0.0 + com.lanternsoftware.util lantern-util-dao-mongo 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 new file mode 100644 index 0000000..03f3a2a --- /dev/null +++ b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/Backup.java @@ -0,0 +1,81 @@ +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.Sequence; +import com.lanternsoftware.datamodel.rules.Event; +import com.lanternsoftware.datamodel.rules.FcmDevice; +import com.lanternsoftware.datamodel.rules.Rule; +import com.lanternsoftware.util.DebugTimer; +import com.lanternsoftware.util.LanternFiles; +import com.lanternsoftware.util.dao.mongo.MongoConfig; + +import java.util.List; + +public class Backup { + public static void main(String[] args) { + CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg")); + CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_PATH + "mongo.cfg")); + + DebugTimer t1 = new DebugTimer("Query Accounts"); + List accounts = dao.getProxy().queryAll(Account.class); + t1.stop(); + DebugTimer t2 = new DebugTimer("Save Accounts"); + backupDao.getProxy().save(accounts); + t2.stop(); + + DebugTimer t3 = new DebugTimer("Query Configs"); + List configs = dao.getProxy().queryAll(BreakerConfig.class); + t3.stop(); + DebugTimer t4 = new DebugTimer("Save Configs"); + backupDao.getProxy().save(configs); + t4.stop(); + + DebugTimer t5 = new DebugTimer("Query Energy"); + List energy = dao.getProxy().queryAll(BreakerGroupEnergy.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); + t7.stop(); + DebugTimer t8 = new DebugTimer("Save Summaries"); + backupDao.getProxy().save(summary); + t8.stop(); + + DebugTimer t9 = new DebugTimer("Query Events"); + List events = dao.getProxy().queryAll(Event.class); + t9.stop(); + DebugTimer t10 = new DebugTimer("Save Events"); + backupDao.getProxy().save(events); + t10.stop(); + + DebugTimer t11 = new DebugTimer("Query Devices"); + List devices = dao.getProxy().queryAll(FcmDevice.class); + t11.stop(); + DebugTimer t12 = new DebugTimer("Save Devices"); + backupDao.getProxy().save(devices); + t12.stop(); + + DebugTimer t13 = new DebugTimer("Query Rules"); + List rules = dao.getProxy().queryAll(Rule.class); + t13.stop(); + DebugTimer t14 = new DebugTimer("Save Rules"); + backupDao.getProxy().save(rules); + t14.stop(); + + DebugTimer t15 = new DebugTimer("Query Sequences"); + List sequences = dao.getProxy().queryAll(Sequence.class); + t15.stop(); + DebugTimer t16 = new DebugTimer("Save Sequences"); + backupDao.getProxy().save(sequences); + t16.stop(); + + dao.shutdown(); + backupDao.shutdown(); + } +} diff --git a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/BackupMinutes.java b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/BackupMinutes.java new file mode 100644 index 0000000..5022f49 --- /dev/null +++ b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/BackupMinutes.java @@ -0,0 +1,52 @@ +package com.lanternsoftware.dataaccess.currentmonitor; + +import com.lanternsoftware.datamodel.currentmonitor.Account; +import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; +import com.lanternsoftware.util.DateUtils; +import com.lanternsoftware.util.DebugTimer; +import com.lanternsoftware.util.LanternFiles; +import com.lanternsoftware.util.NullUtils; +import com.lanternsoftware.util.dao.DaoQuery; +import com.lanternsoftware.util.dao.DaoSort; +import com.lanternsoftware.util.dao.mongo.MongoConfig; + +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +public class BackupMinutes { + public static void main(String[] args) { + CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg")); + CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_PATH + "mongo.cfg")); + Date now = new Date(); + for (Account a : dao.getProxy().queryAll(Account.class)) { + if (a.getId() == 100) + continue; + DebugTimer t = new DebugTimer("Account " + a.getId()); + if (NullUtils.isEmpty(a.getTimezone())) { + a.setTimezone("America/Chicago"); + } + TimeZone tz = TimeZone.getTimeZone(a.getTimezone()); +// Date start = DateUtils.addDays(DateUtils.getMidnightBeforeNow(tz), -2, tz); + HubPowerMinute minute = dao.getProxy().queryOne(HubPowerMinute.class, new DaoQuery("account_id", a.getId()), DaoSort.sort("minute")); + if (minute == null) + continue; + Date start = DateUtils.getMidnightBefore(minute.getMinuteAsDate(), tz); + Date end = DateUtils.addDays(start, 1, tz); + while (end.before(now)) { + DebugTimer t2 = new DebugTimer("Account Id: " + a.getId() + " Query Day " + DateUtils.format("MM/dd/yyyy", tz, start)); + List minutes = dao.getProxy().query(HubPowerMinute.class, new DaoQuery("account_id", a.getId()).andBetweenInclusiveExclusive("minute", (int) (start.getTime() / 60000), (int) (end.getTime() / 60000))); + t2.stop(); + if (!minutes.isEmpty()) { + DebugTimer t3 = new DebugTimer("Save Day"); + backupDao.getProxy().save(minutes); + t3.stop(); + } + start = end; + end = DateUtils.addDays(end, 1, tz); + } + t.stop(); + } + dao.shutdown(); + } +} 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 7a51e5b..24fe45c 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 @@ -31,6 +31,8 @@ public interface CurrentMonitorDao { void putConfig(BreakerConfig _config); void updateSummaries(BreakerGroup _rootGroup, Set _daysToSummarize, TimeZone _tz); + void rebuildSummaries(int _accountId); + void rebuildSummaries(int _accountId, Date _start, Date _end); String addPasswordResetKey(String _email); String getEmailForResetKey(String _key); 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 7a70a38..02a14e0 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,7 +1,6 @@ package com.lanternsoftware.dataaccess.currentmonitor; import com.lanternsoftware.datamodel.currentmonitor.Account; -import com.lanternsoftware.util.dao.auth.AuthCode; import com.lanternsoftware.datamodel.currentmonitor.Breaker; import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig; import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup; @@ -20,6 +19,7 @@ import com.lanternsoftware.util.dao.DaoEntity; import com.lanternsoftware.util.dao.DaoQuery; import com.lanternsoftware.util.dao.DaoSerializer; 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 org.mindrot.jbcrypt.BCrypt; @@ -96,12 +96,14 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao { BreakerConfig config = getConfig(_minute.getAccountId()); BreakerGroup group = CollectionUtils.getFirst(config.getBreakerGroups()); Date day = DateUtils.getMidnightBefore(_minute.getMinuteAsDate(), tz); - BreakerGroupEnergy summary = getBreakerGroupEnergy(_minute.getAccountId(), group.getId(), EnergyBlockViewMode.DAY, day); - if (summary == null) - summary = new BreakerGroupEnergy(group, minutes, EnergyBlockViewMode.DAY, day, 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))); + if (energy == null) + energy = new BreakerGroupEnergy(group, minutes, EnergyBlockViewMode.DAY, day, month, config.getBillingRates(), tz); else - summary.addEnergy(group, minutes); - putBreakerGroupEnergy(summary); + energy.addEnergy(group, minutes, month, config.getBillingRates()); + putBreakerGroupEnergy(energy); updateSummaries(group, CollectionUtils.asHashSet(day), tz); timer.stop(); } @@ -144,7 +146,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao { List groupEnergyIds = new ArrayList<>(); while (calMonthStart.before(end)) { groupEnergyIds.add(BreakerGroupEnergy.toId(_rootGroup.getAccountId(), _rootGroup.getId(), EnergyBlockViewMode.MONTH, calMonthStart.getTime())); - calMonthStart.add(Calendar.DAY_OF_YEAR, 1); + 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); @@ -157,6 +159,42 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao { putBreakerGroupEnergy(summary); } + @Override + public void rebuildSummaries(int _accountId) { + HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sort("minute")); + if (firstMinute == null) + return; + rebuildSummaries(_accountId, firstMinute.getMinuteAsDate(), new Date()); + } + + @Override + public void rebuildSummaries(int _accountId, Date _start, Date _end) { + 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()); + proxy.delete(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, root.getId(), EnergyBlockViewMode.MONTH, monthStart))); + 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); + start = DateUtils.addDays(start, 1, tz); + } + } + @Override public void putBreakerGroupEnergy(BreakerGroupEnergy _energy) { proxy.save(_energy); @@ -179,6 +217,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.setBillingRates(CollectionUtils.aggregate(configs, BreakerConfig::getBillingRates)); return config; } 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 new file mode 100644 index 0000000..dbf1517 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingCurrency.java @@ -0,0 +1,26 @@ +package com.lanternsoftware.datamodel.currentmonitor; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public enum BillingCurrency { + DOLLAR("$"), + EURO("€"), + POUND_STERLING("£"); + + public final String symbol; + + BillingCurrency(String _symbol) { + symbol = _symbol; + } + + public String format(double _value) { + return format(BigDecimal.valueOf(_value)); + } + + public String format(BigDecimal _value) { + if (_value.compareTo(BigDecimal.ZERO) < 0) + return "-" + symbol + _value.abs().setScale(2, RoundingMode.HALF_EVEN); + return symbol + _value.setScale(2, RoundingMode.HALF_EVEN); + } +} diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingMode.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingMode.java new file mode 100644 index 0000000..1e52ef7 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingMode.java @@ -0,0 +1,6 @@ +package com.lanternsoftware.datamodel.currentmonitor; + +public enum BillingMode { + CONSUMPTION, + PRODUCTION; +} 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 new file mode 100644 index 0000000..b834ee3 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BillingRate.java @@ -0,0 +1,179 @@ +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.TimeZone; + +@DBSerializable +public class BillingRate { + private int meter; + private int dayBillingCycleStart; + private BillingMode mode; + private double rate; + private BillingCurrency currency; + private int timeOfDayStart; + private int timeOfDayEnd; + private double monthKWhStart; + private double monthKWhEnd; + private Date beginEffective; + private Date endEffective; + boolean recursAnnually; + + public int getMeter() { + return meter; + } + + public void setMeter(int _meter) { + meter = _meter; + } + + public int getDayBillingCycleStart() { + return dayBillingCycleStart; + } + + public void setDayBillingCycleStart(int _dayBillingCycleStart) { + dayBillingCycleStart = _dayBillingCycleStart; + } + + public BillingMode getMode() { + return mode; + } + + public void setMode(BillingMode _mode) { + mode = _mode; + } + + public double getRate() { + return rate; + } + + public void setRate(double _rate) { + rate = _rate; + } + + public BillingCurrency getCurrency() { + return currency; + } + + public void setCurrency(BillingCurrency _currency) { + currency = _currency; + } + + public int getTimeOfDayStart() { + return timeOfDayStart; + } + + public void setTimeOfDayStart(int _timeOfDayStart) { + timeOfDayStart = _timeOfDayStart; + } + + public int getTimeOfDayEnd() { + return timeOfDayEnd; + } + + public void setTimeOfDayEnd(int _timeOfDayEnd) { + timeOfDayEnd = _timeOfDayEnd; + } + + public double getMonthKWhStart() { + return monthKWhStart; + } + + public void setMonthKWhStart(double _monthKWhStart) { + monthKWhStart = _monthKWhStart; + } + + public double getMonthKWhEnd() { + return monthKWhEnd; + } + + public void setMonthKWhEnd(double _monthKWhEnd) { + monthKWhEnd = _monthKWhEnd; + } + + public Date getBeginEffective() { + return beginEffective; + } + + public void setBeginEffective(Date _beginEffective) { + beginEffective = _beginEffective; + } + + public Date getEndEffective() { + return endEffective; + } + + public void setEndEffective(Date _endEffective) { + endEffective = _endEffective; + } + + public boolean isRecursAnnually() { + return recursAnnually; + } + + public void setRecursAnnually(boolean _recursAnnually) { + recursAnnually = _recursAnnually; + } + + public boolean isApplicable(BillingMode _mode, int _meter, double _monthKWh, Date _time, TimeZone _tz) { + if (mode != _mode) + return false; + if (_meter != meter) + return false; + if ((monthKWhStart > 0) && (_monthKWh < monthKWhStart)) + return false; + if ((monthKWhEnd > 0) && (_monthKWh >= monthKWhEnd)) + return false; + if ((beginEffective != null) && (endEffective != null) && recursAnnually) { + Date begin = beginEffective; + Date end = endEffective; + while (_time.before(begin)) { + begin = DateUtils.addYears(begin, -1, _tz); + end = DateUtils.addYears(end, -1, _tz); + } + while (_time.after(end)) { + begin = DateUtils.addYears(begin, 1, _tz); + end = DateUtils.addYears(end, 1, _tz); + } + if (!DateUtils.isBetween(_time, begin, end)) + return false; + } + else { + if ((beginEffective != null) && _time.before(beginEffective)) + return false; + 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); + } + + public double apply(double _kWh) { + return rate * _kWh; + } + + public BillingRate duplicate() { + BillingRate r = new BillingRate(); + r.setMeter(meter); + r.setDayBillingCycleStart(dayBillingCycleStart); + r.setMode(mode); + r.setRate(rate); + r.setCurrency(currency); + r.setTimeOfDayStart(timeOfDayStart); + r.setTimeOfDayEnd(timeOfDayEnd); + r.setMonthKWhStart(monthKWhStart); + r.setMonthKWhEnd(monthKWhEnd); + r.setBeginEffective(beginEffective); + r.setEndEffective(endEffective); + r.setRecursAnnually(recursAnnually); + return r; + } +} 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 49e204a..178f6ee 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 @@ -17,6 +17,7 @@ public class BreakerConfig { private List panels; private List breakerHubs; private List breakerGroups; + private List billingRates; private int version; public BreakerConfig() { @@ -66,6 +67,14 @@ public class BreakerConfig { breakerGroups = _breakerGroups; } + public List getBillingRates() { + return billingRates; + } + + public void setBillingRates(List _billingRates) { + billingRates = _billingRates; + } + public int getVersion() { return version; } @@ -169,4 +178,8 @@ public class BreakerConfig { } return null; } + + public BillingCurrency getCurrency() { + return CollectionUtils.getFirst(CollectionUtils.transformToSet(billingRates, BillingRate::getCurrency)); + } } 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 fb76616..32e650f 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 @@ -7,12 +7,14 @@ import com.lanternsoftware.util.dao.DaoSerializer; import com.lanternsoftware.util.dao.annotations.DBSerializable; 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.Objects; import java.util.Set; import java.util.TimeZone; import java.util.TreeMap; @@ -33,36 +35,18 @@ public class BreakerGroupEnergy { public BreakerGroupEnergy() { } - public BreakerGroupEnergy(BreakerGroup _group, Map> _powerReadings, EnergyBlockViewMode _viewMode, Date _start, TimeZone _timezone) { + 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, _powerReadings, _viewMode, _start, timezone)); - energyBlocks = new ArrayList<>(); - List breakerKeys = CollectionUtils.transform(_group.getBreakers(), Breaker::getKey); - if (!breakerKeys.isEmpty()) { - for (BreakerPower power : CollectionUtils.aggregate(breakerKeys, _powerReadings::get)) { - addEnergy(groupId, power.getReadTime(), power.getPower()); - } - } + subGroups = CollectionUtils.transform(_group.getSubGroups(), _g -> new BreakerGroupEnergy(_g, null, _viewMode, _start, _month, _rates, timezone)); + addEnergy(_group, _power, _month, _rates); } - public BreakerGroupEnergy(BreakerGroup _group, List _power, EnergyBlockViewMode _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 BreakerGroupEnergy(_g, (List)null, _viewMode, _start, timezone)); - energyBlocks = new ArrayList<>(); - addEnergy(_group, _power); - } - - public void addEnergy(BreakerGroup _group, List _hubPower) { + 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()) { @@ -70,16 +54,20 @@ public class BreakerGroupEnergy { breakerKeyToGroup.put(b.getKey(), group); } } - addEnergy(breakers, breakerKeyToGroup, _hubPower); + addEnergy(breakers, breakerKeyToGroup, _hubPower, _month, _rates); } - public void addEnergy(Map _breakers, Map _breakerKeyToGroup, List _hubPower) { + 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; - Date minute = CollectionUtils.getFirst(_hubPower).getMinuteAsDate(); - resetEnergy(minute); - Map meters = new HashMap<>(); + _hubPower.sort(Comparator.comparing(HubPowerMinute::getMinute)); + for (Date minute : CollectionUtils.transformToSet(_hubPower, HubPowerMinute::getMinuteAsDate)) { + resetEnergy(minute); + } + int idx; + Map meters = new HashMap<>(); for (HubPowerMinute hubPower : _hubPower) { + Date minute = hubPower.getMinuteAsDate(); for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) { Breaker b = _breakers.get(breaker.breakerKey()); if (b == null) @@ -87,26 +75,68 @@ public class BreakerGroupEnergy { BreakerGroup group = _breakerKeyToGroup.get(breaker.breakerKey()); if (group == null) continue; - MeterMinute meter = meters.computeIfAbsent(b.getMeter(), _p->new MeterMinute()); - int idx = 0; - for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) { - if (power > 0) - meter.usage[idx] += power; - else - meter.solar[idx] += -power; - if (power != 0.0) - addEnergy(group.getId(), minute, power); - idx++; + MeterMinuteValues meter = meters.computeIfAbsent(new MeterMinute(b.getMeter(), minute), _p->new MeterMinuteValues()); + 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++; + } } } } - - for (MeterMinute meter : meters.values()) { + double monthFromGrid = _month == null ? 0.0 : _month.getFromGrid(); + double secondFromGrid; + for (Map.Entry meter : meters.entrySet()) { + double monthkWh = monthFromGrid/3600000; + List consumptionRates = CollectionUtils.filter(_rates, _r->_r.isApplicable(BillingMode.CONSUMPTION, meter.getKey().meter, monthkWh, meter.getKey().minute, timezone)); + List productionRates = CollectionUtils.filter(_rates, _r->_r.isApplicable(BillingMode.PRODUCTION, meter.getKey().meter, monthkWh, meter.getKey().minute, timezone)); for (int i = 0; i < 60; i++) { - if (meter.usage[i] > meter.solar[i]) - fromGrid += meter.usage[i] - meter.solar[i]; - else - toGrid += meter.solar[i] - meter.usage[i]; + secondFromGrid = meter.getValue().usage[i] - meter.getValue().solar[i]; + monthFromGrid += secondFromGrid; + if (secondFromGrid > 0) { + fromGrid += secondFromGrid; + for (BillingRate rate : consumptionRates) { + meter.getValue().charges[i] += rate.apply(secondFromGrid/3600000); + } + } + else { + toGrid -= secondFromGrid; + for (BillingRate rate : productionRates) { + meter.getValue().charges[i] += rate.apply(secondFromGrid/3600000); + } + } + } + } + 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; + MeterMinuteValues meter = meters.get(new MeterMinute(b.getMeter(), minute)); + 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); } } } @@ -120,16 +150,31 @@ public class BreakerGroupEnergy { } } - public void addEnergy(String _groupId, Date _readTime, double _joules) { + private EnergyBlock getBlock(String _groupId, Date _readTime) { if (NullUtils.isEqual(groupId, _groupId)) - getBlock(_readTime).addJoules(_joules); + return getBlock(_readTime); else { for (BreakerGroupEnergy subGroup : CollectionUtils.makeNotNull(subGroups)) { - subGroup.addEnergy(_groupId, _readTime, _joules); + EnergyBlock block = subGroup.getBlock(_groupId, _readTime); + if (block != null) + return block; } + return null; } } + private void addEnergy(String _groupId, Date _readTime, double _joules) { + EnergyBlock block = getBlock(_groupId, _readTime); + if (block != null) + block.addJoules(_joules); + } + + private void addCharge(String _groupId, Date _readTime, double _charge) { + EnergyBlock block = getBlock(_groupId, _readTime); + if (block != null) + block.addCharge(_charge); + } + public static BreakerGroupEnergy summary(BreakerGroup _group, Map> _energies, EnergyBlockViewMode _viewMode, Date _start, TimeZone _tz) { BreakerGroupEnergy energy = new BreakerGroupEnergy(); energy.setGroupId(_group.getId()); @@ -142,6 +187,7 @@ public class BreakerGroupEnergy { 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()); } @@ -154,10 +200,10 @@ public class BreakerGroupEnergy { private EnergyBlock getBlock(Date _readTime, boolean _add) { int size = CollectionUtils.size(energyBlocks); - int idx = viewMode.blockIndex(_readTime, timezone); + int idx = viewMode.blockIndex(start, _readTime, timezone); if (_add && (idx >= size)) { if (energyBlocks == null) - energyBlocks = new ArrayList<>(); + energyBlocks = new ArrayList<>(viewMode.initBlockCount()); LinkedList newBlocks = new LinkedList<>(); Date end = viewMode.toBlockEnd(_readTime, timezone); while (idx >= size) { @@ -297,6 +343,38 @@ public class BreakerGroupEnergy { return joules; } + public double charge() { + return charge(null); + } + + public double charge(Set _selectedBreakers) { + return charge(_selectedBreakers, true); + } + + public double charge(Set _selectedBreakers, BillingMode _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, BillingMode _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 == BillingMode.PRODUCTION) && energy.getCharge() < 0.0) || (_mode == BillingMode.CONSUMPTION && energy.getCharge() > 0.0)) + charge += energy.getCharge(); + } + } + return charge; + } + public List getAllGroups() { Map groups = new TreeMap<>(); getAllGroups(groups); @@ -343,7 +421,31 @@ public class BreakerGroupEnergy { } private static class MeterMinute { + private final int meter; + private final Date minute; + + public MeterMinute(int _meter, Date _minute) { + meter = _meter; + minute = _minute; + } + + @Override + public boolean equals(Object _o) { + if (this == _o) return true; + if (_o == null || getClass() != _o.getClass()) return false; + MeterMinute that = (MeterMinute) _o; + return meter == that.meter && minute.equals(that.minute); + } + + @Override + public int hashCode() { + return Objects.hash(meter, minute); + } + } + + private static class MeterMinuteValues { public double[] usage = new double[60]; public double[] solar = new double[60]; + public double[] charges = 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/BreakerGroupSummary.java index b881da2..e9882fd 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/BreakerGroupSummary.java @@ -21,6 +21,7 @@ public class BreakerGroupSummary { private Date start; private List subGroups; private double joules; + private double charge; private double toGrid; private double fromGrid; @@ -35,6 +36,7 @@ public class BreakerGroupSummary { 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(); } @@ -107,6 +109,14 @@ public class BreakerGroupSummary { joules = _joules; } + public double getCharge() { + return charge; + } + + public void setCharge(double _charge) { + charge = _charge; + } + public double getToGrid() { return toGrid; } 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 188ee26..c5fa4e6 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 @@ -10,6 +10,7 @@ public class EnergyBlock { private Date start; private Date end; private double joules; + private double charge; public EnergyBlock() { } @@ -48,6 +49,17 @@ public class EnergyBlock { joules = _joules; } + public double getCharge() { + return charge; + } + + public void setCharge(double _charge) { + charge = _charge; + } + public void addCharge(double _charge) { + charge += _charge; + } + public double wattHours() { return joules / 3600; } 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/EnergyBlockViewMode.java index 53c5860..30ba90f 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/EnergyBlockViewMode.java @@ -109,10 +109,19 @@ public enum EnergyBlockViewMode { return blockCnt; } - public int blockIndex(Date _readTime, TimeZone _tz) { + public int initBlockCount() { + if (this == ALL) + return 1; + if (this == YEAR) + return 12; + if (this == MONTH) + return 31; + return 1500; + } + + public int blockIndex(Date _dayStart, Date _readTime, TimeZone _tz) { if (this == DAY) { - Date start = DateUtils.getMidnightBefore(_readTime, _tz); - return (int)((_readTime.getTime() - start.getTime())/60000); + return (int)((_readTime.getTime() - _dayStart.getTime())/60000); } else if (this == MONTH) { Calendar read = DateUtils.toCalendar(_readTime, _tz); diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/HubConfigCharacteristic.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/HubConfigCharacteristic.java index 4e0aebf..f3e4c73 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/HubConfigCharacteristic.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/HubConfigCharacteristic.java @@ -45,4 +45,12 @@ public enum HubConfigCharacteristic { public boolean isChar(String _char) { return NullUtils.isEqual(name(), _char); } + + public static HubConfigCharacteristic fromUUID(UUID _uuid) { + for (HubConfigCharacteristic c : values()) { + if (c.uuid.equals(_uuid)) + return c; + } + return null; + } } 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 new file mode 100644 index 0000000..7711e2c --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BillingRateSerializer.java @@ -0,0 +1,63 @@ +package com.lanternsoftware.datamodel.currentmonitor.dao; + +import com.lanternsoftware.datamodel.currentmonitor.BillingCurrency; +import com.lanternsoftware.datamodel.currentmonitor.BillingMode; +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 BillingRateSerializer extends AbstractDaoSerializer +{ + @Override + public Class getSupportedClass() + { + return BillingRate.class; + } + + @Override + public List getSupportedProxies() { + return Collections.singletonList(DaoProxyType.MONGO); + } + + @Override + public DaoEntity toDaoEntity(BillingRate _o) + { + DaoEntity d = new DaoEntity(); + d.put("meter", _o.getMeter()); + d.put("day_billing_cycle_start", _o.getDayBillingCycleStart()); + d.put("mode", DaoSerializer.toEnumName(_o.getMode())); + d.put("rate", _o.getRate()); + d.put("unit", 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()); + d.put("month_kwh_end", _o.getMonthKWhEnd()); + d.put("begin_effective", DaoSerializer.toLong(_o.getBeginEffective())); + d.put("end_effective", DaoSerializer.toLong(_o.getEndEffective())); + d.put("recurs_annually", _o.isRecursAnnually()); + return d; + } + + @Override + public BillingRate fromDaoEntity(DaoEntity _d) + { + BillingRate o = new BillingRate(); + o.setMeter(DaoSerializer.getInteger(_d, "meter")); + o.setDayBillingCycleStart(DaoSerializer.getInteger(_d, "day_billing_cycle_start")); + o.setMode(DaoSerializer.getEnum(_d, "mode", BillingMode.class)); + o.setRate(DaoSerializer.getDouble(_d, "rate")); + 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")); + o.setMonthKWhEnd(DaoSerializer.getDouble(_d, "month_kwh_end")); + o.setBeginEffective(DaoSerializer.getDate(_d, "begin_effective")); + o.setEndEffective(DaoSerializer.getDate(_d, "end_effective")); + o.setRecursAnnually(DaoSerializer.getBoolean(_d, "recurs_annually")); + return o; + } +} \ No newline at end of file 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 95f9052..7346bde 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.BillingRate; import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig; import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup; import com.lanternsoftware.datamodel.currentmonitor.BreakerHub; @@ -35,6 +36,7 @@ public class BreakerConfigSerializer extends AbstractDaoSerializer 0) { + ByteBuffer bb = ByteBuffer.wrap(chargeData); + while (bb.hasRemaining()) { + blocks.get(idx++).setCharge(bb.getDouble()); + } + } o.setToGrid(DaoSerializer.getDouble(_d, "to_grid")); o.setFromGrid(DaoSerializer.getDouble(_d, "from_grid")); return o; 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/BreakerGroupSummarySerializer.java index 05a6008..72a8c20 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/BreakerGroupSummarySerializer.java @@ -33,6 +33,7 @@ public class BreakerGroupSummarySerializer extends AbstractDaoSerializer d.put("start", DaoSerializer.toLong(_o.getStart())); d.put("end", DaoSerializer.toLong(_o.getEnd())); d.put("joules", _o.getJoules()); + d.put("charge", _o.getCharge()); return d; } @@ -38,6 +39,7 @@ public class EnergyBlockSerializer extends AbstractDaoSerializer o.setStart(DaoSerializer.getDate(_d, "start")); o.setEnd(DaoSerializer.getDate(_d, "end")); o.setJoules(DaoSerializer.getDouble(_d, "joules")); + o.setCharge(DaoSerializer.getDouble(_d, "charge")); return o; } } \ No newline at end of file diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/resources/META-INF/services/com.lanternsoftware.util.dao.IDaoSerializer b/currentmonitor/lantern-datamodel-currentmonitor/src/main/resources/META-INF/services/com.lanternsoftware.util.dao.IDaoSerializer index 4bfec17..67b6179 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/resources/META-INF/services/com.lanternsoftware.util.dao.IDaoSerializer +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/resources/META-INF/services/com.lanternsoftware.util.dao.IDaoSerializer @@ -1,4 +1,5 @@ com.lanternsoftware.datamodel.currentmonitor.dao.AccountSerializer +com.lanternsoftware.datamodel.currentmonitor.dao.BillingRateSerializer com.lanternsoftware.datamodel.currentmonitor.dao.BreakerConfigSerializer com.lanternsoftware.datamodel.currentmonitor.dao.BreakerGroupEnergySerializer com.lanternsoftware.datamodel.currentmonitor.dao.BreakerGroupSerializer diff --git a/currentmonitor/lantern-service-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/RebuildSummaries.java b/currentmonitor/lantern-service-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/RebuildSummaries.java index d491f9c..bf97e22 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/RebuildSummaries.java +++ b/currentmonitor/lantern-service-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/RebuildSummaries.java @@ -2,98 +2,24 @@ package com.lanternsoftware.currentmonitor; import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao; import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao; -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.EnergyBlockViewMode; -import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; -import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.DateUtils; -import com.lanternsoftware.util.DebugTimer; import com.lanternsoftware.util.LanternFiles; -import com.lanternsoftware.util.dao.DaoQuery; import com.lanternsoftware.util.dao.mongo.MongoConfig; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.TimeZone; public class RebuildSummaries { public static void main(String[] args) { - int accountId = 1; CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg")); - TimeZone tz = dao.getTimeZoneForAccount(accountId); - Date start = DateUtils.date(1, 7, 2021, tz); -// Date start = DateUtils.getMidnightBeforeNow(tz); - Date end = DateUtils.getMidnightAfterNow(tz); - Map> days = CollectionUtils.transformToMultiMap(dao.getProxy().query(HubPowerMinute.class, new DaoQuery("account_id", accountId).andBetweenInclusiveExclusive("minute", (int)(start.getTime()/60000), (int)(end.getTime()/60000))), _m->DateUtils.getMidnightBefore(_m.getMinuteAsDate(), tz)); - BreakerConfig config = dao.getConfig(accountId); - BreakerGroup root = CollectionUtils.getFirst(config.getBreakerGroups()); - Map breakers = CollectionUtils.transformToMap(root.getAllBreakers(), Breaker::getKey); - Map breakerKeyToGroup = new HashMap<>(); - for (BreakerGroup group : root.getAllBreakerGroups()) { - for (Breaker b : group.getAllBreakers()) { - breakerKeyToGroup.put(b.getKey(), group); - } + TimeZone tz = TimeZone.getTimeZone("America/Chicago"); + dao.rebuildSummaries(100, DateUtils.date(8,3,2021, tz), DateUtils.date(8,5,2021, tz)); +/* List accounts = dao.getProxy().queryAll(Account.class); + for (int accountId : CollectionUtils.transform(accounts, Account::getId)) { + if (accountId != 100) + continue; + dao.rebuildSummaries(accountId, DateUtils.date(4,21,2021, tz), DateUtils.date(4,22,2021, tz)); } - - for (Map.Entry> day : days.entrySet()) { - BreakerGroupEnergy energy = null; - DebugTimer timer = new DebugTimer("Time to rebuild one day"); - Map> minutes = CollectionUtils.transformToMultiMap(day.getValue(), HubPowerMinute::getMinute); - for (List minute : minutes.values()) { - if (energy == null) - energy = new BreakerGroupEnergy(root, minute, EnergyBlockViewMode.DAY, day.getKey(), tz); - else - energy.addEnergy(breakers, breakerKeyToGroup, minute); - } - timer.stop(); - if (energy != null) - dao.putBreakerGroupEnergy(energy); - } - dao.updateSummaries(root, days.keySet(), tz); - -// List summaries = dao.getProxy().query(BreakerGroupEnergy.class, null); -// CollectionUtils.edit(summaries, _s->CollectionUtils.edit(_s.getAllGroups(), _t->_t.setAccountId(1))); -// dao.getProxy().save(summaries); - -// List readings = null; -// while ((readings == null) || !readings.isEmpty()) { -// readings = dao.getProxy().query(BreakerPower.class, new DaoQuery("account_id", new DaoQuery("$ne", 1)), null, null, 0, 1000000); -// System.out.println("Adding account id to " + readings.size() + " power readings"); -// CollectionUtils.edit(readings, _s -> _s.setAccountId(1)); -// dao.getProxy().save(readings); -// } -// -// List archives = null; -// while ((archives == null) || !archives.isEmpty()) { -// archives = dao.getProxy().query(BreakerPowerArchive.class, new DaoQuery("account_id", new DaoQuery("$ne", 1)), null, null, 0, 50); -// System.out.println("Adding account id to " + archives.size() + " archives"); -// CollectionUtils.edit(archives, _s -> _s.setAccountId(1)); -// dao.getProxy().save(archives); -// } - -// List readings = CollectionUtils.filter(dao.getBreakerPower(Arrays.asList("0-1", "0-2"), DateUtils.date(6,26,2020, 17, 0, 0, 0, tz), DateUtils.date(6,26,2020, 22, 0, 0, 0, tz)), _p->_p.getPower() > 0.0); -// CollectionUtils.edit(readings, _p->_p.setPower(-_p.getPower())); -// dao.getProxy().save(readings); - -// Map> dups = CollectionUtils.transformToMultiMap(dao.getBreakerPower(Arrays.asList("2-1","2-2","2-3","2-4","2-5","2-6","2-7","2-8","2-9","2-10","2-11","2-12","2-13","2-14","2-15"), DateUtils.date(6,26,2020, 17, 0, 0, 0, tz), DateUtils.date(6,26,2020, 18, 0, 0, 0, tz)), _p->_p.getKey()+_p.getReadTime().getTime()); -// for (List dup : dups.values()) { -// if (dup.size() > 1) { -// CollectionUtils.removeFirst(dup); -// dao.getProxy().delete(BreakerPower.class, DaoQuery.in("_id", CollectionUtils.transform(dup, BreakerPower::getId))); -// } -// } - -// List summaries = dao.getProxy().query(BreakerGroupEnergy.class, null); -// ResourceLoader.writeFile(LanternFiles.OPS_PATH + "summaryBackup.json", DaoSerializer.toJson(DaoSerializer.toDaoEntities(summaries))); -// for (BreakerGroupEnergy summary : summaries) { -// dao.getProxy().save(summary); -// } + */ dao.shutdown(); } } diff --git a/util/lantern-util-common/src/main/java/com/lanternsoftware/util/LanternFiles.java b/util/lantern-util-common/src/main/java/com/lanternsoftware/util/LanternFiles.java index c4d5f47..cfb4294 100644 --- a/util/lantern-util-common/src/main/java/com/lanternsoftware/util/LanternFiles.java +++ b/util/lantern-util-common/src/main/java/com/lanternsoftware/util/LanternFiles.java @@ -2,10 +2,9 @@ package com.lanternsoftware.util; public abstract class LanternFiles { public static final String SOURCE_PATH = "C:\\lantern\\LanternPowerMonitor\\"; -// public static final String OPS_PATH = "D:\\zwave\\"; // public static final String OPS_PATH = "D:\\zwave\\localhost\\"; // public static final String OPS_PATH = "D:\\zwave\\mark4770\\"; -// public static final String OPS_PATH = "D:\\zwave\\prod\\"; // public static final String OPS_PATH = "D:\\zwave\\prodremote\\"; public static final String OPS_PATH = "/opt/tomcat/"; + public static final String BACKUP_PATH = "D:\\zwave\\localhost\\"; } diff --git a/zwave/lantern-datamodel-zwave/src/main/java/com/lanternsoftware/datamodel/zwave/Switch.java b/zwave/lantern-datamodel-zwave/src/main/java/com/lanternsoftware/datamodel/zwave/Switch.java index 8cd6dd6..3c359a9 100644 --- a/zwave/lantern-datamodel-zwave/src/main/java/com/lanternsoftware/datamodel/zwave/Switch.java +++ b/zwave/lantern-datamodel-zwave/src/main/java/com/lanternsoftware/datamodel/zwave/Switch.java @@ -20,6 +20,7 @@ public class Switch { private boolean primary; private boolean hold; private boolean hidden; + private boolean suppressEvents; private String thermometerUrl; private String controllerUrl; private ThermostatMode thermostatMode; @@ -199,6 +200,14 @@ public class Switch { hidden = _hidden; } + public boolean isSuppressEvents() { + return suppressEvents; + } + + public void setSuppressEvents(boolean _suppressEvents) { + suppressEvents = _suppressEvents; + } + public List getSchedule() { return schedule; } diff --git a/zwave/lantern-datamodel-zwave/src/main/java/com/lanternsoftware/datamodel/zwave/dao/SwitchSerializer.java b/zwave/lantern-datamodel-zwave/src/main/java/com/lanternsoftware/datamodel/zwave/dao/SwitchSerializer.java index 3b197dd..d1b70aa 100644 --- a/zwave/lantern-datamodel-zwave/src/main/java/com/lanternsoftware/datamodel/zwave/dao/SwitchSerializer.java +++ b/zwave/lantern-datamodel-zwave/src/main/java/com/lanternsoftware/datamodel/zwave/dao/SwitchSerializer.java @@ -38,6 +38,7 @@ public class SwitchSerializer extends AbstractDaoSerializer d.put("primary", _o.isPrimary()); d.put("hold", _o.isHold()); d.put("hidden", _o.isHidden()); + d.put("suppress_events", _o.isSuppressEvents()); d.put("thermometer_url", _o.getThermometerUrl()); d.put("controller_url", _o.getControllerUrl()); d.put("thermostat_mode", DaoSerializer.toEnumName(_o.getThermostatMode())); @@ -60,6 +61,7 @@ public class SwitchSerializer extends AbstractDaoSerializer o.setPrimary(DaoSerializer.getBoolean(_d, "primary")); o.setHold(DaoSerializer.getBoolean(_d, "hold")); o.setHidden(DaoSerializer.getBoolean(_d, "hidden")); + o.setSuppressEvents(DaoSerializer.getBoolean(_d, "suppress_events")); o.setThermometerUrl(DaoSerializer.getString(_d, "thermometer_url")); o.setControllerUrl(DaoSerializer.getString(_d, "controller_url")); o.setThermostatMode(DaoSerializer.getEnum(_d, "thermostat_mode", ThermostatMode.class)); diff --git a/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java b/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java index 46f0c1d..ac5799a 100644 --- a/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java +++ b/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java @@ -286,7 +286,7 @@ public class ZWaveApp { } public void fireSwitchLevelEvent(Switch _sw) { - if (NullUtils.isEmpty(config.getRulesUrl())) + if (NullUtils.isEmpty(config.getRulesUrl()) || _sw.isSuppressEvents()) return; Event event = new Event(); event.setEventDescription(_sw.getFullDisplay() + " set to " + _sw.getLevel()); @@ -325,7 +325,7 @@ public class ZWaveApp { relayController.setRelay(sw.getGpioPin(), sw.getLowLevel() > 0); } }, 250); - } else { + } else if (!sw.isSecurity()){ setGroupSwitchLevel(sw, _level); } } @@ -351,7 +351,7 @@ public class ZWaveApp { } public void updateSwitch(Switch _sw) { - logger.info("Received update for switch {} level {}", _sw.getFullDisplay(), _sw.getLevel()); + logger.info("Received update for switch {} {} level {}", _sw.getNodeId(), _sw.getFullDisplay(), _sw.getLevel()); Switch sw = CollectionUtils.filterOne(config.getSwitches(), _s->_s.getNodeId() == _sw.getNodeId()); if (sw != null) { sw.setLevel( _sw.getLevel()); @@ -395,6 +395,7 @@ public class ZWaveApp { peers.remove(config.getUrl()); for (String peer : peers) { for (Switch sw : modified) { + logger.info("Sending update for switch {} {} level {} to {}", sw.getNodeId(), sw.getFullDisplay(), sw.getLevel(), peer); HttpPost post = new HttpPost(peer + "/switch/" + sw.getNodeId()); post.setHeader("auth_code", authCode); post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(sw))); diff --git a/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/security/SecurityController.java b/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/security/SecurityController.java index e619394..449379f 100644 --- a/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/security/SecurityController.java +++ b/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/security/SecurityController.java @@ -1,6 +1,7 @@ package com.lanternsoftware.zwave.security; import com.lanternsoftware.datamodel.zwave.Switch; +import com.lanternsoftware.util.concurrency.ConcurrencyUtils; import com.pi4j.io.gpio.GpioFactory; import com.pi4j.io.gpio.GpioPinDigitalInput; import com.pi4j.io.gpio.PinPullResistance; @@ -11,11 +12,16 @@ import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public class SecurityController { protected static final Logger LOG = LoggerFactory.getLogger(SecurityController.class); private final Map pins = new HashMap<>(); + private final Map pinEvents = new HashMap<>(); + private final ExecutorService executor = Executors.newFixedThreadPool(2); + private int eventIdx = 0; public boolean isOpen(int _pin) { GpioPinDigitalInput pin = getPin(_pin); @@ -25,7 +31,24 @@ public class SecurityController { public void listen(Switch _sw, SecurityListener _listener) { GpioPinDigitalInput pin = getPin(_sw.getGpioPin()); if (pin != null) - pin.addListener((GpioPinListenerDigital) _event -> _listener.onStateChanged(_sw.getNodeId(), _event.getState().isHigh())); + pin.addListener((GpioPinListenerDigital) _event -> { + synchronized (pinEvents) { + eventIdx++; + LOG.info("state change from gpio, event {} pin {} to {}", eventIdx, _sw.getGpioPin(), _event.getState().isHigh()); + pinEvents.put(_sw.getGpioPin(), _event.getState().isHigh()); + } + executor.submit(()->{ + ConcurrencyUtils.sleep(500); + Boolean high; + synchronized (pinEvents) { + high = pinEvents.remove(_sw.getGpioPin()); + } + LOG.info("handling event {} pin {} most recent event is {}", eventIdx, _sw.getGpioPin(), high); + if (high == null) + return; + _listener.onStateChanged(_sw.getNodeId(), high); + }); + }); } private GpioPinDigitalInput getPin(int _pin) { @@ -47,6 +70,7 @@ public class SecurityController { pin.removeAllListeners(); } pins.clear(); + executor.shutdownNow(); GpioFactory.getInstance().shutdown(); } }