Initial Commit

This commit is contained in:
Mark Milligan
2021-01-14 16:28:24 -06:00
parent 21c28201c5
commit 1334c110ff
318 changed files with 24160 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
package com.lanternsoftware.dataaccess.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.datamodel.currentmonitor.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.HubPowerMinute;
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 {
void shutdown();
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);
void putBreakerGroupEnergy(BreakerGroupEnergy _energy);
void putHubPowerMinute(HubPowerMinute _power);
BreakerConfig getConfig(int _accountId);
BreakerConfig getMergedConfig(AuthCode _authCode);
void putConfig(BreakerConfig _config);
void updateSummaries(BreakerGroup _rootGroup, Set<Date> _daysToSummarize, TimeZone _tz);
String authenticateAccount(String _username, String _password);
String getAuthCodeForEmail(String _email);
Account authCodeToAccount(String _authCode);
AuthCode decryptAuthCode(String _authCode);
Account putAccount(Account _account);
Account getAccount(int _accountId);
Account getAccountByUsername(String _username);
MongoProxy getProxy();
}

View File

@@ -0,0 +1,57 @@
package com.lanternsoftware.dataaccess.currentmonitor;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.Date;
@DBSerializable(autogen = false)
public class DirtyMinute {
private int accountId;
private int minute;
private Date posted;
public DirtyMinute() {
}
public DirtyMinute(int _accountId, int _minute, Date _posted) {
accountId = _accountId;
minute = _minute;
posted = _posted;
}
public String getId() {
return accountId + "-" + minute;
}
public int getAccountId() {
return accountId;
}
public void setAccountId(int _accountId) {
accountId = _accountId;
}
public Date getMinuteAsDate() {
return new Date(((long)minute)*60000);
}
public int getMinute() {
return minute;
}
public void setMinute(int _minute) {
minute = _minute;
}
public void setMinute(Date _minute) {
minute = (int)(_minute.getTime()/60000);
}
public Date getPosted() {
return posted;
}
public void setPosted(Date _posted) {
posted = _posted;
}
}

View File

