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();
}
}