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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -60,6 +60,7 @@ public class MongoRulesDataAccess implements RulesDataAccess {
@Override
public void putFcmDevice(FcmDevice _device) {
proxy.delete(FcmDevice.class, new DaoQuery("account_id", _device.getAccountId()).and("token", _device.getToken()));
proxy.save(_device);
}

View File

@ -955,4 +955,48 @@ public class CollectionUtils {
return 0;
return _arr.length;
}
public static byte[] toByteArray(float[] _floats) {
if ((_floats == null) || (_floats.length == 0))
return null;
ByteBuffer bb = ByteBuffer.allocate(_floats.length * 4);
for (float f : _floats) {
bb.putFloat(f);
}
return bb.array();
}
public static float[] toFloatArray(byte[] _bytes) {
if ((_bytes == null) || (_bytes.length == 0))
return null;
int offset = 0;
float[] floats = new float[_bytes.length/4];
ByteBuffer bb = ByteBuffer.wrap(_bytes);
while (bb.hasRemaining()) {
floats[offset++] = bb.getFloat();
}
return floats;
}
public static byte[] toByteArray(double[] _doubles) {
if ((_doubles == null) || (_doubles.length == 0))
return null;
ByteBuffer bb = ByteBuffer.allocate(_doubles.length * 8);
for (double d : _doubles) {
bb.putDouble(d);
}
return bb.array();
}
public static double[] toDoubleArray(byte[] _bytes) {
if ((_bytes == null) || (_bytes.length == 0))
return null;
int offset = 0;
double[] doubles = new double[_bytes.length/8];
ByteBuffer bb = ByteBuffer.wrap(_bytes);
while (bb.hasRemaining()) {
doubles[offset++] = bb.getDouble();
}
return doubles;
}
}

View File

