Add billing rates and track cost for all energy readings.

This commit is contained in:
MarkBryanMilligan 2021-08-03 16:47:28 -05:00
parent 8221e8ebd5
commit 8d09ac39f2
26 changed files with 739 additions and 148 deletions

View File

@ -17,6 +17,11 @@
<artifactId>lantern-datamodel-currentmonitor</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.lanternsoftware.rules</groupId>
<artifactId>lantern-datamodel-rules</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-dao-mongo</artifactId>

View File

@ -0,0 +1,81 @@
package com.lanternsoftware.dataaccess.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupEnergy;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupSummary;
import com.lanternsoftware.datamodel.currentmonitor.Sequence;
import com.lanternsoftware.datamodel.rules.Event;
import com.lanternsoftware.datamodel.rules.FcmDevice;
import com.lanternsoftware.datamodel.rules.Rule;
import com.lanternsoftware.util.DebugTimer;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import java.util.List;
public class Backup {
public static void main(String[] args) {
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_PATH + "mongo.cfg"));
DebugTimer t1 = new DebugTimer("Query Accounts");
List<Account> accounts = dao.getProxy().queryAll(Account.class);
t1.stop();
DebugTimer t2 = new DebugTimer("Save Accounts");
backupDao.getProxy().save(accounts);
t2.stop();
DebugTimer t3 = new DebugTimer("Query Configs");
List<BreakerConfig> configs = dao.getProxy().queryAll(BreakerConfig.class);
t3.stop();
DebugTimer t4 = new DebugTimer("Save Configs");
backupDao.getProxy().save(configs);
t4.stop();
DebugTimer t5 = new DebugTimer("Query Energy");
List<BreakerGroupEnergy> energy = dao.getProxy().queryAll(BreakerGroupEnergy.class);
t5.stop();
DebugTimer t6 = new DebugTimer("Save Energy");
backupDao.getProxy().save(energy);
t6.stop();
DebugTimer t7 = new DebugTimer("Query Summaries");
List<BreakerGroupSummary> summary = dao.getProxy().queryAll(BreakerGroupSummary.class);
t7.stop();
DebugTimer t8 = new DebugTimer("Save Summaries");
backupDao.getProxy().save(summary);
t8.stop();
DebugTimer t9 = new DebugTimer("Query Events");
List<Event> events = dao.getProxy().queryAll(Event.class);
t9.stop();
DebugTimer t10 = new DebugTimer("Save Events");
backupDao.getProxy().save(events);
t10.stop();
DebugTimer t11 = new DebugTimer("Query Devices");
List<FcmDevice> devices = dao.getProxy().queryAll(FcmDevice.class);
t11.stop();
DebugTimer t12 = new DebugTimer("Save Devices");
backupDao.getProxy().save(devices);
t12.stop();
DebugTimer t13 = new DebugTimer("Query Rules");
List<Rule> rules = dao.getProxy().queryAll(Rule.class);
t13.stop();
DebugTimer t14 = new DebugTimer("Save Rules");
backupDao.getProxy().save(rules);
t14.stop();
DebugTimer t15 = new DebugTimer("Query Sequences");
List<Sequence> sequences = dao.getProxy().queryAll(Sequence.class);
t15.stop();
DebugTimer t16 = new DebugTimer("Save Sequences");
backupDao.getProxy().save(sequences);
t16.stop();
dao.shutdown();
backupDao.shutdown();
}
}

View File

@ -0,0 +1,52 @@
package com.lanternsoftware.dataaccess.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.DebugTimer;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoQuery;
import com.lanternsoftware.util.dao.DaoSort;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
public class BackupMinutes {
public static void main(String[] args) {
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_PATH + "mongo.cfg"));
Date now = new Date();
for (Account a : dao.getProxy().queryAll(Account.class)) {
if (a.getId() == 100)
continue;
DebugTimer t = new DebugTimer("Account " + a.getId());
if (NullUtils.isEmpty(a.getTimezone())) {
a.setTimezone("America/Chicago");
}
TimeZone tz = TimeZone.getTimeZone(a.getTimezone());
// Date start = DateUtils.addDays(DateUtils.getMidnightBeforeNow(tz), -2, tz);
HubPowerMinute minute = dao.getProxy().queryOne(HubPowerMinute.class, new DaoQuery("account_id", a.getId()), DaoSort.sort("minute"));
if (minute == null)
continue;
Date start = DateUtils.getMidnightBefore(minute.getMinuteAsDate(), tz);
Date end = DateUtils.addDays(start, 1, tz);
while (end.before(now)) {
DebugTimer t2 = new DebugTimer("Account Id: " + a.getId() + " Query Day " + DateUtils.format("MM/dd/yyyy", tz, start));
List<HubPowerMinute> minutes = dao.getProxy().query(HubPowerMinute.class, new DaoQuery("account_id", a.getId()).andBetweenInclusiveExclusive("minute", (int) (start.getTime() / 60000), (int) (end.getTime() / 60000)));
t2.stop();
if (!minutes.isEmpty()) {
DebugTimer t3 = new DebugTimer("Save Day");
backupDao.getProxy().save(minutes);
t3.stop();
}
start = end;
end = DateUtils.addDays(end, 1, tz);
}
t.stop();
}
dao.shutdown();
}
}

View File

