Improve 3A+ case, making it easier to take the pi out. Improve the fit of the Z2 case.

Make it possible for a hub to reload a config automatically when it changes without being restarted.
Prevent the auto-calibration on first install from being stomped by the app.
Allow updating the hub software via the app.
This commit is contained in:
MarkBryanMilligan 2022-01-13 14:33:21 -06:00
parent 88ed044ef7
commit ed75ab1f05
38 changed files with 659 additions and 261 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,7 +3,7 @@
<groupId>com.lanternsoftware.currentmonitor</groupId> <groupId>com.lanternsoftware.currentmonitor</groupId>
<artifactId>lantern-currentmonitor</artifactId> <artifactId>lantern-currentmonitor</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.0.4</version> <version>1.0.6</version>
<name>lantern-currentmonitor</name> <name>lantern-currentmonitor</name>
<properties> <properties>

View File

@ -97,13 +97,18 @@ public class CurrentMonitor {
} }
public void monitorPower(BreakerHub _hub, List<Breaker> _breakers, int _intervalMs, PowerListener _listener) { public void monitorPower(BreakerHub _hub, List<Breaker> _breakers, int _intervalMs, PowerListener _listener) {
try {
stopMonitoring(); stopMonitoring();
listener = _listener; listener = _listener;
List<Breaker> validBreakers = CollectionUtils.filter(_breakers, _b->_b.getPort() > 0 && _b.getPort() < 16); List<Breaker> validBreakers = CollectionUtils.filter(_breakers, _b -> _b.getPort() > 0 && _b.getPort() < 16);
sampler = new Sampler(_hub, validBreakers, _intervalMs, 2); sampler = new Sampler(_hub, validBreakers, _intervalMs, 2);
LOG.info("Starting to monitor ports {}", CollectionUtils.transformToCommaSeparated(validBreakers, _b->String.valueOf(_b.getPort()))); LOG.info("Starting to monitor ports {}", CollectionUtils.transformToCommaSeparated(validBreakers, _b -> String.valueOf(_b.getPort())));
executor.submit(sampler); executor.submit(sampler);
} }
catch (Throwable t) {
LOG.error("throwable", t);
}
}
private GpioPinAnalogInput getPin(int _chip, int _pin) { private GpioPinAnalogInput getPin(int _chip, int _pin) {
GpioPinAnalogInput pin; GpioPinAnalogInput pin;
@ -182,9 +187,11 @@ public class CurrentMonitor {
try { try {
while (true) { while (true) {
synchronized (this) { synchronized (this) {
if (!running) if (!running) {
LOG.error("Power Monitoring Stopped");
break; break;
} }
}
final Date readTime = new Date(); final Date readTime = new Date();
final long intervalStart = (interval * intervalNs) + start; final long intervalStart = (interval * intervalNs) + start;
long intervalEnd = intervalStart + intervalNs; long intervalEnd = intervalStart + intervalNs;

View File

@ -10,6 +10,8 @@ import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup;
import com.lanternsoftware.datamodel.currentmonitor.BreakerHub; import com.lanternsoftware.datamodel.currentmonitor.BreakerHub;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower; import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPowerMinute; import com.lanternsoftware.datamodel.currentmonitor.BreakerPowerMinute;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubCommands;
import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic; import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic;
import com.lanternsoftware.datamodel.currentmonitor.HubConfigService; import com.lanternsoftware.datamodel.currentmonitor.HubConfigService;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
@ -25,6 +27,7 @@ import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.http.HttpPool; import com.lanternsoftware.util.http.HttpPool;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
@ -39,10 +42,12 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.URL;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -64,7 +69,7 @@ public class MonitorApp {
private static final AtomicBoolean running = new AtomicBoolean(true); private static final AtomicBoolean running = new AtomicBoolean(true);
private static final CurrentMonitor monitor = new CurrentMonitor(); private static final CurrentMonitor monitor = new CurrentMonitor();
private static final List<BreakerPower> readings = new ArrayList<>(); private static final List<BreakerPower> readings = new ArrayList<>();
private static final String version = getVersionNumber(); private static String version;
private static final PowerListener logger = _p -> { private static final PowerListener logger = _p -> {
if (!config.isDebug()) { if (!config.isDebug()) {
_p.setHubVersion(version); _p.setHubVersion(version);
@ -76,21 +81,7 @@ public class MonitorApp {
} else } else
LOG.info("Panel{} - Space{} Power: {}W", _p.getPanel(), Breaker.toSpaceDisplay(_p.getSpace()), String.format("%.3f", _p.getPower())); LOG.info("Panel{} - Space{} Power: {}W", _p.getPanel(), Breaker.toSpaceDisplay(_p.getSpace()), String.format("%.3f", _p.getPower()));
}; };
private static MqttPoster mqttPoster; private static final BleCharacteristicListener bluetoothListener = new BleCharacteristicListener() {
public static void main(String[] args) {
config = DaoSerializer.parse(ResourceLoader.loadFileAsString(WORKING_DIR + "config.json"), MonitorConfig.class);
if (config == null) {
LOG.error("Failed to load config file from {}", WORKING_DIR + "config.json");
return;
}
pool = new HttpPool(10, 10, config.getSocketTimeout(), config.getConnectTimeout(), config.getSocketTimeout());
if (NullUtils.isNotEmpty(config.getHost()))
host = NullUtils.terminateWith(config.getHost(), "/");
monitor.setDebug(config.isDebug());
monitor.start();
LEDFlasher.setLEDOn(false);
final BluetoothConfig bluetoothConfig = new BluetoothConfig("Lantern Hub", new BleCharacteristicListener() {
@Override @Override
public void write(String _name, byte[] _value) { public void write(String _name, byte[] _value) {
HubConfigCharacteristic ch = NullUtils.toEnum(HubConfigCharacteristic.class, _name); HubConfigCharacteristic ch = NullUtils.toEnum(HubConfigCharacteristic.class, _name);
@ -162,6 +153,24 @@ public class MonitorApp {
LOG.error("Exception occurred while trying to shutdown", _e); LOG.error("Exception occurred while trying to shutdown", _e);
} }
break; break;
case Update:
monitor.submit(new UpdateChecker(true));
break;
case ReloadConfig:
HttpGet get = new HttpGet(host + "config");
get.addHeader("auth_code", authCode);
BreakerConfig newConfig = DaoSerializer.parse(pool.executeToString(get), BreakerConfig.class);
if (newConfig != null) {
breakerConfig = newConfig;
List<Breaker> breakers = breakerConfig.getBreakersForHub(config.getHub());
BreakerHub hub = breakerConfig.getHub(config.getHub());
if (hub != null) {
LOG.info("Monitoring {} breakers for hub {}", CollectionUtils.size(breakers), hub.getHub());
if (CollectionUtils.size(breakers) > 0)
monitor.monitorPower(hub, breakers, 1000, logger);
}
}
break;
} }
} }
}); });
@ -188,9 +197,34 @@ public class MonitorApp {
log = Arrays.copyOfRange(log, log.length-15, log.length); log = Arrays.copyOfRange(log, log.length-15, log.length);
return ZipUtils.zip(NullUtils.toByteArray(CollectionUtils.delimit(Arrays.asList(log), "\n"))); return ZipUtils.zip(NullUtils.toByteArray(CollectionUtils.delimit(Arrays.asList(log), "\n")));
} }
if (HubConfigCharacteristic.Version == ch)
return NullUtils.toByteArray(version);
return null; return null;
} }
}); };
private static BluetoothConfig bluetoothConfig;
private static MqttPoster mqttPoster;
public static void main(String[] args) {
try {
Runtime.getRuntime().exec(new String[]{"systemctl","restart","dbus"});
ConcurrencyUtils.sleep(500);
} catch (IOException _e) {
LOG.error("Exception occurred while trying to restart", _e);
}
version = getVersionNumber();
config = DaoSerializer.parse(ResourceLoader.loadFileAsString(WORKING_DIR + "config.json"), MonitorConfig.class);
if (config == null) {
LOG.error("Failed to load config file from {}", WORKING_DIR + "config.json");
return;
}
pool = new HttpPool(10, 10, config.getSocketTimeout(), config.getConnectTimeout(), config.getSocketTimeout());
if (NullUtils.isNotEmpty(config.getHost()))
host = NullUtils.terminateWith(config.getHost(), "/");
monitor.setDebug(config.isDebug());
monitor.start();
LEDFlasher.setLEDOn(false);
bluetoothConfig = new BluetoothConfig("Lantern Hub", bluetoothListener);
bluetoothConfig.start(); bluetoothConfig.start();
if (NullUtils.isNotEmpty(config.getAuthCode())) if (NullUtils.isNotEmpty(config.getAuthCode()))
authCode = config.getAuthCode(); authCode = config.getAuthCode();
@ -303,6 +337,7 @@ public class MonitorApp {
if (!readings.isEmpty()) { if (!readings.isEmpty()) {
mqttReadings.addAll(readings); mqttReadings.addAll(readings);
post = new DaoEntity("readings", DaoSerializer.toDaoEntities(readings)); post = new DaoEntity("readings", DaoSerializer.toDaoEntities(readings));
post.put("hub", config.getHub());
if (curMinute != lastMinute) { if (curMinute != lastMinute) {
HubPowerMinute minute = new HubPowerMinute(); HubPowerMinute minute = new HubPowerMinute();
minute.setAccountId(breakerConfig.getAccountId()); minute.setAccountId(breakerConfig.getAccountId());
@ -336,7 +371,8 @@ public class MonitorApp {
} }
if (post != null) { if (post != null) {
byte[] payload = DaoSerializer.toZipBson(post); byte[] payload = DaoSerializer.toZipBson(post);
if (post(payload, "power/batch")) { PostResponse<HubCommands> resp = post(payload, "power/batch", HubCommands.class);
if (resp.success) {
File[] files = new File(WORKING_DIR + "cache").listFiles(); File[] files = new File(WORKING_DIR + "cache").listFiles();
if (files != null) { if (files != null) {
for (File file : files) { for (File file : files) {
@ -347,6 +383,11 @@ public class MonitorApp {
break; break;
} }
} }
if (resp.t != null) {
for (HubCommand command : resp.t.getCommands()) {
bluetoothListener.write(command.getCharacteristic().name(), command.getData());
}
}
} }
} }
} }
@ -355,7 +396,6 @@ public class MonitorApp {
if (DateUtils.diffInSeconds(new Date(), lastUpdateCheck) >= config.getUpdateInterval()) { if (DateUtils.diffInSeconds(new Date(), lastUpdateCheck) >= config.getUpdateInterval()) {
lastUpdateCheck = new Date(); lastUpdateCheck = new Date();
monitor.submit(new UpdateChecker()); monitor.submit(new UpdateChecker());
monitor.submit(new CommandChecker());
} }
long now = new Date().getTime(); long now = new Date().getTime();
long duration = (now - firstPost)%1000; long duration = (now - firstPost)%1000;
@ -371,34 +411,65 @@ public class MonitorApp {
} }
} }
private static void uploadLog() { private static boolean post(byte[] _payload, String _path) {
LOG.info("Commanded to upload log file, preparing..."); return post(_payload, _path, Boolean.class).success;
String log = ResourceLoader.loadFileAsString(WORKING_DIR + "log/log.txt");
if (NullUtils.isNotEmpty(log)) {
DaoEntity payload = new DaoEntity("command", "log").and("payload", log);
post(DaoSerializer.toZipBson(payload), "command");
}
} }
private static boolean post(byte[] _payload, String _path) { private static <T> PostResponse<T> post(byte[] _payload, String _path, Class<T> _class) {
if (NullUtils.isEmpty(host)) if (NullUtils.isEmpty(host))
return false; return new PostResponse<>(false, null);
HttpPost post = new HttpPost(host + _path); HttpPost post = new HttpPost(host + _path);
post.addHeader("auth_code", authCode); post.addHeader("auth_code", authCode);
post.setEntity(new ByteArrayEntity(_payload, ContentType.APPLICATION_OCTET_STREAM)); post.setEntity(new ByteArrayEntity(_payload, ContentType.APPLICATION_OCTET_STREAM));
InputStream is = null;
CloseableHttpResponse resp = pool.execute(post); CloseableHttpResponse resp = pool.execute(post);
try { try {
return ((resp != null) && (resp.getStatusLine() != null) && (resp.getStatusLine().getStatusCode() == 200)); if ((resp != null) && (resp.getStatusLine() != null) && (resp.getStatusLine().getStatusCode() == 200)) {
} finally { T t = null;
HttpEntity entity = resp.getEntity();
if (entity != null) {
is = entity.getContent();
byte[] payload = IOUtils.toByteArray(is);
if (CollectionUtils.length(payload) > 0)
t = DaoSerializer.fromZipBson(payload, _class);
}
return new PostResponse<>(true, t);
}
}
catch (Exception _e) {
LOG.error("Failed to make http request to " + post.getURI().toString(), _e);
}
finally {
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(resp); IOUtils.closeQuietly(resp);
} }
return new PostResponse<>(false, null);
} }
private static class PostResponse<T> {
public final boolean success;
public final T t;
public PostResponse(boolean _success, T _t) {
success = _success;
t = _t;
}
}
private static final class UpdateChecker implements Runnable { private static final class UpdateChecker implements Runnable {
private final boolean force;
public UpdateChecker() {
force = false;
}
public UpdateChecker(boolean _force) {
force = _force;
}
@Override @Override
public void run() { public void run() {
if (NullUtils.isNotEmpty(host) && config.isAutoUpdate()) { if (NullUtils.isNotEmpty(host) && (force || config.isAutoUpdate())) {
DaoEntity meta = DaoSerializer.fromZipBson(pool.executeToByteArray(new HttpGet(host + "update/version"))); DaoEntity meta = DaoSerializer.fromZipBson(pool.executeToByteArray(new HttpGet(host + "update/version")));
String newVersion = DaoSerializer.getString(meta, "version"); String newVersion = DaoSerializer.getString(meta, "version");
if (NullUtils.isNotEqual(newVersion, version)) { if (NullUtils.isNotEqual(newVersion, version)) {
@ -407,9 +478,14 @@ public class MonitorApp {
if (CollectionUtils.length(jar) == DaoSerializer.getInteger(meta, "size") && NullUtils.isEqual(DigestUtils.md5Hex(jar), DaoSerializer.getString(meta, "checksum"))) { if (CollectionUtils.length(jar) == DaoSerializer.getInteger(meta, "size") && NullUtils.isEqual(DigestUtils.md5Hex(jar), DaoSerializer.getString(meta, "checksum"))) {
LOG.info("Update downloaded, writing jar and restarting..."); LOG.info("Update downloaded, writing jar and restarting...");
ResourceLoader.writeFile(WORKING_DIR + "lantern-currentmonitor.jar", jar); ResourceLoader.writeFile(WORKING_DIR + "lantern-currentmonitor.jar", jar);
ConcurrencyUtils.sleep(10000); synchronized (running) {
running.set(false);
}
monitor.stopMonitoring();
bluetoothConfig.stop();
pool.shutdown();
try { try {
Runtime.getRuntime().exec(new String[]{"systemctl", "restart", "currentmonitor"}); Runtime.getRuntime().exec(new String[]{"systemctl","restart","currentmonitor"});
} catch (IOException _e) { } catch (IOException _e) {
LOG.error("Exception occurred while trying to restart", _e); LOG.error("Exception occurred while trying to restart", _e);
} }
@ -419,65 +495,29 @@ public class MonitorApp {
} }
} }
private static final class CommandChecker implements Runnable { public static String getVersionNumber() {
@Override
public void run() {
if (NullUtils.isNotEmpty(host)) {
HttpGet get = new HttpGet(host + "command");
get.addHeader("auth_code", authCode);
DaoEntity meta = DaoSerializer.fromZipBson(pool.executeToByteArray(get));
for (String command : DaoSerializer.getList(meta, "commands", String.class)) {
if (NullUtils.isEqual(command, "log")) {
uploadLog();
} else if (NullUtils.makeNotNull(command).startsWith("timeout")) {
LOG.info("Updating timeouts...");
String[] timeouts = NullUtils.cleanSplit(command, "-");
if (CollectionUtils.size(timeouts) != 3)
continue;
config.setConnectTimeout(DaoSerializer.toInteger(timeouts[1]));
config.setSocketTimeout(DaoSerializer.toInteger(timeouts[2]));
HttpPool old = pool;
pool = new HttpPool(10, 10, config.getSocketTimeout(), config.getConnectTimeout(), config.getSocketTimeout());
old.shutdown();
ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config));
} else if (NullUtils.isEqual(command, "extend_filesystem")) {
LOG.info("Extending filesystem and rebooting");
try { try {
Runtime.getRuntime().exec(new String[]{"sudo", "raspi-config", "--expand-rootfs"}); Enumeration<URL> resources = MonitorApp.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
ConcurrencyUtils.sleep(5000); while (resources.hasMoreElements()) {
Runtime.getRuntime().exec(new String[]{"reboot", "now"});
} catch (IOException _e) {
LOG.error("Exception occurred while trying to extend filesystem", _e);
}
} else if (NullUtils.isEqual(command, "restart")) {
LOG.info("Restarting...");
try {
Runtime.getRuntime().exec(new String[]{"systemctl", "restart", "currentmonitor"});
} catch (IOException _e) {
LOG.error("Exception occurred while trying to restart", _e);
}
}
}
}
}
}
private static String getVersionNumber() {
InputStream is = null; InputStream is = null;
try { try {
is = MonitorApp.class.getResourceAsStream("/META-INF/MANIFEST.MF"); is = resources.nextElement().openStream();
Manifest manifest = new Manifest(is); Manifest manifest = new Manifest(is);
Attributes attr = manifest.getMainAttributes(); Attributes attr = manifest.getMainAttributes();
if (NullUtils.isEqual(attr.getValue("Specification-Title"), "Lantern Power Monitor")) {
String version = attr.getValue("Specification-Version"); String version = attr.getValue("Specification-Version");
LOG.info("Current Version: {}", version); LOG.info("Current Version: {}", version);
return version; return version;
} }
catch (Exception _e) {
LOG.error("Failed to get current version number", _e);
return "";
} }
finally { finally {
IOUtils.closeQuietly(is); IOUtils.closeQuietly(is);
} }
} }
}
catch (Exception _e) {
LOG.error("Failed to get current version number", _e);
}
return "";
}
} }

View File

@ -2,6 +2,8 @@ package com.lanternsoftware.dataaccess.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.Account; import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig; import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.ChargeSummary;
import com.lanternsoftware.datamodel.currentmonitor.ChargeTotal;
import com.lanternsoftware.datamodel.currentmonitor.EnergySummary; import com.lanternsoftware.datamodel.currentmonitor.EnergySummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyTotal; import com.lanternsoftware.datamodel.currentmonitor.EnergyTotal;
import com.lanternsoftware.datamodel.currentmonitor.Sequence; import com.lanternsoftware.datamodel.currentmonitor.Sequence;
@ -10,14 +12,15 @@ import com.lanternsoftware.datamodel.rules.FcmDevice;
import com.lanternsoftware.datamodel.rules.Rule; import com.lanternsoftware.datamodel.rules.Rule;
import com.lanternsoftware.util.DebugTimer; import com.lanternsoftware.util.DebugTimer;
import com.lanternsoftware.util.LanternFiles; import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.dao.DaoQuery;
import com.lanternsoftware.util.dao.mongo.MongoConfig; import com.lanternsoftware.util.dao.mongo.MongoConfig;
import java.util.List; import java.util.List;
public class Backup { public class Backup {
public static void main(String[] args) { public static void main(String[] args) {
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg")); CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_SOURCE + "mongo.cfg"));
CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_PATH + "mongo.cfg")); CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_DEST + "mongo.cfg"));
DebugTimer t1 = new DebugTimer("Query Accounts"); DebugTimer t1 = new DebugTimer("Query Accounts");
List<Account> accounts = dao.getProxy().queryAll(Account.class); List<Account> accounts = dao.getProxy().queryAll(Account.class);
@ -34,17 +37,39 @@ public class Backup {
t4.stop(); t4.stop();
DebugTimer t5 = new DebugTimer("Query Energy"); DebugTimer t5 = new DebugTimer("Query Energy");
List<EnergySummary> energy = dao.getProxy().queryAll(EnergySummary.class); for (Account a : accounts) {
t5.stop(); List<EnergySummary> energy = dao.getProxy().query(EnergySummary.class, new DaoQuery("account_id", a.getId()));
DebugTimer t6 = new DebugTimer("Save Energy"); DebugTimer t = new DebugTimer("Save Energy");
backupDao.getProxy().save(energy); backupDao.getProxy().save(energy);
t.stop();
}
t5.stop();
DebugTimer t6 = new DebugTimer("Query Energy Totals");
for (Account a : accounts) {
List<EnergyTotal> total = dao.getProxy().query(EnergyTotal.class, new DaoQuery("account_id", a.getId()));
DebugTimer t = new DebugTimer("Save Summary");
backupDao.getProxy().save(total);
t.stop();
}
t6.stop(); t6.stop();
DebugTimer t7 = new DebugTimer("Query Summaries"); DebugTimer t7 = new DebugTimer("Query Charges");
List<EnergyTotal> summary = dao.getProxy().queryAll(EnergyTotal.class); for (Account a : accounts) {
List<ChargeSummary> charges = dao.getProxy().query(ChargeSummary.class, new DaoQuery("account_id", a.getId()));
DebugTimer t = new DebugTimer("Save Charges");
backupDao.getProxy().save(charges);
t.stop();
}
t7.stop(); t7.stop();
DebugTimer t8 = new DebugTimer("Save Summaries");
backupDao.getProxy().save(summary); DebugTimer t8 = new DebugTimer("Query Charge Totals");
for (Account a : accounts) {
List<ChargeTotal> charges = dao.getProxy().query(ChargeTotal.class, new DaoQuery("account_id", a.getId()));
DebugTimer t = new DebugTimer("Save Charge Totals");
backupDao.getProxy().save(charges);
t.stop();
}
t8.stop(); t8.stop();
DebugTimer t9 = new DebugTimer("Query Events"); DebugTimer t9 = new DebugTimer("Query Events");

View File

@ -16,8 +16,8 @@ import java.util.TimeZone;
public class BackupMinutes { public class BackupMinutes {
public static void main(String[] args) { public static void main(String[] args) {
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg")); CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_SOURCE + "mongo.cfg"));
CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_PATH + "mongo.cfg")); CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_DEST + "mongo.cfg"));
Date now = new Date(); Date now = new Date();
for (Account a : dao.getProxy().queryAll(Account.class)) { for (Account a : dao.getProxy().queryAll(Account.class)) {
if (a.getId() == 0) if (a.getId() == 0)
@ -30,12 +30,11 @@ public class BackupMinutes {
HubPowerMinute minute = dao.getProxy().queryOne(HubPowerMinute.class, new DaoQuery("account_id", a.getId()), DaoSort.sort("minute")); HubPowerMinute minute = dao.getProxy().queryOne(HubPowerMinute.class, new DaoQuery("account_id", a.getId()), DaoSort.sort("minute"));
if (minute == null) if (minute == null)
continue; continue;
Date minStart = DateUtils.addDays(DateUtils.getMidnightBeforeNow(tz), -60, tz); HubPowerMinute lastBackup = backupDao.getProxy().queryOne(HubPowerMinute.class, new DaoQuery("account_id", a.getId()), DaoSort.sortDesc("minute"));
Date start = DateUtils.getMidnightBefore(minute.getMinuteAsDate(), tz); Date start = lastBackup == null ? DateUtils.getMidnightBefore(minute.getMinuteAsDate(), tz) : lastBackup.getMinuteAsDate();
if (minStart.after(start)) // Date start = DateUtils.date(10,16,2021,tz);
start = minStart;
Date end = DateUtils.addDays(start, 1, tz); Date end = DateUtils.addDays(start, 1, tz);
while (end.before(now)) { while (start.before(now)) {
DebugTimer t2 = new DebugTimer("Account Id: " + a.getId() + " Query Day " + DateUtils.format("MM/dd/yyyy", tz, start)); 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))); 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(); t2.stop();

View File

@ -5,6 +5,7 @@ import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower; import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.EnergySummary; import com.lanternsoftware.datamodel.currentmonitor.EnergySummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode; import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.util.dao.auth.AuthCode; import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.util.dao.mongo.MongoProxy; import com.lanternsoftware.util.dao.mongo.MongoProxy;
@ -48,5 +49,9 @@ public interface CurrentMonitorDao {
TimeZone getTimeZoneForAccount(int _accountId); TimeZone getTimeZoneForAccount(int _accountId);
String getTimeZoneForAccount(String _authCode); String getTimeZoneForAccount(String _authCode);
void putHubCommand(HubCommand _command);
List<HubCommand> getAllHubCommands();
void deleteHubCommand(String _id);
MongoProxy getProxy(); MongoProxy getProxy();
} }

View File

@ -6,12 +6,14 @@ import com.lanternsoftware.datamodel.currentmonitor.BillingRate;
import com.lanternsoftware.datamodel.currentmonitor.Breaker; import com.lanternsoftware.datamodel.currentmonitor.Breaker;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig; import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup; import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup;
import com.lanternsoftware.datamodel.currentmonitor.BreakerHub;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower; import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.ChargeSummary; import com.lanternsoftware.datamodel.currentmonitor.ChargeSummary;
import com.lanternsoftware.datamodel.currentmonitor.ChargeTotal; import com.lanternsoftware.datamodel.currentmonitor.ChargeTotal;
import com.lanternsoftware.datamodel.currentmonitor.EnergySummary; import com.lanternsoftware.datamodel.currentmonitor.EnergySummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyTotal; import com.lanternsoftware.datamodel.currentmonitor.EnergyTotal;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode; import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.datamodel.currentmonitor.Sequence; import com.lanternsoftware.datamodel.currentmonitor.Sequence;
import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.CollectionUtils;
@ -404,6 +406,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
if (config == null) { if (config == null) {
config = new BreakerConfig(); config = new BreakerConfig();
config.setAccountId(_authCode.getAccountId()); config.setAccountId(_authCode.getAccountId());
config.setVersion(config.getVersion());
return config; return config;
} }
} }
@ -416,6 +419,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
config.setMeters(CollectionUtils.aggregate(configs, BreakerConfig::getMeters)); config.setMeters(CollectionUtils.aggregate(configs, BreakerConfig::getMeters));
config.setBillingPlans(CollectionUtils.aggregate(configs, BreakerConfig::getBillingPlans)); config.setBillingPlans(CollectionUtils.aggregate(configs, BreakerConfig::getBillingPlans));
config.setBillingRates(CollectionUtils.aggregate(configs, BreakerConfig::getBillingRates)); config.setBillingRates(CollectionUtils.aggregate(configs, BreakerConfig::getBillingRates));
config.setVersion(CollectionUtils.getLargest(CollectionUtils.transform(configs, BreakerConfig::getVersion)));
return config; return config;
} }
@ -424,6 +428,16 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
DaoQuery configQuery = new DaoQuery("_id", String.valueOf(_config.getAccountId())); DaoQuery configQuery = new DaoQuery("_id", String.valueOf(_config.getAccountId()));
BreakerConfig oldConfig = proxy.queryOne(BreakerConfig.class, configQuery); BreakerConfig oldConfig = proxy.queryOne(BreakerConfig.class, configQuery);
if (oldConfig != null) { if (oldConfig != null) {
logger.info("old version: {}, new version: {}", oldConfig.getVersion(), _config.getVersion());
if (oldConfig.getVersion() > _config.getVersion()) {
for (BreakerHub hub : CollectionUtils.makeNotNull(_config.getBreakerHubs())) {
BreakerHub oldHub = oldConfig.getHub(hub.getHub());
if (oldHub != null) {
logger.info("Prevent overwrite of voltage calibration");
hub.setVoltageCalibrationFactor(oldHub.getRawVoltageCalibrationFactor());
}
}
}
_config.setVersion(oldConfig.getVersion() + 1); _config.setVersion(oldConfig.getVersion() + 1);
if (NullUtils.isNotIdentical(_config, oldConfig)) { if (NullUtils.isNotIdentical(_config, oldConfig)) {
DaoEntity oldEntity = DaoSerializer.toDaoEntity(oldConfig); DaoEntity oldEntity = DaoSerializer.toDaoEntity(oldConfig);
@ -441,6 +455,9 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
}); });
} }
} }
for (BreakerHub hub : CollectionUtils.makeNotNull(_config.getBreakerHubs())) {
logger.info("voltage calibration hub {}: {}", hub.getHub(), hub.getVoltageCalibrationFactor());
}
proxy.save(_config); proxy.save(_config);
} }
@ -459,7 +476,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
AuthCode code = decryptAuthCode(_authCode); AuthCode code = decryptAuthCode(_authCode);
if (code == null) if (code == null)
return null; return null;
return proxy.queryOne(Account.class, new DaoQuery("_id", code.getAccountId())); return proxy.queryOne(Account.class, new DaoQuery("_id", String.valueOf(code.getAccountId())));
} }
@Override @Override
@ -572,6 +589,23 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
return true; return true;
} }
@Override
public void putHubCommand(HubCommand _command) {
BreakerConfig config = getConfig(_command.getAccountId());
if (config != null)
proxy.save(_command.forAllHubs(config));
}
@Override
public List<HubCommand> getAllHubCommands() {
return proxy.queryAll(HubCommand.class);
}
@Override
public void deleteHubCommand(String _id) {
proxy.delete(HubCommand.class, new DaoQuery("_id", _id));
}
@Override @Override
public MongoProxy getProxy() { public MongoProxy getProxy() {
return proxy; return proxy;

View File

@ -209,7 +209,7 @@ public class BreakerConfig implements IIdentical<BreakerConfig> {
@Override @Override
public boolean isIdentical(BreakerConfig _o) { public boolean isIdentical(BreakerConfig _o) {
if (this == _o) return true; if (this == _o) return true;
return accountId == _o.accountId && CollectionUtils.isIdentical(meters, _o.meters) && CollectionUtils.isIdentical(panels, _o.panels) && CollectionUtils.isIdentical(breakerHubs, _o.breakerHubs) && CollectionUtils.isIdentical(breakerGroups, _o.breakerGroups) && CollectionUtils.isEqual(billingPlans, _o.billingPlans); return accountId == _o.accountId && CollectionUtils.isIdentical(meters, _o.meters) && CollectionUtils.isIdentical(panels, _o.panels) && CollectionUtils.isIdentical(breakerHubs, _o.breakerHubs) && CollectionUtils.isIdentical(breakerGroups, _o.breakerGroups) && CollectionUtils.isIdentical(billingPlans, _o.billingPlans);
} }
@Override @Override

View File

@ -6,7 +6,7 @@ import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.Objects; import java.util.Objects;
@DBSerializable @DBSerializable(autogen = false)
public class BreakerHub implements IIdentical<BreakerHub> { public class BreakerHub implements IIdentical<BreakerHub> {
private int hub; private int hub;
private double voltageCalibrationFactor; private double voltageCalibrationFactor;
@ -22,16 +22,24 @@ public class BreakerHub implements IIdentical<BreakerHub> {
hub = _hub; hub = _hub;
} }
public double getRawVoltageCalibrationFactor() {
return voltageCalibrationFactor;
}
public double getVoltageCalibrationFactor() { public double getVoltageCalibrationFactor() {
return voltageCalibrationFactor == 0.0?1.0:voltageCalibrationFactor; return voltageCalibrationFactor == 0.0?0.3445:voltageCalibrationFactor;
} }
public void setVoltageCalibrationFactor(double _voltageCalibrationFactor) { public void setVoltageCalibrationFactor(double _voltageCalibrationFactor) {
voltageCalibrationFactor = _voltageCalibrationFactor; voltageCalibrationFactor = _voltageCalibrationFactor;
} }
public double getRawPortCalibrationFactor() {
return portCalibrationFactor;
}
public double getPortCalibrationFactor() { public double getPortCalibrationFactor() {
return portCalibrationFactor == 0.0?1.0:portCalibrationFactor; return portCalibrationFactor == 0.0?1.25:portCalibrationFactor;
} }
public void setPortCalibrationFactor(double _portCalibrationFactor) { public void setPortCalibrationFactor(double _portCalibrationFactor) {

View File

@ -0,0 +1,104 @@
package com.lanternsoftware.datamodel.currentmonitor;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import com.lanternsoftware.util.dao.annotations.PrimaryKey;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@DBSerializable
public class HubCommand {
@PrimaryKey private String id;
private int accountId;
private int hub;
private Date created;
private HubConfigCharacteristic characteristic;
private byte[] data;
public HubCommand() {
}
public HubCommand(int _accountId, HubConfigCharacteristic _characteristic, byte[] _data) {
accountId = _accountId;
created = new Date();
characteristic = _characteristic;
data = _data;
}
public String getId() {
return id;
}
public void setId(String _id) {
id = _id;
}
public int getAccountId() {
return accountId;
}
public void setAccountId(int _accountId) {
accountId = _accountId;
}
public int getHub() {
return hub;
}
public void setHub(int _hub) {
hub = _hub;
}
public Date getCreated() {
return created;
}
public void setCreated(Date _created) {
created = _created;
}
public HubConfigCharacteristic getCharacteristic() {
return characteristic;
}
public void setCharacteristic(HubConfigCharacteristic _characteristic) {
characteristic = _characteristic;
}
public byte[] getData() {
return data;
}
public void setData(byte[] _data) {
data = _data;
}
public List<HubCommand> forAllHubs(BreakerConfig _config) {
return CollectionUtils.transform(_config.getBreakerHubs(), _h->forHub(_h.getHub()));
}
public HubCommand forHub(int _hub) {
HubCommand c = new HubCommand();
c.setAccountId(accountId);
c.setHub(_hub);
c.setCreated(created);
c.setCharacteristic(characteristic);
c.setData(data);
return c;
}
@Override
public boolean equals(Object _o) {
if (this == _o) return true;
if (_o == null || getClass() != _o.getClass()) return false;
HubCommand that = (HubCommand) _o;
return Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}

View File

@ -0,0 +1,25 @@
package com.lanternsoftware.datamodel.currentmonitor;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.List;
@DBSerializable
public class HubCommands {
private List<HubCommand> commands;
public HubCommands() {
}
public HubCommands(List<HubCommand> _commands) {
commands = _commands;
}
public List<HubCommand> getCommands() {
return commands;
}
public void setCommands(List<HubCommand> _commands) {
commands = _commands;
}
}

View File

@ -18,7 +18,10 @@ public enum HubConfigCharacteristic {
Host(10, CharacteristicFlag.WRITE), Host(10, CharacteristicFlag.WRITE),
Log(11, CharacteristicFlag.READ), Log(11, CharacteristicFlag.READ),
NetworkDetails(12, CharacteristicFlag.READ), NetworkDetails(12, CharacteristicFlag.READ),
Shutdown(13, CharacteristicFlag.WRITE); Shutdown(13, CharacteristicFlag.WRITE),
Version(14, CharacteristicFlag.READ),
Update(15, CharacteristicFlag.WRITE),
ReloadConfig(15, CharacteristicFlag.WRITE);
public final int idx; public final int idx;
public final UUID uuid; public final UUID uuid;

View File

@ -7,7 +7,6 @@ import com.lanternsoftware.util.dao.AbstractDaoSerializer;
import com.lanternsoftware.util.dao.DaoEntity; import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType; import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer; import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -31,7 +30,6 @@ public class BillingRateSerializer extends AbstractDaoSerializer<BillingRate>
d.put("meter", _o.getMeter()); d.put("meter", _o.getMeter());
d.put("flow", DaoSerializer.toEnumName(_o.getFlow())); d.put("flow", DaoSerializer.toEnumName(_o.getFlow()));
d.put("rate", _o.getRate()); d.put("rate", _o.getRate());
d.put("unit", DaoSerializer.toEnumName(_o.getCurrency())); //TODO: Remove post migration
d.put("currency", DaoSerializer.toEnumName(_o.getCurrency())); d.put("currency", DaoSerializer.toEnumName(_o.getCurrency()));
d.put("time_of_day_start", _o.getTimeOfDayStart()); d.put("time_of_day_start", _o.getTimeOfDayStart());
d.put("time_of_day_end", _o.getTimeOfDayEnd()); d.put("time_of_day_end", _o.getTimeOfDayEnd());
@ -51,8 +49,6 @@ public class BillingRateSerializer extends AbstractDaoSerializer<BillingRate>
o.setFlow(DaoSerializer.getEnum(_d, "flow", GridFlow.class)); o.setFlow(DaoSerializer.getEnum(_d, "flow", GridFlow.class));
o.setRate(DaoSerializer.getDouble(_d, "rate")); o.setRate(DaoSerializer.getDouble(_d, "rate"));
o.setCurrency(DaoSerializer.getEnum(_d, "currency", BillingCurrency.class)); o.setCurrency(DaoSerializer.getEnum(_d, "currency", BillingCurrency.class));
if (o.getCurrency() == null)
o.setCurrency(DaoSerializer.getEnum(_d, "unit", BillingCurrency.class));
o.setTimeOfDayStart(DaoSerializer.getInteger(_d, "time_of_day_start")); o.setTimeOfDayStart(DaoSerializer.getInteger(_d, "time_of_day_start"));
o.setTimeOfDayEnd(DaoSerializer.getInteger(_d, "time_of_day_end")); o.setTimeOfDayEnd(DaoSerializer.getInteger(_d, "time_of_day_end"));
o.setMonthKWhStart(DaoSerializer.getDouble(_d, "month_kwh_start")); o.setMonthKWhStart(DaoSerializer.getDouble(_d, "month_kwh_start"));

View File

@ -26,8 +26,8 @@ public class BreakerHubSerializer extends AbstractDaoSerializer<BreakerHub>
{ {
DaoEntity d = new DaoEntity(); DaoEntity d = new DaoEntity();
d.put("hub", _o.getHub()); d.put("hub", _o.getHub());
d.put("voltage_calibration_factor", _o.getVoltageCalibrationFactor()); d.put("voltage_calibration_factor", _o.getRawVoltageCalibrationFactor());
d.put("port_calibration_factor", _o.getPortCalibrationFactor()); d.put("port_calibration_factor", _o.getRawPortCalibrationFactor());
d.put("frequency", _o.getFrequency()); d.put("frequency", _o.getFrequency());
d.put("bluetooth_mac", _o.getBluetoothMac()); d.put("bluetooth_mac", _o.getBluetoothMac());
return d; return d;

View File

@ -0,0 +1,51 @@
package com.lanternsoftware.datamodel.currentmonitor.dao;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic;
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 HubCommandSerializer extends AbstractDaoSerializer<HubCommand>
{
@Override
public Class<HubCommand> getSupportedClass()
{
return HubCommand.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(HubCommand _o)
{
DaoEntity d = new DaoEntity();
if (_o.getId() != null)
d.put("_id", _o.getId());
d.put("account_id", _o.getAccountId());
d.put("hub", _o.getHub());
d.put("created", DaoSerializer.toLong(_o.getCreated()));
d.put("characteristic", DaoSerializer.toEnumName(_o.getCharacteristic()));
d.put("data", _o.getData());
return d;
}
@Override
public HubCommand fromDaoEntity(DaoEntity _d)
{
HubCommand o = new HubCommand();
o.setId(DaoSerializer.getString(_d, "_id"));
o.setAccountId(DaoSerializer.getInteger(_d, "account_id"));
o.setHub(DaoSerializer.getInteger(_d, "hub"));
o.setCreated(DaoSerializer.getDate(_d, "created"));
o.setCharacteristic(DaoSerializer.getEnum(_d, "characteristic", HubConfigCharacteristic.class));
o.setData(DaoSerializer.getByteArray(_d, "data"));
return o;
}
}

View File

@ -0,0 +1,40 @@
package com.lanternsoftware.datamodel.currentmonitor.dao;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubCommands;
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 HubCommandsSerializer extends AbstractDaoSerializer<HubCommands>
{
@Override
public Class<HubCommands> getSupportedClass()
{
return HubCommands.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(HubCommands _o)
{
DaoEntity d = new DaoEntity();
d.put("commands", DaoSerializer.toDaoEntities(_o.getCommands(), DaoProxyType.MONGO));
return d;
}
@Override
public HubCommands fromDaoEntity(DaoEntity _d)
{
HubCommands o = new HubCommands();
o.setCommands(DaoSerializer.getList(_d, "commands", HubCommand.class));
return o;
}
}

View File

@ -14,6 +14,8 @@ com.lanternsoftware.datamodel.currentmonitor.dao.ChargeTotalSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.EnergyBlockSerializer com.lanternsoftware.datamodel.currentmonitor.dao.EnergyBlockSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.EnergySummarySerializer com.lanternsoftware.datamodel.currentmonitor.dao.EnergySummarySerializer
com.lanternsoftware.datamodel.currentmonitor.dao.EnergyTotalSerializer com.lanternsoftware.datamodel.currentmonitor.dao.EnergyTotalSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.HubCommandSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.HubCommandsSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.HubPowerMinuteSerializer com.lanternsoftware.datamodel.currentmonitor.dao.HubPowerMinuteSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.MeterSerializer com.lanternsoftware.datamodel.currentmonitor.dao.MeterSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.NetworkStatusSerializer com.lanternsoftware.datamodel.currentmonitor.dao.NetworkStatusSerializer

View File

@ -2,20 +2,31 @@ package com.lanternsoftware.currentmonitor.context;
import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao; import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao;
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao; import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubCommands;
import com.lanternsoftware.rules.RulesEngine; import com.lanternsoftware.rules.RulesEngine;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.LanternFiles; import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.dao.mongo.MongoConfig; import com.lanternsoftware.util.dao.mongo.MongoConfig;
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener; import javax.servlet.ServletContextListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
public class Globals implements ServletContextListener { public class Globals implements ServletContextListener {
public static CurrentMonitorDao dao; public static CurrentMonitorDao dao;
private static final Map<Integer, Map<Integer, List<HubCommand>>> commands = new HashMap<>();
@Override @Override
public void contextInitialized(ServletContextEvent sce) { public void contextInitialized(ServletContextEvent sce) {
dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg")); dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
RulesEngine.instance().start(); RulesEngine.instance().start();
RulesEngine.instance().schedule(new CommandTask(), 0);
} }
@Override @Override
@ -23,4 +34,38 @@ public class Globals implements ServletContextListener {
dao.shutdown(); dao.shutdown();
RulesEngine.shutdown(); RulesEngine.shutdown();
} }
public static HubCommands getCommandsForHub(int _accountId, int _hub) {
List<HubCommand> c = null;
synchronized (commands) {
Map<Integer, List<HubCommand>> hubCommands = commands.get(_accountId);
if (hubCommands != null)
c = hubCommands.remove(_hub);
}
if (c != null) {
for (HubCommand command : c) {
dao.deleteHubCommand(command.getId());
}
return new HubCommands(c);
}
return null;
}
private static final class CommandTask extends TimerTask {
@Override
public void run() {
List<HubCommand> c = Globals.dao.getAllHubCommands();
Date stale = DateUtils.addMinutes(new Date(), -5);
synchronized (commands) {
commands.clear();
for (HubCommand command : c) {
if (DateUtils.isBefore(command.getCreated(), stale))
dao.deleteHubCommand(command.getId());
else
commands.computeIfAbsent(command.getAccountId(), _t -> new HashMap<>()).computeIfAbsent(command.getHub(), _h->new ArrayList<>()).add(command);
}
}
RulesEngine.instance().schedule(new CommandTask(), 1000);
}
}
} }

View File

@ -44,11 +44,8 @@ public class AuthServlet extends LanternServlet {
GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(transport, new GsonFactory(), "https://oauth2.googleapis.com/token", googleClientId, googleClientSecret, auth.getPassword(), "").execute(); GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(transport, new GsonFactory(), "https://oauth2.googleapis.com/token", googleClientId, googleClientSecret, auth.getPassword(), "").execute();
if (tokenResponse != null) { if (tokenResponse != null) {
GoogleIdToken idToken = tokenResponse.parseIdToken(); GoogleIdToken idToken = tokenResponse.parseIdToken();
if (idToken != null) { if (idToken != null)
logger.info("Successfully received google id token");
authCode = Globals.dao.getAuthCodeForEmail(idToken.getPayload().getEmail(), DateUtils.fromTimeZoneId(_req.getHeader("timezone"))); authCode = Globals.dao.getAuthCodeForEmail(idToken.getPayload().getEmail(), DateUtils.fromTimeZoneId(_req.getHeader("timezone")));
logger.info("Auth code for google user is valid: " + (authCode != null));
}
} }
} catch (Exception _e) { } catch (Exception _e) {
logger.error("Failed to validate google auth code", _e); logger.error("Failed to validate google auth code", _e);

View File

@ -1,49 +1,19 @@
package com.lanternsoftware.currentmonitor.servlet; package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.util.dao.auth.AuthCode; import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.ResourceLoader;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer;
import javax.servlet.annotation.WebServlet; import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@WebServlet("/command") @WebServlet("/command")
public class CommandServlet extends SecureServlet { public class CommandServlet extends SecureServlet {
@Override @Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
File folder = new File(LanternFiles.OPS_PATH + _authCode.getAccountId());
List<String> commands = new ArrayList<>();
if (folder.exists() && folder.isDirectory()) {
for (File command : CollectionUtils.asArrayList(folder.listFiles())) {
if (command.isDirectory())
continue;
String c = command.getName();
String extension = NullUtils.after(c, ".");
if (NullUtils.isNotEmpty(extension))
c = c.replace("." + extension, "");
commands.add(c);
}
}
zipBsonResponse(_rep, new DaoEntity("commands", commands));
} }
@Override @Override
protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
DaoEntity payload = getRequestZipBson(_req);
if (payload == null)
return;
String command = DaoSerializer.getString(payload, "command");
String path = LanternFiles.OPS_PATH + _authCode.getAccountId() + File.separator + "payload" + File.separator;
new File(path).mkdirs();
ResourceLoader.writeFile(path+ command + ".txt", DaoSerializer.getString(payload, "payload"));
} }
} }

View File

@ -2,6 +2,8 @@ package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals; import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig; import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic;
import com.lanternsoftware.util.dao.auth.AuthCode; import com.lanternsoftware.util.dao.auth.AuthCode;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -34,6 +36,10 @@ public class ConfigServlet extends SecureServlet {
return; return;
} }
logger.info("Received config for account {}", config.getAccountId()); logger.info("Received config for account {}", config.getAccountId());
BreakerConfig oldConfig = Globals.dao.getConfig(config.getAccountId());
if ((oldConfig == null) || !oldConfig.isIdentical(config))
Globals.dao.putHubCommand(new HubCommand(config.getAccountId(), HubConfigCharacteristic.ReloadConfig, null));
Globals.dao.putConfig(config); Globals.dao.putConfig(config);
zipBsonResponse(_rep, Globals.dao.getMergedConfig(_authCode));
} }
} }

View File

@ -1,12 +1,17 @@
package com.lanternsoftware.currentmonitor.servlet; package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals; import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
import com.lanternsoftware.datamodel.currentmonitor.HubCommands;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.auth.AuthCode; import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower; import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer; import com.lanternsoftware.util.dao.DaoSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.annotation.WebServlet; import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -15,6 +20,8 @@ import java.util.List;
@WebServlet("/power/*") @WebServlet("/power/*")
public class PowerServlet extends SecureServlet { public class PowerServlet extends SecureServlet {
private static final Logger logger = LoggerFactory.getLogger(MongoCurrentMonitorDao.class);
@Override @Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
String[] path = path(_req); String[] path = path(_req);
@ -32,19 +39,29 @@ public class PowerServlet extends SecureServlet {
String[] path = path(_req); String[] path = path(_req);
if ((path.length > 0) && NullUtils.isEqual(CollectionUtils.get(path, 0), "hub")) { if ((path.length > 0) && NullUtils.isEqual(CollectionUtils.get(path, 0), "hub")) {
HubPowerMinute m = getRequestPayload(_req, HubPowerMinute.class); HubPowerMinute m = getRequestPayload(_req, HubPowerMinute.class);
if (m == null)
return;
logger.info("Hub Power from ip {}, account {}, hub {}", _req.getRemoteAddr(), m.getAccountId(), m.getHub());
m.setAccountId(_authCode.getAccountId()); m.setAccountId(_authCode.getAccountId());
Globals.dao.putHubPowerMinute(m); Globals.dao.putHubPowerMinute(m);
return; return;
} }
if ((path.length > 0) && NullUtils.isEqual(CollectionUtils.get(path, 0), "batch")) { if ((path.length > 0) && NullUtils.isEqual(CollectionUtils.get(path, 0), "batch")) {
List<BreakerPower> powers = DaoSerializer.getList(getRequestZipBson(_req), "readings", BreakerPower.class); DaoEntity payload = getRequestZipBson(_req);
List<BreakerPower> powers = DaoSerializer.getList(payload, "readings", BreakerPower.class);
if (!powers.isEmpty()) { if (!powers.isEmpty()) {
CollectionUtils.edit(powers, _p->_p.setAccountId(_authCode.getAccountId())); CollectionUtils.edit(powers, _p->_p.setAccountId(_authCode.getAccountId()));
Globals.dao.getProxy().save(powers); Globals.dao.getProxy().save(powers);
int hub = DaoSerializer.getInteger(payload, "hub");
HubCommands commands = Globals.getCommandsForHub(_authCode.getAccountId(), hub);
if (commands != null)
zipBsonResponse(_rep, commands);
} }
return; return;
} }
BreakerPower power = getRequestPayload(_req, BreakerPower.class); BreakerPower power = getRequestPayload(_req, BreakerPower.class);
if (power == null)
return;
power.setAccountId(_authCode.getAccountId()); power.setAccountId(_authCode.getAccountId());
Globals.dao.putBreakerPower(power); Globals.dao.putBreakerPower(power);
} }

View File

@ -2,6 +2,8 @@ package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals; import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.Account; import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer; import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.auth.AuthCode; import com.lanternsoftware.util.dao.auth.AuthCode;
@ -9,17 +11,23 @@ import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@WebServlet("/rebuildSummaries") @WebServlet("/rebuildSummaries/*")
public class RebuildSummariesServlet extends SecureServlet { public class RebuildSummariesServlet extends SecureServlet {
@Override @Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
if (_authCode.getAccountId() == 100) { if (_authCode.getAccountId() == 100) {
String[] path = path(_req);
if (path.length > 0) {
Globals.dao.rebuildSummariesAsync(DaoSerializer.toInteger(CollectionUtils.get(path, 0)));
}
else {
for (String sId : Globals.dao.getProxy().queryForField(Account.class, null, "_id")) { for (String sId : Globals.dao.getProxy().queryForField(Account.class, null, "_id")) {
int id = DaoSerializer.toInteger(sId); int id = DaoSerializer.toInteger(sId);
if (id != 0) if (id != 0)
Globals.dao.rebuildSummariesAsync(id); Globals.dao.rebuildSummariesAsync(id);
} }
} }
}
else else
_rep.setStatus(401); _rep.setStatus(401);
} }

View File

@ -2,16 +2,21 @@
<configuration> <configuration>
<property name="log.pattern" value="%date %-5level %logger{0} - %message%n"/> <property name="log.pattern" value="%date %-5level %logger{0} - %message%n"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/opt/tomcat/logs/log.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>/opt/tomcat/log/log.%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<maxFileSize>20MB</maxFileSize>
<maxHistory>20</maxHistory>
</rollingPolicy>
<encoder> <encoder>
<pattern>${log.pattern}</pattern> <pattern>${log.pattern}</pattern>
</encoder> </encoder>
</appender> </appender>
<logger name="com.lanternsoftware" level="DEBUG"/> <logger name="com.lanternsoftware" level="DEBUG"/>
<root level="OFF"> <root level="OFF">
<appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/>
</root> </root>
</configuration> </configuration>

View File

@ -2,16 +2,21 @@
<configuration> <configuration>
<property name="log.pattern" value="%date %-5level %logger{0} - %message%n"/> <property name="log.pattern" value="%date %-5level %logger{0} - %message%n"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/opt/tomcat/logs/log.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>/opt/tomcat/log/log.%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<maxFileSize>20MB</maxFileSize>
<maxHistory>20</maxHistory>
</rollingPolicy>
<encoder> <encoder>
<pattern>${log.pattern}</pattern> <pattern>${log.pattern}</pattern>
</encoder> </encoder>
</appender> </appender>
<logger name="com.lanternsoftware" level="DEBUG"/> <logger name="com.lanternsoftware" level="DEBUG"/>
<root level="OFF"> <root level="OFF">
<appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/>
</root> </root>
</configuration> </configuration>

View File

@ -113,6 +113,12 @@ public class RulesEngine {
timer.schedule(nextTask, nextDate); timer.schedule(nextTask, nextDate);
} }
public void schedule(TimerTask _task, long _delay) {
if (timer == null)
return;
timer.schedule(_task, _delay);
}
public static void shutdown() { public static void shutdown() {
if (INSTANCE == null) if (INSTANCE == null)
return; return;

View File

@ -2,9 +2,9 @@ package com.lanternsoftware.util;
public abstract class LanternFiles { public abstract class LanternFiles {
public static final String SOURCE_PATH = "C:\\lantern\\LanternPowerMonitor\\"; public static final String SOURCE_PATH = "C:\\lantern\\LanternPowerMonitor\\";
// 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\\prodremote\\";
public static final String OPS_PATH = "/opt/tomcat/"; public static final String OPS_PATH = "/opt/tomcat/";
public static final String BACKUP_PATH = "D:\\zwave\\localhost\\"; // public static final String OPS_PATH = "D:\\zwave\\prodremote\\";
// public static final String OPS_PATH = "D:\\zwave\\localhost\\";
public static final String BACKUP_SOURCE = "D:\\zwave\\prodremote\\";
public static final String BACKUP_DEST = "D:\\zwave\\localhost\\";
} }

View File

@ -46,7 +46,7 @@ public class SecurityController {
LOG.info("handling event {} pin {} most recent event is {}", eventIdx, _sw.getGpioPin(), high); LOG.info("handling event {} pin {} most recent event is {}", eventIdx, _sw.getGpioPin(), high);
if (high == null) if (high == null)
return; return;
_listener.onStateChanged(_sw.getNodeId(), high); _listener.onStateChanged(_sw.getNodeId(), pin.isHigh());
}); });
}); });
} }