mirror of
https://github.com/zyphlar/LanternPowerMonitor.git
synced 2024-03-08 14:07:47 +00:00
Add billing rates and track cost for all energy readings.
This commit is contained in:
parent
8221e8ebd5
commit
8d09ac39f2
|
@ -17,6 +17,11 @@
|
|||
<artifactId>lantern-datamodel-currentmonitor</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lanternsoftware.rules</groupId>
|
||||
<artifactId>lantern-datamodel-rules</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lanternsoftware.util</groupId>
|
||||
<artifactId>lantern-util-dao-mongo</artifactId>
|
||||
|
|
|
@ -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<Account> 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<BreakerConfig> 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<BreakerGroupEnergy> 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<BreakerGroupSummary> 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<Event> 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<FcmDevice> 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<Rule> 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<Sequence> sequences = dao.getProxy().queryAll(Sequence.class);
|
||||
t15.stop();
|
||||
DebugTimer t16 = new DebugTimer("Save Sequences");
|
||||
backupDao.getProxy().save(sequences);
|
||||
t16.stop();
|
||||
|
||||
dao.shutdown();
|
||||
backupDao.shutdown();
|
||||
}
|
||||
}
|
|
@ -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<HubPowerMinute> 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();
|
||||
}
|
||||
}
|
|
@ -31,6 +31,8 @@ public interface CurrentMonitorDao {
|
|||
void putConfig(BreakerConfig _config);
|
||||
|
||||
void updateSummaries(BreakerGroup _rootGroup, Set<Date> _daysToSummarize, TimeZone _tz);
|
||||
void rebuildSummaries(int _accountId);
|
||||
void rebuildSummaries(int _accountId, Date _start, Date _end);
|
||||
|
||||
String addPasswordResetKey(String _email);
|
||||
String getEmailForResetKey(String _key);
|
||||
|
|
|
@ -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<String> 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<BreakerGroupSummary> groupEnergies = CollectionUtils.aggregate(proxy.query(BreakerGroupSummary.class, DaoQuery.in("_id", groupEnergyIds)), BreakerGroupSummary::getAllGroups);
|
||||
Map<String, List<BreakerGroupSummary>> 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<HubPowerMinute> minutes = proxy.query(HubPowerMinute.class, new DaoQuery("account_id", _accountId).andBetweenInclusiveExclusive("minute", (int) (start.getTime() / 60000), (int) (dayEnd.getTime() / 60000)));
|
||||
t1.stop();
|
||||
monthStart = DateUtils.getStartOfMonth(start, tz);
|
||||
BreakerGroupSummary month = null;
|
||||
if (monthStart.equals(start))
|
||||
proxy.delete(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, root.getId(), EnergyBlockViewMode.MONTH, monthStart)));
|
||||
else
|
||||
month = proxy.queryOne(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, root.getId(), EnergyBlockViewMode.MONTH, monthStart)));
|
||||
BreakerGroupEnergy energy = new BreakerGroupEnergy(root, minutes, EnergyBlockViewMode.DAY, start, month, config.getBillingRates(), tz);
|
||||
timer.stop();
|
||||
putBreakerGroupEnergy(energy);
|
||||
updateSummaries(root, CollectionUtils.asHashSet(start), tz);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package com.lanternsoftware.datamodel.currentmonitor;
|
||||
|
||||
public enum BillingMode {
|
||||
CONSUMPTION,
|
||||
PRODUCTION;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ public class BreakerConfig {
|
|||
private List<BreakerPanel> panels;
|
||||
private List<BreakerHub> breakerHubs;
|
||||
private List<BreakerGroup> breakerGroups;
|
||||
private List<BillingRate> billingRates;
|
||||
private int version;
|
||||
|
||||
public BreakerConfig() {
|
||||
|
@ -66,6 +67,14 @@ public class BreakerConfig {
|
|||
breakerGroups = _breakerGroups;
|
||||
}
|
||||
|
||||
public List<BillingRate> getBillingRates() {
|
||||
return billingRates;
|
||||
}
|
||||
|
||||
public void setBillingRates(List<BillingRate> _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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, List<BreakerPower>> _powerReadings, EnergyBlockViewMode _viewMode, Date _start, TimeZone _timezone) {
|
||||
public BreakerGroupEnergy(BreakerGroup _group, List<HubPowerMinute> _power, EnergyBlockViewMode _viewMode, Date _start, BreakerGroupSummary _month, List<BillingRate> _rates, TimeZone _timezone) {
|
||||
groupId = _group.getId();
|
||||
groupName = _group.getName();
|
||||
viewMode = _viewMode;
|
||||
start = _start;
|
||||
accountId = _group.getAccountId();
|
||||
timezone = _timezone;
|
||||
subGroups = CollectionUtils.transform(_group.getSubGroups(), _g -> new BreakerGroupEnergy(_g, _powerReadings, _viewMode, _start, timezone));
|
||||
energyBlocks = new ArrayList<>();
|
||||
List<String> 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<HubPowerMinute> _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<HubPowerMinute>)null, _viewMode, _start, timezone));
|
||||
energyBlocks = new ArrayList<>();
|
||||
addEnergy(_group, _power);
|
||||
}
|
||||
|
||||
public void addEnergy(BreakerGroup _group, List<HubPowerMinute> _hubPower) {
|
||||
public void addEnergy(BreakerGroup _group, List<HubPowerMinute> _hubPower, BreakerGroupSummary _month, List<BillingRate> _rates) {
|
||||
Map<String, Breaker> breakers = CollectionUtils.transformToMap(_group.getAllBreakers(), Breaker::getKey);
|
||||
Map<String, BreakerGroup> breakerKeyToGroup = new HashMap<>();
|
||||
for (BreakerGroup group : _group.getAllBreakerGroups()) {
|
||||
|
@ -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<String, Breaker> _breakers, Map<String, BreakerGroup> _breakerKeyToGroup, List<HubPowerMinute> _hubPower) {
|
||||
public void addEnergy(Map<String, Breaker> _breakers, Map<String, BreakerGroup> _breakerKeyToGroup, List<HubPowerMinute> _hubPower, BreakerGroupSummary _month, List<BillingRate> _rates) {
|
||||
if (CollectionUtils.isEmpty(_hubPower) || CollectionUtils.anyQualify(_hubPower, _p->_p.getAccountId() != accountId))
|
||||
return;
|
||||
Date minute = CollectionUtils.getFirst(_hubPower).getMinuteAsDate();
|
||||
_hubPower.sort(Comparator.comparing(HubPowerMinute::getMinute));
|
||||
for (Date minute : CollectionUtils.transformToSet(_hubPower, HubPowerMinute::getMinuteAsDate)) {
|
||||
resetEnergy(minute);
|
||||
Map<Integer, MeterMinute> meters = new HashMap<>();
|
||||
}
|
||||
int idx;
|
||||
Map<MeterMinute, MeterMinuteValues> 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;
|
||||
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;
|
||||
if (power != 0.0)
|
||||
addEnergy(group.getId(), minute, power);
|
||||
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<MeterMinute, MeterMinuteValues> meter : meters.entrySet()) {
|
||||
double monthkWh = monthFromGrid/3600000;
|
||||
List<BillingRate> consumptionRates = CollectionUtils.filter(_rates, _r->_r.isApplicable(BillingMode.CONSUMPTION, meter.getKey().meter, monthkWh, meter.getKey().minute, timezone));
|
||||
List<BillingRate> 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,14 +150,29 @@ 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<String, List<BreakerGroupSummary>> _energies, EnergyBlockViewMode _viewMode, Date _start, TimeZone _tz) {
|
||||
|
@ -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<EnergyBlock> 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<String> _selectedBreakers) {
|
||||
return charge(_selectedBreakers, true);
|
||||
}
|
||||
|
||||
public double charge(Set<String> _selectedBreakers, BillingMode _mode) {
|
||||
return charge(_selectedBreakers, true, _mode);
|
||||
}
|
||||
|
||||
public double charge(Set<String> _selectedBreakers, boolean _includeSubgroups) {
|
||||
return charge(_selectedBreakers, _includeSubgroups, null);
|
||||
}
|
||||
|
||||
public double charge(Set<String> _selectedBreakers, boolean _includeSubgroups, 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<BreakerGroupEnergy> getAllGroups() {
|
||||
Map<String, BreakerGroupEnergy> 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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ public class BreakerGroupSummary {
|
|||
private Date start;
|
||||
private List<BreakerGroupSummary> 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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<BillingRate>
|
||||
{
|
||||
@Override
|
||||
public Class<BillingRate> getSupportedClass()
|
||||
{
|
||||
return BillingRate.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DaoProxyType> 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;
|
||||
}
|
||||
}
|
|
@ -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<BreakerConfig
|
|||
d.put("panels", DaoSerializer.toDaoEntities(_o.getPanels(), DaoProxyType.MONGO));
|
||||
d.put("breaker_hubs", DaoSerializer.toDaoEntities(_o.getBreakerHubs(), DaoProxyType.MONGO));
|
||||
d.put("breaker_groups", DaoSerializer.toDaoEntities(_o.getBreakerGroups(), DaoProxyType.MONGO));
|
||||
d.put("billing_rates", DaoSerializer.toDaoEntities(_o.getBillingRates(), DaoProxyType.MONGO));
|
||||
d.put("version", _o.getVersion());
|
||||
return d;
|
||||
}
|
||||
|
@ -48,6 +50,7 @@ public class BreakerConfigSerializer extends AbstractDaoSerializer<BreakerConfig
|
|||
o.setPanels(DaoSerializer.getList(_d, "panels", BreakerPanel.class));
|
||||
o.setBreakerHubs(DaoSerializer.getList(_d, "breaker_hubs", BreakerHub.class));
|
||||
o.setBreakerGroups(DaoSerializer.getList(_d, "breaker_groups", BreakerGroup.class));
|
||||
o.setBillingRates(DaoSerializer.getList(_d, "billing_rates", BillingRate.class));
|
||||
o.setVersion(DaoSerializer.getInteger(_d, "version"));
|
||||
return o;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ public class BreakerGroupEnergySerializer extends AbstractDaoSerializer<BreakerG
|
|||
Date start = _o.getStart();
|
||||
Date now = new Date();
|
||||
ByteBuffer bb = ByteBuffer.allocate(_o.getViewMode().blockCount(start, tz) * 4);
|
||||
ByteBuffer cb = ByteBuffer.allocate(_o.getViewMode().blockCount(start, tz) * 8);
|
||||
for (EnergyBlock b : _o.getEnergyBlocks()) {
|
||||
if (b.getStart().before(start))
|
||||
continue;
|
||||
|
@ -55,15 +56,21 @@ public class BreakerGroupEnergySerializer extends AbstractDaoSerializer<BreakerG
|
|||
break;
|
||||
while (start.before(b.getStart())) {
|
||||
bb.putFloat(0);
|
||||
cb.putDouble(0);
|
||||
start = _o.getViewMode().toBlockEnd(start, tz);
|
||||
}
|
||||
bb.putFloat((float) b.getJoules());
|
||||
cb.putDouble(b.getCharge());
|
||||
start = _o.getViewMode().toBlockEnd(start, tz);
|
||||
}
|
||||
if (bb.position() < bb.limit())
|
||||
if (bb.position() < bb.limit()) {
|
||||
d.put("blocks", Arrays.copyOfRange(bb.array(), 0, bb.position()));
|
||||
else
|
||||
d.put("charges", Arrays.copyOfRange(cb.array(), 0, cb.position()));
|
||||
}
|
||||
else {
|
||||
d.put("blocks", bb.array());
|
||||
d.put("charges", cb.array());
|
||||
}
|
||||
}
|
||||
d.put("to_grid", _o.getToGrid());
|
||||
d.put("from_grid", _o.getFromGrid());
|
||||
|
@ -93,6 +100,14 @@ public class BreakerGroupEnergySerializer extends AbstractDaoSerializer<BreakerG
|
|||
}
|
||||
}
|
||||
o.setEnergyBlocks(blocks);
|
||||
byte[] chargeData = DaoSerializer.getByteArray(_d, "charges");
|
||||
int idx = 0;
|
||||
if (CollectionUtils.length(chargeData) > 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;
|
||||
|
|
|
@ -33,6 +33,7 @@ public class BreakerGroupSummarySerializer extends AbstractDaoSerializer<Breaker
|
|||
d.put("start", DaoSerializer.toLong(_o.getStart()));
|
||||
d.put("sub_groups", DaoSerializer.toDaoEntities(_o.getSubGroups(), DaoProxyType.MONGO));
|
||||
d.put("joules", _o.getJoules());
|
||||
d.put("charge", _o.getCharge());
|
||||
d.put("to_grid", _o.getToGrid());
|
||||
d.put("from_grid", _o.getFromGrid());
|
||||
return d;
|
||||
|
@ -48,6 +49,7 @@ public class BreakerGroupSummarySerializer extends AbstractDaoSerializer<Breaker
|
|||
o.setStart(DaoSerializer.getDate(_d, "start"));
|
||||
o.setSubGroups(DaoSerializer.getList(_d, "sub_groups", BreakerGroupSummary.class));
|
||||
o.setJoules(DaoSerializer.getDouble(_d, "joules"));
|
||||
o.setCharge(DaoSerializer.getDouble(_d, "charge"));
|
||||
o.setToGrid(DaoSerializer.getDouble(_d, "to_grid"));
|
||||
o.setFromGrid(DaoSerializer.getDouble(_d, "from_grid"));
|
||||
return o;
|
||||
|
|
|
@ -28,6 +28,7 @@ public class EnergyBlockSerializer extends AbstractDaoSerializer<EnergyBlock>
|
|||
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<EnergyBlock>
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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<Date, List<HubPowerMinute>> 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<String, Breaker> breakers = CollectionUtils.transformToMap(root.getAllBreakers(), Breaker::getKey);
|
||||
Map<String, BreakerGroup> 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<Account> 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<Date, List<HubPowerMinute>> day : days.entrySet()) {
|
||||
BreakerGroupEnergy energy = null;
|
||||
DebugTimer timer = new DebugTimer("Time to rebuild one day");
|
||||
Map<Integer, List<HubPowerMinute>> minutes = CollectionUtils.transformToMultiMap(day.getValue(), HubPowerMinute::getMinute);
|
||||
for (List<HubPowerMinute> 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<BreakerGroupEnergy> summaries = dao.getProxy().query(BreakerGroupEnergy.class, null);
|
||||
// CollectionUtils.edit(summaries, _s->CollectionUtils.edit(_s.getAllGroups(), _t->_t.setAccountId(1)));
|
||||
// dao.getProxy().save(summaries);
|
||||
|
||||
// List<BreakerPower> 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<BreakerPowerArchive> 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<BreakerPower> 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<String, List<BreakerPower>> 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<BreakerPower> dup : dups.values()) {
|
||||
// if (dup.size() > 1) {
|
||||
// CollectionUtils.removeFirst(dup);
|
||||
// dao.getProxy().delete(BreakerPower.class, DaoQuery.in("_id", CollectionUtils.transform(dup, BreakerPower::getId)));
|
||||
// }
|
||||
// }
|
||||
|
||||
// List<BreakerGroupEnergy> 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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\\";
|
||||
}
|
||||
|
|
|
@ -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<SwitchSchedule> getSchedule() {
|
||||
return schedule;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ public class SwitchSerializer extends AbstractDaoSerializer<Switch>
|
|||
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<Switch>
|
|||
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));
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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<Integer, GpioPinDigitalInput> pins = new HashMap<>();
|
||||
private final Map<Integer, Boolean> 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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user