@ -31,6 +31,8 @@ public interface CurrentMonitorDao {
void putConfig(BreakerConfig _config);
void updateSummaries(BreakerGroup _rootGroup, Set<Date> _daysToSummarize, TimeZone _tz);
void rebuildSummaries(int _accountId);
void rebuildSummaries(int _accountId, Date _start, Date _end);
String addPasswordResetKey(String _email);
String getEmailForResetKey(String _key);

View File

@ -1,7 +1,6 @@
package com.lanternsoftware.dataaccess.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.datamodel.currentmonitor.Breaker;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup;
@ -20,6 +19,7 @@ import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoQuery;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.DaoSort;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import com.lanternsoftware.util.dao.mongo.MongoProxy;
import org.mindrot.jbcrypt.BCrypt;
@ -96,12 +96,14 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
BreakerConfig config = getConfig(_minute.getAccountId());
BreakerGroup group = CollectionUtils.getFirst(config.getBreakerGroups());
Date day = DateUtils.getMidnightBefore(_minute.getMinuteAsDate(), tz);
BreakerGroupEnergy summary = getBreakerGroupEnergy(_minute.getAccountId(), group.getId(), EnergyBlockViewMode.DAY, day);
if (summary == null)
summary = new BreakerGroupEnergy(group, minutes, EnergyBlockViewMode.DAY, day, tz);
BreakerGroupEnergy energy = getBreakerGroupEnergy(_minute.getAccountId(), group.getId(), EnergyBlockViewMode.DAY, day);
Date monthStart = DateUtils.getStartOfMonth(day, tz);
BreakerGroupSummary month = proxy.queryOne(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_minute.getAccountId(), group.getId(), EnergyBlockViewMode.MONTH, monthStart)));
if (energy == null)
energy = new BreakerGroupEnergy(group, minutes, EnergyBlockViewMode.DAY, day, month, config.getBillingRates(), tz);
else
summary.addEnergy(group, minutes);
putBreakerGroupEnergy(summary);
energy.addEnergy(group, minutes, month, config.getBillingRates());
putBreakerGroupEnergy(energy);
updateSummaries(group, CollectionUtils.asHashSet(day), tz);
timer.stop();
}
@ -144,7 +146,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
List<String> groupEnergyIds = new ArrayList<>();
while (calMonthStart.before(end)) {
groupEnergyIds.add(BreakerGroupEnergy.toId(_rootGroup.getAccountId(), _rootGroup.getId(), EnergyBlockViewMode.MONTH, calMonthStart.getTime()));
calMonthStart.add(Calendar.DAY_OF_YEAR, 1);
calMonthStart.add(Calendar.MONTH, 1);
}
List<BreakerGroupSummary> groupEnergies = CollectionUtils.aggregate(proxy.query(BreakerGroupSummary.class, DaoQuery.in("_id", groupEnergyIds)), BreakerGroupSummary::getAllGroups);
Map<String, List<BreakerGroupSummary>> energies = CollectionUtils.transformToMultiMap(groupEnergies, BreakerGroupSummary::getGroupId);
@ -157,6 +159,42 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
putBreakerGroupEnergy(summary);
}
@Override
public void rebuildSummaries(int _accountId) {
HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sort("minute"));
if (firstMinute == null)
return;
rebuildSummaries(_accountId, firstMinute.getMinuteAsDate(), new Date());
}
@Override
public void rebuildSummaries(int _accountId, Date _start, Date _end) {
BreakerConfig config = getConfig(_accountId);
TimeZone tz = getTimeZoneForAccount(_accountId);
Date start = DateUtils.getMidnightBefore(_start, tz);
Date monthStart = DateUtils.getStartOfMonth(_start, tz);
BreakerGroup root = CollectionUtils.getFirst(config.getBreakerGroups());
proxy.delete(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, root.getId(), EnergyBlockViewMode.MONTH, monthStart)));
while (start.before(_end)) {
Date dayEnd = DateUtils.getMidnightAfter(start, tz);
DebugTimer timer = new DebugTimer("Time to rebuild one day");
DebugTimer t1 = new DebugTimer("Loading hub power for day, account: " + _accountId + " day: " + DateUtils.format("MM/dd/yyyy", tz, start));
List<HubPowerMinute> minutes = proxy.query(HubPowerMinute.class, new DaoQuery("account_id", _accountId).andBetweenInclusiveExclusive("minute", (int) (start.getTime() / 60000), (int) (dayEnd.getTime() / 60000)));
t1.stop();
monthStart = DateUtils.getStartOfMonth(start, tz);
BreakerGroupSummary month = null;
if (monthStart.equals(start))
proxy.delete(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, root.getId(), EnergyBlockViewMode.MONTH, monthStart)));
else
month = proxy.queryOne(BreakerGroupSummary.class, new DaoQuery("_id", BreakerGroupEnergy.toId(_accountId, root.getId(), EnergyBlockViewMode.MONTH, monthStart)));
BreakerGroupEnergy energy = new BreakerGroupEnergy(root, minutes, EnergyBlockViewMode.DAY, start, month, config.getBillingRates(), tz);
timer.stop();
putBreakerGroupEnergy(energy);
updateSummaries(root, CollectionUtils.asHashSet(start), tz);
start = DateUtils.addDays(start, 1, tz);
}
}
@Override
public void putBreakerGroupEnergy(BreakerGroupEnergy _energy) {
proxy.save(_energy);
@ -179,6 +217,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
config.setBreakerGroups(CollectionUtils.aggregate(configs, BreakerConfig::getBreakerGroups));
config.setPanels(CollectionUtils.aggregate(configs, BreakerConfig::getPanels));
config.setMeters(CollectionUtils.aggregate(configs, BreakerConfig::getMeters));
config.setBillingRates(CollectionUtils.aggregate(configs, BreakerConfig::getBillingRates));
return config;
}

View File

@ -0,0 +1,26 @@
package com.lanternsoftware.datamodel.currentmonitor;
import java.math.BigDecimal;
import java.math.RoundingMode;
public enum BillingCurrency {
DOLLAR("$"),
EURO(""),
POUND_STERLING("£");
public final String symbol;
BillingCurrency(String _symbol) {
symbol = _symbol;
}
public String format(double _value) {
return format(BigDecimal.valueOf(_value));
}
public String format(BigDecimal _value) {
if (_value.compareTo(BigDecimal.ZERO) < 0)
return "-" + symbol + _value.abs().setScale(2, RoundingMode.HALF_EVEN);
return symbol + _value.setScale(2, RoundingMode.HALF_EVEN);
}
}

View File

@ -0,0 +1,6 @@
package com.lanternsoftware.datamodel.currentmonitor;
public enum BillingMode {
CONSUMPTION,
PRODUCTION;
}

View File

