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));
List<HubPowerMinute> minutes = proxy.query(HubPowerMinute.class, new DaoQuery("account_id", _accountId).andBetweenInclusiveExclusive("minute", (int) (start.getTime() / 60000), (int) (dayEnd.getTime() / 60000)));
t1.stop();
monthStart = DateUtils.getStartOfMonth(start, tz);
BreakerGroupSummary month = null;
if (monthStart.equals(start))
proxy.delete(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, root.getId(), EnergyBlockViewMode.MONTH, monthStart)));
else
month = proxy.queryOne(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, root.getId(), EnergyBlockViewMode.MONTH, monthStart)));
BreakerGroupEnergy energy = new BreakerGroupEnergy(root, minutes, EnergyBlockViewMode.DAY, start, month, config.getBillingRates(), tz);
timer.stop();
putBreakerGroupEnergy(energy);
updateSummaries(root, CollectionUtils.asHashSet(start), tz);
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();
if (!minutes.isEmpty()) {
DebugTimer t2 = new DebugTimer("In memory rebuild", logger);
EnergySummary energy = new EnergySummary(root, minutes, EnergyViewMode.DAY, start, tz);
t2.stop();
timer.stop();
putEnergySummary(energy);
DebugTimer t3 = new DebugTimer("Updating charges", logger);
updateChargeSummary(config, energy, tz);
t3.stop();
}
dates.add(start);
start = DateUtils.addDays(start, 1, tz);
}
DebugTimer t4 = new DebugTimer("Updating month/year/lifetime energy summaries", logger);
updateEnergySummaries(root, dates, tz);
t4.stop();
DebugTimer t5 = new DebugTimer("Updating month/year/lifetime charge summaries", logger);
updateChargeSummaries(config, dates, tz);
t5.stop();
}
public void rebuildChargeSummaries(BreakerConfig _config, BillingPlan _plan) {
TimeZone tz = getTimeZoneForAccount(_config.getAccountId());
HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _config.getAccountId()), DaoSort.sort("minute"));
if (firstMinute == null)
return;
Date start = DateUtils.getMidnightBefore(firstMinute.getMinuteAsDate(), tz);
Date end = DateUtils.getMidnightAfter(new Date(), tz);
BreakerGroup root = CollectionUtils.getFirst(_config.getBreakerGroups());
if (root == null)
return;
Set<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;