diff --git a/currentmonitor/lantern-currentmonitor/pom.xml b/currentmonitor/lantern-currentmonitor/pom.xml index 6e9c4a1..360449b 100644 --- a/currentmonitor/lantern-currentmonitor/pom.xml +++ b/currentmonitor/lantern-currentmonitor/pom.xml @@ -2,7 +2,7 @@ 4.0.0 lantern-currentmonitor jar - 1.1.0 + 1.1.1 lantern-currentmonitor diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/BreakerSamples.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/BreakerSamples.java index aa9d46a..dbe77a8 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/BreakerSamples.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/BreakerSamples.java @@ -2,6 +2,7 @@ package com.lanternsoftware.currentmonitor; import com.lanternsoftware.currentmonitor.adc.MCP3008Pin; import com.lanternsoftware.datamodel.currentmonitor.Breaker; +import com.lanternsoftware.datamodel.currentmonitor.hub.PowerSample; import java.util.List; diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/CurrentMonitor.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/CurrentMonitor.java index e6915c4..4e04a55 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/CurrentMonitor.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/CurrentMonitor.java @@ -6,6 +6,9 @@ import com.lanternsoftware.datamodel.currentmonitor.Breaker; import com.lanternsoftware.datamodel.currentmonitor.BreakerHub; import com.lanternsoftware.datamodel.currentmonitor.BreakerPolarity; import com.lanternsoftware.datamodel.currentmonitor.BreakerPower; +import com.lanternsoftware.datamodel.currentmonitor.hub.BreakerSample; +import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample; +import com.lanternsoftware.datamodel.currentmonitor.hub.PowerSample; import com.lanternsoftware.pigpio.PiGpioFactory; import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.concurrency.ConcurrencyUtils; @@ -18,6 +21,8 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -29,6 +34,23 @@ public class CurrentMonitor { private Sampler sampler; private PowerListener listener; private boolean debug = false; + private boolean postSamples = false; + + public boolean isDebug() { + return debug; + } + + public void setDebug(boolean _debug) { + debug = _debug; + } + + public boolean isPostSamples() { + return postSamples; + } + + public void setPostSamples(boolean _postSamples) { + postSamples = _postSamples; + } public void stop() { stopMonitoring(); @@ -40,10 +62,6 @@ public class CurrentMonitor { LOG.info("Power Monitor Service Stopped"); } - public void setDebug(boolean _debug) { - debug = _debug; - } - public CalibrationResult calibrateVoltage(double _curCalibration) { LOG.info("Calibrating Voltage"); MCP3008Pin voltagePin = new MCP3008Pin(getChip(0), 0); @@ -148,10 +166,10 @@ public class CurrentMonitor { private boolean running = true; private final BreakerHub hub; private final List> breakers; - private final int intervalNs; + private final long intervalNs; private final int concurrentBreakerCnt; - public Sampler(BreakerHub _hub, List _breakers, int _intervalMs, int _concurrentBreakerCnt) { + public Sampler(BreakerHub _hub, List _breakers, long _intervalMs, int _concurrentBreakerCnt) { hub = _hub; MCP3008Pin voltagePin = new MCP3008Pin(getChip(0), 0); breakers = CollectionUtils.transform(_breakers, _b->{ @@ -177,6 +195,10 @@ public class CurrentMonitor { long interval = 0; int cycle; int curBreaker; + long intervalStart; + long intervalEnd; + long cycleEnd; + long curTime; BreakerSamples[] cycleBreakers = new BreakerSamples[concurrentBreakerCnt]; try { while (true) { @@ -187,8 +209,8 @@ public class CurrentMonitor { } } final Date readTime = new Date(); - final long intervalStart = (interval * intervalNs) + start; - long intervalEnd = intervalStart + intervalNs; + intervalStart = (interval * intervalNs) + start; + intervalEnd = intervalStart + intervalNs; cycle = 0; final int batch = (int) (interval % BATCH_CNT); while (System.nanoTime() < intervalEnd) { @@ -197,22 +219,73 @@ public class CurrentMonitor { cycleBreakers[curBreaker].incrementCycleCnt(); } cycle++; - long cycleEnd = intervalStart + (cycle * (intervalNs / hub.getFrequency())); - while (System.nanoTime() < cycleEnd) { + cycleEnd = intervalStart + (cycle * (intervalNs / hub.getFrequency())); + curTime = System.nanoTime(); + while (curTime < cycleEnd) { for (curBreaker = 0; curBreaker < concurrentBreakerCnt; curBreaker++) { PowerSample sample = cycleBreakers[curBreaker].incrementSample(); + sample.nanoTime = curTime; + sample.cycle = cycle; sample.voltage = cycleBreakers[curBreaker].getVoltagePin().read(); sample.current = cycleBreakers[curBreaker].getCurrentPin().read(); } + curTime = System.nanoTime(); } } interval++; + final HubSample hubSample = (postSamples && (interval == 10)) ? new HubSample() : null; executor.submit(() -> { + long cycleLength = 1000000000/hub.getFrequency(); + if (hubSample != null) { + hubSample.setSampleDate(new Date()); + hubSample.setBreakers(new ArrayList<>()); + } for (List breaker : breakers) { - double vOffset = 0.0; - double iOffset = 0.0; BreakerSamples samples = breaker.get(batch); List validSamples = samples.getSamples().subList(0, samples.getSampleCnt()); + if (hubSample != null) { + BreakerSample breakerSample = new BreakerSample(); + breakerSample.setSamples(validSamples); + breakerSample.setPanel(samples.getBreaker().getPanel()); + breakerSample.setSpace(samples.getBreaker().getSpace()); + hubSample.getBreakers().add(breakerSample); + } + int phaseOffsetNs = samples.getBreaker().getPhaseOffsetNs()-hub.getPhaseOffsetNs(); + if (phaseOffsetNs != 0) { + Map> cycles = CollectionUtils.transformToMultiMap(validSamples, _p->_p.cycle); + for (List cycleSamples : cycles.values()) { + long minNano; + long maxNano = minNano = cycleSamples.get(0).nanoTime; + for (PowerSample sample : cycleSamples) { + if (sample.nanoTime < minNano) + minNano = sample.nanoTime; + if (sample.nanoTime > maxNano) + maxNano = sample.nanoTime; + } + TreeMap offsetSamples = new TreeMap<>(); + for (PowerSample sample : cycleSamples) { + if (sample.nanoTime + phaseOffsetNs < minNano) + offsetSamples.put(sample.nanoTime + phaseOffsetNs + cycleLength, sample.voltage); + else if (sample.nanoTime + phaseOffsetNs > maxNano) + offsetSamples.put(sample.nanoTime + phaseOffsetNs - cycleLength, sample.voltage); + else + offsetSamples.put(sample.nanoTime + phaseOffsetNs, sample.voltage); + } + for (PowerSample sample : cycleSamples) { + List voltages = new ArrayList<>(); + Entry floorEntry = offsetSamples.floorEntry(sample.nanoTime); + if (floorEntry != null) + voltages.add(floorEntry.getValue()); + Entry ceilingEntry = offsetSamples.ceilingEntry(sample.nanoTime); + if (ceilingEntry != null) + voltages.add(ceilingEntry.getValue()); + sample.voltage = CollectionUtils.mean(voltages); + } + } + } + + double vOffset = 0.0; + double iOffset = 0.0; for (PowerSample sample : validSamples) { vOffset += sample.voltage; iOffset += sample.current; @@ -223,6 +296,7 @@ public class CurrentMonitor { double pSum = 0.0; double vRms = 0.0; double lowPassFilter = samples.getBreaker().getLowPassFilter(); + for (PowerSample sample : validSamples) { sample.current -= iOffset; if (Math.abs(sample.current) < lowPassFilter) @@ -233,11 +307,15 @@ public class CurrentMonitor { } vRms /= validSamples.size(); vRms = hub.getVoltageCalibrationFactor() * Math.sqrt(vRms); - int lowSampleRatio = (lowSamples * 100) / samples.getSampleCnt(); - double realPower = Math.abs((hub.getVoltageCalibrationFactor() * hub.getPortCalibrationFactor() * samples.getBreaker().getFinalCalibrationFactor() * pSum) / samples.getSampleCnt()); - if ((lowSampleRatio > 75) && realPower < 13.0) + int lowSampleRatio = (lowSamples * 100) / validSamples.size(); + double realPower = (hub.getVoltageCalibrationFactor() * hub.getPortCalibrationFactor() * samples.getBreaker().getFinalCalibrationFactor() * pSum) / validSamples.size(); + if ((lowSampleRatio > 75) && Math.abs(realPower) < 13.0) realPower = 0.0; - if (samples.getBreaker().getPolarity() == BreakerPolarity.SOLAR) + if (samples.getBreaker().getPolarity() == BreakerPolarity.NORMAL) + realPower = Math.abs(realPower); + else if (samples.getBreaker().getPolarity() == BreakerPolarity.SOLAR) + realPower = -Math.abs(realPower); + else if (samples.getBreaker().getPolarity() == BreakerPolarity.BI_DIRECTIONAL_INVERTED) realPower = -realPower; if (samples.getBreaker().isDoublePower()) realPower *= 2.0; @@ -263,6 +341,8 @@ public class CurrentMonitor { samples.setCycleCnt(0); listener.onPowerEvent(new BreakerPower(samples.getBreaker().getPanel(), samples.getBreaker().getSpace(), readTime, realPower, vRms)); } + if (hubSample != null) + listener.onSampleEvent(hubSample); }); } } diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorApp.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorApp.java index da609ec..5d73f14 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorApp.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorApp.java @@ -16,6 +16,7 @@ import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic; import com.lanternsoftware.datamodel.currentmonitor.HubConfigService; import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; import com.lanternsoftware.datamodel.currentmonitor.NetworkStatus; +import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample; import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.ResourceLoader; @@ -65,16 +66,24 @@ public class MonitorApp { private static final CurrentMonitor monitor = new CurrentMonitor(); private static final List readings = new ArrayList<>(); private static String version; - private static final PowerListener logger = _p -> { - if (!config.isDebug()) { - _p.setHubVersion(version); - if (breakerConfig != null) - _p.setAccountId(breakerConfig.getAccountId()); - synchronized (readings) { - readings.add(_p); - } - } else - LOG.info("Panel{} - Space{} Power: {}W", _p.getPanel(), Breaker.toSpaceDisplay(_p.getSpace()), String.format("%.3f", _p.getPower())); + private static final PowerListener logger = new PowerListener() { + @Override + public void onPowerEvent(BreakerPower _power) { + if (!config.isDebug()) { + _power.setHubVersion(version); + if (breakerConfig != null) + _power.setAccountId(breakerConfig.getAccountId()); + synchronized (readings) { + readings.add(_power); + } + } else + LOG.info("Panel{} - Space{} Power: {}W", _power.getPanel(), Breaker.toSpaceDisplay(_power.getSpace()), String.format("%.3f", _power.getPower())); + } + + @Override + public void onSampleEvent(HubSample _sample) { + post(DaoSerializer.toZipBson(_sample), "sample"); + } }; private static final BleCharacteristicListener bluetoothListener = new BleCharacteristicListener() { @Override @@ -214,6 +223,7 @@ public class MonitorApp { if (NullUtils.isNotEmpty(config.getHost())) host = NullUtils.terminateWith(config.getHost(), "/"); monitor.setDebug(config.isDebug()); + monitor.setPostSamples(config.isPostSamples()); LEDFlasher.setLEDOn(false); if (NullUtils.isNotEmpty(config.getAuthCode())) authCode = config.getAuthCode(); diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorConfig.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorConfig.java index 4c040b0..dd971fd 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorConfig.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/MonitorConfig.java @@ -17,6 +17,7 @@ public class MonitorConfig { private boolean debug; private int connectTimeout; private int socketTimeout; + private boolean postSamples = false; private boolean needsCalibration = true; private String mqttBrokerUrl; private String mqttUserName; @@ -98,6 +99,14 @@ public class MonitorConfig { socketTimeout = _socketTimeout; } + public boolean isPostSamples() { + return postSamples; + } + + public void setPostSamples(boolean _postSamples) { + postSamples = _postSamples; + } + public boolean isNeedsCalibration() { return needsCalibration; } diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/PowerListener.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/PowerListener.java index a3a0a5a..2730b3e 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/PowerListener.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/PowerListener.java @@ -1,7 +1,9 @@ package com.lanternsoftware.currentmonitor; import com.lanternsoftware.datamodel.currentmonitor.BreakerPower; +import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample; public interface PowerListener { void onPowerEvent(BreakerPower _power); + void onSampleEvent(HubSample _sample); } diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/PowerSample.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/PowerSample.java deleted file mode 100644 index 5efb1f4..0000000 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/PowerSample.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.lanternsoftware.currentmonitor; - -public class PowerSample { - public double voltage; - public double current; -} diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/dao/MonitorConfigSerializer.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/dao/MonitorConfigSerializer.java index ad54fda..a4e3e2b 100644 --- a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/dao/MonitorConfigSerializer.java +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/dao/MonitorConfigSerializer.java @@ -34,6 +34,7 @@ public class MonitorConfigSerializer extends AbstractDaoSerializer getAllHubCommands(); void deleteHubCommand(String _id); + void putHubSample(HubSample _sample); + List getSamplesForAccount(int _accountId); + MongoProxy getProxy(); } diff --git a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/MongoCurrentMonitorDao.java b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/MongoCurrentMonitorDao.java index cfa1bc3..80f0bd9 100644 --- a/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/MongoCurrentMonitorDao.java +++ b/currentmonitor/lantern-dataaccess-currentmonitor/src/main/java/com/lanternsoftware/dataaccess/currentmonitor/MongoCurrentMonitorDao.java @@ -21,6 +21,7 @@ import com.lanternsoftware.datamodel.currentmonitor.archive.ArchiveStatus; import com.lanternsoftware.datamodel.currentmonitor.archive.BreakerEnergyArchive; import com.lanternsoftware.datamodel.currentmonitor.archive.DailyEnergyArchive; import com.lanternsoftware.datamodel.currentmonitor.archive.MonthlyEnergyArchive; +import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample; import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.DateRange; import com.lanternsoftware.util.DateUtils; @@ -487,17 +488,13 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao { } } - @Override - public void rebuildSummariesAsync(int _accountId) { - executor.submit(() -> rebuildSummaries(_accountId)); - } - @Override public void rebuildSummaries(int _accountId) { HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sort("minute")); if (firstMinute == null) return; - rebuildSummaries(_accountId, firstMinute.getMinuteAsDate(), new Date()); + HubPowerMinute lastMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sortDesc("minute")); + rebuildSummaries(_accountId, firstMinute.getMinuteAsDate(), lastMinute.getMinuteAsDate()); } @Override @@ -812,6 +809,16 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao { proxy.delete(HubCommand.class, new DaoQuery("_id", _id)); } + @Override + public void putHubSample(HubSample _sample) { + proxy.save(_sample); + } + + @Override + public List getSamplesForAccount(int _accountId) { + return proxy.query(HubSample.class, new DaoQuery("account_id", _accountId)); + } + @Override public MongoProxy getProxy() { return proxy; diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/Breaker.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/Breaker.java index 2306865..6bd5414 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/Breaker.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/Breaker.java @@ -21,6 +21,7 @@ public class Breaker implements IIdentical { private String name; private String description; private int sizeAmps; + private int phaseOffsetNs; private double calibrationFactor; private double lowPassFilter; private BreakerPolarity polarity; @@ -140,6 +141,14 @@ public class Breaker implements IIdentical { sizeAmps = _sizeAmps; } + public int getPhaseOffsetNs() { + return phaseOffsetNs; + } + + public void setPhaseOffsetNs(int _phaseOffsetNs) { + phaseOffsetNs = _phaseOffsetNs; + } + public double getLowPassFilter() { return Math.abs(lowPassFilter) < 0.05 ? 1.6 : lowPassFilter; } @@ -278,7 +287,7 @@ public class Breaker implements IIdentical { @Override public boolean isIdentical(Breaker _o) { if (this == _o) return true; - return panel == _o.panel && space == _o.space && meter == _o.meter && hub == _o.hub && port == _o.port && sizeAmps == _o.sizeAmps && Double.compare(_o.calibrationFactor, calibrationFactor) == 0 && Double.compare(_o.lowPassFilter, lowPassFilter) == 0 && doublePower == _o.doublePower && Objects.equals(name, _o.name) && Objects.equals(description, _o.description) && polarity == _o.polarity && type == _o.type; + return panel == _o.panel && space == _o.space && meter == _o.meter && hub == _o.hub && port == _o.port && sizeAmps == _o.sizeAmps && phaseOffsetNs == _o.phaseOffsetNs && Double.compare(_o.calibrationFactor, calibrationFactor) == 0 && Double.compare(_o.lowPassFilter, lowPassFilter) == 0 && doublePower == _o.doublePower && Objects.equals(name, _o.name) && Objects.equals(description, _o.description) && polarity == _o.polarity && type == _o.type; } @Override diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerHub.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerHub.java index 09b5ab2..be70d19 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerHub.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerHub.java @@ -11,6 +11,8 @@ public class BreakerHub implements IIdentical { private int hub; private double voltageCalibrationFactor; private double portCalibrationFactor; + private int phaseCnt; + private int phaseOffsetNs; private int frequency; private String bluetoothMac; @@ -46,8 +48,24 @@ public class BreakerHub implements IIdentical { portCalibrationFactor = _portCalibrationFactor; } + public int getPhaseCnt() { + return phaseCnt == 0 ? 2 : phaseCnt; + } + + public void setPhaseCnt(int _phaseCnt) { + phaseCnt = _phaseCnt; + } + + public int getPhaseOffsetNs() { + return phaseOffsetNs; + } + + public void setPhaseOffsetNs(int _phaseOffsetNs) { + phaseOffsetNs = _phaseOffsetNs; + } + public int getFrequency() { - return frequency; + return frequency == 0 ? 60 : frequency; } public void setFrequency(int _frequency) { @@ -73,7 +91,7 @@ public class BreakerHub implements IIdentical { @Override public boolean isIdentical(BreakerHub _o) { if (this == _o) return true; - return hub == _o.hub && Double.compare(_o.voltageCalibrationFactor, voltageCalibrationFactor) == 0 && Double.compare(_o.portCalibrationFactor, portCalibrationFactor) == 0 && frequency == _o.frequency && Objects.equals(bluetoothMac, _o.bluetoothMac); + return hub == _o.hub && Double.compare(_o.voltageCalibrationFactor, voltageCalibrationFactor) == 0 && Double.compare(_o.portCalibrationFactor, portCalibrationFactor) == 0 && getPhaseCnt() == _o.getPhaseCnt() && getPhaseOffsetNs() == _o.getPhaseOffsetNs() && getFrequency() == _o.getFrequency() && Objects.equals(bluetoothMac, _o.bluetoothMac); } @Override diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerPolarity.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerPolarity.java index 13fe60b..e84b8ed 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerPolarity.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerPolarity.java @@ -2,5 +2,7 @@ package com.lanternsoftware.datamodel.currentmonitor; public enum BreakerPolarity { NORMAL, - SOLAR; + SOLAR, + BI_DIRECTIONAL, + BI_DIRECTIONAL_INVERTED } diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergySummary.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergySummary.java index 0ccaaed..465b4ee 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergySummary.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/EnergySummary.java @@ -140,9 +140,9 @@ public class EnergySummary { idx = 0; double flow = 0.0; for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) { - if ((b.getPolarity() == BreakerPolarity.SOLAR) && (meter.flow[idx] < 0.0)) + if (power < 0 && (meter.flow[idx] < 0.0)) flow -= meter.flow[idx] * (power / meter.solar[idx]); - else if ((b.getPolarity() != BreakerPolarity.SOLAR) && (meter.flow[idx] > 0.0)) + else if (power > 0 && (meter.flow[idx] > 0.0)) flow += meter.flow[idx] * (power / meter.usage[idx]); idx++; } @@ -152,11 +152,11 @@ public class EnergySummary { } public void resetEnergy(Date _readTime) { - if (energy == null) - return; - int idx = viewMode.blockIndex(start, _readTime, timezone); - if (idx < energy.length) - energy[idx] = 0f; + if (energy != null) { + int idx = viewMode.blockIndex(start, _readTime, timezone); + if (idx < energy.length) + energy[idx] = 0f; + } for (EnergySummary subGroup : CollectionUtils.makeNotNull(subGroups)) { subGroup.resetEnergy(_readTime); } diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerHubSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerHubSerializer.java index 776059b..135559d 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerHubSerializer.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerHubSerializer.java @@ -28,6 +28,8 @@ public class BreakerHubSerializer extends AbstractDaoSerializer d.put("hub", _o.getHub()); d.put("voltage_calibration_factor", _o.getRawVoltageCalibrationFactor()); d.put("port_calibration_factor", _o.getRawPortCalibrationFactor()); + d.put("phase_cnt", _o.getPhaseCnt()); + d.put("phase_offset_ns", _o.getPhaseOffsetNs()); d.put("frequency", _o.getFrequency()); d.put("bluetooth_mac", _o.getBluetoothMac()); return d; @@ -40,6 +42,8 @@ public class BreakerHubSerializer extends AbstractDaoSerializer o.setHub(DaoSerializer.getInteger(_d, "hub")); o.setVoltageCalibrationFactor(DaoSerializer.getDouble(_d, "voltage_calibration_factor")); o.setPortCalibrationFactor(DaoSerializer.getDouble(_d, "port_calibration_factor")); + o.setPhaseCnt(DaoSerializer.getInteger(_d, "phase_cnt")); + o.setPhaseOffsetNs(DaoSerializer.getInteger(_d, "phase_offset_ns")); o.setFrequency(DaoSerializer.getInteger(_d, "frequency")); o.setBluetoothMac(DaoSerializer.getString(_d, "bluetooth_mac")); return o; diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerSerializer.java index cfb3361..d8654d0 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerSerializer.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/dao/BreakerSerializer.java @@ -35,6 +35,7 @@ public class BreakerSerializer extends AbstractDaoSerializer d.put("name", _o.getName()); d.put("description", _o.getDescription()); d.put("size_amps", _o.getSizeAmps()); + d.put("phase_offset_ns", _o.getPhaseOffsetNs()); d.put("calibration_factor", _o.getCalibrationFactor()); d.put("low_pass_filter", _o.getLowPassFilter()); d.put("polarity", DaoSerializer.toEnumName(_o.getPolarity())); @@ -56,6 +57,7 @@ public class BreakerSerializer extends AbstractDaoSerializer o.setName(DaoSerializer.getString(_d, "name")); o.setDescription(DaoSerializer.getString(_d, "description")); o.setSizeAmps(DaoSerializer.getInteger(_d, "size_amps")); + o.setPhaseOffsetNs(DaoSerializer.getInteger(_d, "phase_offset_ns")); o.setCalibrationFactor(DaoSerializer.getDouble(_d, "calibration_factor")); o.setLowPassFilter(DaoSerializer.getDouble(_d, "low_pass_filter")); o.setPolarity(DaoSerializer.getEnum(_d, "polarity", BreakerPolarity.class)); diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/BreakerSample.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/BreakerSample.java new file mode 100644 index 0000000..67f80a4 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/BreakerSample.java @@ -0,0 +1,41 @@ +package com.lanternsoftware.datamodel.currentmonitor.hub; + +import com.lanternsoftware.datamodel.currentmonitor.Breaker; +import com.lanternsoftware.util.dao.annotations.DBSerializable; + +import java.util.List; + +@DBSerializable +public class BreakerSample { + private int panel; + private int space; + private List samples; + + public int key() { + return Breaker.intKey(panel, space); + } + + public int getPanel() { + return panel; + } + + public void setPanel(int _panel) { + panel = _panel; + } + + public int getSpace() { + return space; + } + + public void setSpace(int _space) { + space = _space; + } + + public List getSamples() { + return samples; + } + + public void setSamples(List _samples) { + samples = _samples; + } +} diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/HubSample.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/HubSample.java new file mode 100644 index 0000000..0934648 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/HubSample.java @@ -0,0 +1,42 @@ +package com.lanternsoftware.datamodel.currentmonitor.hub; + +import com.lanternsoftware.util.DateUtils; +import com.lanternsoftware.util.dao.annotations.DBSerializable; + +import java.util.Date; +import java.util.List; + +@DBSerializable(autogen = false) +public class HubSample { + private int accountId; + private Date sampleDate; + private List breakers; + + public String getId() { + return String.format("%d-%d", accountId, DateUtils.toLong(sampleDate)); + } + + public int getAccountId() { + return accountId; + } + + public void setAccountId(int _accountId) { + accountId = _accountId; + } + + public Date getSampleDate() { + return sampleDate; + } + + public void setSampleDate(Date _sampleDate) { + sampleDate = _sampleDate; + } + + public List getBreakers() { + return breakers; + } + + public void setBreakers(List _breakers) { + breakers = _breakers; + } +} diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/PowerSample.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/PowerSample.java new file mode 100644 index 0000000..cb7aa93 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/PowerSample.java @@ -0,0 +1,43 @@ +package com.lanternsoftware.datamodel.currentmonitor.hub; + +import com.lanternsoftware.util.dao.annotations.DBSerializable; + +@DBSerializable +public class PowerSample { + public long nanoTime; + public int cycle; + public double voltage; + public double current; + + public long getNanoTime() { + return nanoTime; + } + + public void setNanoTime(long _nanoTime) { + nanoTime = _nanoTime; + } + + public int getCycle() { + return cycle; + } + + public void setCycle(int _cycle) { + cycle = _cycle; + } + + public double getVoltage() { + return voltage; + } + + public void setVoltage(double _voltage) { + voltage = _voltage; + } + + public double getCurrent() { + return current; + } + + public void setCurrent(double _current) { + current = _current; + } +} diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/dao/BreakerSampleSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/dao/BreakerSampleSerializer.java new file mode 100644 index 0000000..21a3c99 --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/dao/BreakerSampleSerializer.java @@ -0,0 +1,44 @@ +package com.lanternsoftware.datamodel.currentmonitor.hub.dao; + +import com.lanternsoftware.datamodel.currentmonitor.hub.BreakerSample; +import com.lanternsoftware.datamodel.currentmonitor.hub.PowerSample; +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 BreakerSampleSerializer extends AbstractDaoSerializer +{ + @Override + public Class getSupportedClass() + { + return BreakerSample.class; + } + + @Override + public List getSupportedProxies() { + return Collections.singletonList(DaoProxyType.MONGO); + } + + @Override + public DaoEntity toDaoEntity(BreakerSample _o) + { + DaoEntity d = new DaoEntity(); + d.put("panel", _o.getPanel()); + d.put("space", _o.getSpace()); + d.put("samples", DaoSerializer.toDaoEntities(_o.getSamples(), DaoProxyType.MONGO)); + return d; + } + + @Override + public BreakerSample fromDaoEntity(DaoEntity _d) + { + BreakerSample o = new BreakerSample(); + o.setPanel(DaoSerializer.getInteger(_d, "panel")); + o.setSpace(DaoSerializer.getInteger(_d, "space")); + o.setSamples(DaoSerializer.getList(_d, "samples", PowerSample.class)); + return o; + } +} \ No newline at end of file diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/dao/HubSampleSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/dao/HubSampleSerializer.java new file mode 100644 index 0000000..b734e3c --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/dao/HubSampleSerializer.java @@ -0,0 +1,45 @@ +package com.lanternsoftware.datamodel.currentmonitor.hub.dao; + +import com.lanternsoftware.datamodel.currentmonitor.hub.BreakerSample; +import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample; +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 HubSampleSerializer extends AbstractDaoSerializer +{ + @Override + public Class getSupportedClass() + { + return HubSample.class; + } + + @Override + public List getSupportedProxies() { + return Collections.singletonList(DaoProxyType.MONGO); + } + + @Override + public DaoEntity toDaoEntity(HubSample _o) + { + DaoEntity d = new DaoEntity(); + d.put("_id", _o.getId()); + d.put("account_id", _o.getAccountId()); + d.put("sample_date", DaoSerializer.toLong(_o.getSampleDate())); + d.put("breakers", DaoSerializer.toDaoEntities(_o.getBreakers(), DaoProxyType.MONGO)); + return d; + } + + @Override + public HubSample fromDaoEntity(DaoEntity _d) + { + HubSample o = new HubSample(); + o.setAccountId(DaoSerializer.getInteger(_d, "account_id")); + o.setSampleDate(DaoSerializer.getDate(_d, "sample_date")); + o.setBreakers(DaoSerializer.getList(_d, "breakers", BreakerSample.class)); + return o; + } +} \ No newline at end of file diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/dao/PowerSampleSerializer.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/dao/PowerSampleSerializer.java new file mode 100644 index 0000000..aaf0b6c --- /dev/null +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/hub/dao/PowerSampleSerializer.java @@ -0,0 +1,45 @@ +package com.lanternsoftware.datamodel.currentmonitor.hub.dao; + +import com.lanternsoftware.datamodel.currentmonitor.hub.PowerSample; +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 PowerSampleSerializer extends AbstractDaoSerializer +{ + @Override + public Class getSupportedClass() + { + return PowerSample.class; + } + + @Override + public List getSupportedProxies() { + return Collections.singletonList(DaoProxyType.MONGO); + } + + @Override + public DaoEntity toDaoEntity(PowerSample _o) + { + DaoEntity d = new DaoEntity(); + d.put("nano_time", _o.getNanoTime()); + d.put("cycle", _o.getCycle()); + d.put("voltage", _o.getVoltage()); + d.put("current", _o.getCurrent()); + return d; + } + + @Override + public PowerSample fromDaoEntity(DaoEntity _d) + { + PowerSample o = new PowerSample(); + o.setNanoTime(DaoSerializer.getLong(_d, "nano_time")); + o.setCycle(DaoSerializer.getInteger(_d, "cycle")); + o.setVoltage(DaoSerializer.getDouble(_d, "voltage")); + o.setCurrent(DaoSerializer.getDouble(_d, "current")); + return o; + } +} \ No newline at end of file diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/resources/META-INF/services/com.lanternsoftware.util.dao.IDaoSerializer b/currentmonitor/lantern-datamodel-currentmonitor/src/main/resources/META-INF/services/com.lanternsoftware.util.dao.IDaoSerializer index 96fc8ac..b512210 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/resources/META-INF/services/com.lanternsoftware.util.dao.IDaoSerializer +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/resources/META-INF/services/com.lanternsoftware.util.dao.IDaoSerializer @@ -26,3 +26,6 @@ com.lanternsoftware.datamodel.currentmonitor.dao.MeterSerializer com.lanternsoftware.datamodel.currentmonitor.dao.NetworkStatusSerializer com.lanternsoftware.datamodel.currentmonitor.dao.SequenceSerializer com.lanternsoftware.datamodel.currentmonitor.dao.SignupResponseSerializer +com.lanternsoftware.datamodel.currentmonitor.hub.dao.BreakerSampleSerializer +com.lanternsoftware.datamodel.currentmonitor.hub.dao.HubSampleSerializer +com.lanternsoftware.datamodel.currentmonitor.hub.dao.PowerSampleSerializer diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/context/Globals.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/context/Globals.java index 95d5b50..9c5c529 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/context/Globals.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/context/Globals.java @@ -18,9 +18,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TimerTask; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public class Globals implements ServletContextListener { public static CurrentMonitorDao dao; + public static ExecutorService opsExecutor; private static final Map>> commands = new HashMap<>(); @Override @@ -28,10 +31,12 @@ public class Globals implements ServletContextListener { dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg")); RulesEngine.instance().start(); RulesEngine.instance().schedule(new CommandTask(), 0); + opsExecutor = Executors.newFixedThreadPool(7); } @Override public void contextDestroyed(ServletContextEvent sce) { + opsExecutor.shutdown(); dao.shutdown(); HttpFactory.shutdown(); RulesEngine.shutdown(); diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ConfigServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ConfigServlet.java index b6c7702..ab3e4d6 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ConfigServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/ConfigServlet.java @@ -4,6 +4,7 @@ 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.rules.RulesEngine; import com.lanternsoftware.util.dao.auth.AuthCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,6 +41,8 @@ public class ConfigServlet extends SecureServiceServlet { 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)); + config = Globals.dao.getMergedConfig(_authCode); + RulesEngine.instance().sendFcmMessage(config.getAccountId(), config); + zipBsonResponse(_rep, config); } } diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/RebuildSummariesServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/RebuildSummariesServlet.java index 9befc30..391ffec 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/RebuildSummariesServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/RebuildSummariesServlet.java @@ -17,13 +17,14 @@ public class RebuildSummariesServlet extends SecureServiceServlet { if (_authCode.getAccountId() == 100) { String[] path = path(_req); if (path.length > 0) { - Globals.dao.rebuildSummariesAsync(DaoSerializer.toInteger(CollectionUtils.get(path, 0))); + Globals.opsExecutor.submit(() -> Globals.dao.rebuildSummaries(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); + if (id != 0) { + Globals.opsExecutor.submit(() -> Globals.dao.rebuildSummaries(id)); + } } } } diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/SampleServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/SampleServlet.java new file mode 100644 index 0000000..c2ed488 --- /dev/null +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/SampleServlet.java @@ -0,0 +1,21 @@ +package com.lanternsoftware.currentmonitor.servlet; + +import com.lanternsoftware.currentmonitor.context.Globals; +import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample; +import com.lanternsoftware.util.dao.auth.AuthCode; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@WebServlet("/sample") +public class SampleServlet extends SecureServiceServlet { + @Override + protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { + HubSample sample = getRequestPayload(_req, HubSample.class); + if (sample == null) + return; + sample.setAccountId(_authCode.getAccountId()); + Globals.dao.putHubSample(sample); + } +} diff --git a/pigpio/lantern-pigpio/src/main/java/com/lanternsoftware/pigpio/PIGPIO.java b/pigpio/lantern-pigpio/src/main/java/com/lanternsoftware/pigpio/PIGPIO.java index c1bc3f6..22dcdf7 100644 --- a/pigpio/lantern-pigpio/src/main/java/com/lanternsoftware/pigpio/PIGPIO.java +++ b/pigpio/lantern-pigpio/src/main/java/com/lanternsoftware/pigpio/PIGPIO.java @@ -4,9 +4,6 @@ import com.lanternsoftware.util.ResourceLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.nio.file.Files; - public class PIGPIO { protected static final Logger LOG = LoggerFactory.getLogger(PIGPIO.class); @@ -20,10 +17,11 @@ public class PIGPIO { osArch = "armhf"; String path = "/lib/" + osArch + "/lantern-pigpio.so"; byte[] file = ResourceLoader.getByteArrayResource(PIGPIO.class, path); - String target = Files.createTempFile("lantern-pigpio", "so").toAbsolutePath().toString(); - ResourceLoader.writeFile(target, file); - System.load(target); - } catch (IOException _e) { + LOG.info("library size: {}", file.length); + String libPath = "/opt/currentmonitor/lantern-pigpio.so"; + ResourceLoader.writeFile(libPath, file); + System.load(libPath); + } catch (Exception _e) { LOG.error("Failed to load lantern-pigpio.so from resource", _e); } } diff --git a/rules/lantern-service-rules/src/main/java/com/lanternsoftware/rules/RulesEngine.java b/rules/lantern-service-rules/src/main/java/com/lanternsoftware/rules/RulesEngine.java index de50191..ea31e33 100644 --- a/rules/lantern-service-rules/src/main/java/com/lanternsoftware/rules/RulesEngine.java +++ b/rules/lantern-service-rules/src/main/java/com/lanternsoftware/rules/RulesEngine.java @@ -11,13 +11,16 @@ import com.lanternsoftware.datamodel.rules.Criteria; import com.lanternsoftware.datamodel.rules.Event; import com.lanternsoftware.datamodel.rules.EventId; import com.lanternsoftware.datamodel.rules.EventType; +import com.lanternsoftware.datamodel.rules.FcmDevice; import com.lanternsoftware.datamodel.rules.Rule; import com.lanternsoftware.rules.actions.ActionImpl; import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.DateUtils; -import com.lanternsoftware.util.external.LanternFiles; +import com.lanternsoftware.util.cloudservices.google.FirebaseHelper; +import com.lanternsoftware.util.dao.DaoEntity; import com.lanternsoftware.util.dao.DaoSerializer; import com.lanternsoftware.util.dao.mongo.MongoConfig; +import com.lanternsoftware.util.external.LanternFiles; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,6 +39,7 @@ import java.util.concurrent.Executors; public class RulesEngine { protected static final Logger LOG = LoggerFactory.getLogger(RulesEngine.class); + protected static final FirebaseHelper firebaseHelper = new FirebaseHelper(LanternFiles.CONFIG_PATH + "google_account_key.json"); private static RulesEngine INSTANCE; private final ExecutorService executor = Executors.newCachedThreadPool(); @@ -69,6 +73,15 @@ public class RulesEngine { return dao; } + public void sendFcmMessage(int _accountId, Object _payload) { + List devices = RulesEngine.instance().dao().getFcmDevicesForAccount(_accountId); + if (devices.isEmpty()) + return; + for (FcmDevice device : devices) { + firebaseHelper.sendMessage(device.getToken(), new DaoEntity("payload", DaoSerializer.toBase64ZipBson(_payload)).and("payloadClass", _payload.getClass().getCanonicalName())); + } + } + public void fireEvent(Event _event) { if (_event.getType() != EventType.TIME) dao.putEvent(_event); @@ -108,6 +121,8 @@ public class RulesEngine { return; Collection dates = CollectionUtils.aggregate(rules, _r->CollectionUtils.transform(_r.getAllCriteria(), _c->_c.getNextTriggerDate(tz))); Date nextDate = CollectionUtils.getSmallest(dates); + if (nextDate == null) + return; LOG.info("Scheduling next time event for account {} at {}", _accountId, DateUtils.format("MM/dd/yyyy HH:mm:ss", nextDate)); nextTask = new EventTimeTask(_accountId, nextDate); timer.schedule(nextTask, nextDate); diff --git a/rules/lantern-service-rules/src/main/java/com/lanternsoftware/rules/actions/AbstractAlertAction.java b/rules/lantern-service-rules/src/main/java/com/lanternsoftware/rules/actions/AbstractAlertAction.java index 3247b46..39764a9 100644 --- a/rules/lantern-service-rules/src/main/java/com/lanternsoftware/rules/actions/AbstractAlertAction.java +++ b/rules/lantern-service-rules/src/main/java/com/lanternsoftware/rules/actions/AbstractAlertAction.java @@ -1,28 +1,11 @@ package com.lanternsoftware.rules.actions; import com.lanternsoftware.datamodel.rules.Alert; -import com.lanternsoftware.datamodel.rules.FcmDevice; import com.lanternsoftware.datamodel.rules.Rule; import com.lanternsoftware.rules.RulesEngine; -import com.lanternsoftware.util.dao.DaoEntity; -import com.lanternsoftware.util.dao.DaoSerializer; -import com.lanternsoftware.util.external.LanternFiles; -import com.lanternsoftware.util.cloudservices.google.FirebaseHelper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; public abstract class AbstractAlertAction implements ActionImpl { - protected static final Logger logger = LoggerFactory.getLogger(AbstractAlertAction.class); - protected static final FirebaseHelper firebaseHelper = new FirebaseHelper(LanternFiles.CONFIG_PATH + "google_account_key.json"); - protected void sendAlert(Rule _rule, Alert _alert) { - List devices = RulesEngine.instance().dao().getFcmDevicesForAccount(_rule.getAccountId()); - if (devices.isEmpty()) - return; - for (FcmDevice device : devices) { - firebaseHelper.sendMessage(device.getToken(), new DaoEntity("payload", DaoSerializer.toBase64ZipBson(_alert)).and("payloadClass", Alert.class.getCanonicalName())); - } + RulesEngine.instance().sendFcmMessage(_rule.getAccountId(), _alert); } } diff --git a/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java b/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java index 6677daa..7a28df3 100644 --- a/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java +++ b/zwave/lantern-service-zwave/src/main/java/com/lanternsoftware/zwave/context/ZWaveApp.java @@ -2,7 +2,6 @@ package com.lanternsoftware.zwave.context; import com.lanternsoftware.datamodel.rules.Event; import com.lanternsoftware.datamodel.rules.EventType; -import com.lanternsoftware.util.dao.auth.AuthCode; import com.lanternsoftware.datamodel.zwave.Switch; import com.lanternsoftware.datamodel.zwave.SwitchSchedule; import com.lanternsoftware.datamodel.zwave.SwitchTransition; @@ -10,13 +9,14 @@ import com.lanternsoftware.datamodel.zwave.ThermostatMode; import com.lanternsoftware.datamodel.zwave.ZWaveConfig; import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.DateUtils; -import com.lanternsoftware.util.external.LanternFiles; import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.ResourceLoader; import com.lanternsoftware.util.concurrency.ConcurrencyUtils; import com.lanternsoftware.util.cryptography.AESTool; import com.lanternsoftware.util.dao.DaoSerializer; +import com.lanternsoftware.util.dao.auth.AuthCode; import com.lanternsoftware.util.dao.mongo.MongoConfig; +import com.lanternsoftware.util.external.LanternFiles; import com.lanternsoftware.util.http.HttpPool; import com.lanternsoftware.zwave.controller.Controller; import com.lanternsoftware.zwave.dao.MongoZWaveDao; @@ -54,6 +54,8 @@ import java.util.Set; import java.util.TimeZone; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public class ZWaveApp { public static final AESTool aes = AESTool.authTool(); @@ -75,11 +77,13 @@ public class ZWaveApp { private SwitchScheduleTask nextScheduleTask; private final Map sensors = new HashMap<>(); private final Object ZWAVE_MUTEX = new Object(); + private ExecutorService executor = null; public void start() { try { pool = new HttpPool(100, 20, 5000, 5000, 5000); config = DaoSerializer.parse(ResourceLoader.loadFile(LanternFiles.CONFIG_PATH + "config.json"), ZWaveConfig.class); + executor = Executors.newFixedThreadPool(5); if (config == null) { dao = new MongoZWaveDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg")); config = dao.getConfig(1); @@ -202,6 +206,7 @@ public class ZWaveApp { @Override public void onMessage(MultilevelSwitchReportRequest _message) { + logger.info("Received MultilevelSwitchReportRequest"); onSwitchLevelChange(_message.getNodeId(), _message.getLevel()); } }); @@ -214,6 +219,7 @@ public class ZWaveApp { @Override public void onMessage(BinarySwitchReportRequest _message) { + logger.info("Received BinarySwitchReportRequest"); onSwitchLevelChange(_message.getNodeId(), _message.getLevel()); } }); @@ -226,10 +232,18 @@ public class ZWaveApp { @Override public void onMessage(CRC16EncapRequest _message) { - onSwitchLevelChange(_message.getNodeId(), _message.isOn()?0xFF:0); +// logger.info("Received CRC16EncapRequest"); +// onSwitchLevelChange(_message.getNodeId(), _message.isOn()?0xFF:0); } }); +// for (Switch sw : config.getSwitches()) { +// if (sw.getNodeId() < 255) { +// controller.send(new NodeNeighborUpdateRequest(sw.getNodeId())); +// ConcurrencyUtils.sleep(5000); +// } +// } + // controller.send(new MultilevelSwitchSetRequest((byte)2, 0xFF)); // controller.send(new MultilevelSensorGetRequest((byte)11)); @@ -244,27 +258,31 @@ public class ZWaveApp { // controller.send(new ThermostatModeGetRequest((byte)11)); } - private void onSwitchLevelChange(int _secondaryNodeId, int _primaryLevel) { + private void onSwitchLevelChange(int _nodeId, int _level) { synchronized (switches) { - Switch sw = switches.get(_secondaryNodeId); - if ((sw != null) && !sw.isPrimary()) { - int newLevel = sw.isMultilevel()?_primaryLevel:((_primaryLevel == 0)?0:99); + Switch sw = switches.get(_nodeId); + if (sw != null) { + logger.info("Received level change for node {} to level {} via z-wave", _nodeId, _level); + if (_level == -1) + _level = 255; + int newLevel = sw.isMultilevel()?NullUtils.bound(_level, 0, 99):((_level == 0)?0:99); sw.setLevel(newLevel); fireSwitchLevelEvent(sw); - for (Switch peer : CollectionUtils.makeNotNull(peers.get(_secondaryNodeId))) { - if (peer.isPrimary()) { - logger.info("Mirror Event from node {} to node {}", _secondaryNodeId, peer.getNodeId()); - if (peer.isMultilevel()) { - peer.setLevel(newLevel); - controller.send(new MultilevelSwitchSetRequest((byte) peer.getNodeId(), newLevel)); - } else { - peer.setLevel(newLevel > 0 ? 0xff : 0); - controller.send(new BinarySwitchSetRequest((byte) peer.getNodeId(), newLevel > 0)); - } + for (Switch peer : CollectionUtils.makeNotNull(peers.get(_nodeId))) { + logger.info("Mirror Event from {} node {} to {} node {} level {}", sw.isPrimary() ? "primary" : "secondary", _nodeId, peer.isPrimary() ? "primary" : "secondary", peer.getNodeId(), newLevel); + if (peer.isMultilevel()) { + peer.setLevel(newLevel); + controller.send(new MultilevelSwitchSetRequest((byte) peer.getNodeId(), newLevel)); + } else { + peer.setLevel(newLevel != 0 ? 0xff : 0); + controller.send(new BinarySwitchSetRequest((byte) peer.getNodeId(), newLevel != 0)); } } persistConfig(); } + else { + logger.info("Received level change for unknown node {}", _nodeId); + } } } @@ -294,18 +312,20 @@ public class ZWaveApp { public void fireSwitchLevelEvent(Switch _sw) { if (NullUtils.isEmpty(config.getRulesUrl()) || _sw.isSuppressEvents()) return; - Event event = new Event(); - event.setEventDescription(_sw.getFullDisplay() + " set to " + _sw.getLevel()); - event.setType(EventType.SWITCH_LEVEL); - event.setTime(new Date()); - event.setValue(_sw.getLevel()); - event.setSourceId(String.valueOf(_sw.getNodeId())); - event.setAccountId(config.getAccountId()); - logger.info("Sending event to rules server - " + event.getEventDescription()); - HttpPost post = new HttpPost(NullUtils.terminateWith(config.getRulesUrl(), "/") + "event"); - post.setHeader("auth_code", authCode); - post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(event))); - pool.execute(post); + executor.submit(()->{ + Event event = new Event(); + event.setEventDescription(_sw.getFullDisplay() + " set to " + _sw.getLevel()); + event.setType(EventType.SWITCH_LEVEL); + event.setTime(new Date()); + event.setValue(_sw.getLevel()); + event.setSourceId(String.valueOf(_sw.getNodeId())); + event.setAccountId(config.getAccountId()); + logger.info("Sending event to rules server - " + event.getEventDescription()); + HttpPost post = new HttpPost(NullUtils.terminateWith(config.getRulesUrl(), "/") + "event"); + post.setHeader("auth_code", authCode); + post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(event))); + pool.execute(post); + }); } public void setSwitchLevel(int _nodeId, int _level, boolean _updatePeers) { @@ -401,11 +421,13 @@ public class ZWaveApp { peers.remove(config.getUrl()); for (String peer : peers) { for (Switch sw : modified) { - logger.info("Sending update for switch {} {} level {} to {}", sw.getNodeId(), sw.getFullDisplay(), sw.getLevel(), peer); - HttpPost post = new HttpPost(peer + "/switch/" + sw.getNodeId()); - post.setHeader("auth_code", authCode); - post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(sw))); - pool.execute(post); + executor.submit(()->{ + logger.info("Sending update for switch {} {} level {} to {}", sw.getNodeId(), sw.getFullDisplay(), sw.getLevel(), peer); + HttpPost post = new HttpPost(peer + "/switch/" + sw.getNodeId()); + post.setHeader("auth_code", authCode); + post.setEntity(new ByteArrayEntity(DaoSerializer.toZipBson(sw))); + pool.execute(post); + }); } } } @@ -441,6 +463,10 @@ public class ZWaveApp { pool.shutdown(); pool = null; } + if (executor != null) { + executor.shutdown(); + executor = null; + } if (dao != null) { dao.shutdown(); dao = null; @@ -455,7 +481,7 @@ public class ZWaveApp { for (Switch node : nodes) { logger.info("Setting {}, Node {} to {}", node.getName(), node.getNodeId(), _level); byte nid = (byte) (node.getNodeId()%1000); - controller.send(node.isMultilevel() ? new MultilevelSwitchSetRequest(nid, _level) : new BinarySwitchSetRequest(nid, _level > 0)); + controller.send(node.isMultilevel() ? new MultilevelSwitchSetRequest(nid, _level) : new BinarySwitchSetRequest(nid, _level != 0)); } } diff --git a/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/message/impl/BinarySwitchReportRequest.java b/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/message/impl/BinarySwitchReportRequest.java index 9b6feef..3ded94c 100644 --- a/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/message/impl/BinarySwitchReportRequest.java +++ b/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/message/impl/BinarySwitchReportRequest.java @@ -14,7 +14,7 @@ public class BinarySwitchReportRequest extends RequestMessage { @Override public void fromPayload(byte[] _payload) { nodeId = _payload[5]; - level = _payload[9]; + level = 0xFF & _payload[9]; } public int getLevel() { diff --git a/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/message/impl/NodeNeighborUpdateRequest.java b/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/message/impl/NodeNeighborUpdateRequest.java new file mode 100644 index 0000000..6b4e320 --- /dev/null +++ b/zwave/lantern-zwave/src/main/java/com/lanternsoftware/zwave/message/impl/NodeNeighborUpdateRequest.java @@ -0,0 +1,15 @@ +package com.lanternsoftware.zwave.message.impl; + +import com.lanternsoftware.zwave.message.CommandClass; +import com.lanternsoftware.zwave.message.ControllerMessageType; +import com.lanternsoftware.zwave.message.RequestMessage; + +public class NodeNeighborUpdateRequest extends RequestMessage { + public NodeNeighborUpdateRequest() { + super(ControllerMessageType.RequestNodeNeighborUpdate, CommandClass.NO_OPERATION, (byte) 0); + } + + public NodeNeighborUpdateRequest(int _nodeId) { + super((byte) _nodeId, ControllerMessageType.RequestNodeNeighborUpdate, CommandClass.NO_OPERATION, (byte) 0); + } +} diff --git a/zwave/lantern-zwave/src/main/resources/META-INF/services/com.lanternsoftware.zwave.message.Message b/zwave/lantern-zwave/src/main/resources/META-INF/services/com.lanternsoftware.zwave.message.Message index 11fb745..3abbeda 100644 --- a/zwave/lantern-zwave/src/main/resources/META-INF/services/com.lanternsoftware.zwave.message.Message +++ b/zwave/lantern-zwave/src/main/resources/META-INF/services/com.lanternsoftware.zwave.message.Message @@ -21,6 +21,7 @@ com.lanternsoftware.zwave.message.impl.MultilevelSwitchReportRequest com.lanternsoftware.zwave.message.impl.MultilevelSwitchSetRequest com.lanternsoftware.zwave.message.impl.NodeInfoRequest com.lanternsoftware.zwave.message.impl.NodeInfoResponse +com.lanternsoftware.zwave.message.impl.NodeNeighborUpdateRequest com.lanternsoftware.zwave.message.impl.RemoveNodeFromNetworkStartRequest com.lanternsoftware.zwave.message.impl.RemoveNodeFromNetworkStopRequest com.lanternsoftware.zwave.message.impl.SendDataRequest