@ -0,0 +1,179 @@
package com.lanternsoftware.datamodel.currentmonitor;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
@DBSerializable
public class BillingRate {
private int meter;
private int dayBillingCycleStart;
private BillingMode mode;
private double rate;
private BillingCurrency currency;
private int timeOfDayStart;
private int timeOfDayEnd;
private double monthKWhStart;
private double monthKWhEnd;
private Date beginEffective;
private Date endEffective;
boolean recursAnnually;
public int getMeter() {
return meter;
}
public void setMeter(int _meter) {
meter = _meter;
}
public int getDayBillingCycleStart() {
return dayBillingCycleStart;
}
public void setDayBillingCycleStart(int _dayBillingCycleStart) {
dayBillingCycleStart = _dayBillingCycleStart;
}
public BillingMode getMode() {
return mode;
}
public void setMode(BillingMode _mode) {
mode = _mode;
}
public double getRate() {
return rate;
}
public void setRate(double _rate) {
rate = _rate;
}
public BillingCurrency getCurrency() {
return currency;
}
public void setCurrency(BillingCurrency _currency) {
currency = _currency;
}
public int getTimeOfDayStart() {
return timeOfDayStart;
}
public void setTimeOfDayStart(int _timeOfDayStart) {
timeOfDayStart = _timeOfDayStart;
}
public int getTimeOfDayEnd() {
return timeOfDayEnd;
}
public void setTimeOfDayEnd(int _timeOfDayEnd) {
timeOfDayEnd = _timeOfDayEnd;
}
public double getMonthKWhStart() {
return monthKWhStart;
}
public void setMonthKWhStart(double _monthKWhStart) {
monthKWhStart = _monthKWhStart;
}
public double getMonthKWhEnd() {
return monthKWhEnd;
}
public void setMonthKWhEnd(double _monthKWhEnd) {
monthKWhEnd = _monthKWhEnd;
}
public Date getBeginEffective() {
return beginEffective;
}
public void setBeginEffective(Date _beginEffective) {
beginEffective = _beginEffective;
}
public Date getEndEffective() {
return endEffective;
}
public void setEndEffective(Date _endEffective) {
endEffective = _endEffective;
}
public boolean isRecursAnnually() {
return recursAnnually;
}
public void setRecursAnnually(boolean _recursAnnually) {
recursAnnually = _recursAnnually;
}
public boolean isApplicable(BillingMode _mode, int _meter, double _monthKWh, Date _time, TimeZone _tz) {
if (mode != _mode)
return false;
if (_meter != meter)
return false;
if ((monthKWhStart > 0) && (_monthKWh < monthKWhStart))
return false;
if ((monthKWhEnd > 0) && (_monthKWh >= monthKWhEnd))
return false;
if ((beginEffective != null) && (endEffective != null) && recursAnnually) {
Date begin = beginEffective;
Date end = endEffective;
while (_time.before(begin)) {
begin = DateUtils.addYears(begin, -1, _tz);
end = DateUtils.addYears(end, -1, _tz);
}
while (_time.after(end)) {
begin = DateUtils.addYears(begin, 1, _tz);
end = DateUtils.addYears(end, 1, _tz);
}
if (!DateUtils.isBetween(_time, begin, end))
return false;
}
else {
if ((beginEffective != null) && _time.before(beginEffective))
return false;
if ((endEffective != null) && endEffective.before(_time))
return false;
}
if ((timeOfDayStart == 0) && (timeOfDayEnd == 0))
return true;
Calendar midnight = DateUtils.getMidnightBeforeCal(_time, _tz);
int timeOfDay = (int)((_time.getTime() - midnight.getTimeInMillis()) / 1000);
if ((timeOfDayStart > 0) && (timeOfDay < timeOfDayStart))
return false;
return (timeOfDayEnd == 0) || (timeOfDay < timeOfDayEnd);
}
public double apply(double _kWh) {
return rate * _kWh;
}
public BillingRate duplicate() {
BillingRate r = new BillingRate();
r.setMeter(meter);
r.setDayBillingCycleStart(dayBillingCycleStart);
r.setMode(mode);
r.setRate(rate);
r.setCurrency(currency);
r.setTimeOfDayStart(timeOfDayStart);
r.setTimeOfDayEnd(timeOfDayEnd);
r.setMonthKWhStart(monthKWhStart);
r.setMonthKWhEnd(monthKWhEnd);
r.setBeginEffective(beginEffective);
r.setEndEffective(endEffective);
r.setRecursAnnually(recursAnnually);
return r;
}
}

View File

@ -17,6 +17,7 @@ public class BreakerConfig {
private List<BreakerPanel> panels;
private List<BreakerHub> breakerHubs;
private List<BreakerGroup> breakerGroups;
private List<BillingRate> billingRates;
private int version;
public BreakerConfig() {
@ -66,6 +67,14 @@ public class BreakerConfig {
breakerGroups = _breakerGroups;
}
public List<BillingRate> getBillingRates() {
return billingRates;
}
public void setBillingRates(List<BillingRate> _billingRates) {
billingRates = _billingRates;
}
public int getVersion() {
return version;
}
@ -169,4 +178,8 @@ public class BreakerConfig {
}
return null;
}
public BillingCurrency getCurrency() {
return CollectionUtils.getFirst(CollectionUtils.transformToSet(billingRates, BillingRate::getCurrency));
}
}

View File

