mirror of
https://github.com/zyphlar/LanternPowerMonitor.git
synced 2024-03-08 14:07:47 +00:00
Auto-detect frequency on first hub startup. If frequency is 50Hz, assume 230V. (This should work for 95% of cases)
This commit is contained in:
parent
94ebf5fa93
commit
a892c7f0e8
|
@ -3,7 +3,7 @@
|
||||||
<groupId>com.lanternsoftware.currentmonitor</groupId>
|
<groupId>com.lanternsoftware.currentmonitor</groupId>
|
||||||
<artifactId>lantern-currentmonitor</artifactId>
|
<artifactId>lantern-currentmonitor</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<version>1.0.6</version>
|
<version>1.0.7</version>
|
||||||
<name>lantern-currentmonitor</name>
|
<name>lantern-currentmonitor</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.lanternsoftware.currentmonitor;
|
||||||
|
|
||||||
|
public class CalibrationSample {
|
||||||
|
public long time;
|
||||||
|
public double voltage;
|
||||||
|
}
|
|
@ -56,44 +56,71 @@ public class CurrentMonitor {
|
||||||
chips.clear();
|
chips.clear();
|
||||||
pins.clear();
|
pins.clear();
|
||||||
gpio.shutdown();
|
gpio.shutdown();
|
||||||
LOG.info("Current Monitor Stopped");
|
LOG.info("Power Monitor Service Stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDebug(boolean _debug) {
|
public void setDebug(boolean _debug) {
|
||||||
debug = _debug;
|
debug = _debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double calibrateVoltage(double _curCalibration, float _voltage) {
|
public CalibrationResult calibrateVoltage(double _curCalibration) {
|
||||||
GpioPinAnalogInput voltagePin = getPin(0, 0);
|
GpioPinAnalogInput voltagePin = getPin(0, 0);
|
||||||
if (voltagePin == null)
|
if (voltagePin == null)
|
||||||
return 0.0;
|
return null;
|
||||||
List<Double> samples = new ArrayList<>(120000);
|
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
|
long intervalEnd = System.nanoTime() + 2000000000L; //Scan voltage for 2 seconds
|
||||||
while (System.nanoTime() < intervalEnd) {
|
while (offset < maxSamples) {
|
||||||
samples.add(voltagePin.getValue());
|
samples[offset].time = System.nanoTime();
|
||||||
|
samples[offset].voltage = voltagePin.getValue();
|
||||||
|
offset++;
|
||||||
|
if (samples[offset-1].time > intervalEnd)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
double vOffset = 0.0;
|
double vOffset = 0.0;
|
||||||
for (Double sample : samples) {
|
for (CalibrationSample sample : samples) {
|
||||||
vOffset += sample;
|
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;
|
double vRms = 0.0;
|
||||||
for (Double sample : samples) {
|
for (int sample = 0; sample < offset; sample++) {
|
||||||
sample -= vOffset;
|
voltage = samples[sample].voltage - vOffset;
|
||||||
vRms += sample * sample;
|
vRms += voltage * voltage;
|
||||||
|
if (under && (samples[sample].voltage > (vOffset * 1.3))) {
|
||||||
|
cycles += 1;
|
||||||
|
under = false;
|
||||||
}
|
}
|
||||||
vRms /= samples.size();
|
else if (samples[sample].voltage < vOffset * 0.7) {
|
||||||
|
under = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vRms /= offset;
|
||||||
|
|
||||||
double oldVrms = _curCalibration * Math.sqrt(vRms);
|
double oldVrms = _curCalibration * Math.sqrt(vRms);
|
||||||
if (oldVrms < 20) {
|
if (oldVrms < 20) {
|
||||||
LOG.error("Could not get a valid voltage read, please check that your AC/AC transformer is connected");
|
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);
|
double newVrms = newCal * Math.sqrt(vRms);
|
||||||
LOG.info("Old Voltage Calibration: {} Old vRMS: {}", _curCalibration, oldVrms);
|
LOG.info("Old Voltage Calibration: {} Old vRMS: {}", _curCalibration, oldVrms);
|
||||||
LOG.info("New Voltage Calibration: {} New vRMS: {}", newCal, newVrms);
|
LOG.info("New Voltage Calibration: {} New vRMS: {}", newCal, newVrms);
|
||||||
return newCal;
|
return new CalibrationResult(newCal, frequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void monitorPower(BreakerHub _hub, List<Breaker> _breakers, int _intervalMs, PowerListener _listener) {
|
public void monitorPower(BreakerHub _hub, List<Breaker> _breakers, int _intervalMs, PowerListener _listener) {
|
||||||
|
@ -101,6 +128,9 @@ public class CurrentMonitor {
|
||||||
stopMonitoring();
|
stopMonitoring();
|
||||||
listener = _listener;
|
listener = _listener;
|
||||||
List<Breaker> validBreakers = CollectionUtils.filter(_breakers, _b -> _b.getPort() > 0 && _b.getPort() < 16);
|
List<Breaker> validBreakers = CollectionUtils.filter(_breakers, _b -> _b.getPort() > 0 && _b.getPort() < 16);
|
||||||
|
if (CollectionUtils.isEmpty(validBreakers))
|
||||||
|
return;
|
||||||
|
LOG.info("Monitoring {} breakers for hub {}", CollectionUtils.size(validBreakers), _hub.getHub());
|
||||||
sampler = new Sampler(_hub, validBreakers, _intervalMs, 2);
|
sampler = new Sampler(_hub, validBreakers, _intervalMs, 2);
|
||||||
LOG.info("Starting to monitor ports {}", CollectionUtils.transformToCommaSeparated(validBreakers, _b -> String.valueOf(_b.getPort())));
|
LOG.info("Starting to monitor ports {}", CollectionUtils.transformToCommaSeparated(validBreakers, _b -> String.valueOf(_b.getPort())));
|
||||||
executor.submit(sampler);
|
executor.submit(sampler);
|
||||||
|
@ -188,7 +218,7 @@ public class CurrentMonitor {
|
||||||
while (true) {
|
while (true) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!running) {
|
if (!running) {
|
||||||
LOG.error("Power Monitoring Stopped");
|
LOG.info("Power Monitoring Stopped");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,12 +164,9 @@ public class MonitorApp {
|
||||||
breakerConfig = newConfig;
|
breakerConfig = newConfig;
|
||||||
List<Breaker> breakers = breakerConfig.getBreakersForHub(config.getHub());
|
List<Breaker> breakers = breakerConfig.getBreakersForHub(config.getHub());
|
||||||
BreakerHub hub = breakerConfig.getHub(config.getHub());
|
BreakerHub hub = breakerConfig.getHub(config.getHub());
|
||||||
if (hub != null) {
|
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);
|
monitor.monitorPower(hub, breakers, 1000, logger);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -270,18 +267,17 @@ public class MonitorApp {
|
||||||
LOG.info("Breaker Config loaded");
|
LOG.info("Breaker Config loaded");
|
||||||
BreakerHub hub = breakerConfig.getHub(config.getHub());
|
BreakerHub hub = breakerConfig.getHub(config.getHub());
|
||||||
if (hub != null) {
|
if (hub != null) {
|
||||||
if (config.isNeedsCalibration() && (config.getAutoCalibrationVoltage() != 0.0)) {
|
if (config.isNeedsCalibration()) {
|
||||||
double newCal = monitor.calibrateVoltage(hub.getVoltageCalibrationFactor(), config.getAutoCalibrationVoltage());
|
CalibrationResult cal = monitor.calibrateVoltage(hub.getVoltageCalibrationFactor());
|
||||||
if (newCal != 0.0) {
|
if (cal != null) {
|
||||||
hub.setVoltageCalibrationFactor(newCal);
|
hub.setVoltageCalibrationFactor(cal.getVoltageCalibrationFactor());
|
||||||
|
hub.setFrequency(cal.getFrequency());
|
||||||
config.setNeedsCalibration(false);
|
config.setNeedsCalibration(false);
|
||||||
ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config));
|
ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config));
|
||||||
post(DaoSerializer.toZipBson(breakerConfig), "config");
|
post(DaoSerializer.toZipBson(breakerConfig), "config");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<Breaker> breakers = breakerConfig.getBreakersForHub(config.getHub());
|
List<Breaker> 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());
|
monitor.submit(new PowerPoster());
|
||||||
|
|
|
@ -187,7 +187,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
|
||||||
for (int offset = 0; offset < bytesInDay; offset += 4) {
|
for (int offset = 0; offset < bytesInDay; offset += 4) {
|
||||||
nanBuffer.putFloat(offset, Float.NaN);
|
nanBuffer.putFloat(offset, Float.NaN);
|
||||||
}
|
}
|
||||||
for (int key : breakerKeys.keySet()) {
|
for (int key : breakerKeys.values()) {
|
||||||
dayReadings.computeIfAbsent(key, _k->nanArray);
|
dayReadings.computeIfAbsent(key, _k->nanArray);
|
||||||
}
|
}
|
||||||
for (Entry<Integer, byte[]> be : dayReadings.entrySet()) {
|
for (Entry<Integer, byte[]> be : dayReadings.entrySet()) {
|
||||||
|
|
|
@ -185,6 +185,14 @@ public class BreakerConfig implements IIdentical<BreakerConfig> {
|
||||||
return null;
|
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) {
|
public BreakerGroup findParentGroup(BreakerGroup _group) {
|
||||||
for (BreakerGroup group : CollectionUtils.makeNotNull(breakerGroups)) {
|
for (BreakerGroup group : CollectionUtils.makeNotNull(breakerGroups)) {
|
||||||
BreakerGroup parent = group.findParentGroup(_group);
|
BreakerGroup parent = group.findParentGroup(_group);
|
||||||
|
|
|
@ -11,6 +11,15 @@ public class Meter implements IIdentical<Meter> {
|
||||||
private int index;
|
private int index;
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
public Meter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Meter(int _accountId, int _index, String _name) {
|
||||||
|
accountId = _accountId;
|
||||||
|
index = _index;
|
||||||
|
name = _name;
|
||||||
|
}
|
||||||
|
|
||||||
public int getAccountId() {
|
public int getAccountId() {
|
||||||
return accountId;
|
return accountId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,15 +75,16 @@ public class ExportServlet extends SecureConsoleServlet {
|
||||||
redirect(_rep, _req.getContextPath() + "/export");
|
redirect(_rep, _req.getContextPath() + "/export");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StringBuilder header = new StringBuilder("Timestamp");
|
StringBuilder header = new StringBuilder("\"Timestamp\"");
|
||||||
for (BreakerEnergyArchive ba : CollectionUtils.makeNotNull(fday.getBreakers())) {
|
for (BreakerEnergyArchive ba : CollectionUtils.makeNotNull(fday.getBreakers())) {
|
||||||
Breaker b = breakers.get(Breaker.intKey(ba.getPanel(), ba.getSpace()));
|
Breaker b = breakers.get(Breaker.intKey(ba.getPanel(), ba.getSpace()));
|
||||||
header.append(",");
|
header.append(",\"");
|
||||||
if (b != null) {
|
if (b != null) {
|
||||||
header.append(b.getKey());
|
header.append(b.getKey());
|
||||||
header.append("-");
|
header.append("-");
|
||||||
header.append(b.getName());
|
header.append(b.getName());
|
||||||
}
|
}
|
||||||
|
header.append("\"");
|
||||||
}
|
}
|
||||||
header.append("\n");
|
header.append("\n");
|
||||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||||
|
|
|
@ -1,16 +1,3 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<#if inprogress><meta http-equiv="refresh" content="1"></#if>
|
|
||||||
<title>Lantern Console</title>
|
|
||||||
<link href="${link_prefix}bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="${link_prefix}bootstrap/css/style.min.css" rel="stylesheet">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container-fluid login-container">
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-1 col-lg-3 col-4k-4"></div>
|
<div class="col-1 col-lg-3 col-4k-4"></div>
|
||||||
<div class="col-10 col-lg-6 col-4k-4">
|
<div class="col-10 col-lg-6 col-4k-4">
|
||||||
|
@ -44,7 +31,6 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="col-1 col-lg-3 col-4k-4"></div>
|
<div class="col-1 col-lg-3 col-4k-4"></div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -4,9 +4,7 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="description" content="">
|
<#if inprogress!><meta http-equiv="refresh" content="1"></#if>
|
||||||
<meta name="author" content="">
|
|
||||||
|
|
||||||
<title>Lantern Power Monitor</title>
|
<title>Lantern Power Monitor</title>
|
||||||
<link rel="icon" type="image/png" href="${link_prefix}img/favicon.png">
|
<link rel="icon" type="image/png" href="${link_prefix}img/favicon.png">
|
||||||
<link href="${link_prefix}bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
<link href="${link_prefix}bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
@ -17,7 +15,7 @@
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row header-menu py-2">
|
<div class="row header-menu py-2">
|
||||||
<div class="col-2"></div>
|
<div class="col-2"></div>
|
||||||
<div class="col-auto"><img class="img-fluid" alt="Logo" src="${link_prefix}img/logo_40.png"/></div>
|
<div class="col-auto"><img class="img-fluid header-logo" alt="Logo" src="${link_prefix}img/logo_40.png"/></div>
|
||||||
<div class="col"></div>
|
<div class="col"></div>
|
||||||
<div class="col-auto mx-2"><a href="${link_prefix}" class="text-decoration-none header-link fmnu1">DATA EXPORT</a></div>
|
<div class="col-auto mx-2"><a href="${link_prefix}" class="text-decoration-none header-link fmnu1">DATA EXPORT</a></div>
|
||||||
<div class="col-auto mx-2"><a href="${link_prefix}logout" class="text-decoration-none header-link fmnu1">LOGOUT</a></div>
|
<div class="col-auto mx-2"><a href="${link_prefix}logout" class="text-decoration-none header-link fmnu1">LOGOUT</a></div>
|
||||||
|
|
|
@ -227,3 +227,7 @@
|
||||||
.error {
|
.error {
|
||||||
color: darkred;
|
color: darkred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-logo {
|
||||||
|
height: calc(1.5rem + .8vw)
|
||||||
|
}
|
|
@ -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}
|
.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)}
|
Loading…
Reference in New Issue
Block a user