@ -2,6 +2,8 @@ package com.lanternsoftware.util;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@ -224,6 +226,11 @@ public abstract class DateUtils {
return String.format("%d years", iYears);
}
public static int getDaysBetween(Date _start, Date _end, TimeZone _tz) {
ZoneId tz = _tz.toZoneId();
return (int)ChronoUnit.DAYS.between(_start.toInstant().atZone(tz).toLocalDate(), _end.toInstant().atZone(tz).toLocalDate());
}
public static int getMonthsBetween(Date _dtStart, Date _dtEnd) {
Calendar calStart = getGMTCalendar(_dtStart.getTime());
Calendar calEnd = getGMTCalendar(_dtEnd.getTime());

View File

@ -0,0 +1,28 @@
package com.lanternsoftware.util.mutable;
public class MutableDouble {
private double value;
public MutableDouble() {
}
public MutableDouble(double _value) {
value = _value;
}
public double getValue() {
return value;
}
public void setValue(double _value) {
value = _value;
}
public void add(double _value) {
value += _value;
}
public void subtract(double _value) {
value -= _value;
}
}

View File

@ -3,12 +3,14 @@ package com.lanternsoftware.util.servlet;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer;
import freemarker.template.Configuration;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public abstract class FreemarkerServlet extends LanternServlet {
@ -48,6 +50,16 @@ public abstract class FreemarkerServlet extends LanternServlet {
protected static DaoEntity model(HttpServletRequest _req) {
DaoEntity model = new DaoEntity("context", _req.getContextPath());
String linkPrefix = "";
String[] path = getPath(_req);
if (path.length > 1) {
StringBuilder prefix = new StringBuilder();
for(int i=0; i<path.length-1; i++) {
prefix.append("../");
}
linkPrefix = prefix.toString();
}
model.put("link_prefix", linkPrefix);
model.put("css_version", "1.0.0");
return model;
}
@ -78,4 +90,46 @@ public abstract class FreemarkerServlet extends LanternServlet {
}
return null;
}
protected void ajaxRender(HttpServletResponse _rep, String _template, Map<String, Object> _templateModel) {
ajaxRender(_rep, _template, _templateModel, null);
}
protected void ajaxRender(HttpServletResponse _rep, String _templateName, Map<String, Object> _templateModel, Map<String, Object> _jsonRep) {
ajaxHtml(_rep, FreemarkerUtil.render(getFreemarkerConfig(), _templateName, _templateModel), _jsonRep);
}
protected static void ajaxHtml(HttpServletResponse _rep, String _html) {
ajaxHtml(_rep, _html, null);
}
protected static void ajaxHtml(HttpServletResponse _rep, String _html, Map<String, Object> _model) {
if (_model == null) {
_model = new HashMap<>();
}
_model.put("html", _html);
ajaxJson(_rep, _model);
}
protected static void ajaxJson(HttpServletResponse _rep, Map<String, Object> _model) {
DaoEntity json = new DaoEntity(_model);
setResponseEntity(_rep, "application/json", DaoSerializer.toJson(json));
}
protected void ajaxRedirect(HttpServletResponse _rep, String _url) {
setResponseEntity(_rep, "application/json", DaoSerializer.toJson(new DaoEntity("redirect", _url)));
}
protected void ajaxError(HttpServletResponse _rep, String _error) {
ajaxError(_rep, _error, null);
}
protected void ajaxError(HttpServletResponse _rep, String _error, DaoEntity _model) {
if (_model == null) {
_model = new DaoEntity();
}
_model.put("error", _error);
setResponseEntity(_rep, "application/json", DaoSerializer.toJson(_model, false, false));
}
}

View File

@ -1,74 +0,0 @@
package com.lanternsoftware.zwave;
import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao;
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
import com.lanternsoftware.datamodel.zwave.Switch;
import com.lanternsoftware.datamodel.zwave.SwitchSchedule;
import com.lanternsoftware.datamodel.zwave.ZWaveConfig;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.ResourceLoader;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import com.lanternsoftware.zwave.dao.MongoZWaveDao;
public class CreateConfig {
public static void main(String[] args) {
MongoZWaveDao dao = new MongoZWaveDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
ZWaveConfig config = dao.getConfig(1);
// ZWaveConfig cconfig = DaoSerializer.parse(ResourceLoader.loadFile(LanternFiles.OPS_PATH + "config - christmas lights.dat"), ZWaveConfig.class);
// Switch c = CollectionUtils.filterOne(config.getSwitches(), _s->_s.getName().contains("hristm"));
// CollectionUtils.filterMod(config.getSwitches(), _s->!_s.getRoom().equals("Treehouse"));
// Switch treehouse = new Switch("Treehouse", "Interior", 14, true, true, null, 0);
// Switch to = new Switch("Treehouse", "Floods", 15, true, true, null, 0);
// Switch out = new Switch("Outside", "Repeater Outlet", 10, true, false, null, 0);
// config.getSwitches().add(out);
Switch c = CollectionUtils.filterOne(config.getSwitches(), _s->_s.getName().contains("Agitator"));
c.setName("Septic Aerator");
dao.putConfig(config);
// if (c != null) {
// c.setNodeId(8);
// dao.putConfig(config);
// }
// ZWaveConfig config = DaoSerializer.parse(ResourceLoader.loadFile(LanternFiles.OPS_PATH + "config.dat"), ZWaveConfig.class);
// config.setAccountId(1);
// config.getSwitches().add(new Switch("Garage", "Septic Agitator", 12, 0, true, false, false, null, 0, Arrays.asList(new SwitchTransition(20))));
// Switch thermo = CollectionUtils.filterOne(config.getSwitches(), _sw->_sw.getNodeId() == 0);
// if (thermo != null)
// thermo.setNodeId(100);
// config.getSwitches().add(new Switch("Basement", "Temperature", 101, true, false, "https://basement.lanternsoftware.com/thermometer/temp", 0));
/* ZWaveConfig config = new ZWaveConfig();
List<Switch> switches = new ArrayList<>();
switches.add(new Switch("Basement", "Main", 3, true, true, null, 0));
switches.add(new Switch("Basement", "Main", 5, false, true, null, 0));
switches.add(new Switch("Basement", "Bar", 4, true, true, null, 0));
switches.add(new Switch("Basement", "Bar", 6, false, true, null, 0));
switches.add(new Switch("Master Bedroom", "Heater", 7, true, true, "https://thermometer.lanternsoftware.com/thermometer/temp", 0));
switches.add(new Switch("Bruce's Room", "Heater", 8, true, true, "https://bruce.lanternsoftware.com/thermometer/temp", 0));
switches.add(new Switch("Master Bedroom", "Heater", 9, false, true, "", 0));
Switch out = new Switch("Outside", "Christmas Lights", 10, true, false, "", 0);
out.setSchedule(CollectionUtils.asArrayList(
new SwitchTransition(Calendar.FRIDAY, 12, 42, 0, 0),
new SwitchTransition(Calendar.FRIDAY, 12, 42, 10, 0xFF),
new SwitchTransition(Calendar.FRIDAY, 12, 42, 20, 0),
new SwitchTransition(Calendar.FRIDAY, 12, 42, 30, 0xFF),
new SwitchTransition(Calendar.FRIDAY, 12, 42, 40, 0),
new SwitchTransition(Calendar.FRIDAY, 12, 42, 50, 0xFF)));
switches.add(out);
config.setSwitches(switches);
*/
// config.setSwitches(switches);
// Switch sump = CollectionUtils.filterOne(config.getSwitches(), _c->_c.getNodeId() == 12);
// SwitchSchedule transition = CollectionUtils.getFirst(sump.getSchedule());
// transition.setLevel(0xFF);
// transition.setMinutesPerHour(15);
// dao.putConfig(config);
dao.shutdown();
// TimeZone tz = TimeZone.getTimeZone("America/Chicago");
// Date next = transition.getNextTransition(tz);
// System.out.println("Next Transition: " + DateUtils.format(tz, next, "hh:mm:ssa"));
// ResourceLoader.writeFile(LanternFiles.OPS_PATH + "config.dat", DaoSerializer.toJson(config));
}
}