@ -7,12 +7,14 @@ import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
@ -33,36 +35,18 @@ public class BreakerGroupEnergy {
public BreakerGroupEnergy() {
}
public BreakerGroupEnergy(BreakerGroup _group, Map<String, List<BreakerPower>> _powerReadings, EnergyBlockViewMode _viewMode, Date _start, TimeZone _timezone) {
public BreakerGroupEnergy(BreakerGroup _group, List<HubPowerMinute> _power, EnergyBlockViewMode _viewMode, Date _start, BreakerGroupSummary _month, List<BillingRate> _rates, TimeZone _timezone) {
groupId = _group.getId();
groupName = _group.getName();
viewMode = _viewMode;
start = _start;
accountId = _group.getAccountId();
timezone = _timezone;
subGroups = CollectionUtils.transform(_group.getSubGroups(), _g -> new BreakerGroupEnergy(_g, _powerReadings, _viewMode, _start, timezone));
energyBlocks = new ArrayList<>();
List<String> breakerKeys = CollectionUtils.transform(_group.getBreakers(), Breaker::getKey);
if (!breakerKeys.isEmpty()) {
for (BreakerPower power : CollectionUtils.aggregate(breakerKeys, _powerReadings::get)) {
addEnergy(groupId, power.getReadTime(), power.getPower());
}
}
subGroups = CollectionUtils.transform(_group.getSubGroups(), _g -> new BreakerGroupEnergy(_g, null, _viewMode, _start, _month, _rates, timezone));
addEnergy(_group, _power, _month, _rates);
}
public BreakerGroupEnergy(BreakerGroup _group, List<HubPowerMinute> _power, EnergyBlockViewMode _viewMode, Date _start, TimeZone _timezone) {
groupId = _group.getId();
groupName = _group.getName();
viewMode = _viewMode;
start = _start;
accountId = _group.getAccountId();
timezone = _timezone;
subGroups = CollectionUtils.transform(_group.getSubGroups(), _g -> new BreakerGroupEnergy(_g, (List<HubPowerMinute>)null, _viewMode, _start, timezone));
energyBlocks = new ArrayList<>();
addEnergy(_group, _power);
}
public void addEnergy(BreakerGroup _group, List<HubPowerMinute> _hubPower) {
public void addEnergy(BreakerGroup _group, List<HubPowerMinute> _hubPower, BreakerGroupSummary _month, List<BillingRate> _rates) {
Map<String, Breaker> breakers = CollectionUtils.transformToMap(_group.getAllBreakers(), Breaker::getKey);
Map<String, BreakerGroup> breakerKeyToGroup = new HashMap<>();
for (BreakerGroup group : _group.getAllBreakerGroups()) {
@ -70,16 +54,20 @@ public class BreakerGroupEnergy {
breakerKeyToGroup.put(b.getKey(), group);
}
}
addEnergy(breakers, breakerKeyToGroup, _hubPower);
addEnergy(breakers, breakerKeyToGroup, _hubPower, _month, _rates);
}
public void addEnergy(Map<String, Breaker> _breakers, Map<String, BreakerGroup> _breakerKeyToGroup, List<HubPowerMinute> _hubPower) {
public void addEnergy(Map<String, Breaker> _breakers, Map<String, BreakerGroup> _breakerKeyToGroup, List<HubPowerMinute> _hubPower, BreakerGroupSummary _month, List<BillingRate> _rates) {
if (CollectionUtils.isEmpty(_hubPower) || CollectionUtils.anyQualify(_hubPower, _p->_p.getAccountId() != accountId))
return;
Date minute = CollectionUtils.getFirst(_hubPower).getMinuteAsDate();
resetEnergy(minute);
Map<Integer, MeterMinute> meters = new HashMap<>();
_hubPower.sort(Comparator.comparing(HubPowerMinute::getMinute));
for (Date minute : CollectionUtils.transformToSet(_hubPower, HubPowerMinute::getMinuteAsDate)) {
resetEnergy(minute);
}
int idx;
Map<MeterMinute, MeterMinuteValues> meters = new HashMap<>();
for (HubPowerMinute hubPower : _hubPower) {
Date minute = hubPower.getMinuteAsDate();
for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) {
Breaker b = _breakers.get(breaker.breakerKey());
if (b == null)
@ -87,26 +75,68 @@ public class BreakerGroupEnergy {
BreakerGroup group = _breakerKeyToGroup.get(breaker.breakerKey());
if (group == null)
continue;
MeterMinute meter = meters.computeIfAbsent(b.getMeter(), _p->new MeterMinute());
int idx = 0;
for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) {
if (power > 0)
meter.usage[idx] += power;
else
meter.solar[idx] += -power;
if (power != 0.0)
addEnergy(group.getId(), minute, power);
idx++;
MeterMinuteValues meter = meters.computeIfAbsent(new MeterMinute(b.getMeter(), minute), _p->new MeterMinuteValues());
idx = 0;
EnergyBlock block = getBlock(group.getId(), minute);
if (block != null) {
for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) {
if (idx >= 60)
break;
if (power > 0)
meter.usage[idx] += power;
else
meter.solar[idx] -= power;
block.addJoules(power);
idx++;
}
}
}
}
for (MeterMinute meter : meters.values()) {
double monthFromGrid = _month == null ? 0.0 : _month.getFromGrid();
double secondFromGrid;
for (Map.Entry<MeterMinute, MeterMinuteValues> meter : meters.entrySet()) {
double monthkWh = monthFromGrid/3600000;
List<BillingRate> consumptionRates = CollectionUtils.filter(_rates, _r->_r.isApplicable(BillingMode.CONSUMPTION, meter.getKey().meter, monthkWh, meter.getKey().minute, timezone));
List<BillingRate> productionRates = CollectionUtils.filter(_rates, _r->_r.isApplicable(BillingMode.PRODUCTION, meter.getKey().meter, monthkWh, meter.getKey().minute, timezone));
for (int i = 0; i < 60; i++) {
if (meter.usage[i] > meter.solar[i])
fromGrid += meter.usage[i] - meter.solar[i];
else
toGrid += meter.solar[i] - meter.usage[i];
secondFromGrid = meter.getValue().usage[i] - meter.getValue().solar[i];
monthFromGrid += secondFromGrid;
if (secondFromGrid > 0) {
fromGrid += secondFromGrid;
for (BillingRate rate : consumptionRates) {
meter.getValue().charges[i] += rate.apply(secondFromGrid/3600000);
}
}
else {
toGrid -= secondFromGrid;
for (BillingRate rate : productionRates) {
meter.getValue().charges[i] += rate.apply(secondFromGrid/3600000);
}
}
}
}
for (HubPowerMinute hubPower : _hubPower) {
Date minute = hubPower.getMinuteAsDate();
for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) {
Breaker b = _breakers.get(breaker.breakerKey());
if (b == null)
continue;
BreakerGroup group = _breakerKeyToGroup.get(breaker.breakerKey());
if (group == null)
continue;
MeterMinuteValues meter = meters.get(new MeterMinute(b.getMeter(), minute));
idx = 0;
double charge = 0.0;
for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) {
if (b.getPolarity() == BreakerPolarity.SOLAR) {
if (meter.charges[idx] < 0.0)
charge -= meter.charges[idx] * (power/meter.solar[idx]);
}
else if (meter.charges[idx] > 0.0)
charge += meter.charges[idx] * (power/meter.usage[idx]);
idx++;
}
addCharge(group.getId(), minute, charge);
}
}
}
@ -120,16 +150,31 @@ public class BreakerGroupEnergy {
}
}
public void addEnergy(String _groupId, Date _readTime, double _joules) {
private EnergyBlock getBlock(String _groupId, Date _readTime) {
if (NullUtils.isEqual(groupId, _groupId))
getBlock(_readTime).addJoules(_joules);
return getBlock(_readTime);
else {
for (BreakerGroupEnergy subGroup : CollectionUtils.makeNotNull(subGroups)) {
subGroup.addEnergy(_groupId, _readTime, _joules);
EnergyBlock block = subGroup.getBlock(_groupId, _readTime);
if (block != null)
return block;
}
return null;
}
}
private void addEnergy(String _groupId, Date _readTime, double _joules) {
EnergyBlock block = getBlock(_groupId, _readTime);
if (block != null)
block.addJoules(_joules);
}
private void addCharge(String _groupId, Date _readTime, double _charge) {
EnergyBlock block = getBlock(_groupId, _readTime);
if (block != null)
block.addCharge(_charge);
}
public static BreakerGroupEnergy summary(BreakerGroup _group, Map<String, List<BreakerGroupSummary>> _energies, EnergyBlockViewMode _viewMode, Date _start, TimeZone _tz) {
BreakerGroupEnergy energy = new BreakerGroupEnergy();
energy.setGroupId(_group.getId());
@ -142,6 +187,7 @@ public class BreakerGroupEnergy {
for (BreakerGroupSummary curEnergy : CollectionUtils.makeNotNull(_energies.get(_group.getId()))) {
EnergyBlock block = energy.getBlock(curEnergy.getStart());
block.addJoules(curEnergy.getJoules());
block.addCharge(curEnergy.getCharge());
energy.setToGrid(energy.getToGrid()+curEnergy.getToGrid());
energy.setFromGrid(energy.getFromGrid()+curEnergy.getFromGrid());
}
@ -154,10 +200,10 @@ public class BreakerGroupEnergy {
private EnergyBlock getBlock(Date _readTime, boolean _add) {
int size = CollectionUtils.size(energyBlocks);
int idx = viewMode.blockIndex(_readTime, timezone);
int idx = viewMode.blockIndex(start, _readTime, timezone);
if (_add && (idx >= size)) {
if (energyBlocks == null)
energyBlocks = new ArrayList<>();
energyBlocks = new ArrayList<>(viewMode.initBlockCount());
LinkedList<EnergyBlock> newBlocks = new LinkedList<>();
Date end = viewMode.toBlockEnd(_readTime, timezone);
while (idx >= size) {
@ -297,6 +343,38 @@ public class BreakerGroupEnergy {
return joules;
}
public double charge() {
return charge(null);
}
public double charge(Set<String> _selectedBreakers) {
return charge(_selectedBreakers, true);
}
public double charge(Set<String> _selectedBreakers, BillingMode _mode) {
return charge(_selectedBreakers, true, _mode);
}
public double charge(Set<String> _selectedBreakers, boolean _includeSubgroups) {
return charge(_selectedBreakers, _includeSubgroups, null);
}
public double charge(Set<String> _selectedBreakers, boolean _includeSubgroups, BillingMode _mode) {
double charge = 0.0;
if (_includeSubgroups) {
for (BreakerGroupEnergy group : CollectionUtils.makeNotNull(subGroups)) {
charge += group.charge(_selectedBreakers, true, _mode);
}
}
if ((energyBlocks != null) && ((_selectedBreakers == null) || _selectedBreakers.contains(getGroupId()))) {
for (EnergyBlock energy : energyBlocks) {
if ((_mode == null) || ((_mode == BillingMode.PRODUCTION) && energy.getCharge() < 0.0) || (_mode == BillingMode.CONSUMPTION && energy.getCharge() > 0.0))
charge += energy.getCharge();
}
}
return charge;
}
public List<BreakerGroupEnergy> getAllGroups() {
Map<String, BreakerGroupEnergy> groups = new TreeMap<>();
getAllGroups(groups);
@ -343,7 +421,31 @@ public class BreakerGroupEnergy {
}
private static class MeterMinute {
private final int meter;
private final Date minute;
public MeterMinute(int _meter, Date _minute) {
meter = _meter;
minute = _minute;
}
@Override
public boolean equals(Object _o) {
if (this == _o) return true;
if (_o == null || getClass() != _o.getClass()) return false;
MeterMinute that = (MeterMinute) _o;
return meter == that.meter && minute.equals(that.minute);
}
@Override
public int hashCode() {
return Objects.hash(meter, minute);
}
}
private static class MeterMinuteValues {
public double[] usage = new double[60];
public double[] solar = new double[60];
public double[] charges = new double[60];
}
}

View File

@ -21,6 +21,7 @@ public class BreakerGroupSummary {
private Date start;
private List<BreakerGroupSummary> subGroups;
private double joules;
private double charge;
private double toGrid;
private double fromGrid;
@ -35,6 +36,7 @@ public class BreakerGroupSummary {
start = _energy.getStart();
subGroups = CollectionUtils.transform(_energy.getSubGroups(), BreakerGroupSummary::new);
joules = _energy.joules(null, false);
charge = _energy.charge(null, false);
toGrid = _energy.getToGrid();
fromGrid = _energy.getFromGrid();
}
@ -107,6 +109,14 @@ public class BreakerGroupSummary {
joules = _joules;
}
public double getCharge() {
return charge;
}
public void setCharge(double _charge) {
charge = _charge;
}
public double getToGrid() {
return toGrid;
}

View File

@ -10,6 +10,7 @@ public class EnergyBlock {
private Date start;
private Date end;
private double joules;
private double charge;
public EnergyBlock() {
}
@ -48,6 +49,17 @@ public class EnergyBlock {
joules = _joules;
}
public double getCharge() {
return charge;
}
public void setCharge(double _charge) {
charge = _charge;
}
public void addCharge(double _charge) {
charge += _charge;
}
public double wattHours() {
return joules / 3600;
}

View File

@ -109,10 +109,19 @@ public enum EnergyBlockViewMode {
return blockCnt;
}
public int blockIndex(Date _readTime, TimeZone _tz) {
public int initBlockCount() {
if (this == ALL)
return 1;
if (this == YEAR)
return 12;
if (this == MONTH)
return 31;
return 1500;
}
public int blockIndex(Date _dayStart, Date _readTime, TimeZone _tz) {
if (this == DAY) {
Date start = DateUtils.getMidnightBefore(_readTime, _tz);
return (int)((_readTime.getTime() - start.getTime())/60000);
return (int)((_readTime.getTime() - _dayStart.getTime())/60000);
}
else if (this == MONTH) {
Calendar read = DateUtils.toCalendar(_readTime, _tz);

View File

@ -45,4 +45,12 @@ public enum HubConfigCharacteristic {
public boolean isChar(String _char) {
return NullUtils.isEqual(name(), _char);
}
public static HubConfigCharacteristic fromUUID(UUID _uuid) {
for (HubConfigCharacteristic c : values()) {
if (c.uuid.equals(_uuid))
return c;
}
return null;
}
}

View File

@ -0,0 +1,63 @@
package com.lanternsoftware.datamodel.currentmonitor.dao;
import com.lanternsoftware.datamodel.currentmonitor.BillingCurrency;
import com.lanternsoftware.datamodel.currentmonitor.BillingMode;
import com.lanternsoftware.datamodel.currentmonitor.BillingRate;
import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
public class BillingRateSerializer extends AbstractDaoSerializer<BillingRate>
{
@Override
public Class<BillingRate> getSupportedClass()
{
return BillingRate.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(BillingRate _o)
{
DaoEntity d = new DaoEntity();
d.put("meter", _o.getMeter());
d.put("day_billing_cycle_start", _o.getDayBillingCycleStart());
d.put("mode", DaoSerializer.toEnumName(_o.getMode()));
d.put("rate", _o.getRate());
d.put("unit", DaoSerializer.toEnumName(_o.getCurrency()));
d.put("time_of_day_start", _o.getTimeOfDayStart());
d.put("time_of_day_end", _o.getTimeOfDayEnd());
d.put("month_kwh_start", _o.getMonthKWhStart());
d.put("month_kwh_end", _o.getMonthKWhEnd());
d.put("begin_effective", DaoSerializer.toLong(_o.getBeginEffective()));
d.put("end_effective", DaoSerializer.toLong(_o.getEndEffective()));
d.put("recurs_annually", _o.isRecursAnnually());
return d;
}
@Override
public BillingRate fromDaoEntity(DaoEntity _d)
{
BillingRate o = new BillingRate();
o.setMeter(DaoSerializer.getInteger(_d, "meter"));
o.setDayBillingCycleStart(DaoSerializer.getInteger(_d, "day_billing_cycle_start"));
o.setMode(DaoSerializer.getEnum(_d, "mode", BillingMode.class));
o.setRate(DaoSerializer.getDouble(_d, "rate"));
o.setCurrency(DaoSerializer.getEnum(_d, "unit", BillingCurrency.class));
o.setTimeOfDayStart(DaoSerializer.getInteger(_d, "time_of_day_start"));
o.setTimeOfDayEnd(DaoSerializer.getInteger(_d, "time_of_day_end"));
o.setMonthKWhStart(DaoSerializer.getDouble(_d, "month_kwh_start"));
o.setMonthKWhEnd(DaoSerializer.getDouble(_d, "month_kwh_end"));
o.setBeginEffective(DaoSerializer.getDate(_d, "begin_effective"));
o.setEndEffective(DaoSerializer.getDate(_d, "end_effective"));
o.setRecursAnnually(DaoSerializer.getBoolean(_d, "recurs_annually"));
return o;
}
}

View File

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

View File

@ -48,6 +48,7 @@ public class BreakerGroupEnergySerializer extends AbstractDaoSerializer<BreakerG
Date start = _o.getStart();
Date now = new Date();
ByteBuffer bb = ByteBuffer.allocate(_o.getViewMode().blockCount(start, tz) * 4);
ByteBuffer cb = ByteBuffer.allocate(_o.getViewMode().blockCount(start, tz) * 8);
for (EnergyBlock b : _o.getEnergyBlocks()) {
if (b.getStart().before(start))
continue;
@ -55,15 +56,21 @@ public class BreakerGroupEnergySerializer extends AbstractDaoSerializer<BreakerG
break;
while (start.before(b.getStart())) {
bb.putFloat(0);
cb.putDouble(0);
start = _o.getViewMode().toBlockEnd(start, tz);
}
bb.putFloat((float) b.getJoules());
cb.putDouble(b.getCharge());
start = _o.getViewMode().toBlockEnd(start, tz);
}
if (bb.position() < bb.limit())
if (bb.position() < bb.limit()) {
d.put("blocks", Arrays.copyOfRange(bb.array(), 0, bb.position()));
else
d.put("charges", Arrays.copyOfRange(cb.array(), 0, cb.position()));
}
else {
d.put("blocks", bb.array());
d.put("charges", cb.array());
}
}
d.put("to_grid", _o.getToGrid());
d.put("from_grid", _o.getFromGrid());
@ -93,6 +100,14 @@ public class BreakerGroupEnergySerializer extends AbstractDaoSerializer<BreakerG
}
}
o.setEnergyBlocks(blocks);
byte[] chargeData = DaoSerializer.getByteArray(_d, "charges");
int idx = 0;
if (CollectionUtils.length(chargeData) > 0) {
ByteBuffer bb = ByteBuffer.wrap(chargeData);
while (bb.hasRemaining()) {
blocks.get(idx++).setCharge(bb.getDouble());
}
}
o.setToGrid(DaoSerializer.getDouble(_d, "to_grid"));
o.setFromGrid(DaoSerializer.getDouble(_d, "from_grid"));
return o;

View File

@ -33,6 +33,7 @@ public class BreakerGroupSummarySerializer extends AbstractDaoSerializer<Breaker
d.put("start", DaoSerializer.toLong(_o.getStart()));
d.put("sub_groups", DaoSerializer.toDaoEntities(_o.getSubGroups(), DaoProxyType.MONGO));
d.put("joules", _o.getJoules());
d.put("charge", _o.getCharge());
d.put("to_grid", _o.getToGrid());
d.put("from_grid", _o.getFromGrid());
return d;
@ -48,6 +49,7 @@ public class BreakerGroupSummarySerializer extends AbstractDaoSerializer<Breaker
o.setStart(DaoSerializer.getDate(_d, "start"));
o.setSubGroups(DaoSerializer.getList(_d, "sub_groups", BreakerGroupSummary.class));
o.setJoules(DaoSerializer.getDouble(_d, "joules"));
o.setCharge(DaoSerializer.getDouble(_d, "charge"));
o.setToGrid(DaoSerializer.getDouble(_d, "to_grid"));
o.setFromGrid(DaoSerializer.getDouble(_d, "from_grid"));
return o;

View File

@ -28,6 +28,7 @@ public class EnergyBlockSerializer extends AbstractDaoSerializer<EnergyBlock>
d.put("start", DaoSerializer.toLong(_o.getStart()));
d.put("end", DaoSerializer.toLong(_o.getEnd()));
d.put("joules", _o.getJoules());
d.put("charge", _o.getCharge());
return d;
}
@ -38,6 +39,7 @@ public class EnergyBlockSerializer extends AbstractDaoSerializer<EnergyBlock>
o.setStart(DaoSerializer.getDate(_d, "start"));
o.setEnd(DaoSerializer.getDate(_d, "end"));
o.setJoules(DaoSerializer.getDouble(_d, "joules"));
o.setCharge(DaoSerializer.getDouble(_d, "charge"));
return o;
}
}

View File

@ -1,4 +1,5 @@
com.lanternsoftware.datamodel.currentmonitor.dao.AccountSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BillingRateSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerConfigSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerGroupEnergySerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerGroupSerializer

View File

@ -2,98 +2,24 @@ package com.lanternsoftware.currentmonitor;
import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao;
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
import com.lanternsoftware.datamodel.currentmonitor.Breaker;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupEnergy;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroupSummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyBlockViewMode;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.DebugTimer;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.dao.DaoQuery;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
public class RebuildSummaries {
public static void main(String[] args) {
int accountId = 1;
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
TimeZone tz = dao.getTimeZoneForAccount(accountId);
Date start = DateUtils.date(1, 7, 2021, tz);
// Date start = DateUtils.getMidnightBeforeNow(tz);
Date end = DateUtils.getMidnightAfterNow(tz);
Map<Date, List<HubPowerMinute>> days = CollectionUtils.transformToMultiMap(dao.getProxy().query(HubPowerMinute.class, new DaoQuery("account_id", accountId).andBetweenInclusiveExclusive("minute", (int)(start.getTime()/60000), (int)(end.getTime()/60000))), _m->DateUtils.getMidnightBefore(_m.getMinuteAsDate(), tz));
BreakerConfig config = dao.getConfig(accountId);
BreakerGroup root = CollectionUtils.getFirst(config.getBreakerGroups());
Map<String, Breaker> breakers = CollectionUtils.transformToMap(root.getAllBreakers(), Breaker::getKey);
Map<String, BreakerGroup> breakerKeyToGroup = new HashMap<>();
for (BreakerGroup group : root.getAllBreakerGroups()) {
for (Breaker b : group.getAllBreakers()) {
breakerKeyToGroup.put(b.getKey(), group);
}
TimeZone tz = TimeZone.getTimeZone("America/Chicago");
dao.rebuildSummaries(100, DateUtils.date(8,3,2021, tz), DateUtils.date(8,5,2021, tz));
/* List<Account> accounts = dao.getProxy().queryAll(Account.class);
for (int accountId : CollectionUtils.transform(accounts, Account::getId)) {
if (accountId != 100)
continue;
dao.rebuildSummaries(accountId, DateUtils.date(4,21,2021, tz), DateUtils.date(4,22,2021, tz));
}
for (Map.Entry<Date, List<HubPowerMinute>> day : days.entrySet()) {
BreakerGroupEnergy energy = null;
DebugTimer timer = new DebugTimer("Time to rebuild one day");
Map<Integer, List<HubPowerMinute>> minutes = CollectionUtils.transformToMultiMap(day.getValue(), HubPowerMinute::getMinute);
for (List<HubPowerMinute> minute : minutes.values()) {
if (energy == null)
energy = new BreakerGroupEnergy(root, minute, EnergyBlockViewMode.DAY, day.getKey(), tz);
else
energy.addEnergy(breakers, breakerKeyToGroup, minute);
}
timer.stop();
if (energy != null)
dao.putBreakerGroupEnergy(energy);
}
dao.updateSummaries(root, days.keySet(), tz);
// List<BreakerGroupEnergy> summaries = dao.getProxy().query(BreakerGroupEnergy.class, null);
// CollectionUtils.edit(summaries, _s->CollectionUtils.edit(_s.getAllGroups(), _t->_t.setAccountId(1)));
// dao.getProxy().save(summaries);
// List<BreakerPower> readings = null;
// while ((readings == null) || !readings.isEmpty()) {
// readings = dao.getProxy().query(BreakerPower.class, new DaoQuery("account_id", new DaoQuery("$ne", 1)), null, null, 0, 1000000);
// System.out.println("Adding account id to " + readings.size() + " power readings");
// CollectionUtils.edit(readings, _s -> _s.setAccountId(1));
// dao.getProxy().save(readings);
// }
//
// List<BreakerPowerArchive> archives = null;
// while ((archives == null) || !archives.isEmpty()) {
// archives = dao.getProxy().query(BreakerPowerArchive.class, new DaoQuery("account_id", new DaoQuery("$ne", 1)), null, null, 0, 50);
// System.out.println("Adding account id to " + archives.size() + " archives");
// CollectionUtils.edit(archives, _s -> _s.setAccountId(1));
// dao.getProxy().save(archives);
// }
// List<BreakerPower> readings = CollectionUtils.filter(dao.getBreakerPower(Arrays.asList("0-1", "0-2"), DateUtils.date(6,26,2020, 17, 0, 0, 0, tz), DateUtils.date(6,26,2020, 22, 0, 0, 0, tz)), _p->_p.getPower() > 0.0);
// CollectionUtils.edit(readings, _p->_p.setPower(-_p.getPower()));
// dao.getProxy().save(readings);
// Map<String, List<BreakerPower>> dups = CollectionUtils.transformToMultiMap(dao.getBreakerPower(Arrays.asList("2-1","2-2","2-3","2-4","2-5","2-6","2-7","2-8","2-9","2-10","2-11","2-12","2-13","2-14","2-15"), DateUtils.date(6,26,2020, 17, 0, 0, 0, tz), DateUtils.date(6,26,2020, 18, 0, 0, 0, tz)), _p->_p.getKey()+_p.getReadTime().getTime());
// for (List<BreakerPower> dup : dups.values()) {
// if (dup.size() > 1) {
// CollectionUtils.removeFirst(dup);
// dao.getProxy().delete(BreakerPower.class, DaoQuery.in("_id", CollectionUtils.transform(dup, BreakerPower::getId)));
// }
// }
// List<BreakerGroupEnergy> summaries = dao.getProxy().query(BreakerGroupEnergy.class, null);
// ResourceLoader.writeFile(LanternFiles.OPS_PATH + "summaryBackup.json", DaoSerializer.toJson(DaoSerializer.toDaoEntities(summaries)));
// for (BreakerGroupEnergy summary : summaries) {
// dao.getProxy().save(summary);
// }
*/
dao.shutdown();
}
}

View File

@ -2,10 +2,9 @@ package com.lanternsoftware.util;
public abstract class LanternFiles {
public static final String SOURCE_PATH = "C:\\lantern\\LanternPowerMonitor\\";
// public static final String OPS_PATH = "D:\\zwave\\";
// public static final String OPS_PATH = "D:\\zwave\\localhost\\";
// public static final String OPS_PATH = "D:\\zwave\\mark4770\\";
// public static final String OPS_PATH = "D:\\zwave\\prod\\";
// public static final String OPS_PATH = "D:\\zwave\\prodremote\\";
public static final String OPS_PATH = "/opt/tomcat/";
public static final String BACKUP_PATH = "D:\\zwave\\localhost\\";
}

View File

@ -20,6 +20,7 @@ public class Switch {
private boolean primary;
private boolean hold;
private boolean hidden;
private boolean suppressEvents;
private String thermometerUrl;
private String controllerUrl;
private ThermostatMode thermostatMode;
@ -199,6 +200,14 @@ public class Switch {
hidden = _hidden;
}
public boolean isSuppressEvents() {
return suppressEvents;
}
public void setSuppressEvents(boolean _suppressEvents) {
suppressEvents = _suppressEvents;
}
public List<SwitchSchedule> getSchedule() {
return schedule;
}

View File

@ -38,6 +38,7 @@ public class SwitchSerializer extends AbstractDaoSerializer<Switch>
d.put("primary", _o.isPrimary());
d.put("hold", _o.isHold());
d.put("hidden", _o.isHidden());
d.put("suppress_events", _o.isSuppressEvents());
d.put("thermometer_url", _o.getThermometerUrl());
d.put("controller_url", _o.getControllerUrl());
d.put("thermostat_mode", DaoSerializer.toEnumName(_o.getThermostatMode()));
@ -60,6 +61,7 @@ public class SwitchSerializer extends AbstractDaoSerializer<Switch>
o.setPrimary(DaoSerializer.getBoolean(_d, "primary"));
o.setHold(DaoSerializer.getBoolean(_d, "hold"));
o.setHidden(DaoSerializer.getBoolean(_d, "hidden"));
o.setSuppressEvents(DaoSerializer.getBoolean(_d, "suppress_events"));
o.setThermometerUrl(DaoSerializer.getString(_d, "thermometer_url"));
o.setControllerUrl(DaoSerializer.getString(_d, "controller_url"));
o.setThermostatMode(DaoSerializer.getEnum(_d, "thermostat_mode", ThermostatMode.class));

View File

@ -286,7 +286,7 @@ public class ZWaveApp {
}
public void fireSwitchLevelEvent(Switch _sw) {
if (NullUtils.isEmpty(config.getRulesUrl()))
if (NullUtils.isEmpty(config.getRulesUrl()) || _sw.isSuppressEvents())
return;
Event event = new Event();
event.setEventDescription(_sw.getFullDisplay() + " set to " + _sw.getLevel());
@ -325,7 +325,7 @@ public class ZWaveApp {
relayController.setRelay(sw.getGpioPin(), sw.getLowLevel() > 0);
}
}, 250);
} else {
} else if (!sw.isSecurity()){
setGroupSwitchLevel(sw, _level);
}
}
@ -351,7 +351,7 @@ public class ZWaveApp {
}
public void updateSwitch(Switch _sw) {
logger.info("Received update for switch {} level {}", _sw.getFullDisplay(), _sw.getLevel());
logger.info("Received update for switch {} {} level {}", _sw.getNodeId(), _sw.getFullDisplay(), _sw.getLevel());
Switch sw = CollectionUtils.filterOne(config.getSwitches(), _s->_s.getNodeId() == _sw.getNodeId());
if (sw != null) {
sw.setLevel( _sw.getLevel());
@ -395,6 +395,7 @@ public class ZWaveApp {
peers.remove(config.getUrl());
for (String peer : peers) {
for (Switch sw : modified) {
logger.info("Sending update for switch {} {} level {} to {}", sw.getNodeId(), sw.getFullDisplay(), sw.getLevel(), peer);
HttpPost post = new HttpPost(peer + "/switch/" + sw.getNodeId());
post.setHeader("auth_code", authCode);
post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(sw)));

View File

@ -1,6 +1,7 @@
package com.lanternsoftware.zwave.security;
import com.lanternsoftware.datamodel.zwave.Switch;
import com.lanternsoftware.util.concurrency.ConcurrencyUtils;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalInput;
import com.pi4j.io.gpio.PinPullResistance;
@ -11,11 +12,16 @@ import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SecurityController {
protected static final Logger LOG = LoggerFactory.getLogger(SecurityController.class);
private final Map<Integer, GpioPinDigitalInput> pins = new HashMap<>();
private final Map<Integer, Boolean> pinEvents = new HashMap<>();
private final ExecutorService executor = Executors.newFixedThreadPool(2);
private int eventIdx = 0;
public boolean isOpen(int _pin) {
GpioPinDigitalInput pin = getPin(_pin);
@ -25,7 +31,24 @@ public class SecurityController {
public void listen(Switch _sw, SecurityListener _listener) {
GpioPinDigitalInput pin = getPin(_sw.getGpioPin());
if (pin != null)
pin.addListener((GpioPinListenerDigital) _event -> _listener.onStateChanged(_sw.getNodeId(), _event.getState().isHigh()));
pin.addListener((GpioPinListenerDigital) _event -> {
synchronized (pinEvents) {
eventIdx++;
LOG.info("state change from gpio, event {} pin {} to {}", eventIdx, _sw.getGpioPin(), _event.getState().isHigh());
pinEvents.put(_sw.getGpioPin(), _event.getState().isHigh());
}
executor.submit(()->{
ConcurrencyUtils.sleep(500);
Boolean high;
synchronized (pinEvents) {
high = pinEvents.remove(_sw.getGpioPin());
}
LOG.info("handling event {} pin {} most recent event is {}", eventIdx, _sw.getGpioPin(), high);
if (high == null)
return;
_listener.onStateChanged(_sw.getNodeId(), high);
});
});
}
private GpioPinDigitalInput getPin(int _pin) {
@ -47,6 +70,7 @@ public class SecurityController {
pin.removeAllListeners();
}
pins.clear();
executor.shutdownNow();
GpioFactory.getInstance().shutdown();
}
}