@@ -0,0 +1,273 @@
package com.lanternsoftware.dataaccess.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.datamodel.currentmonitor.AuthCode;
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.HubPowerMinute;
import com.lanternsoftware.datamodel.currentmonitor.Sequence;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.DebugTimer;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.ResourceLoader;
import com.lanternsoftware.util.cryptography.AESTool;
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.mongo.MongoConfig;
import com.lanternsoftware.util.dao.mongo.MongoProxy;
import org.mindrot.jbcrypt.BCrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MongoCurrentMonitorDao implements CurrentMonitorDao {
private static final Logger logger = LoggerFactory.getLogger(MongoCurrentMonitorDao.class);
private static final AESTool aes = new AESTool(ResourceLoader.loadFile(LanternFiles.OPS_PATH + "authKey.dat"));
private static final int BCRYPT_ROUNDS = 11;
private final Timer delayTimer = new Timer();
private final ExecutorService executor = Executors.newCachedThreadPool();
private final MongoProxy proxy;
public MongoCurrentMonitorDao(MongoConfig _config) {
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"));
proxy.ensureIndex(DirtyMinute.class, DaoSort.sort("posted"));
for (DirtyMinute minute : proxy.queryAll(DirtyMinute.class)) {
updateSummaries(minute);
}
proxy.delete(DirtyMinute.class, new DaoQuery());
}
public void shutdown() {
delayTimer.cancel();
executor.shutdownNow();
proxy.shutdown();
}
@Override
public void putBreakerPower(BreakerPower _power) {
proxy.save(_power);
}
@Override
public void putHubPowerMinute(HubPowerMinute _power) {
if (_power == null)
return;
proxy.save(_power);
DirtyMinute minute = new DirtyMinute(_power.getAccountId(), _power.getMinute(), new Date());
proxy.save(minute);
delayTimer.schedule(new TimerTask(){
@Override
public void run() {
executor.submit(()->{
if (proxy.queryOneAndDelete(DirtyMinute.class, new DaoQuery("_id", minute.getId())) != null)
updateSummaries(new DirtyMinute(_power.getAccountId(), _power.getMinute(), new Date()));
});
}
}, 10000);
}
private void updateSummaries(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 = TimeZone.getTimeZone("America/Chicago");
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);
else
summary.addEnergy(group, minutes, tz);
putBreakerGroupEnergy(summary);
updateSummaries(group, CollectionUtils.asHashSet(day), tz);
timer.stop();
}
@Override
public List<BreakerPower> getBreakerPowerForAccount(int _accountId) {
return proxy.query(BreakerPower.class, new DaoQuery("account_id", _accountId));
}
@Override
public BreakerPower getLatestBreakerPower(int _accountId, int _panel, int _space) {
return proxy.queryOne(BreakerPower.class, new DaoQuery("account_id", _accountId).and("key", Breaker.key(_panel, _space)), DaoSort.sortDesc("read_time"));
}
@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)));
}
@Override
public void updateSummaries(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) {
Calendar calDayStart = DateUtils.toCalendar(month, _tz);
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()));
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);
}
for (Date year : yearsToSummarize) {
Calendar calMonthStart = DateUtils.toCalendar(year, _tz);
Calendar end = DateUtils.getEndOfYearCal(year, _tz);
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);
}
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<BreakerGroupSummary> groupEnergies = CollectionUtils.aggregate(proxy.query(BreakerGroupSummary.class, new DaoQuery("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);
}
@Override
public void putBreakerGroupEnergy(BreakerGroupEnergy _energy) {
proxy.save(_energy);
proxy.save(new BreakerGroupSummary(_energy));
}
@Override
public BreakerConfig getConfig(int _accountId) {
return proxy.queryOne(BreakerConfig.class, new DaoQuery("_id", String.valueOf(_accountId)));
}
@Override
public BreakerConfig getMergedConfig(AuthCode _authCode) {
if (_authCode == null)
return null;
List<BreakerConfig> configs = CollectionUtils.transform(_authCode.getAllAccountIds(), this::getConfig, true);
BreakerConfig config = new BreakerConfig();
config.setAccountId(_authCode.getAccountId());
config.setBreakerHubs(CollectionUtils.aggregate(configs, BreakerConfig::getBreakerHubs));
config.setBreakerGroups(CollectionUtils.aggregate(configs, BreakerConfig::getBreakerGroups));
config.setPanels(CollectionUtils.aggregate(configs, BreakerConfig::getPanels));
config.setMeters(CollectionUtils.aggregate(configs, BreakerConfig::getMeters));
return config;
}
@Override
public void putConfig(BreakerConfig _config) {
DaoQuery configQuery = new DaoQuery("_id", String.valueOf(_config.getAccountId()));
BreakerConfig oldConfig = proxy.queryOne(BreakerConfig.class, configQuery);
if (oldConfig != null)
proxy.delete(BreakerGroup.class, DaoQuery.in("_id", oldConfig.getAllBreakerGroupIds()));
proxy.save(_config);
}
@Override
public String authenticateAccount(String _username, String _password) {
Account acct = proxy.queryOne(Account.class, new DaoQuery("username", _username));
if ((acct == null) || !BCrypt.checkpw(_password, acct.getPassword()))
return null;
return aes.encryptToBase64(DaoSerializer.toZipBson(new AuthCode(acct.getId(), acct.getAuxiliaryAccountIds())));
}
@Override
public Account authCodeToAccount(String _authCode) {
AuthCode code = decryptAuthCode(_authCode);
if (code == null)
return null;
return proxy.queryOne(Account.class, new DaoQuery("_id", code.getAccountId()));
}
@Override
public AuthCode decryptAuthCode(String _authCode) {
return DaoSerializer.fromZipBson(aes.decryptFromBase64(_authCode), AuthCode.class);
}
@Override
public String getAuthCodeForEmail(String _email) {
_email = _email.toLowerCase().trim();
Account account = getAccountByUsername(_email);
if (account == null) {
account = new Account();
account.setUsername(_email);
putAccount(account);
}
return aes.encryptToBase64(DaoSerializer.toZipBson(new AuthCode(account.getId(), account.getAuxiliaryAccountIds())));
}
@Override
public Account putAccount(Account _account) {
if (_account == null)
return null;
_account.setUsername(NullUtils.makeNotNull(_account.getUsername()).toLowerCase().trim());
Account account = getAccountByUsername(_account.getUsername());
if (account != null) {
_account.setId(account.getId());
if (NullUtils.isEmpty(_account.getPassword()))
_account.setPassword(account.getPassword());
else
_account.setPassword(BCrypt.hashpw(_account.getPassword(), BCrypt.gensalt(BCRYPT_ROUNDS)));
}
else if (NullUtils.isNotEmpty(_account.getPassword())) {
_account.setPassword(BCrypt.hashpw(_account.getPassword(), BCrypt.gensalt(BCRYPT_ROUNDS)));
}
if (_account.getId() == 0)
_account.setId(proxy.updateOne(Sequence.class, null, new DaoEntity("$inc", new DaoEntity("sequence", 1))).getSequence());
proxy.save(_account);
return clearPassword(_account);
}
@Override
public Account getAccount(int _accountId) {
return clearPassword(proxy.queryOne(Account.class, new DaoQuery("_id", String.valueOf(_accountId))));
}
@Override
public Account getAccountByUsername(String _username) {
return clearPassword(proxy.queryOne(Account.class, new DaoQuery("username", NullUtils.makeNotNull(_username).toLowerCase().trim())));
}
private Account clearPassword(Account _account) {
if (_account == null)
return null;
_account.setPassword(null);
return _account;
}
@Override
public MongoProxy getProxy() {
return proxy;
}
}

View File

@@ -0,0 +1,44 @@
package com.lanternsoftware.dataaccess.currentmonitor.dao;
import com.lanternsoftware.dataaccess.currentmonitor.DirtyMinute;
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 DirtyMinuteSerializer extends AbstractDaoSerializer<DirtyMinute>
{
@Override
public Class<DirtyMinute> getSupportedClass()
{
return DirtyMinute.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(DirtyMinute _o)
{
DaoEntity d = new DaoEntity();
d.put("_id", _o.getId());
d.put("account_id", _o.getAccountId());
d.put("minute", _o.getMinute());
d.put("posted", DaoSerializer.toLong(_o.getPosted()));
return d;
}
@Override
public DirtyMinute fromDaoEntity(DaoEntity _d)
{
DirtyMinute o = new DirtyMinute();
o.setAccountId(DaoSerializer.getInteger(_d, "account_id"));
o.setMinute(DaoSerializer.getInteger(_d, "minute"));
o.setPosted(DaoSerializer.getDate(_d, "posted"));
return o;
}
}

View File

@@ -0,0 +1 @@
com.lanternsoftware.dataaccess.currentmonitor.dao.DirtyMinuteSerializer