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>
<artifactId>lantern-currentmonitor</artifactId>
<packaging>jar</packaging>
<version>1.0.4</version>
<version>1.0.6</version>
<name>lantern-currentmonitor</name>
<properties>

View File

@ -97,13 +97,18 @@ public class CurrentMonitor {
}
public void monitorPower(BreakerHub _hub, List<Breaker> _breakers, int _intervalMs, PowerListener _listener) {
try {
stopMonitoring();
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);
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);
}
catch (Throwable t) {
LOG.error("throwable", t);
}
}
private GpioPinAnalogInput getPin(int _chip, int _pin) {
GpioPinAnalogInput pin;
@ -182,9 +187,11 @@ public class CurrentMonitor {
try {
while (true) {
synchronized (this) {
if (!running)
if (!running) {
LOG.error("Power Monitoring Stopped");
break;
}
}
final Date readTime = new Date();
final long intervalStart = (interval * intervalNs) + start;
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.BreakerPower;
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.HubConfigService;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
@ -25,6 +27,7 @@ import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.http.HttpPool;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
@ -39,10 +42,12 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -64,7 +69,7 @@ public class MonitorApp {
private static final AtomicBoolean running = new AtomicBoolean(true);
private static final CurrentMonitor monitor = new CurrentMonitor();
private static final List<BreakerPower> readings = new ArrayList<>();
private static final String version = getVersionNumber();
private static String version;
private static final PowerListener logger = _p -> {
if (!config.isDebug()) {
_p.setHubVersion(version);
@ -76,21 +81,7 @@ public class MonitorApp {
} else
LOG.info("Panel{} - Space{} Power: {}W", _p.getPanel(), Breaker.toSpaceDisplay(_p.getSpace()), String.format("%.3f", _p.getPower()));
};
private static MqttPoster mqttPoster;
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() {
private static final BleCharacteristicListener bluetoothListener = new BleCharacteristicListener() {
@Override
public void write(String _name, byte[] _value) {
HubConfigCharacteristic ch = NullUtils.toEnum(HubConfigCharacteristic.class, _name);
@ -162,6 +153,24 @@ public class MonitorApp {
LOG.error("Exception occurred while trying to shutdown", _e);
}
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);
return ZipUtils.zip(NullUtils.toByteArray(CollectionUtils.delimit(Arrays.asList(log), "\n")));
}
if (HubConfigCharacteristic.Version == ch)
return NullUtils.toByteArray(version);
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();
if (NullUtils.isNotEmpty(config.getAuthCode()))
authCode = config.getAuthCode();
@ -303,6 +337,7 @@ public class MonitorApp {
if (!readings.isEmpty()) {
mqttReadings.addAll(readings);
post = new DaoEntity("readings", DaoSerializer.toDaoEntities(readings));
post.put("hub", config.getHub());
if (curMinute != lastMinute) {
HubPowerMinute minute = new HubPowerMinute();
minute.setAccountId(breakerConfig.getAccountId());
@ -336,7 +371,8 @@ public class MonitorApp {
}
if (post != null) {
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();
if (files != null) {
for (File file : files) {
@ -347,6 +383,11 @@ public class MonitorApp {
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()) {
lastUpdateCheck = new Date();
monitor.submit(new UpdateChecker());
monitor.submit(new CommandChecker());
}
long now = new Date().getTime();
long duration = (now - firstPost)%1000;
@ -371,34 +411,65 @@ public class MonitorApp {
}
}
private static void uploadLog() {
LOG.info("Commanded to upload log file, preparing...");
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) {
return post(_payload, _path, Boolean.class).success;
}
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))
return false;
return new PostResponse<>(false, null);
HttpPost post = new HttpPost(host + _path);
post.addHeader("auth_code", authCode);
post.setEntity(new ByteArrayEntity(_payload, ContentType.APPLICATION_OCTET_STREAM));
InputStream is = null;
CloseableHttpResponse resp = pool.execute(post);
try {
return ((resp != null) && (resp.getStatusLine() != null) && (resp.getStatusLine().getStatusCode() == 200));
} finally {
if ((resp != null) && (resp.getStatusLine() != null) && (resp.getStatusLine().getStatusCode() == 200)) {
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);
}
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 final boolean force;
public UpdateChecker() {
force = false;
}
public UpdateChecker(boolean _force) {
force = _force;
}
@Override
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")));
String newVersion = DaoSerializer.getString(meta, "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"))) {
LOG.info("Update downloaded, writing jar and restarting...");
ResourceLoader.writeFile(WORKING_DIR + "lantern-currentmonitor.jar", jar);
ConcurrencyUtils.sleep(10000);
synchronized (running) {
running.set(false);
}
monitor.stopMonitoring();
bluetoothConfig.stop();
pool.shutdown();
try {
Runtime.getRuntime().exec(new String[]{"systemctl", "restart", "currentmonitor"});
Runtime.getRuntime().exec(new String[]{"systemctl","restart","currentmonitor"});
} catch (IOException _e) {
LOG.error("Exception occurred while trying to restart", _e);
}
@ -419,65 +495,29 @@ public class MonitorApp {
}
}
private static final class CommandChecker implements Runnable {
@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");
public static String getVersionNumber() {
try {
Runtime.getRuntime().exec(new String[]{"sudo", "raspi-config", "--expand-rootfs"});
ConcurrencyUtils.sleep(5000);
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() {
Enumeration<URL> resources = MonitorApp.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
while (resources.hasMoreElements()) {
InputStream is = null;
try {
is = MonitorApp.class.getResourceAsStream("/META-INF/MANIFEST.MF");
is = resources.nextElement().openStream();
Manifest manifest = new Manifest(is);
Attributes attr = manifest.getMainAttributes();
if (NullUtils.isEqual(attr.getValue("Specification-Title"), "Lantern Power Monitor")) {
String version = attr.getValue("Specification-Version");
LOG.info("Current Version: {}", version);
return version;
}
catch (Exception _e) {
LOG.error("Failed to get current version number", _e);
return "";
}
finally {
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.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.ChargeSummary;
import com.lanternsoftware.datamodel.currentmonitor.ChargeTotal;
import com.lanternsoftware.datamodel.currentmonitor.EnergySummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyTotal;
import com.lanternsoftware.datamodel.currentmonitor.Sequence;
@ -10,14 +12,15 @@ 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.DaoQuery;
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"));
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_SOURCE + "mongo.cfg"));
CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_DEST + "mongo.cfg"));
DebugTimer t1 = new DebugTimer("Query Accounts");
List<Account> accounts = dao.getProxy().queryAll(Account.class);
@ -34,17 +37,39 @@ public class Backup {
t4.stop();
DebugTimer t5 = new DebugTimer("Query Energy");
List<EnergySummary> energy = dao.getProxy().queryAll(EnergySummary.class);
t5.stop();
DebugTimer t6 = new DebugTimer("Save Energy");
for (Account a : accounts) {
List<EnergySummary> energy = dao.getProxy().query(EnergySummary.class, new DaoQuery("account_id", a.getId()));
DebugTimer t = new DebugTimer("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();
DebugTimer t7 = new DebugTimer("Query Summaries");
List<EnergyTotal> summary = dao.getProxy().queryAll(EnergyTotal.class);
DebugTimer t7 = new DebugTimer("Query Charges");
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();
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();
DebugTimer t9 = new DebugTimer("Query Events");

View File

@ -16,8 +16,8 @@ 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"));
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_SOURCE + "mongo.cfg"));
CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_DEST + "mongo.cfg"));
Date now = new Date();
for (Account a : dao.getProxy().queryAll(Account.class)) {
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"));
if (minute == null)
continue;
Date minStart = DateUtils.addDays(DateUtils.getMidnightBeforeNow(tz), -60, tz);
Date start = DateUtils.getMidnightBefore(minute.getMinuteAsDate(), tz);
if (minStart.after(start))
start = minStart;
HubPowerMinute lastBackup = backupDao.getProxy().queryOne(HubPowerMinute.class, new DaoQuery("account_id", a.getId()), DaoSort.sortDesc("minute"));
Date start = lastBackup == null ? DateUtils.getMidnightBefore(minute.getMinuteAsDate(), tz) : lastBackup.getMinuteAsDate();
// Date start = DateUtils.date(10,16,2021,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));
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();

View File

@ -5,6 +5,7 @@ import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.EnergySummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.util.dao.mongo.MongoProxy;
@ -48,5 +49,9 @@ public interface CurrentMonitorDao {
TimeZone getTimeZoneForAccount(int _accountId);
String getTimeZoneForAccount(String _authCode);
void putHubCommand(HubCommand _command);
List<HubCommand> getAllHubCommands();
void deleteHubCommand(String _id);
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.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.BreakerGroup;
import com.lanternsoftware.datamodel.currentmonitor.BreakerHub;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.ChargeSummary;
import com.lanternsoftware.datamodel.currentmonitor.ChargeTotal;
import com.lanternsoftware.datamodel.currentmonitor.EnergySummary;
import com.lanternsoftware.datamodel.currentmonitor.EnergyTotal;
import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.datamodel.currentmonitor.Sequence;
import com.lanternsoftware.util.CollectionUtils;
@ -404,6 +406,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
if (config == null) {
config = new BreakerConfig();
config.setAccountId(_authCode.getAccountId());
config.setVersion(config.getVersion());
return config;
}
}
@ -416,6 +419,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
config.setMeters(CollectionUtils.aggregate(configs, BreakerConfig::getMeters));
config.setBillingPlans(CollectionUtils.aggregate(configs, BreakerConfig::getBillingPlans));
config.setBillingRates(CollectionUtils.aggregate(configs, BreakerConfig::getBillingRates));
config.setVersion(CollectionUtils.getLargest(CollectionUtils.transform(configs, BreakerConfig::getVersion)));
return config;
}
@ -424,6 +428,16 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
DaoQuery configQuery = new DaoQuery("_id", String.valueOf(_config.getAccountId()));
BreakerConfig oldConfig = proxy.queryOne(BreakerConfig.class, configQuery);
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);
if (NullUtils.isNotIdentical(_config, 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);
}
@ -459,7 +476,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
AuthCode code = decryptAuthCode(_authCode);
if (code == 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
@ -572,6 +589,23 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
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
public MongoProxy getProxy() {
return proxy;

View File

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

View File

@ -6,7 +6,7 @@ import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.Objects;
@DBSerializable
@DBSerializable(autogen = false)
public class BreakerHub implements IIdentical<BreakerHub> {
private int hub;
private double voltageCalibrationFactor;
@ -22,16 +22,24 @@ public class BreakerHub implements IIdentical<BreakerHub> {
hub = _hub;
}
public double getRawVoltageCalibrationFactor() {
return voltageCalibrationFactor;
}
public double getVoltageCalibrationFactor() {
return voltageCalibrationFactor == 0.0?1.0:voltageCalibrationFactor;
return voltageCalibrationFactor == 0.0?0.3445:voltageCalibrationFactor;
}
public void setVoltageCalibrationFactor(double _voltageCalibrationFactor) {
voltageCalibrationFactor = _voltageCalibrationFactor;
}
public double getRawPortCalibrationFactor() {
return portCalibrationFactor;
}
public double getPortCalibrationFactor() {
return portCalibrationFactor == 0.0?1.0:portCalibrationFactor;
return portCalibrationFactor == 0.0?1.25: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),
Log(11, 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 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.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer;
import java.util.Collections;
import java.util.List;
@ -31,7 +30,6 @@ public class BillingRateSerializer extends AbstractDaoSerializer<BillingRate>
d.put("meter", _o.getMeter());
d.put("flow", DaoSerializer.toEnumName(_o.getFlow()));
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("time_of_day_start", _o.getTimeOfDayStart());
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.setRate(DaoSerializer.getDouble(_d, "rate"));
o.setCurrency(DaoSerializer.getEnum(_d, "currency", BillingCurrency.class));
if (o.getCurrency() == null)
o.setCurrency(DaoSerializer.getEnum(_d, "unit", BillingCurrency.class));
o.setTimeOfDayStart(DaoSerializer.getInteger(_d, "time_of_day_start"));
o.setTimeOfDayEnd(DaoSerializer.getInteger(_d, "time_of_day_end"));
o.setMonthKWhStart(DaoSerializer.getDouble(_d, "month_kwh_start"));

View File

@ -26,8 +26,8 @@ public class BreakerHubSerializer extends AbstractDaoSerializer<BreakerHub>
{
DaoEntity d = new DaoEntity();
d.put("hub", _o.getHub());
d.put("voltage_calibration_factor", _o.getVoltageCalibrationFactor());
d.put("port_calibration_factor", _o.getPortCalibrationFactor());
d.put("voltage_calibration_factor", _o.getRawVoltageCalibrationFactor());
d.put("port_calibration_factor", _o.getRawPortCalibrationFactor());
d.put("frequency", _o.getFrequency());
d.put("bluetooth_mac", _o.getBluetoothMac());
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.EnergySummarySerializer
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.MeterSerializer
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.MongoCurrentMonitorDao;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubCommands;
import com.lanternsoftware.rules.RulesEngine;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import javax.servlet.ServletContextEvent;
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 static CurrentMonitorDao dao;
private static final Map<Integer, Map<Integer, List<HubCommand>>> commands = new HashMap<>();
@Override
public void contextInitialized(ServletContextEvent sce) {
dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
RulesEngine.instance().start();
RulesEngine.instance().schedule(new CommandTask(), 0);
}
@Override
@ -23,4 +34,38 @@ public class Globals implements ServletContextListener {
dao.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();
if (tokenResponse != null) {
GoogleIdToken idToken = tokenResponse.parseIdToken();
if (idToken != null) {
logger.info("Successfully received google id token");
if (idToken != null)
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) {
logger.error("Failed to validate google auth code", _e);

View File

@ -1,49 +1,19 @@
package com.lanternsoftware.currentmonitor.servlet;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@WebServlet("/command")
public class CommandServlet extends SecureServlet {
@Override
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
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.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic;
import com.lanternsoftware.util.dao.auth.AuthCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -34,6 +36,10 @@ public class ConfigServlet extends SecureServlet {
return;
}
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);
zipBsonResponse(_rep, Globals.dao.getMergedConfig(_authCode));
}
}

View File

@ -1,12 +1,17 @@
package com.lanternsoftware.currentmonitor.servlet;
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.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
@ -15,6 +20,8 @@ import java.util.List;
@WebServlet("/power/*")
public class PowerServlet extends SecureServlet {
private static final Logger logger = LoggerFactory.getLogger(MongoCurrentMonitorDao.class);
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
String[] path = path(_req);
@ -32,19 +39,29 @@ public class PowerServlet extends SecureServlet {
String[] path = path(_req);
if ((path.length > 0) && NullUtils.isEqual(CollectionUtils.get(path, 0), "hub")) {
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());
Globals.dao.putHubPowerMinute(m);
return;
}
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()) {
CollectionUtils.edit(powers, _p->_p.setAccountId(_authCode.getAccountId()));
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;
}
BreakerPower power = getRequestPayload(_req, BreakerPower.class);
if (power == null)
return;
power.setAccountId(_authCode.getAccountId());
Globals.dao.putBreakerPower(power);
}

View File

@ -2,6 +2,8 @@ package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
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.auth.AuthCode;
@ -9,17 +11,23 @@ import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/rebuildSummaries")
@WebServlet("/rebuildSummaries/*")
public class RebuildSummariesServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
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")) {
int id = DaoSerializer.toInteger(sId);
if (id != 0)
Globals.dao.rebuildSummariesAsync(id);
}
}
}
else
_rep.setStatus(401);
}

View File

@ -2,16 +2,21 @@
<configuration>
<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>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<logger name="com.lanternsoftware" level="DEBUG"/>
<root level="OFF">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
</configuration>

View File

@ -2,16 +2,21 @@
<configuration>
<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>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<logger name="com.lanternsoftware" level="DEBUG"/>
<root level="OFF">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
</configuration>

View File

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

View File

@ -2,9 +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\\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 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);
if (high == null)
return;
_listener.onStateChanged(_sw.getNodeId(), high);
_listener.onStateChanged(_sw.getNodeId(), pin.isHigh());
});
});
}