diff --git a/currentmonitor/lantern-currentmonitor/pom.xml b/currentmonitor/lantern-currentmonitor/pom.xml index c21dc13..e2b3ff7 100644 --- a/currentmonitor/lantern-currentmonitor/pom.xml +++ b/currentmonitor/lantern-currentmonitor/pom.xml @@ -3,7 +3,7 @@ com.lanternsoftware.currentmonitor lantern-currentmonitor jar - 1.0.6 + 1.0.7 lantern-currentmonitor diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/CalibrationResult.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/CalibrationResult.java new file mode 100644 index 0000000..4b5ac4d --- /dev/null +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/CalibrationResult.java @@ -0,0 +1,19 @@ +package com.lanternsoftware.currentmonitor; + +public class CalibrationResult { + private final double voltageCalibrationFactor; + private final int frequency; + + public CalibrationResult(double _voltageCalibrationFactor, int _frequency) { + voltageCalibrationFactor = _voltageCalibrationFactor; + frequency = _frequency; + } + + public double getVoltageCalibrationFactor() { + return voltageCalibrationFactor; + } + + public int getFrequency() { + return frequency; + } +} diff --git a/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/CalibrationSample.java b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/CalibrationSample.java new file mode 100644 index 0000000..6596fb4 --- /dev/null +++ b/currentmonitor/lantern-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/CalibrationSample.java @@ -0,0 +1,6 @@ +package com.lanternsoftware.currentmonitor; + +public class CalibrationSample { + public long time; + public double voltage; +} 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 4ea7013..21cfba6 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 @@ -56,44 +56,71 @@ public class CurrentMonitor { chips.clear(); pins.clear(); gpio.shutdown(); - LOG.info("Current Monitor Stopped"); + LOG.info("Power Monitor Service Stopped"); } public void setDebug(boolean _debug) { debug = _debug; } - public double calibrateVoltage(double _curCalibration, float _voltage) { + public CalibrationResult calibrateVoltage(double _curCalibration) { GpioPinAnalogInput voltagePin = getPin(0, 0); if (voltagePin == null) - return 0.0; - List samples = new ArrayList<>(120000); + return null; + int maxSamples = 120000; + CalibrationSample[] samples = new CalibrationSample[maxSamples]; + int offset = 0; + for (;offset < maxSamples; offset++) { + samples[offset] = new CalibrationSample(); + } + offset = 0; long intervalEnd = System.nanoTime() + 2000000000L; //Scan voltage for 2 seconds - while (System.nanoTime() < intervalEnd) { - samples.add(voltagePin.getValue()); + while (offset < maxSamples) { + samples[offset].time = System.nanoTime(); + samples[offset].voltage = voltagePin.getValue(); + offset++; + if (samples[offset-1].time > intervalEnd) + break; } - double vOffset = 0.0; - for (Double sample : samples) { - vOffset += sample; + for (CalibrationSample sample : samples) { + vOffset += sample.voltage; } - vOffset /= samples.size(); + vOffset /= offset; + int cycles = 0; + boolean under = true; + if (samples[0].voltage > (vOffset * 1.3)) { + cycles = 1; + under = false; + } + double voltage; double vRms = 0.0; - for (Double sample : samples) { - sample -= vOffset; - vRms += sample * sample; + for (int sample = 0; sample < offset; sample++) { + voltage = samples[sample].voltage - vOffset; + vRms += voltage * voltage; + if (under && (samples[sample].voltage > (vOffset * 1.3))) { + cycles += 1; + under = false; + } + else if (samples[sample].voltage < vOffset * 0.7) { + under = true; + } } - vRms /= samples.size(); + vRms /= offset; + double oldVrms = _curCalibration * Math.sqrt(vRms); if (oldVrms < 20) { LOG.error("Could not get a valid voltage read, please check that your AC/AC transformer is connected"); - return 0.0; + return null; } - double newCal = (_voltage/oldVrms) * _curCalibration; + int frequency = Math.round(cycles/((samples[offset-1].time-samples[0].time)/100000000f))*10; + LOG.info("Detected Frequency: " + frequency); + + double newCal = ((frequency > 55 ? 120:230)/oldVrms) * _curCalibration; double newVrms = newCal * Math.sqrt(vRms); LOG.info("Old Voltage Calibration: {} Old vRMS: {}", _curCalibration, oldVrms); LOG.info("New Voltage Calibration: {} New vRMS: {}", newCal, newVrms); - return newCal; + return new CalibrationResult(newCal, frequency); } public void monitorPower(BreakerHub _hub, List _breakers, int _intervalMs, PowerListener _listener) { @@ -101,6 +128,9 @@ public class CurrentMonitor { stopMonitoring(); listener = _listener; List validBreakers = CollectionUtils.filter(_breakers, _b -> _b.getPort() > 0 && _b.getPort() < 16); + if (CollectionUtils.isEmpty(validBreakers)) + return; + LOG.info("Monitoring {} breakers for hub {}", CollectionUtils.size(validBreakers), _hub.getHub()); sampler = new Sampler(_hub, validBreakers, _intervalMs, 2); LOG.info("Starting to monitor ports {}", CollectionUtils.transformToCommaSeparated(validBreakers, _b -> String.valueOf(_b.getPort()))); executor.submit(sampler); @@ -188,7 +218,7 @@ public class CurrentMonitor { while (true) { synchronized (this) { if (!running) { - LOG.error("Power Monitoring Stopped"); + LOG.info("Power Monitoring Stopped"); break; } } 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 ab70184..263dcd8 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 @@ -164,11 +164,8 @@ public class MonitorApp { breakerConfig = newConfig; List 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); - } + if (hub != null) + monitor.monitorPower(hub, breakers, 1000, logger); } break; } @@ -270,19 +267,18 @@ public class MonitorApp { LOG.info("Breaker Config loaded"); BreakerHub hub = breakerConfig.getHub(config.getHub()); if (hub != null) { - if (config.isNeedsCalibration() && (config.getAutoCalibrationVoltage() != 0.0)) { - double newCal = monitor.calibrateVoltage(hub.getVoltageCalibrationFactor(), config.getAutoCalibrationVoltage()); - if (newCal != 0.0) { - hub.setVoltageCalibrationFactor(newCal); + if (config.isNeedsCalibration()) { + CalibrationResult cal = monitor.calibrateVoltage(hub.getVoltageCalibrationFactor()); + if (cal != null) { + hub.setVoltageCalibrationFactor(cal.getVoltageCalibrationFactor()); + hub.setFrequency(cal.getFrequency()); config.setNeedsCalibration(false); ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config)); post(DaoSerializer.toZipBson(breakerConfig), "config"); } } List breakers = breakerConfig.getBreakersForHub(config.getHub()); - LOG.info("Monitoring {} breakers for hub {}", CollectionUtils.size(breakers), hub.getHub()); - if (CollectionUtils.size(breakers) > 0) - monitor.monitorPower(hub, breakers, 1000, logger); + monitor.monitorPower(hub, breakers, 1000, logger); } monitor.submit(new PowerPoster()); } 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 d12a9c7..473997d 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 @@ -187,7 +187,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao { for (int offset = 0; offset < bytesInDay; offset += 4) { nanBuffer.putFloat(offset, Float.NaN); } - for (int key : breakerKeys.keySet()) { + for (int key : breakerKeys.values()) { dayReadings.computeIfAbsent(key, _k->nanArray); } for (Entry be : dayReadings.entrySet()) { diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerConfig.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerConfig.java index 9e97ec4..8748512 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerConfig.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/BreakerConfig.java @@ -185,6 +185,14 @@ public class BreakerConfig implements IIdentical { return null; } + public Meter getMeterForHub(int _hub) { + Meter m = null; + Breaker b = CollectionUtils.filterOne(getAllBreakers(), _b->_b.getHub() == _hub); + if (b != null) + m = CollectionUtils.filterOne(meters, _m->_m.getIndex() == b.getMeter()); + return (m != null) ? m : new Meter(getAccountId(), 0, "Main"); + } + public BreakerGroup findParentGroup(BreakerGroup _group) { for (BreakerGroup group : CollectionUtils.makeNotNull(breakerGroups)) { BreakerGroup parent = group.findParentGroup(_group); diff --git a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/Meter.java b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/Meter.java index 76392d2..53e0335 100644 --- a/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/Meter.java +++ b/currentmonitor/lantern-datamodel-currentmonitor/src/main/java/com/lanternsoftware/datamodel/currentmonitor/Meter.java @@ -11,6 +11,15 @@ public class Meter implements IIdentical { private int index; private String name; + public Meter() { + } + + public Meter(int _accountId, int _index, String _name) { + accountId = _accountId; + index = _index; + name = _name; + } + public int getAccountId() { return accountId; } diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/ExportServlet.java b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/ExportServlet.java index ff35712..f45c17e 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/ExportServlet.java +++ b/currentmonitor/lantern-service-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/servlet/console/ExportServlet.java @@ -75,15 +75,16 @@ public class ExportServlet extends SecureConsoleServlet { redirect(_rep, _req.getContextPath() + "/export"); return; } - StringBuilder header = new StringBuilder("Timestamp"); + StringBuilder header = new StringBuilder("\"Timestamp\""); for (BreakerEnergyArchive ba : CollectionUtils.makeNotNull(fday.getBreakers())) { Breaker b = breakers.get(Breaker.intKey(ba.getPanel(), ba.getSpace())); - header.append(","); + header.append(",\""); if (b != null) { header.append(b.getKey()); header.append("-"); header.append(b.getName()); } + header.append("\""); } header.append("\n"); DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/resources/templates/export.ftl b/currentmonitor/lantern-service-currentmonitor/src/main/resources/templates/export.ftl index fa58307..dd9a577 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/resources/templates/export.ftl +++ b/currentmonitor/lantern-service-currentmonitor/src/main/resources/templates/export.ftl @@ -1,50 +1,36 @@ - - - - - - - <#if inprogress> - Lantern Console - - - - - diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/resources/templates/frame.ftl b/currentmonitor/lantern-service-currentmonitor/src/main/resources/templates/frame.ftl index 742da83..e28421b 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/resources/templates/frame.ftl +++ b/currentmonitor/lantern-service-currentmonitor/src/main/resources/templates/frame.ftl @@ -4,9 +4,7 @@ - - - + <#if inprogress!> Lantern Power Monitor @@ -17,7 +15,7 @@
-
Logo
+
diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/style.css b/currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/style.css index e426bfb..9e04d40 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/style.css +++ b/currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/style.css @@ -226,4 +226,8 @@ .error { color: darkred; +} + +.header-logo { + height: calc(1.5rem + .8vw) } \ No newline at end of file diff --git a/currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/style.min.css b/currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/style.min.css index 45467ad..b11968c 100644 --- a/currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/style.min.css +++ b/currentmonitor/lantern-service-currentmonitor/src/main/webapp/bootstrap/css/style.min.css @@ -1 +1 @@ -.scrollarea{overflow-y:auto}.fw-semibold{font-weight:600}.lh-tight{line-height:1.25}.menu_link{text-decoration:none!important}.menu_subitem{color:#404040}.menu_item,.menu_subitem:focus,.menu_subitem:hover{background-color:#1a9acc;color:#f0f0f0}.lpm_menu{z-index:1;background:#fff;min-height:100%;max-height:100%}#toggle_label{margin-top:.5rem;display:block;width:2rem;position:absolute;z-index:10}.blue,.title{color:#1a9acc}.kill{color:red}.max-w-300{max-width:min(100%,300px)}.max-w-400{max-width:min(100%,400px)}.max-w-500{max-width:min(100%,500px)}.max-w-600{max-width:min(100%,600px)}.max-w-700{max-width:min(100%,700px)}.max-w-800{max-width:min(100%,800px)}.max-w-900{max-width:min(100%,900px)}.max-w-1000{max-width:min(100%,1000px)}.max-w-1200{max-width:min(100%,1200px)}.align-col-right{display:flex!important;justify-content:flex-end!important;padding-right:1vw!important}.align-col-left{display:flex!important;justify-content:flex-start!important;padding-left:1vw!important}.title{font-size:2em}.sub-title{font-size:1.1em}@media (max-width:991px){#menu_toggle:checked~#toggle_label{position:fixed!important}.lpm_menu{display:none!important}#menu_toggle:checked~.lpm_menu{display:block!important}#menu_toggle:checked~.lpm_body{display:block!important;width:50%}}@media (min-width:992px){.hsy{margin-top:2.3rem!important}}@media (min-width:3000px){#menu_toggle:checked~.lpm_menu{display:block!important}#menu_toggle:checked~.lpm_body{display:block!important;width:50%}}.fm1{font-size:calc(1rem + 1.25vw)}.fm2{font-size:calc(.7rem + 1vw)}.fm3{font-size:calc(.8rem + .5vw)}.fmnu1{font-size:calc(1rem + .5vw)}.fmnu2{font-size:calc(.8rem + .5vw)}.login-bkgnd,.login-container{z-index:2;position:absolute}.login-bkgnd{z-index:1;width:100%;height:100%;top:0;left:0;background-image:url(../../img/pcb.png);background-size:cover;background-position:center}.login-input{width:100%}::placeholder{color:#a0a0a0;opacity:1}.btn-primary{background-color:#1a9acc;border-color:#1a9acc}.btn-primary:focus,.btn-primary:hover{background-color:#20c0ff;border-color:#20c0ff}.header-menu{background-color:#1a9acc}.header-link{color:#f0f0f0}.header-link:hover{color:#d0d0d0}.gso{border-style:none;width:10rem;height:2.5rem;background-image:url(../../img/gso.png);background-position:center;background-size:cover}.gso,.gso:hover{background-color:#0000}.gso:focus{background-image:url(../../img/gso_pressed.png)}.border-none{border-style:none}.error{color:#8b0000} \ No newline at end of file +.scrollarea{overflow-y:auto}.fw-semibold{font-weight:600}.lh-tight{line-height:1.25}.menu_link{text-decoration:none!important}.menu_subitem{color:#404040}.menu_item,.menu_subitem:focus,.menu_subitem:hover{background-color:#1a9acc;color:#f0f0f0}.lpm_menu{z-index:1;background:#fff;min-height:100%;max-height:100%}#toggle_label{margin-top:.5rem;display:block;width:2rem;position:absolute;z-index:10}.blue,.title{color:#1a9acc}.kill{color:red}.max-w-300{max-width:min(100%,300px)}.max-w-400{max-width:min(100%,400px)}.max-w-500{max-width:min(100%,500px)}.max-w-600{max-width:min(100%,600px)}.max-w-700{max-width:min(100%,700px)}.max-w-800{max-width:min(100%,800px)}.max-w-900{max-width:min(100%,900px)}.max-w-1000{max-width:min(100%,1000px)}.max-w-1200{max-width:min(100%,1200px)}.align-col-right{display:flex!important;justify-content:flex-end!important;padding-right:1vw!important}.align-col-left{display:flex!important;justify-content:flex-start!important;padding-left:1vw!important}.title{font-size:2em}.sub-title{font-size:1.1em}@media (max-width:991px){#menu_toggle:checked~#toggle_label{position:fixed!important}.lpm_menu{display:none!important}#menu_toggle:checked~.lpm_menu{display:block!important}#menu_toggle:checked~.lpm_body{display:block!important;width:50%}}@media (min-width:992px){.hsy{margin-top:2.3rem!important}}@media (min-width:3000px){#menu_toggle:checked~.lpm_menu{display:block!important}#menu_toggle:checked~.lpm_body{display:block!important;width:50%}}.fm1{font-size:calc(1rem + 1.25vw)}.fm2{font-size:calc(.7rem + 1vw)}.fm3{font-size:calc(.8rem + .5vw)}.fmnu1{font-size:calc(1rem + .5vw)}.fmnu2{font-size:calc(.8rem + .5vw)}.login-bkgnd,.login-container{z-index:2;position:absolute}.login-bkgnd{z-index:1;width:100%;height:100%;top:0;left:0;background-image:url(../../img/pcb.png);background-size:cover;background-position:center}.login-input{width:100%}::placeholder{color:#a0a0a0;opacity:1}.btn-primary{background-color:#1a9acc;border-color:#1a9acc}.btn-primary:focus,.btn-primary:hover{background-color:#20c0ff;border-color:#20c0ff}.header-menu{background-color:#1a9acc}.header-link{color:#f0f0f0}.header-link:hover{color:#d0d0d0}.gso{border-style:none;width:10rem;height:2.5rem;background-image:url(../../img/gso.png);background-position:center;background-size:cover}.gso,.gso:hover{background-color:#0000}.gso:focus{background-image:url(../../img/gso_pressed.png)}.border-none{border-style:none}.error{color:#8b0000}.header-logo{height:calc(1.5rem + .8vw)} \ No newline at end of file