Compare commits

...

21 Commits
1.0.7 ... main

Author SHA1 Message Date
Mark Milligan
95a554422d Synchronize around the wait call that keeps the service running. 2022-11-17 17:21:36 -06:00
Mark Milligan
bf648f069c Prevent an NPE if apple SSO is not configured on a self-hosted server. 2022-11-06 18:50:09 -06:00
Mark Milligan
07765061bd Add config flag for hubs to post to self-hosted server with a self-signed SSL certificate.
Support Raspberry Pi 2B by handling missing BLE controller and supporting 32-bit pigpio.
2022-11-06 18:21:54 -06:00
Mark Milligan
a8236cebc1 Merge branch 'main' of https://github.com/MarkBryanMilligan/LanternPowerMonitor into main 2022-10-22 14:36:35 -05:00
Mark Milligan
b14e6a086d Update BOM for out of stock parts 2022-10-22 14:35:40 -05:00
MarkBryanMilligan
bdf8652450
Update README.md 2022-08-21 09:26:58 -05:00
Mark Milligan
e0d4dafd3a Implement 3-phase voltage logic. 2022-08-20 21:20:50 -05:00
Mark Milligan
1369529e8d Fix the 3B+ lid size. 2022-08-08 17:31:27 -05:00
Mark Milligan
a23ea4d8b1 Update the non-flanged 3B+ case. 2022-08-08 17:20:08 -05:00
Mark Milligan
b0ffd93f8d Added an example of querying an energy summary for a day using the API. 2022-06-05 22:36:50 -05:00
Mark Milligan
b8acfd4ea4 Merge remote-tracking branch 'origin/main' into main 2022-05-22 23:14:08 -05:00
Mark Milligan
0f730aac32 Start work on tracking the mains separately. 2022-05-22 23:13:54 -05:00
MarkBryanMilligan
e37a4905db
Update README.md 2022-05-04 16:41:12 -05:00
Mark Milligan
d7edf3db4a Turns out we don't actually need 30MB of bloated jars to make a single HTTP post to get a Google SSO auth token. Don't need them for Firebase either. And not for Apple SSO. Shoot while we're at it, might as well get rid of pi4j too since making a JNI wrapper for PiGPio is easy enough. 2022-05-02 18:20:03 -05:00
Mark Milligan
c8319d6369 Migrating from from wiringpi to pigpio. This increases the sample rate by a factor of 3 and will allow creation of a board that can monitor over 30 breakers with a single raspberry pi.
This is based on pi4j 2.0 which is in a beta status.  I have fixed a few bugs in a local version of pi4j 2.0 to get it to work but I haven't submitted those changes to pi4j yet.

This change requires a migration to Java 11 and will *NOT* be backwards compatible.  Upgrading to this hub software will require that java 11 and pigpio be installed on the hub.  This can be done from an ssh session with the following commands:

apt-get update
apt-get install openjdk-11-jre-headless
apt-get install pigpio

Alternatively, you can download a new sd image, reflash your sd card, and re-adopt your hub.
2022-04-29 14:59:56 -05:00
MarkBryanMilligan
079206fcd7 Update the open source PCB to have better ground connections. 2022-04-08 13:28:54 -05:00
MarkBryanMilligan
d30fc4b4ce Fix a bug that was making it impossible to display billing information in the android app. 2022-04-04 17:20:15 -05:00
MarkBryanMilligan
8387216c44 Increase the clearances on the 4B case a little bit so you don't need a rubber mallet to get the Pi in there. 2022-03-10 15:05:08 -06:00
MarkBryanMilligan
119173f2d2 Move the config creation files to their own shaded jar so they can be run from the command line without an IDE or messy classpath specification. 2022-02-25 22:15:54 -06:00
MarkBryanMilligan
c916b25427 Force all console pages to SSL. 2022-02-17 15:26:32 -06:00
MarkBryanMilligan
dabefd1e7c Update the flanged cases for the 3B and 4B so the screws are recessed. 2022-02-12 15:17:30 -06:00
153 changed files with 18312 additions and 31406 deletions

View File

@ -13,6 +13,10 @@ The android application is available here:
<br> <br>
[Lantern Power Monitor - Google Play](https://play.google.com/store/apps/details?id=com.lanternsoftware.lantern) [Lantern Power Monitor - Google Play](https://play.google.com/store/apps/details?id=com.lanternsoftware.lantern)
<br><br> <br><br>
The iOS application is available here:
<br>
[Lantern Power Monitor - App Store](https://apps.apple.com/us/app/lantern-power-monitor/id1620735464)
<br><br>
The LanternPowerMonitor subreddit is a great place to ask questions and stay tuned for updates and news. The LanternPowerMonitor subreddit is a great place to ask questions and stay tuned for updates and news.
<br> <br>
[/r/LanternPowerMonitor](https://www.reddit.com/r/LanternPowerMonitor/) [/r/LanternPowerMonitor](https://www.reddit.com/r/LanternPowerMonitor/)
@ -45,15 +49,15 @@ This is only tangentially related. A java library for running a zwave controlle
# Ok, how do I run this thing? # Ok, how do I run this thing?
The easiest way to run the software on a hub is to download a pre-built SD card image. One can be downloaded here:<br> The easiest way to run the software on a hub is to download a pre-built SD card image. One can be downloaded here:<br>
[hub_1.0.7.zip](https://cf.lanternpowermonitor.com/hub_1.0.7.zip) [hub_1.1.1.zip](https://cf.lanternpowermonitor.com/hub_1.1.1.zip)
<br><br> <br><br>
Flash this to any micro sd card (4gig or larger) and you're good to go. Fire up the hub and the phone app should be able to connect to it via bluetooth to finish the configuration. The default password on this image is pi/LanternPowerMonitor<br><br> Flash this to any micro sd card (4gig or larger) and you're good to go. Fire up the hub and the phone app should be able to connect to it via bluetooth to finish the configuration. The default password on this image is pi/LanternPowerMonitor<br><br>
When you add the hub to your configuration via the app, you can change where the hub posts data. If you use lanternsoftware.com (the default host), your data will be stored there securely and won't be shared with or sold to anyone. If you really want to run your own server, you're of course welcome to do that instead, instructions are located further down. When you add the hub to your configuration via the app, you can change where the hub posts data. If you use lanternpowermonitor.com (the default host), your data will be stored there securely and won't be shared with or sold to anyone. If you really want to run your own server, you're of course welcome to do that instead, instructions are located further down.
## Now that the service is running on the pi, how do I configure everything in the android app? ## Now that the service is running on the pi, how do I configure everything in the android app?
1. Create your panel in the "Configure Panels" page from the main menu. Before you have your hub connected, there will be no place to select a hub and port for each breaker. Don't worry, we'll get to that later. 1. Create your panel in the "Configure Panels" page from the main menu. Before you have your hub connected, there will be no place to select a hub and port for each breaker. Don't worry, we'll get to that later.
1. With your hub plugged in and running for at least 30 seconds or so, go into the "Configure Hubs" page from the main menu. In here you'll see a status of "Scanning for Hubs..." (if you're on at least 1.0.7 of the app). If you're in range of your hub and its service is running, the app should find it pretty quickly (less than 15 seconds). If this is the first hub you've added, it will prompt you for your wifi credentials. After that, it will send via bluetooth the hub index (so it knows which hub it is), host (so it knows where to post data), auth code (so it knows which account it is and can post data), the encrypted wifi credentials, and finally a command to reboot. 1. With your hub plugged in and running for at least a minute, go into the "Configure Hubs" page from the main menu. In here you'll see a status of "Scanning for Hubs..." If you're in range of your hub and its service is running, the app should find it pretty quickly (less than 15 seconds). If this is the first hub you've added, it will prompt you for your wifi credentials. After that, it will send via bluetooth the hub index (so it knows which hub it is), host (so it knows where to post data), auth code (so it knows which account it is and can post data), the encrypted wifi credentials, and finally a command to reboot.
1. After your hub reboots, it should acquire an ip from your router, start the service, and try to start posting data. The hubs will try to auto-calibrate your voltage to 120V too, but if your AC/AC transformer is not plugged in, it will notice that and not try to auto-calibrate. It will continue to try to auto-calibrate each time you restart the hub until it does so succesfully. 1. After your hub reboots, it should acquire an ip from your router, start the service, and try to start posting data. The hubs will try to auto-calibrate your voltage to 120V too, but if your AC/AC transformer is not plugged in, it will notice that and not try to auto-calibrate. It will continue to try to auto-calibrate each time you restart the hub until it does so succesfully.
@ -72,27 +76,17 @@ First, you and I will get along just fine. Second, do a reactor build from the
The compiled service will be at LanternPowerMonitor/currentmonitor/lantern-currentmonitor/target/lantern-currentmonitor.jar<br> The compiled service will be at LanternPowerMonitor/currentmonitor/lantern-currentmonitor/target/lantern-currentmonitor.jar<br>
This is a shaded jar that contains all of the required components to function. It must be copied to /opt/currentmonitor on the pi.<br><br> This is a shaded jar that contains all of the required components to function. It must be copied to /opt/currentmonitor on the pi.<br><br>
After that, you need to install wiring-pi: After that, you need to install pigpio:
``` ```
sudo apt-get install wiringpi sudo apt-get install pigpio
``` ```
You also need to have java 1.8 or newer installed. You also need to have java 1.8 or newer installed.
Create a configuration file at /opt/currentmonitor/config.json<br>
Use the format below to get started
```
{
"hub": 0,
"host": "https://lanternsoftware.com/currentmonitor",
"auto_calibration_voltage": 120.0,
"needs_calibration": true
}
```
To install the current monitor service, use a service file like the one below: To install the current monitor service, use a service file like the one below:
``` ```
[Unit] [Unit]
Description=Current Monitor Description=Current Monitor
After=syslog.target network.target After=syslog.target network-online.target
Wants=network-online.target
[Service] [Service]
Type=simple Type=simple
@ -119,7 +113,7 @@ After your reactor build, the compiled war will be at LanternPowerMonitor/curren
That can be deployed to tomcat. The 'host' parameter in the raspberry pi config.json file needs to point to wherever you deploy the service so your hubs post the data to your server instead of the official lantern software one.<br> That can be deployed to tomcat. The 'host' parameter in the raspberry pi config.json file needs to point to wherever you deploy the service so your hubs post the data to your server instead of the official lantern software one.<br>
I'd recommend a valid dns entry and an ssl certificate, but, it's up to you, you're already knee deep in "I'll do what I want" territory here.<br><br> I'd recommend a valid dns entry and an ssl certificate, but, it's up to you, you're already knee deep in "I'll do what I want" territory here.<br><br>
Before you deploy it, you need to generate a config file that contains the mongodb credentials.<br> Before you deploy it, you need to generate a config file that contains the mongodb credentials.<br>
There is a file at lantern-service-currentmonitor/src/test/java/com/lanternsoftware/currentmonitor/CreateMongoConfig.java that can do this for you.<br> There is a file at lantern-config-currentmonitor/src/main/java/com/lanternsoftware/currentmonitor/CreateMongoConfig.java that can do this for you.<br>
Place the generated config file in /opt/tomcat (which is where I have tomcat installed). If you want it to be read from somewhere else, you can modify the paths in LanternFiles.java<br><br> Place the generated config file in /opt/tomcat (which is where I have tomcat installed). If you want it to be read from somewhere else, you can modify the paths in LanternFiles.java<br><br>
The last thing you need is a private aes key to encrypt user auth tokens. One of those can be generated with CreateAuthKey.java.<br> The last thing you need is a private aes key to encrypt user auth tokens. One of those can be generated with CreateAuthKey.java.<br>
I realize these instructions aren't complete, but if you're going down this path, I suspect you sort of already know what you're doing, so hopefully that's enough to point you in the right direction. I realize these instructions aren't complete, but if you're going down this path, I suspect you sort of already know what you're doing, so hopefully that's enough to point you in the right direction.

Binary file not shown.

10
bom/LCSC_BOM.csv Normal file
View File

@ -0,0 +1,10 @@
LCSC Part Number,Quantity
C2977589,1
C43846,1
C43840,1
C385441,2
C385449,1
C385460,1
C385498,1
C433508,1
C397337,16
1 LCSC Part Number Quantity
2 C2977589 1
3 C43846 1
4 C43840 1
5 C385441 2
6 C385449 1
7 C385460 1
8 C385498 1
9 C433508 1
10 C397337 16

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,105 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>lantern-config-currentmonitor</artifactId>
<packaging>jar</packaging>
<version>1.1.0</version>
<name>lantern-config-currentmonitor</name>
<parent>
<groupId>com.lanternsoftware.currentmonitor</groupId>
<artifactId>currentmonitor</artifactId>
<version>1.1.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-dao-mongo</artifactId>
<version>${util.version}</version>
</dependency>
<dependency>
<groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-common</artifactId>
<version>${util.version}</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<executions>
<execution>
<goals>
<goal>testCompile</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
<configuration>
<optimize>true</optimize>
<showDeprecation>true</showDeprecation>
<encoding>UTF-8</encoding>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>lantern-config-currentmonitor</finalName>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.lanternsoftware.currentmonitor.CreateMongoConfig</Main-Class>
<Specification-Title>Lantern Power Monitor</Specification-Title>
<Specification-Version>${project.version}</Specification-Version>
<Specification-Vendor>Lantern Software, Inc.</Specification-Vendor>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,14 @@
package com.lanternsoftware.currentmonitor;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import com.lanternsoftware.util.external.LanternFiles;
public class CreateMongoConfig {
public static void main(String[] args) {
if (CollectionUtils.size(args) == 3)
new MongoConfig(args[0], args[1], args[2], "CURRENT_MONITOR").saveToDisk(LanternFiles.CONFIG_PATH + "mongo.cfg");
else
new MongoConfig("lanternsoftware.com", "*redacted*", "*redacted*", "CURRENT_MONITOR").saveToDisk(LanternFiles.CONFIG_PATH + "mongo.cfg");
}
}

View File

@ -1,15 +1,15 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.lanternsoftware.currentmonitor</groupId>
<artifactId>lantern-currentmonitor</artifactId> <artifactId>lantern-currentmonitor</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.0.7</version> <version>1.1.3</version>
<name>lantern-currentmonitor</name> <name>lantern-currentmonitor</name>
<properties> <parent>
<maven.compiler.source>1.8</maven.compiler.source> <groupId>com.lanternsoftware.currentmonitor</groupId>
<maven.compiler.target>1.8</maven.compiler.target> <artifactId>currentmonitor</artifactId>
</properties> <version>1.1.0</version>
</parent>
<dependencies> <dependencies>
<dependency> <dependency>
@ -23,9 +23,9 @@
<version>1.2.3</version> <version>1.2.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.pi4j</groupId> <groupId>com.lanternsoftware.pigpio</groupId>
<artifactId>pi4j-gpio-extension</artifactId> <artifactId>lantern-pigpio</artifactId>
<version>1.3</version> <version>${pigpio.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.hypfvieh</groupId> <groupId>com.github.hypfvieh</groupId>
@ -35,12 +35,12 @@
<dependency> <dependency>
<groupId>com.lanternsoftware.currentmonitor</groupId> <groupId>com.lanternsoftware.currentmonitor</groupId>
<artifactId>lantern-datamodel-currentmonitor</artifactId> <artifactId>lantern-datamodel-currentmonitor</artifactId>
<version>1.0.0</version> <version>${cm.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.lanternsoftware.util</groupId> <groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-http</artifactId> <artifactId>lantern-util-http</artifactId>
<version>1.0.0</version> <version>${util.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.paho</groupId> <groupId>org.eclipse.paho</groupId>
@ -58,7 +58,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version> <version>3.10.1</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
@ -71,14 +71,14 @@
<optimize>true</optimize> <optimize>true</optimize>
<showDeprecation>true</showDeprecation> <showDeprecation>true</showDeprecation>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
<source>1.8</source> <source>${maven.compiler.source}</source>
<target>1.8</target> <target>${maven.compiler.target}</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version> <version>3.3.0</version>
<configuration> <configuration>
<createDependencyReducedPom>false</createDependencyReducedPom> <createDependencyReducedPom>false</createDependencyReducedPom>
<filters> <filters>

View File

@ -7,27 +7,38 @@ import com.lanternsoftware.currentmonitor.bluetooth.BleHelper;
import com.lanternsoftware.currentmonitor.bluetooth.BleService; import com.lanternsoftware.currentmonitor.bluetooth.BleService;
import com.lanternsoftware.datamodel.currentmonitor.HubConfigService; import com.lanternsoftware.datamodel.currentmonitor.HubConfigService;
import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List; import java.util.List;
public class BluetoothConfig { public class BluetoothConfig {
private static final Logger LOG = LoggerFactory.getLogger(BluetoothConfig.class);
private final BleApplication app; private final BleApplication app;
public BluetoothConfig(String _hubName, BleCharacteristicListener _listener) { public BluetoothConfig(String _hubName, BleCharacteristicListener _listener) {
BleHelper.getAdapter().setPowered(true); BleApplication a = null;
BleHelper.requestBusName("com.lanternsoftware"); try {
BleHelper.setBasePath("/com/lanternsoftware"); BleHelper.getAdapter().setPowered(true);
HubConfigService service = new HubConfigService(); BleHelper.requestBusName("com.lanternsoftware");
List<BleCharacteristic> chars = CollectionUtils.transform(service.getCharacteristics(), _c->new BleCharacteristic("HubConfig", _c.getUUID(), _c.name(), _c.getFlags())); BleHelper.setBasePath("/com/lanternsoftware");
chars.forEach(_c->_c.setListener(_listener)); List<BleCharacteristic> chars = CollectionUtils.transform(HubConfigService.getCharacteristics(), _c -> new BleCharacteristic("HubConfig", _c.getUUID(), _c.name(), _c.getFlags()));
app = new BleApplication("Lantern", _hubName, new BleService("HubConfig", service.getServiceUUID(), chars)); chars.forEach(_c -> _c.setListener(_listener));
a = new BleApplication("Lantern", _hubName, new BleService("HubConfig", HubConfigService.getServiceUUID(), chars));
}
catch (Throwable _t) {
LOG.error("Failed to initialize BLE service", _t);
}
app = a;
} }
public void start() { public void start() {
app.start(); if (app != null)
app.start();
} }
public void stop() { public void stop() {
app.stop(); if (app != null)
app.stop();
} }
} }

View File

@ -1,18 +1,20 @@
package com.lanternsoftware.currentmonitor; package com.lanternsoftware.currentmonitor;
import com.lanternsoftware.currentmonitor.adc.MCP3008Pin;
import com.lanternsoftware.datamodel.currentmonitor.Breaker; import com.lanternsoftware.datamodel.currentmonitor.Breaker;
import com.pi4j.io.gpio.GpioPinAnalogInput; import com.lanternsoftware.datamodel.currentmonitor.hub.PowerSample;
import java.util.List; import java.util.List;
public class BreakerSamples { public class BreakerSamples {
private final Breaker breaker; private final Breaker breaker;
private final GpioPinAnalogInput voltagePin; private final MCP3008Pin voltagePin;
private final GpioPinAnalogInput currentPin; private final MCP3008Pin currentPin;
private final List<PowerSample> samples; private final List<PowerSample> samples;
private int cycleCnt;
private int sampleCnt; private int sampleCnt;
public BreakerSamples(Breaker _breaker, GpioPinAnalogInput _voltagePin, GpioPinAnalogInput _currentPin, List<PowerSample> _samples) { public BreakerSamples(Breaker _breaker, MCP3008Pin _voltagePin, MCP3008Pin _currentPin, List<PowerSample> _samples) {
breaker = _breaker; breaker = _breaker;
voltagePin = _voltagePin; voltagePin = _voltagePin;
currentPin = _currentPin; currentPin = _currentPin;
@ -23,11 +25,11 @@ public class BreakerSamples {
return breaker; return breaker;
} }
public GpioPinAnalogInput getVoltagePin() { public MCP3008Pin getVoltagePin() {
return voltagePin; return voltagePin;
} }
public GpioPinAnalogInput getCurrentPin() { public MCP3008Pin getCurrentPin() {
return currentPin; return currentPin;
} }
@ -35,8 +37,20 @@ public class BreakerSamples {
return samples; return samples;
} }
public PowerSample getSample(int _sample) { public void incrementCycleCnt() {
return samples.get(_sample); cycleCnt++;
}
public PowerSample incrementSample() {
return samples.get(sampleCnt++);
}
public int getCycleCnt() {
return cycleCnt;
}
public void setCycleCnt(int _cycleCnt) {
cycleCnt = _cycleCnt;
} }
public int getSampleCnt() { public int getSampleCnt() {

View File

@ -1,51 +1,55 @@
package com.lanternsoftware.currentmonitor; package com.lanternsoftware.currentmonitor;
import com.lanternsoftware.currentmonitor.adc.MCP3008;
import com.lanternsoftware.currentmonitor.adc.MCP3008Pin;
import com.lanternsoftware.datamodel.currentmonitor.Breaker; import com.lanternsoftware.datamodel.currentmonitor.Breaker;
import com.lanternsoftware.datamodel.currentmonitor.BreakerHub; import com.lanternsoftware.datamodel.currentmonitor.BreakerHub;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPolarity; import com.lanternsoftware.datamodel.currentmonitor.BreakerPolarity;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower; 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.CollectionUtils;
import com.lanternsoftware.util.concurrency.ConcurrencyUtils; import com.lanternsoftware.util.concurrency.ConcurrencyUtils;
import com.pi4j.gpio.extension.base.AdcGpioProvider;
import com.pi4j.gpio.extension.mcp.MCP3008GpioProvider;
import com.pi4j.gpio.extension.mcp.MCP3008Pin;
import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinAnalogInput;
import com.pi4j.io.spi.SpiChannel;
import com.pi4j.io.spi.SpiDevice;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
public class CurrentMonitor { public class CurrentMonitor {
private static final Logger LOG = LoggerFactory.getLogger(CurrentMonitor.class); private static final Logger LOG = LoggerFactory.getLogger(CurrentMonitor.class);
private static final int BATCH_CNT = 4; private static final int BATCH_CNT = 4;
private GpioController gpio;
private final ExecutorService executor = Executors.newCachedThreadPool(); private final ExecutorService executor = Executors.newCachedThreadPool();
private final Map<Integer, AdcGpioProvider> chips = new HashMap<>(); private final Map<Integer, MCP3008> chips = new HashMap<>();
private final Map<Integer, GpioPinAnalogInput> pins = new HashMap<>();
private Sampler sampler; private Sampler sampler;
private PowerListener listener; private PowerListener listener;
private boolean debug = false; private boolean debug = false;
private boolean postSamples = false;
public void start() { public boolean isDebug() {
try { return debug;
gpio = GpioFactory.getInstance(); }
LOG.info("Current Monitor Started");
} public void setDebug(boolean _debug) {
catch (Throwable t) { debug = _debug;
LOG.info("Failed to get gpio factory", t); }
}
public boolean isPostSamples() {
return postSamples;
}
public void setPostSamples(boolean _postSamples) {
postSamples = _postSamples;
} }
public void stop() { public void stop() {
@ -53,21 +57,15 @@ public class CurrentMonitor {
ConcurrencyUtils.sleep(1000); ConcurrencyUtils.sleep(1000);
executor.shutdown(); executor.shutdown();
ConcurrencyUtils.sleep(1000); ConcurrencyUtils.sleep(1000);
PiGpioFactory.shutdown();
chips.clear(); chips.clear();
pins.clear();
gpio.shutdown();
LOG.info("Power Monitor Service Stopped"); LOG.info("Power Monitor Service Stopped");
} }
public void setDebug(boolean _debug) {
debug = _debug;
}
public CalibrationResult calibrateVoltage(double _curCalibration) { public CalibrationResult calibrateVoltage(double _curCalibration) {
GpioPinAnalogInput voltagePin = getPin(0, 0); LOG.info("Calibrating Voltage");
if (voltagePin == null) MCP3008Pin voltagePin = new MCP3008Pin(getChip(0), 0);
return null; int maxSamples = 240000;
int maxSamples = 120000;
CalibrationSample[] samples = new CalibrationSample[maxSamples]; CalibrationSample[] samples = new CalibrationSample[maxSamples];
int offset = 0; int offset = 0;
for (;offset < maxSamples; offset++) { for (;offset < maxSamples; offset++) {
@ -77,7 +75,7 @@ public class CurrentMonitor {
long intervalEnd = System.nanoTime() + 2000000000L; //Scan voltage for 2 seconds long intervalEnd = System.nanoTime() + 2000000000L; //Scan voltage for 2 seconds
while (offset < maxSamples) { while (offset < maxSamples) {
samples[offset].time = System.nanoTime(); samples[offset].time = System.nanoTime();
samples[offset].voltage = voltagePin.getValue(); samples[offset].voltage = voltagePin.read();
offset++; offset++;
if (samples[offset-1].time > intervalEnd) if (samples[offset-1].time > intervalEnd)
break; break;
@ -128,10 +126,12 @@ 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)) if (CollectionUtils.isEmpty(validBreakers)) {
LOG.error("No breakers found for hub number {}", _hub.getHub());
return; return;
}
LOG.info("Monitoring {} breakers for hub {}", CollectionUtils.size(validBreakers), _hub.getHub()); LOG.info("Monitoring {} breakers for hub {}", CollectionUtils.size(validBreakers), _hub.getHub());
sampler = new Sampler(_hub, validBreakers, _intervalMs, 2); sampler = new Sampler(_hub, validBreakers, _intervalMs, 5);
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);
} }
@ -140,34 +140,15 @@ public class CurrentMonitor {
} }
} }
private GpioPinAnalogInput getPin(int _chip, int _pin) { private synchronized MCP3008 getChip(int _chip) {
GpioPinAnalogInput pin; MCP3008 chip = chips.get(_chip);
synchronized (pins) { if (chip == null) {
AdcGpioProvider chip = chips.get(_chip); String id = "SPI" + _chip;
if (chip == null) { LOG.info("Creating chip {}", id);
SpiChannel channel = SpiChannel.getByNumber(_chip); chip = new MCP3008(PiGpioFactory.getSpiChannel(_chip, 810000, false));
if (channel == null) chips.put(_chip, chip);
return null;
try {
chip = new MCP3008GpioProvider(channel, 1250000, SpiDevice.DEFAULT_SPI_MODE, false);
chips.put(_chip, chip);
} catch (IOException _e) {
LOG.error("Failed to connect to chip {}", _chip, _e);
return null;
}
}
int pinKey = pinKey(_chip, _pin);
pin = pins.get(pinKey);
if (pin == null) {
pin = gpio.provisionAnalogInputPin(chip, MCP3008Pin.ALL[_pin], String.valueOf(pinKey));
pins.put(pinKey, pin);
}
} }
return pin; return chip;
}
private Integer pinKey(int _chip, int _pin) {
return (_chip*8)+_pin;
} }
public void submit(Runnable _runnable) { public void submit(Runnable _runnable) {
@ -185,19 +166,19 @@ public class CurrentMonitor {
private boolean running = true; private boolean running = true;
private final BreakerHub hub; private final BreakerHub hub;
private final List<List<BreakerSamples>> breakers; private final List<List<BreakerSamples>> breakers;
private final int intervalNs; private final long intervalNs;
private final int concurrentBreakerCnt; private final int concurrentBreakerCnt;
public Sampler(BreakerHub _hub, List<Breaker> _breakers, int _intervalMs, int _concurrentBreakerCnt) { public Sampler(BreakerHub _hub, List<Breaker> _breakers, long _intervalMs, int _concurrentBreakerCnt) {
hub = _hub; hub = _hub;
GpioPinAnalogInput voltagePin = getPin(0, 0); MCP3008Pin voltagePin = new MCP3008Pin(getChip(0), 0);
breakers = CollectionUtils.transform(_breakers, _b->{ breakers = CollectionUtils.transform(_breakers, _b->{
LOG.info("Getting Chip {}, Pin {} for port {}", _b.getChip(), _b.getPin(), _b.getPort()); LOG.info("Getting Chip {}, Pin {} for port {}", _b.getChip(), _b.getPin(), _b.getPort());
GpioPinAnalogInput currentPin = getPin(_b.getChip(), _b.getPin()); MCP3008Pin currentPin = new MCP3008Pin(getChip(_b.getChip()), _b.getPin());
List<BreakerSamples> batches = new ArrayList<>(BATCH_CNT); List<BreakerSamples> batches = new ArrayList<>(BATCH_CNT);
for (int i=0; i<BATCH_CNT; i++) { for (int i=0; i<BATCH_CNT; i++) {
List<PowerSample> samples = new ArrayList<>(30000/_breakers.size()); List<PowerSample> samples = new ArrayList<>(30000/_breakers.size());
for (int j=0; j<30000/_breakers.size(); j++) { for (int j=0; j<60000/_breakers.size(); j++) {
samples.add(new PowerSample()); samples.add(new PowerSample());
} }
batches.add(new BreakerSamples(_b, voltagePin, currentPin, samples)); batches.add(new BreakerSamples(_b, voltagePin, currentPin, samples));
@ -213,6 +194,11 @@ public class CurrentMonitor {
long start = System.nanoTime(); long start = System.nanoTime();
long interval = 0; long interval = 0;
int cycle; int cycle;
int curBreaker;
long intervalStart;
long intervalEnd;
long cycleEnd;
long curTime;
BreakerSamples[] cycleBreakers = new BreakerSamples[concurrentBreakerCnt]; BreakerSamples[] cycleBreakers = new BreakerSamples[concurrentBreakerCnt];
try { try {
while (true) { while (true) {
@ -223,36 +209,83 @@ public class CurrentMonitor {
} }
} }
final Date readTime = new Date(); final Date readTime = new Date();
final long intervalStart = (interval * intervalNs) + start; intervalStart = (interval * intervalNs) + start;
long intervalEnd = intervalStart + intervalNs; intervalEnd = intervalStart + intervalNs;
cycle = 0; cycle = 0;
final int batch = (int) (interval % BATCH_CNT); final int batch = (int) (interval % BATCH_CNT);
int curBreaker;
for (curBreaker = 0; curBreaker < breakers.size(); curBreaker++) {
breakers.get(curBreaker).get(batch).setSampleCnt(0);
}
while (System.nanoTime() < intervalEnd) { while (System.nanoTime() < intervalEnd) {
for (curBreaker = 0; curBreaker < concurrentBreakerCnt; curBreaker++) { for (curBreaker = 0; curBreaker < concurrentBreakerCnt; curBreaker++) {
cycleBreakers[curBreaker] = breakers.get(((cycle * concurrentBreakerCnt) + curBreaker) % breakers.size()).get(batch); cycleBreakers[curBreaker] = breakers.get(((cycle * concurrentBreakerCnt) + curBreaker) % breakers.size()).get(batch);
cycleBreakers[curBreaker].incrementCycleCnt();
} }
cycle++; cycle++;
long cycleEnd = intervalStart + (cycle * (intervalNs / hub.getFrequency())); cycleEnd = intervalStart + (cycle * (intervalNs / hub.getFrequency()));
while (System.nanoTime() < cycleEnd) { curTime = System.nanoTime();
while (curTime < cycleEnd) {
for (curBreaker = 0; curBreaker < concurrentBreakerCnt; curBreaker++) { for (curBreaker = 0; curBreaker < concurrentBreakerCnt; curBreaker++) {
PowerSample sample = cycleBreakers[curBreaker].getSample(cycleBreakers[curBreaker].getSampleCnt()); PowerSample sample = cycleBreakers[curBreaker].incrementSample();
sample.voltage = cycleBreakers[curBreaker].getVoltagePin().getValue(); sample.nanoTime = curTime;
sample.current = cycleBreakers[curBreaker].getCurrentPin().getValue(); sample.cycle = cycle;
cycleBreakers[curBreaker].setSampleCnt(cycleBreakers[curBreaker].getSampleCnt()+1); sample.voltage = cycleBreakers[curBreaker].getVoltagePin().read();
sample.current = cycleBreakers[curBreaker].getCurrentPin().read();
} }
curTime = System.nanoTime();
} }
} }
interval++; interval++;
final HubSample hubSample = (postSamples && (interval == 10)) ? new HubSample() : null;
executor.submit(() -> { executor.submit(() -> {
long cycleLength = 1000000000/hub.getFrequency();
if (hubSample != null) {
hubSample.setSampleDate(new Date());
hubSample.setBreakers(new ArrayList<>());
}
for (List<BreakerSamples> breaker : breakers) { for (List<BreakerSamples> breaker : breakers) {
double vOffset = 0.0;
double iOffset = 0.0;
BreakerSamples samples = breaker.get(batch); BreakerSamples samples = breaker.get(batch);
List<PowerSample> validSamples = samples.getSamples().subList(0, samples.getSampleCnt()); List<PowerSample> 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<Integer, List<PowerSample>> cycles = CollectionUtils.transformToMultiMap(validSamples, _p->_p.cycle);
for (List<PowerSample> 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<Long, Double> 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<Double> voltages = new ArrayList<>();
Entry<Long, Double> floorEntry = offsetSamples.floorEntry(sample.nanoTime);
if (floorEntry != null)
voltages.add(floorEntry.getValue());
Entry<Long, Double> 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) { for (PowerSample sample : validSamples) {
vOffset += sample.voltage; vOffset += sample.voltage;
iOffset += sample.current; iOffset += sample.current;
@ -263,6 +296,7 @@ public class CurrentMonitor {
double pSum = 0.0; double pSum = 0.0;
double vRms = 0.0; double vRms = 0.0;
double lowPassFilter = samples.getBreaker().getLowPassFilter(); double lowPassFilter = samples.getBreaker().getLowPassFilter();
for (PowerSample sample : validSamples) { for (PowerSample sample : validSamples) {
sample.current -= iOffset; sample.current -= iOffset;
if (Math.abs(sample.current) < lowPassFilter) if (Math.abs(sample.current) < lowPassFilter)
@ -273,21 +307,26 @@ public class CurrentMonitor {
} }
vRms /= validSamples.size(); vRms /= validSamples.size();
vRms = hub.getVoltageCalibrationFactor() * Math.sqrt(vRms); vRms = hub.getVoltageCalibrationFactor() * Math.sqrt(vRms);
int lowSampleRatio = (lowSamples * 100) / samples.getSampleCnt(); int lowSampleRatio = (lowSamples * 100) / validSamples.size();
double realPower = Math.abs((hub.getVoltageCalibrationFactor() * hub.getPortCalibrationFactor() * samples.getBreaker().getFinalCalibrationFactor() * pSum) / samples.getSampleCnt()); double realPower = (hub.getVoltageCalibrationFactor() * hub.getPortCalibrationFactor() * samples.getBreaker().getFinalCalibrationFactor() * pSum) / validSamples.size();
if ((lowSampleRatio > 75) && realPower < 13.0) if ((lowSampleRatio > 75) && Math.abs(realPower) < 13.0)
realPower = 0.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; realPower = -realPower;
if (samples.getBreaker().isDoublePower()) if (samples.getBreaker().isDoublePower())
realPower *= 2.0; realPower *= 2.0;
if (debug) { if (debug) {
synchronized (CurrentMonitor.this) { synchronized (CurrentMonitor.this) {
LOG.info("===========================Start Port {}", samples.getBreaker().getPort()); LOG.info("===========================Start Port {}", samples.getBreaker().getPort());
LOG.info("Cycles: {}", samples.getCycleCnt());
LOG.info("Samples: {}", samples.getSampleCnt()); LOG.info("Samples: {}", samples.getSampleCnt());
LOG.info("vMin: {}, vMax: {}, vOffset: {}", String.format("%.3f", CollectionUtils.getSmallest(validSamples, Comparator.comparing(_v -> _v.voltage)).voltage), String.format("%.3f", CollectionUtils.getLargest(validSamples, Comparator.comparing(_v -> _v.voltage)).voltage), String.format("%.3f", vOffset)); LOG.info("vMin: {}, vMax: {}, vOffset: {}", String.format("%.3f", CollectionUtils.getSmallest(validSamples, Comparator.comparing(_v -> _v.voltage)).voltage), String.format("%.3f", CollectionUtils.getLargest(validSamples, Comparator.comparing(_v -> _v.voltage)).voltage), String.format("%.3f", vOffset));
LOG.info("iMin: {}, iMax: {}, iOffset: {}", String.format("%.3f", CollectionUtils.getSmallest(validSamples, Comparator.comparing(_v -> _v.current)).current), String.format("%.3f", CollectionUtils.getLargest(validSamples, Comparator.comparing(_v -> _v.current)).current), String.format("%.3f", iOffset)); LOG.info("iMin: {}, iMax: {}, iOffset: {}", String.format("%.3f", CollectionUtils.getSmallest(validSamples, Comparator.comparing(_v -> _v.current)).current), String.format("%.3f", CollectionUtils.getLargest(validSamples, Comparator.comparing(_v -> _v.current)).current), String.format("%.3f", iOffset));
double iRms = samples.getBreaker().getFinalCalibrationFactor() * Math.sqrt(CollectionUtils.mean(CollectionUtils.transform(validSamples, _p -> _p.current * _p.current))); double iRms = hub.getPortCalibrationFactor() * samples.getBreaker().getFinalCalibrationFactor() * Math.sqrt(CollectionUtils.mean(CollectionUtils.transform(validSamples, _p -> _p.current * _p.current)));
LOG.info("vRms: {}", String.format("%.3f", vRms)); LOG.info("vRms: {}", String.format("%.3f", vRms));
LOG.info("iRms: {}", String.format("%.3f", iRms)); LOG.info("iRms: {}", String.format("%.3f", iRms));
double apparentPower = vRms * iRms; double apparentPower = vRms * iRms;
@ -298,8 +337,12 @@ public class CurrentMonitor {
LOG.info("===========================End Port {}", samples.getBreaker().getPort()); LOG.info("===========================End Port {}", samples.getBreaker().getPort());
} }
} }
samples.setSampleCnt(0);
samples.setCycleCnt(0);
listener.onPowerEvent(new BreakerPower(samples.getBreaker().getPanel(), samples.getBreaker().getSpace(), readTime, realPower, vRms)); listener.onPowerEvent(new BreakerPower(samples.getBreaker().getPanel(), samples.getBreaker().getSpace(), readTime, realPower, vRms));
} }
if (hubSample != null)
listener.onSampleEvent(hubSample);
}); });
} }
} }

View File

@ -16,6 +16,7 @@ import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic;
import com.lanternsoftware.datamodel.currentmonitor.HubConfigService; import com.lanternsoftware.datamodel.currentmonitor.HubConfigService;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.datamodel.currentmonitor.NetworkStatus; import com.lanternsoftware.datamodel.currentmonitor.NetworkStatus;
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.ResourceLoader; import com.lanternsoftware.util.ResourceLoader;
@ -35,12 +36,9 @@ import org.apache.http.entity.ContentType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.Console;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL; import java.net.URL;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
@ -68,16 +66,24 @@ public class MonitorApp {
private static final CurrentMonitor monitor = new CurrentMonitor(); private static final CurrentMonitor monitor = new CurrentMonitor();
private static final List<BreakerPower> readings = new ArrayList<>(); private static final List<BreakerPower> readings = new ArrayList<>();
private static String version; private static String version;
private static final PowerListener logger = _p -> { private static final PowerListener logger = new PowerListener() {
if (!config.isDebug()) { @Override
_p.setHubVersion(version); public void onPowerEvent(BreakerPower _power) {
if (breakerConfig != null) if (!config.isDebug()) {
_p.setAccountId(breakerConfig.getAccountId()); _power.setHubVersion(version);
synchronized (readings) { if (breakerConfig != null)
readings.add(_p); _power.setAccountId(breakerConfig.getAccountId());
} synchronized (readings) {
} else readings.add(_power);
LOG.info("Panel{} - Space{} Power: {}W", _p.getPanel(), Breaker.toSpaceDisplay(_p.getSpace()), String.format("%.3f", _p.getPower())); }
} 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() { private static final BleCharacteristicListener bluetoothListener = new BleCharacteristicListener() {
@Override @Override
@ -210,17 +216,15 @@ public class MonitorApp {
version = getVersionNumber(); version = getVersionNumber();
config = DaoSerializer.parse(ResourceLoader.loadFileAsString(WORKING_DIR + "config.json"), MonitorConfig.class); config = DaoSerializer.parse(ResourceLoader.loadFileAsString(WORKING_DIR + "config.json"), MonitorConfig.class);
if (config == null) { if (config == null) {
LOG.error("Failed to load config file from {}", WORKING_DIR + "config.json"); config = new MonitorConfig();
return; ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config));
} }
pool = new HttpPool(10, 10, config.getSocketTimeout(), config.getConnectTimeout(), config.getSocketTimeout()); pool = HttpPool.builder().withValidateSSLCertificates(!config.isAcceptSelfSignedCertificates()).build();
if (NullUtils.isNotEmpty(config.getHost())) if (NullUtils.isNotEmpty(config.getHost()))
host = NullUtils.terminateWith(config.getHost(), "/"); host = NullUtils.terminateWith(config.getHost(), "/");
monitor.setDebug(config.isDebug()); monitor.setDebug(config.isDebug());
monitor.start(); monitor.setPostSamples(config.isPostSamples());
LEDFlasher.setLEDOn(false); LEDFlasher.setLEDOn(false);
bluetoothConfig = new BluetoothConfig("Lantern Hub", bluetoothListener);
bluetoothConfig.start();
if (NullUtils.isNotEmpty(config.getAuthCode())) if (NullUtils.isNotEmpty(config.getAuthCode()))
authCode = config.getAuthCode(); authCode = config.getAuthCode();
else if (NullUtils.isNotEmpty(host) && NullUtils.isNotEmpty(config.getUsername()) && NullUtils.isNotEmpty(config.getPassword())) { else if (NullUtils.isNotEmpty(host) && NullUtils.isNotEmpty(config.getUsername()) && NullUtils.isNotEmpty(config.getPassword())) {
@ -231,7 +235,8 @@ public class MonitorApp {
if (NullUtils.isNotEmpty(config.getMqttBrokerUrl())) if (NullUtils.isNotEmpty(config.getMqttBrokerUrl()))
mqttPoster = new MqttPoster(config); mqttPoster = new MqttPoster(config);
if (NullUtils.isNotEmpty(host) && NullUtils.isNotEmpty(authCode)) { if (NullUtils.isNotEmpty(host) && NullUtils.isNotEmpty(authCode)) {
while (true) { int configAttempts = 0;
while (configAttempts < 5) {
HttpGet get = new HttpGet(host + "config"); HttpGet get = new HttpGet(host + "config");
get.addHeader("auth_code", authCode); get.addHeader("auth_code", authCode);
breakerConfig = DaoSerializer.parse(pool.executeToString(get), BreakerConfig.class); breakerConfig = DaoSerializer.parse(pool.executeToString(get), BreakerConfig.class);
@ -239,8 +244,11 @@ public class MonitorApp {
break; break;
LOG.error("Failed to load breaker config. Retrying in 5 seconds..."); LOG.error("Failed to load breaker config. Retrying in 5 seconds...");
ConcurrencyUtils.sleep(5000); ConcurrencyUtils.sleep(5000);
configAttempts++;
} }
} }
bluetoothConfig = new BluetoothConfig("Lantern Hub", bluetoothListener);
bluetoothConfig.start();
if ((mqttPoster != null) && (breakerConfig == null)) { if ((mqttPoster != null) && (breakerConfig == null)) {
LOG.info("Hub not configured by a Lantern Power Monitor server, defaulting to MQTT mode only"); LOG.info("Hub not configured by a Lantern Power Monitor server, defaulting to MQTT mode only");
BreakerHub hub = new BreakerHub(); BreakerHub hub = new BreakerHub();
@ -266,13 +274,18 @@ public class MonitorApp {
BreakerHub hub = breakerConfig.getHub(config.getHub()); BreakerHub hub = breakerConfig.getHub(config.getHub());
if (hub != null) { if (hub != null) {
if (config.isNeedsCalibration()) { if (config.isNeedsCalibration()) {
CalibrationResult cal = monitor.calibrateVoltage(hub.getVoltageCalibrationFactor()); try {
if (cal != null) { CalibrationResult cal = monitor.calibrateVoltage(hub.getVoltageCalibrationFactor());
hub.setVoltageCalibrationFactor(cal.getVoltageCalibrationFactor()); if (cal != null) {
hub.setFrequency(cal.getFrequency()); hub.setVoltageCalibrationFactor(cal.getVoltageCalibrationFactor());
config.setNeedsCalibration(false); hub.setFrequency(cal.getFrequency());
ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config)); config.setNeedsCalibration(false);
post(DaoSerializer.toZipBson(breakerConfig), "config"); ResourceLoader.writeFile(WORKING_DIR + "config.json", DaoSerializer.toJson(config));
post(DaoSerializer.toZipBson(breakerConfig), "config");
}
}
catch (Throwable t) {
LOG.error("Exception trying to read from voltage pin", t);
} }
} }
List<Breaker> breakers = breakerConfig.getBreakersForHub(config.getHub()); List<Breaker> breakers = breakerConfig.getBreakersForHub(config.getHub());
@ -288,17 +301,11 @@ public class MonitorApp {
monitor.stop(); monitor.stop();
pool.shutdown(); pool.shutdown();
}, "Monitor Shutdown")); }, "Monitor Shutdown"));
Console c = System.console(); synchronized (monitor) {
BufferedReader reader = (c == null)?new BufferedReader(new InputStreamReader(System.in)):null;
while (running.get()) {
try { try {
String command = c != null ? c.readLine() : reader.readLine(); monitor.wait();
if (NullUtils.isEqual("exit", command)) } catch (InterruptedException _e) {
break; LOG.error("Interrupted, shutting down", _e);
}
catch (Exception _e) {
LOG.error("Exception while reading from console input", _e);
break;
} }
} }
} }
@ -360,7 +367,7 @@ public class MonitorApp {
byte[] payload = DaoSerializer.toZipBson(minutePost); byte[] payload = DaoSerializer.toZipBson(minutePost);
if (!post(payload, "power/hub")) { if (!post(payload, "power/hub")) {
LOG.info("Failed Posting HubPowerMinute, writing cache"); LOG.info("Failed Posting HubPowerMinute, writing cache");
ResourceLoader.writeFile(WORKING_DIR + "cache/" + UUID.randomUUID().toString() + ".min", payload); ResourceLoader.writeFile(WORKING_DIR + "cache/" + UUID.randomUUID() + ".min", payload);
} }
} }
if (post != null) { if (post != null) {

View File

@ -2,6 +2,7 @@ package com.lanternsoftware.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.Breaker; import com.lanternsoftware.datamodel.currentmonitor.Breaker;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.annotations.DBSerializable; import com.lanternsoftware.util.dao.annotations.DBSerializable;
import java.util.List; import java.util.List;
@ -12,11 +13,13 @@ public class MonitorConfig {
private String authCode; private String authCode;
private String username; private String username;
private String password; private String password;
private int hub; private int hub = -1;
private boolean debug; private boolean debug;
private int connectTimeout; private int connectTimeout;
private int socketTimeout; private int socketTimeout;
private boolean needsCalibration; private boolean postSamples = false;
private boolean needsCalibration = true;
private boolean acceptSelfSignedCertificates = false;
private String mqttBrokerUrl; private String mqttBrokerUrl;
private String mqttUserName; private String mqttUserName;
private String mqttPassword; private String mqttPassword;
@ -34,7 +37,7 @@ public class MonitorConfig {
} }
public String getHost() { public String getHost() {
return host; return NullUtils.isEmpty(host) ? "https://lanternpowermonitor.com/currentmonitor/" : host;
} }
public void setHost(String _host) { public void setHost(String _host) {
@ -97,6 +100,14 @@ public class MonitorConfig {
socketTimeout = _socketTimeout; socketTimeout = _socketTimeout;
} }
public boolean isPostSamples() {
return postSamples;
}
public void setPostSamples(boolean _postSamples) {
postSamples = _postSamples;
}
public boolean isNeedsCalibration() { public boolean isNeedsCalibration() {
return needsCalibration; return needsCalibration;
} }
@ -105,6 +116,14 @@ public class MonitorConfig {
needsCalibration = _needsCalibration; needsCalibration = _needsCalibration;
} }
public boolean isAcceptSelfSignedCertificates() {
return acceptSelfSignedCertificates;
}
public void setAcceptSelfSignedCertificates(boolean _acceptSelfSignedCertificates) {
acceptSelfSignedCertificates = _acceptSelfSignedCertificates;
}
public String getMqttBrokerUrl() { public String getMqttBrokerUrl() {
return mqttBrokerUrl; return mqttBrokerUrl;
} }

View File

@ -1,7 +1,9 @@
package com.lanternsoftware.currentmonitor; package com.lanternsoftware.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower; import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
public interface PowerListener { public interface PowerListener {
void onPowerEvent(BreakerPower _power); void onPowerEvent(BreakerPower _power);
void onSampleEvent(HubSample _sample);
} }

View File

@ -1,6 +0,0 @@
package com.lanternsoftware.currentmonitor;
public class PowerSample {
public double voltage;
public double current;
}

View File

@ -0,0 +1,29 @@
package com.lanternsoftware.currentmonitor.adc;
import com.lanternsoftware.pigpio.Spi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MCP3008 {
protected static final Logger LOG = LoggerFactory.getLogger(MCP3008.class);
private static final byte[][] pins = new byte[8][];
private final Spi spi;
private final byte[] resp = new byte[3];
static {
for (int p = 0; p < 8; p++) {
pins[p] = new byte[]{1,(byte)(p + 8 << 4),0};
}
}
public MCP3008(Spi _spi) {
spi = _spi;
}
public int readPin(int _pin) {
if (spi != null && spi.transfer(pins[_pin], resp) > 2)
return ((resp[1] & 0x03) << 8) + (resp[2] & 0xFF);
return 0;
}
}

View File

@ -0,0 +1,15 @@
package com.lanternsoftware.currentmonitor.adc;
public class MCP3008Pin {
private final MCP3008 chip;
private final int pin;
public MCP3008Pin(MCP3008 _chip, int _pin) {
chip = _chip;
pin = _pin;
}
public int read() {
return chip.readPin(pin);
}
}

View File

@ -34,7 +34,9 @@ public class MonitorConfigSerializer extends AbstractDaoSerializer<MonitorConfig
d.put("debug", _o.isDebug()); d.put("debug", _o.isDebug());
d.put("connect_timeout", _o.getConnectTimeout()); d.put("connect_timeout", _o.getConnectTimeout());
d.put("socket_timeout", _o.getSocketTimeout()); d.put("socket_timeout", _o.getSocketTimeout());
d.put("post_samples", _o.isPostSamples());
d.put("needs_calibration", _o.isNeedsCalibration()); d.put("needs_calibration", _o.isNeedsCalibration());
d.put("accept_self_signed_certificates", _o.isAcceptSelfSignedCertificates());
d.put("mqtt_broker_url", _o.getMqttBrokerUrl()); d.put("mqtt_broker_url", _o.getMqttBrokerUrl());
d.put("mqtt_user_name", _o.getMqttUserName()); d.put("mqtt_user_name", _o.getMqttUserName());
d.put("mqtt_password", _o.getMqttPassword()); d.put("mqtt_password", _o.getMqttPassword());
@ -57,7 +59,9 @@ public class MonitorConfigSerializer extends AbstractDaoSerializer<MonitorConfig
o.setDebug(DaoSerializer.getBoolean(_d, "debug")); o.setDebug(DaoSerializer.getBoolean(_d, "debug"));
o.setConnectTimeout(DaoSerializer.getInteger(_d, "connect_timeout")); o.setConnectTimeout(DaoSerializer.getInteger(_d, "connect_timeout"));
o.setSocketTimeout(DaoSerializer.getInteger(_d, "socket_timeout")); o.setSocketTimeout(DaoSerializer.getInteger(_d, "socket_timeout"));
o.setPostSamples(DaoSerializer.getBoolean(_d, "post_samples"));
o.setNeedsCalibration(DaoSerializer.getBoolean(_d, "needs_calibration")); o.setNeedsCalibration(DaoSerializer.getBoolean(_d, "needs_calibration"));
o.setAcceptSelfSignedCertificates(DaoSerializer.getBoolean(_d, "accept_self_signed_certificates"));
o.setMqttBrokerUrl(DaoSerializer.getString(_d, "mqtt_broker_url")); o.setMqttBrokerUrl(DaoSerializer.getString(_d, "mqtt_broker_url"));
o.setMqttUserName(DaoSerializer.getString(_d, "mqtt_user_name")); o.setMqttUserName(DaoSerializer.getString(_d, "mqtt_user_name"));
o.setMqttPassword(DaoSerializer.getString(_d, "mqtt_password")); o.setMqttPassword(DaoSerializer.getString(_d, "mqtt_password"));

View File

@ -2,12 +2,6 @@
<configuration> <configuration>
<property name="log.pattern" value="%date %-5level %logger{0} - %message%n"/> <property name="log.pattern" value="%date %-5level %logger{0} - %message%n"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/opt/currentmonitor/log/log.txt</file> <file>/opt/currentmonitor/log/log.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
@ -21,6 +15,7 @@
</appender> </appender>
<logger name="com.lanternsoftware" level="INFO"/> <logger name="com.lanternsoftware" level="INFO"/>
<logger name="com.pi4j" level="INFO"/>
<root level="OFF"> <root level="OFF">
<appender-ref ref="FILE"/> <appender-ref ref="FILE"/>

View File

@ -1,31 +1,31 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.lanternsoftware.currentmonitor</groupId>
<artifactId>lantern-dataaccess-currentmonitor</artifactId> <artifactId>lantern-dataaccess-currentmonitor</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.0.0</version> <version>1.1.0</version>
<name>lantern-dataaccess-currentmonitor</name> <name>lantern-dataaccess-currentmonitor</name>
<properties> <parent>
<maven.compiler.source>1.8</maven.compiler.source> <groupId>com.lanternsoftware.currentmonitor</groupId>
<maven.compiler.target>1.8</maven.compiler.target> <artifactId>currentmonitor</artifactId>
</properties> <version>1.1.0</version>
</parent>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.lanternsoftware.currentmonitor</groupId> <groupId>com.lanternsoftware.currentmonitor</groupId>
<artifactId>lantern-datamodel-currentmonitor</artifactId> <artifactId>lantern-datamodel-currentmonitor</artifactId>
<version>1.0.0</version> <version>${cm.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.lanternsoftware.rules</groupId> <groupId>com.lanternsoftware.rules</groupId>
<artifactId>lantern-datamodel-rules</artifactId> <artifactId>lantern-datamodel-rules</artifactId>
<version>1.0.0</version> <version>${rules.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.lanternsoftware.util</groupId> <groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-dao-mongo</artifactId> <artifactId>lantern-util-dao-mongo</artifactId>
<version>1.0.0</version> <version>${util.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mindrot</groupId> <groupId>org.mindrot</groupId>
@ -43,7 +43,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version> <version>3.10.1</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
@ -56,14 +56,14 @@
<optimize>true</optimize> <optimize>true</optimize>
<showDeprecation>true</showDeprecation> <showDeprecation>true</showDeprecation>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
<source>1.8</source> <source>${maven.compiler.source}</source>
<target>1.8</target> <target>${maven.compiler.target}</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>
<version>2.4</version> <version>3.2.1</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>
@ -73,16 +73,6 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<configuration>
<archive>
<index>true</index>
</archive>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -4,22 +4,31 @@ import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.util.DateUtils; import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.DebugTimer; import com.lanternsoftware.util.DebugTimer;
import com.lanternsoftware.util.concurrency.ConcurrencyUtils;
import com.lanternsoftware.util.external.LanternFiles; import com.lanternsoftware.util.external.LanternFiles;
import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoQuery; import com.lanternsoftware.util.dao.DaoQuery;
import com.lanternsoftware.util.dao.DaoSort; import com.lanternsoftware.util.dao.DaoSort;
import com.lanternsoftware.util.dao.mongo.MongoConfig; import com.lanternsoftware.util.dao.mongo.MongoConfig;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class BackupMinutes { public class BackupMinutes {
public static void main(String[] args) { public static void main(String[] args) {
CurrentMonitorDao dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg")); CurrentMonitorDao sourceDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
CurrentMonitorDao backupDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_DEST_PATH + "mongo.cfg")); CurrentMonitorDao destDao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.BACKUP_DEST_PATH + "mongo.cfg"));
Date now = new Date(); ExecutorService ex = Executors.newFixedThreadPool(8);
for (Account a : dao.getProxy().queryAll(Account.class)) { List<Future<?>> tasks = new ArrayList<>();
for (Account a : sourceDao.getProxy().queryAll(Account.class)) {
if (a.getId() == 0) if (a.getId() == 0)
continue; continue;
DebugTimer t = new DebugTimer("Account " + a.getId()); DebugTimer t = new DebugTimer("Account " + a.getId());
@ -27,28 +36,35 @@ public class BackupMinutes {
a.setTimezone("America/Chicago"); a.setTimezone("America/Chicago");
} }
TimeZone tz = TimeZone.getTimeZone(a.getTimezone()); TimeZone tz = TimeZone.getTimeZone(a.getTimezone());
HubPowerMinute minute = dao.getProxy().queryOne(HubPowerMinute.class, new DaoQuery("account_id", a.getId()), DaoSort.sort("minute")); HubPowerMinute firstMinute = sourceDao.getProxy().queryOne(HubPowerMinute.class, new DaoQuery("account_id", a.getId()), DaoSort.sort("minute"));
if (minute == null) if (firstMinute == null)
continue; continue;
HubPowerMinute lastBackup = backupDao.getProxy().queryOne(HubPowerMinute.class, new DaoQuery("account_id", a.getId()), DaoSort.sortDesc("minute")); HubPowerMinute lastMinute = sourceDao.getProxy().queryOne(HubPowerMinute.class, new DaoQuery("account_id", a.getId()), DaoSort.sortDesc("minute"));
Date start = lastBackup == null ? DateUtils.getMidnightBefore(minute.getMinuteAsDate(), tz) : lastBackup.getMinuteAsDate(); HubPowerMinute lastBackup = destDao.getProxy().queryOne(HubPowerMinute.class, new DaoQuery("account_id", a.getId()), DaoSort.sortDesc("minute"));
// Date start = DateUtils.date(10,16,2021,tz); Date start = lastBackup == null ? DateUtils.getMidnightBefore(firstMinute.getMinuteAsDate(), tz) : lastBackup.getMinuteAsDate();
Date lastMin = lastMinute.getMinuteAsDate();
Date end = DateUtils.addDays(start, 1, tz); Date end = DateUtils.addDays(start, 1, tz);
while (start.before(now)) { while (start.before(lastMin)) {
DebugTimer t2 = new DebugTimer("Account Id: " + a.getId() + " Query Day " + DateUtils.format("MM/dd/yyyy", tz, start)); final Date curStart = start;
List<HubPowerMinute> minutes = dao.getProxy().query(HubPowerMinute.class, new DaoQuery("account_id", a.getId()).andBetweenInclusiveExclusive("minute", (int) (start.getTime() / 60000), (int) (end.getTime() / 60000))); final Date curEnd = end;
t2.stop(); tasks.add(ex.submit(() -> {
if (!minutes.isEmpty()) { DebugTimer t2 = new DebugTimer("Account Id: " + a.getId() + " Query Day " + DateUtils.format("MM/dd/yyyy", tz, curStart));
DebugTimer t3 = new DebugTimer("Save Day"); List<HubPowerMinute> minutes = sourceDao.getProxy().query(HubPowerMinute.class, new DaoQuery("account_id", a.getId()).andBetweenInclusiveExclusive("minute", (int) (curStart.getTime() / 60000), (int) (curEnd.getTime() / 60000)));
backupDao.getProxy().save(minutes); t2.stop();
t3.stop(); if (!minutes.isEmpty()) {
} DebugTimer t3 = new DebugTimer("Save Day");
destDao.getProxy().save(minutes);
t3.stop();
}
}));
start = end; start = end;
end = DateUtils.addDays(end, 1, tz); end = DateUtils.addDays(end, 1, tz);
} }
t.stop(); t.stop();
} }
dao.shutdown(); ConcurrencyUtils.getAll(tasks);
backupDao.shutdown(); ex.shutdown();
sourceDao.shutdown();
destDao.shutdown();
} }
} }

View File

@ -8,6 +8,7 @@ import com.lanternsoftware.datamodel.currentmonitor.EnergyViewMode;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand; import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute; import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.datamodel.currentmonitor.archive.ArchiveStatus; import com.lanternsoftware.datamodel.currentmonitor.archive.ArchiveStatus;
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
import com.lanternsoftware.util.DateRange; import com.lanternsoftware.util.DateRange;
import com.lanternsoftware.util.dao.auth.AuthCode; import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.util.dao.mongo.MongoProxy; import com.lanternsoftware.util.dao.mongo.MongoProxy;
@ -44,7 +45,6 @@ public interface CurrentMonitorDao {
void putConfig(BreakerConfig _config); void putConfig(BreakerConfig _config);
void rebuildSummaries(int _accountId); void rebuildSummaries(int _accountId);
void rebuildSummariesAsync(int _accountId);
void rebuildSummaries(int _accountId, Date _start, Date _end); void rebuildSummaries(int _accountId, Date _start, Date _end);
String addPasswordResetKey(String _email); String addPasswordResetKey(String _email);
@ -54,6 +54,7 @@ public interface CurrentMonitorDao {
String getAuthCodeForEmail(String _email, TimeZone _tz); String getAuthCodeForEmail(String _email, TimeZone _tz);
Account authCodeToAccount(String _authCode); Account authCodeToAccount(String _authCode);
AuthCode decryptAuthCode(String _authCode); AuthCode decryptAuthCode(String _authCode);
String exchangeAuthCode(String _authCode, int _acctId);
Account putAccount(Account _account); Account putAccount(Account _account);
Account getAccount(int _accountId); Account getAccount(int _accountId);
@ -65,5 +66,8 @@ public interface CurrentMonitorDao {
List<HubCommand> getAllHubCommands(); List<HubCommand> getAllHubCommands();
void deleteHubCommand(String _id); void deleteHubCommand(String _id);
void putHubSample(HubSample _sample);
List<HubSample> getSamplesForAccount(int _accountId);
MongoProxy getProxy(); MongoProxy getProxy();
} }

View File

@ -21,6 +21,7 @@ import com.lanternsoftware.datamodel.currentmonitor.archive.ArchiveStatus;
import com.lanternsoftware.datamodel.currentmonitor.archive.BreakerEnergyArchive; import com.lanternsoftware.datamodel.currentmonitor.archive.BreakerEnergyArchive;
import com.lanternsoftware.datamodel.currentmonitor.archive.DailyEnergyArchive; import com.lanternsoftware.datamodel.currentmonitor.archive.DailyEnergyArchive;
import com.lanternsoftware.datamodel.currentmonitor.archive.MonthlyEnergyArchive; import com.lanternsoftware.datamodel.currentmonitor.archive.MonthlyEnergyArchive;
import com.lanternsoftware.datamodel.currentmonitor.hub.HubSample;
import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.DateRange; import com.lanternsoftware.util.DateRange;
import com.lanternsoftware.util.DateUtils; import com.lanternsoftware.util.DateUtils;
@ -90,6 +91,9 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
updateEnergySummaries(minute); updateEnergySummaries(minute);
} }
proxy.delete(DirtyMinute.class, new DaoQuery()); proxy.delete(DirtyMinute.class, new DaoQuery());
if (!proxy.exists(Sequence.class, null)) {
proxy.save(new Sequence());
}
} }
public void shutdown() { public void shutdown() {
@ -303,7 +307,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
TimeZone tz = getTimeZoneForAccount(_accountId); TimeZone tz = getTimeZoneForAccount(_accountId);
Date month = DateUtils.getStartOfMonth(range.getStart(), tz); Date month = DateUtils.getStartOfMonth(range.getStart(), tz);
Date end = DateUtils.getEndOfMonth(range.getEnd(), tz); Date end = DateUtils.getEndOfMonth(range.getEnd(), tz);
while (month.before(end)) { while ((month != null) && month.before(end)) {
statuses.computeIfAbsent(month, _m->new ArchiveStatus(_accountId, _m, 0)); statuses.computeIfAbsent(month, _m->new ArchiveStatus(_accountId, _m, 0));
month = DateUtils.addMonths(month, 1, tz); month = DateUtils.addMonths(month, 1, tz);
} }
@ -326,6 +330,8 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
TimeZone tz = getTimeZoneForAccount(_minute.getAccountId()); TimeZone tz = getTimeZoneForAccount(_minute.getAccountId());
BreakerConfig config = getConfig(_minute.getAccountId()); BreakerConfig config = getConfig(_minute.getAccountId());
BreakerGroup group = CollectionUtils.getFirst(config.getBreakerGroups()); BreakerGroup group = CollectionUtils.getFirst(config.getBreakerGroups());
if (group == null)
return;
Date day = DateUtils.getMidnightBefore(_minute.getMinuteAsDate(), tz); Date day = DateUtils.getMidnightBefore(_minute.getMinuteAsDate(), tz);
DebugTimer t2 = new DebugTimer("Updating energy", logger); DebugTimer t2 = new DebugTimer("Updating energy", logger);
EnergySummary energy = getEnergySummary(_minute.getAccountId(), group.getId(), EnergyViewMode.DAY, day); EnergySummary energy = getEnergySummary(_minute.getAccountId(), group.getId(), EnergyViewMode.DAY, day);
@ -412,13 +418,13 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
putEnergySummary(summary); putEnergySummary(summary);
} }
private void updateChargeSummary(BreakerConfig _config, EnergySummary _energySummary, TimeZone _tz) { public void updateChargeSummary(BreakerConfig _config, EnergySummary _energySummary, TimeZone _tz) {
Date lookback = null; Date lookback = null;
for (BillingPlan p : CollectionUtils.makeNotNull(_config.getBillingPlans())) { for (BillingPlan p : CollectionUtils.makeNotNull(_config.getBillingPlans())) {
Date cycleStart = p.getBillingCycleStart(_energySummary.getStart(), _tz); Date cycleStart = p.getBillingCycleStart(_energySummary.getStart(), _tz);
if (cycleStart.after(_energySummary.getStart())) if (cycleStart.after(_energySummary.getStart()))
cycleStart = DateUtils.addMonths(cycleStart, -1, _tz); cycleStart = DateUtils.addMonths(cycleStart, -1, _tz);
if ((lookback == null) || cycleStart.before(lookback)) if ((lookback == null) || ((cycleStart != null) && cycleStart.before(lookback)))
lookback = cycleStart; lookback = cycleStart;
} }
if (lookback != null) { if (lookback != null) {
@ -462,7 +468,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
Date yearMonthStart = yearStart; Date yearMonthStart = yearStart;
Set<String> monthSummaryIds = new HashSet<>(); Set<String> monthSummaryIds = new HashSet<>();
Date loopEnd = DateUtils.addDays(yearEnd, 1, _tz); Date loopEnd = DateUtils.addDays(yearEnd, 1, _tz);
while (yearMonthStart.before(loopEnd)) { while ((yearMonthStart != null) && yearMonthStart.before(loopEnd)) {
Date billingStart = plan.getBillingCycleStart(yearMonthStart, _tz); Date billingStart = plan.getBillingCycleStart(yearMonthStart, _tz);
if (DateUtils.isBetween(billingStart, yearStart, yearEnd)) if (DateUtils.isBetween(billingStart, yearStart, yearEnd))
monthSummaryIds.add(ChargeSummary.toId(rootGroup.getAccountId(), plan.getPlanId(), rootGroup.getId(), EnergyViewMode.MONTH, billingStart)); monthSummaryIds.add(ChargeSummary.toId(rootGroup.getAccountId(), plan.getPlanId(), rootGroup.getId(), EnergyViewMode.MONTH, billingStart));
@ -482,17 +488,13 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
} }
} }
@Override
public void rebuildSummariesAsync(int _accountId) {
executor.submit(() -> rebuildSummaries(_accountId));
}
@Override @Override
public void rebuildSummaries(int _accountId) { public void rebuildSummaries(int _accountId) {
HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sort("minute")); HubPowerMinute firstMinute = proxy.queryOne(HubPowerMinute.class, new DaoQuery("account_id", _accountId), DaoSort.sort("minute"));
if (firstMinute == null) if (firstMinute == null)
return; 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 @Override
@ -660,7 +662,7 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
if (NullUtils.isEmpty(_username) || NullUtils.isEmpty(_password)) if (NullUtils.isEmpty(_username) || NullUtils.isEmpty(_password))
return null; return null;
Account acct = proxy.queryOne(Account.class, new DaoQuery("username", _username.toLowerCase().trim())); Account acct = proxy.queryOne(Account.class, new DaoQuery("username", _username.toLowerCase().trim()));
if ((acct == null) || !BCrypt.checkpw(_password, acct.getPassword())) if ((acct == null) || !BCrypt.checkpw(_password, NullUtils.makeNotNull(acct.getPassword())))
return null; return null;
return toAuthCode(acct.getId(), acct.getAuxiliaryAccountIds()); return toAuthCode(acct.getId(), acct.getAuxiliaryAccountIds());
} }
@ -691,6 +693,11 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
return (account == null)?null:toAuthCode(account.getId(), account.getAuxiliaryAccountIds()); return (account == null)?null:toAuthCode(account.getId(), account.getAuxiliaryAccountIds());
} }
@Override
public String exchangeAuthCode(String _authCode, int _acctId) {
return null;
}
public String toAuthCode(int _acctId, List<Integer> _auxAcctIds) { public String toAuthCode(int _acctId, List<Integer> _auxAcctIds) {
if (_acctId < 1) if (_acctId < 1)
return null; return null;
@ -777,6 +784,8 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
if (entity == null) if (entity == null)
return false; return false;
Account acct = getAccountByUsername(aes.decryptFromBase64ToString(_key)); Account acct = getAccountByUsername(aes.decryptFromBase64ToString(_key));
if (acct == null)
return false;
acct.setPassword(_password); acct.setPassword(_password);
putAccount(acct); putAccount(acct);
proxy.delete("password_reset", new DaoQuery("_id", _key)); proxy.delete("password_reset", new DaoQuery("_id", _key));
@ -800,6 +809,16 @@ public class MongoCurrentMonitorDao implements CurrentMonitorDao {
proxy.delete(HubCommand.class, new DaoQuery("_id", _id)); proxy.delete(HubCommand.class, new DaoQuery("_id", _id));
} }
@Override
public void putHubSample(HubSample _sample) {
proxy.save(_sample);
}
@Override
public List<HubSample> getSamplesForAccount(int _accountId) {
return proxy.query(HubSample.class, new DaoQuery("account_id", _accountId));
}
@Override @Override
public MongoProxy getProxy() { public MongoProxy getProxy() {
return proxy; return proxy;

View File

@ -1,21 +1,21 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.lanternsoftware.currentmonitor</groupId>
<artifactId>lantern-datamodel-currentmonitor</artifactId> <artifactId>lantern-datamodel-currentmonitor</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.0.0</version> <version>1.1.0</version>
<name>lantern-datamodel-currentmonitor</name> <name>lantern-datamodel-currentmonitor</name>
<properties> <parent>
<maven.compiler.source>1.8</maven.compiler.source> <groupId>com.lanternsoftware.currentmonitor</groupId>
<maven.compiler.target>1.8</maven.compiler.target> <artifactId>currentmonitor</artifactId>
</properties> <version>1.1.0</version>
</parent>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.lanternsoftware.util</groupId> <groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-dao</artifactId> <artifactId>lantern-util-dao</artifactId>
<version>1.0.0</version> <version>${util.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
@ -28,7 +28,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version> <version>3.10.1</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
@ -41,14 +41,14 @@
<optimize>true</optimize> <optimize>true</optimize>
<showDeprecation>true</showDeprecation> <showDeprecation>true</showDeprecation>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
<source>1.8</source> <source>${maven.compiler.source}</source>
<target>1.8</target> <target>${maven.compiler.target}</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>
<version>2.4</version> <version>3.2.1</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>
@ -58,16 +58,6 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<configuration>
<archive>
<index>true</index>
</archive>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -21,11 +21,13 @@ public class Breaker implements IIdentical<Breaker> {
private String name; private String name;
private String description; private String description;
private int sizeAmps; private int sizeAmps;
private int phaseOffsetNs;
private double calibrationFactor; private double calibrationFactor;
private double lowPassFilter; private double lowPassFilter;
private BreakerPolarity polarity; private BreakerPolarity polarity;
private boolean doublePower; private boolean doublePower;
private BreakerType type; private BreakerType type;
private boolean main;
private transient String key; private transient String key;
public Breaker() { public Breaker() {
@ -139,6 +141,14 @@ public class Breaker implements IIdentical<Breaker> {
sizeAmps = _sizeAmps; sizeAmps = _sizeAmps;
} }
public int getPhaseOffsetNs() {
return phaseOffsetNs;
}
public void setPhaseOffsetNs(int _phaseOffsetNs) {
phaseOffsetNs = _phaseOffsetNs;
}
public double getLowPassFilter() { public double getLowPassFilter() {
return Math.abs(lowPassFilter) < 0.05 ? 1.6 : lowPassFilter; return Math.abs(lowPassFilter) < 0.05 ? 1.6 : lowPassFilter;
} }
@ -148,7 +158,7 @@ public class Breaker implements IIdentical<Breaker> {
} }
public BreakerPolarity getPolarity() { public BreakerPolarity getPolarity() {
return polarity; return polarity == null ? BreakerPolarity.NORMAL : polarity;
} }
public void setPolarity(BreakerPolarity _polarity) { public void setPolarity(BreakerPolarity _polarity) {
@ -184,6 +194,14 @@ public class Breaker implements IIdentical<Breaker> {
type = _type; type = _type;
} }
public boolean isMain() {
return main;
}
public void setMain(boolean _main) {
main = _main;
}
public double getFinalCalibrationFactor() { public double getFinalCalibrationFactor() {
return getCalibrationFactor() * getSizeAmps() / 380.0; return getCalibrationFactor() * getSizeAmps() / 380.0;
} }
@ -269,7 +287,7 @@ public class Breaker implements IIdentical<Breaker> {
@Override @Override
public boolean isIdentical(Breaker _o) { public boolean isIdentical(Breaker _o) {
if (this == _o) return true; 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 && Objects.equals(key, _o.key); 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 @Override

View File

@ -10,6 +10,7 @@ import com.lanternsoftware.util.dao.annotations.PrimaryKey;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
@DBSerializable(autogen = false) @DBSerializable(autogen = false)
public class BreakerConfig implements IIdentical<BreakerConfig> { public class BreakerConfig implements IIdentical<BreakerConfig> {
@ -202,8 +203,20 @@ public class BreakerConfig implements IIdentical<BreakerConfig> {
return null; return null;
} }
public boolean containsPolarity(Set<String> _groupIds, BreakerPolarity _polarity) {
for (BreakerGroup subGroup : CollectionUtils.makeNotNull(breakerGroups)) {
if (subGroup.containsPolarity(_groupIds, _polarity))
return true;
}
return false;
}
public BillingCurrency getCurrency() { public BillingCurrency getCurrency() {
return CollectionUtils.getFirst(CollectionUtils.transformToSet(billingRates, BillingRate::getCurrency)); return CollectionUtils.getFirst(CollectionUtils.transformToSet(CollectionUtils.aggregate(billingPlans, BillingPlan::getRates), BillingRate::getCurrency));
}
public boolean isMainsPowerTrackedForMeter(int _meter) {
return CollectionUtils.anyQualify(getAllBreakers(), _b->_b.isMain() && (_b.getMeter() == _meter));
} }
@Override @Override

View File

@ -182,6 +182,20 @@ public class BreakerGroup implements IIdentical<BreakerGroup> {
return groups; return groups;
} }
public boolean containsPolarity(Set<String> _groupIds, BreakerPolarity _polarity) {
if ((CollectionUtils.isEmpty(_groupIds) || _groupIds.contains(id)) && CollectionUtils.anyQualify(breakers, _b->_b.getPolarity() == _polarity))
return true;
for (BreakerGroup subGroup : CollectionUtils.makeNotNull(subGroups)) {
if (subGroup.containsPolarity(_groupIds, _polarity))
return true;
}
return false;
}
public boolean isMain() {
return CollectionUtils.anyQualify(breakers, Breaker::isMain);
}
public boolean removeInvalidGroups(Set<Integer> _validPanels) { public boolean removeInvalidGroups(Set<Integer> _validPanels) {
if (subGroups != null) if (subGroups != null)
subGroups.removeIf(_g->!_g.removeInvalidGroups(_validPanels)); subGroups.removeIf(_g->!_g.removeInvalidGroups(_validPanels));

View File

@ -11,6 +11,8 @@ public class BreakerHub implements IIdentical<BreakerHub> {
private int hub; private int hub;
private double voltageCalibrationFactor; private double voltageCalibrationFactor;
private double portCalibrationFactor; private double portCalibrationFactor;
private int phaseCnt;
private int phaseOffsetNs;
private int frequency; private int frequency;
private String bluetoothMac; private String bluetoothMac;
@ -39,15 +41,31 @@ public class BreakerHub implements IIdentical<BreakerHub> {
} }
public double getPortCalibrationFactor() { public double getPortCalibrationFactor() {
return portCalibrationFactor == 0.0?1.25:portCalibrationFactor; return portCalibrationFactor == 0.0?1.20:portCalibrationFactor;
} }
public void setPortCalibrationFactor(double _portCalibrationFactor) { public void setPortCalibrationFactor(double _portCalibrationFactor) {
portCalibrationFactor = _portCalibrationFactor; 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() { public int getFrequency() {
return frequency; return frequency == 0 ? 60 : frequency;
} }
public void setFrequency(int _frequency) { public void setFrequency(int _frequency) {
@ -73,7 +91,7 @@ public class BreakerHub implements IIdentical<BreakerHub> {
@Override @Override
public boolean isIdentical(BreakerHub _o) { public boolean isIdentical(BreakerHub _o) {
if (this == _o) return true; 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 @Override

View File

@ -2,5 +2,7 @@ package com.lanternsoftware.datamodel.currentmonitor;
public enum BreakerPolarity { public enum BreakerPolarity {
NORMAL, NORMAL,
SOLAR; SOLAR,
BI_DIRECTIONAL,
BI_DIRECTIONAL_INVERTED
} }

View File

@ -0,0 +1,52 @@
package com.lanternsoftware.datamodel.currentmonitor;
import com.lanternsoftware.util.dao.annotations.DBSerializable;
@DBSerializable
public class EmailCredentials {
private EmailProvider provider;
private String apiKey;
private String apiSecret;
private String emailFrom;
private String serverUrlBase;
public EmailProvider getProvider() {
return provider;
}
public void setProvider(EmailProvider _provider) {
provider = _provider;
}
public String getApiKey() {
return apiKey;
}
public void setApiKey(String _apiKey) {
apiKey = _apiKey;
}
public String getApiSecret() {
return apiSecret;
}
public void setApiSecret(String _apiSecret) {
apiSecret = _apiSecret;
}
public String getEmailFrom() {
return emailFrom;
}
public void setEmailFrom(String _emailFrom) {
emailFrom = _emailFrom;
}
public String getServerUrlBase() {
return serverUrlBase;
}
public void setServerUrlBase(String _serverUrlBase) {
serverUrlBase = _serverUrlBase;
}
}

View File

@ -0,0 +1,6 @@
package com.lanternsoftware.datamodel.currentmonitor;
public enum EmailProvider {
SENDGRID,
MAILJET
}

View File

@ -8,7 +8,6 @@ import com.lanternsoftware.util.dao.annotations.DBSerializable;
import com.lanternsoftware.util.mutable.MutableDouble; import com.lanternsoftware.util.mutable.MutableDouble;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@ -26,6 +25,7 @@ public class EnergySummary {
private EnergyViewMode viewMode; private EnergyViewMode viewMode;
private Date start; private Date start;
private List<EnergySummary> subGroups; private List<EnergySummary> subGroups;
private boolean main;
private float[] energy; private float[] energy;
private float[] gridEnergy; private float[] gridEnergy;
private double peakToGrid; private double peakToGrid;
@ -40,6 +40,7 @@ public class EnergySummary {
public EnergySummary(BreakerGroup _group, List<HubPowerMinute> _power, EnergyViewMode _viewMode, Date _start, TimeZone _timezone) { public EnergySummary(BreakerGroup _group, List<HubPowerMinute> _power, EnergyViewMode _viewMode, Date _start, TimeZone _timezone) {
groupId = _group.getId(); groupId = _group.getId();
groupName = _group.getName(); groupName = _group.getName();
main = _group.isMain();
viewMode = _viewMode; viewMode = _viewMode;
start = _start; start = _start;
accountId = _group.getAccountId(); accountId = _group.getAccountId();
@ -50,29 +51,30 @@ public class EnergySummary {
} }
public void addEnergy(BreakerGroup _group, List<HubPowerMinute> _hubPower) { public void addEnergy(BreakerGroup _group, List<HubPowerMinute> _hubPower) {
Map<String, Breaker> breakers = CollectionUtils.transformToMap(_group.getAllBreakers(), Breaker::getKey); Map<Integer, Breaker> breakers = CollectionUtils.transformToMap(_group.getAllBreakers(), Breaker::getIntKey);
Map<String, BreakerGroup> breakerKeyToGroup = new HashMap<>(); Map<Integer, BreakerGroup> breakerKeyToGroup = new HashMap<>();
for (BreakerGroup group : _group.getAllBreakerGroups()) { for (BreakerGroup group : _group.getAllBreakerGroups()) {
for (Breaker b : CollectionUtils.makeNotNull(group.getBreakers())) { for (Breaker b : CollectionUtils.makeNotNull(group.getBreakers())) {
breakerKeyToGroup.put(b.getKey(), group); breakerKeyToGroup.put(b.getIntKey(), group);
} }
} }
addEnergy(breakers, breakerKeyToGroup, _hubPower); addEnergy(breakers, breakerKeyToGroup, _hubPower);
} }
public void addEnergy(Map<String, Breaker> _breakers, Map<String, BreakerGroup> _breakerKeyToGroup, List<HubPowerMinute> _hubPower) { public void addEnergy(Map<Integer, Breaker> _breakers, Map<Integer, BreakerGroup> _breakerKeyToGroup, List<HubPowerMinute> _hubPower) {
if (CollectionUtils.isEmpty(_hubPower) || CollectionUtils.anyQualify(_hubPower, _p -> _p.getAccountId() != accountId)) if (CollectionUtils.isEmpty(_hubPower) || CollectionUtils.anyQualify(_hubPower, _p -> _p.getAccountId() != accountId))
return; return;
_hubPower.sort(Comparator.comparing(HubPowerMinute::getMinute)); _hubPower.sort(Comparator.comparing(HubPowerMinute::getMinute));
for (Date minute : CollectionUtils.transformToSet(_hubPower, HubPowerMinute::getMinuteAsDate)) { for (Date minute : CollectionUtils.transformToSet(_hubPower, HubPowerMinute::getMinuteAsDate)) {
resetEnergy(minute); resetEnergy(minute);
} }
Set<Integer> meterMainsTracked = CollectionUtils.transformToSet(CollectionUtils.filter(_breakers.values(), Breaker::isMain), Breaker::getMeter);
int idx; int idx;
Map<Integer, Map<Integer, MeterMinute>> minutes = new HashMap<>(); Map<Integer, Map<Integer, MeterMinute>> minutes = new HashMap<>();
for (HubPowerMinute hubPower : _hubPower) { for (HubPowerMinute hubPower : _hubPower) {
Date minute = hubPower.getMinuteAsDate(); Date minute = hubPower.getMinuteAsDate();
for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) { for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) {
String key = breaker.breakerKey(); int key = breaker.breakerIntKey();
Breaker b = _breakers.get(key); Breaker b = _breakers.get(key);
if (b == null) if (b == null)
continue; continue;
@ -84,10 +86,12 @@ public class EnergySummary {
for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) { for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) {
if (idx >= 60) if (idx >= 60)
break; break;
if (power > 0) if (!meterMainsTracked.contains(b.getMeter()) || b.isMain()) {
meter.usage[idx] += power; if (power > 0)
else meter.usage[idx] += power;
meter.solar[idx] -= power; else
meter.solar[idx] -= power;
}
addEnergy(group.getId(), minute, power); addEnergy(group.getId(), minute, power);
idx++; idx++;
} }
@ -125,7 +129,7 @@ public class EnergySummary {
for (HubPowerMinute hubPower : _hubPower) { for (HubPowerMinute hubPower : _hubPower) {
Date minute = hubPower.getMinuteAsDate(); Date minute = hubPower.getMinuteAsDate();
for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) { for (BreakerPowerMinute breaker : CollectionUtils.makeNotNull(hubPower.getBreakers())) {
String key = breaker.breakerKey(); int key = breaker.breakerIntKey();
Breaker b = _breakers.get(key); Breaker b = _breakers.get(key);
if (b == null) if (b == null)
continue; continue;
@ -136,9 +140,9 @@ public class EnergySummary {
idx = 0; idx = 0;
double flow = 0.0; double flow = 0.0;
for (Float power : CollectionUtils.makeNotNull(breaker.getReadings())) { 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]); 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]); flow += meter.flow[idx] * (power / meter.usage[idx]);
idx++; idx++;
} }
@ -148,11 +152,11 @@ public class EnergySummary {
} }
public void resetEnergy(Date _readTime) { public void resetEnergy(Date _readTime) {
if (energy == null) if (energy != null) {
return; int idx = viewMode.blockIndex(start, _readTime, timezone);
int idx = viewMode.blockIndex(start, _readTime, timezone); if (idx < energy.length)
if (idx < energy.length) energy[idx] = 0f;
energy[idx] = 0f; }
for (EnergySummary subGroup : CollectionUtils.makeNotNull(subGroups)) { for (EnergySummary subGroup : CollectionUtils.makeNotNull(subGroups)) {
subGroup.resetEnergy(_readTime); subGroup.resetEnergy(_readTime);
} }
@ -288,6 +292,14 @@ public class EnergySummary {
subGroups = _subGroups; subGroups = _subGroups;
} }
public boolean isMain() {
return main;
}
public void setMain(boolean _main) {
main = _main;
}
public float[] getEnergy() { public float[] getEnergy() {
return energy; return energy;
} }
@ -362,7 +374,7 @@ public class EnergySummary {
public double joules(Set<String> _selectedBreakers, boolean _includeSubgroups, GridFlow _mode) { public double joules(Set<String> _selectedBreakers, boolean _includeSubgroups, GridFlow _mode) {
double joules = 0.0; double joules = 0.0;
if (_includeSubgroups) { if (_includeSubgroups && !isMain()) {
for (EnergySummary group : CollectionUtils.makeNotNull(subGroups)) { for (EnergySummary group : CollectionUtils.makeNotNull(subGroups)) {
joules += group.joules(_selectedBreakers, true, _mode); joules += group.joules(_selectedBreakers, true, _mode);
} }

View File

@ -21,7 +21,7 @@ public enum HubConfigCharacteristic {
Shutdown(13, CharacteristicFlag.WRITE), Shutdown(13, CharacteristicFlag.WRITE),
Version(14, CharacteristicFlag.READ), Version(14, CharacteristicFlag.READ),
Update(15, CharacteristicFlag.WRITE), Update(15, CharacteristicFlag.WRITE),
ReloadConfig(15, CharacteristicFlag.WRITE); ReloadConfig(16, CharacteristicFlag.WRITE);
public final int idx; public final int idx;
public final UUID uuid; public final UUID uuid;

View File

@ -8,7 +8,7 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
public class HubConfigService { public abstract class HubConfigService {
public static final UUIDFormatter uuidFormat = new UUIDFormatter("c5650001-d50f-49af-b906-cada0dc17937"); public static final UUIDFormatter uuidFormat = new UUIDFormatter("c5650001-d50f-49af-b906-cada0dc17937");
private static final AESTool aes = new AESTool(37320708309265127L,-8068168662055796771L,-4867793276337148572L,4425609941731230765L); private static final AESTool aes = new AESTool(37320708309265127L,-8068168662055796771L,-4867793276337148572L,4425609941731230765L);
private static final UUID serviceUUID = uuidFormat.format(1); private static final UUID serviceUUID = uuidFormat.format(1);
@ -20,7 +20,7 @@ public class HubConfigService {
return serviceUUID; return serviceUUID;
} }
public List<HubConfigCharacteristic> getCharacteristics() { public static List<HubConfigCharacteristic> getCharacteristics() {
return Arrays.asList(HubConfigCharacteristic.values()); return Arrays.asList(HubConfigCharacteristic.values());
} }

View File

@ -36,6 +36,10 @@ public class NetworkStatus {
pingSuccessful = _pingSuccessful; pingSuccessful = _pingSuccessful;
} }
public boolean isNetworkConnected() {
return isWifiConnected() || isEthernetConnected();
}
public boolean isWifiConnected() { public boolean isWifiConnected() {
return CollectionUtils.isNotEmpty(wifiIPs); return CollectionUtils.isNotEmpty(wifiIPs);
} }

View File

@ -15,59 +15,45 @@ import java.util.concurrent.atomic.AtomicInteger;
public class BOM { public class BOM {
List<LineItem> lineItems; List<LineItem> lineItems;
private static final Map<Integer, String> ctSizes = new TreeMap<>();
static {
ctSizes.put(15, "https://store.lanternpowermonitor.com/product/15a-yhdc-current-transformer/3");
ctSizes.put(20, "https://store.lanternpowermonitor.com/product/20a-yhdc-current-transformer/4");
ctSizes.put(30, "https://store.lanternpowermonitor.com/product/30a-yhdc-current-transformer/5");
ctSizes.put(50, "https://store.lanternpowermonitor.com/product/50a-yhdc-current-transformer/6");
ctSizes.put(60, "https://store.lanternpowermonitor.com/product/60a-yhdc-current-transformer/7");
ctSizes.put(100, "https://store.lanternpowermonitor.com/product/100a-yhdc-current-transformer/8");
}
public static BOM fromConfig(BreakerConfig _config) { public static BOM fromConfig(BreakerConfig _config) {
BOM bom = new BOM(); BOM bom = new BOM();
bom.setLineItems(new ArrayList<>()); bom.setLineItems(new ArrayList<>());
Map<Integer, AtomicInteger> ctCnts = new TreeMap<>(); Map<Integer, AtomicInteger> ctCnts = new TreeMap<>();
Map<Integer, AtomicInteger> ctDuplicates = new TreeMap<>(); Map<Integer, Breaker> breakers = CollectionUtils.transformToMap(_config.getAllBreakers(), Breaker::getIntKey);
for (Breaker breaker : CollectionUtils.makeNotNull(_config.getAllBreakers())) { for (Breaker breaker : breakers.values()) {
if (breaker.getSizeAmps() <= 20) { if (bom.isUntrackedBottom(breakers, breaker))
ctCnts.computeIfAbsent(20, (_k) -> new AtomicInteger(0)).getAndIncrement(); continue;
if (breaker.getType() == BreakerType.DOUBLE_POLE_TOP_ONE_CT) for (int size : ctSizes.keySet()) {
ctDuplicates.computeIfAbsent(20, (_k) -> new AtomicInteger(0)).getAndIncrement(); if (breaker.getSizeAmps() <= size) {
ctCnts.computeIfAbsent(size, (_k) -> new AtomicInteger(0)).getAndIncrement();
break;
}
} }
else if (breaker.getSizeAmps() <= 30) {
ctCnts.computeIfAbsent(30, (_k) -> new AtomicInteger(0)).getAndIncrement();
if (breaker.getType() == BreakerType.DOUBLE_POLE_TOP_ONE_CT)
ctDuplicates.computeIfAbsent(30, (_k) -> new AtomicInteger(0)).getAndIncrement();
}
else {
ctCnts.computeIfAbsent(50, (_k) -> new AtomicInteger(0)).getAndIncrement();
if (breaker.getType() == BreakerType.DOUBLE_POLE_TOP_ONE_CT)
ctDuplicates.computeIfAbsent(50, (_k) -> new AtomicInteger(0)).getAndIncrement();
}
}
for (Map.Entry<Integer, AtomicInteger> ctCnt : ctDuplicates.entrySet()) {
AtomicInteger cnt = ctCnts.get(ctCnt.getKey());
if (cnt != null)
cnt.getAndAdd(-ctCnt.getValue().get());
} }
int breakerCnt = CollectionUtils.sumIntegers(CollectionUtils.transform(ctCnts.values(), AtomicInteger::get)); int breakerCnt = CollectionUtils.sumIntegers(CollectionUtils.transform(ctCnts.values(), AtomicInteger::get));
int hubCnt = (int)Math.ceil(breakerCnt/15.0); int hubCnt = (int)Math.ceil(breakerCnt/15.0);
bom.getLineItems().add(new LineItem("Lantern Power Monitor Case", "LPMC1", "https://github.com/MarkBryanMilligan/LanternPowerMonitor/tree/main/case", 0.10, 3.00, hubCnt)); bom.getLineItems().add(new LineItem("Lantern Power Monitor Case", "LPMC1", "https://github.com/MarkBryanMilligan/LanternPowerMonitor/tree/main/case", 0.10, 3.00, hubCnt));
bom.getLineItems().add(new LineItem("Lantern Power Monitor Case Lid", "LPMCL1", "https://github.com/MarkBryanMilligan/LanternPowerMonitor/tree/main/case", 0.10, 2.00, hubCnt)); bom.getLineItems().add(new LineItem("Lantern Power Monitor Case Lid", "LPMCL1", "https://github.com/MarkBryanMilligan/LanternPowerMonitor/tree/main/case", 0.10, 2.00, hubCnt));
bom.getLineItems().add(new LineItem("Lantern Power Monitor Soldering Jig", "LPMSJ1", "https://github.com/MarkBryanMilligan/LanternPowerMonitor/tree/main/case", 0.10, 4.00, 1)); bom.getLineItems().add(new LineItem("Lantern Power Monitor PCB", "LPMPCB1", "https://store.lanternpowermonitor.com/product/assembled-lantern-power-monitor-pcb/1", 1.00, 5.00, hubCnt));
bom.getLineItems().add(new LineItem("Lantern Power Monitor PCB", "LPMPCB1", "https://github.com/MarkBryanMilligan/LanternPowerMonitor/tree/main/pcb", 1.00, 5.00, hubCnt));
bom.getLineItems().add(new LineItem("Raspberry Pi 3 Model A+", "3A+", "https://www.raspberrypi.org/products/raspberry-pi-3-model-a-plus/", 25.0, 35.0, hubCnt)); bom.getLineItems().add(new LineItem("Raspberry Pi 3 Model A+", "3A+", "https://www.raspberrypi.org/products/raspberry-pi-3-model-a-plus/", 25.0, 35.0, hubCnt));
bom.getLineItems().add(new LineItem("Jameco 12V AC/AC Adapter", "10428", "https://www.jameco.com/z/ACU120100Z9121-Jameco-Reliapro-AC-to-AC-Wall-Adapter-Transformer-12-Volt-AC-1000mA-Black-Straight-3-5mm-Male-Plug_10428.html", 10.95, 15.00, hubCnt)); bom.getLineItems().add(new LineItem("Jameco 12V AC/AC Adapter", "10428", "https://store.lanternpowermonitor.com/product/120vac-to-12vac-voltage-transformer/2", 10.95, 15.00, hubCnt));
bom.getLineItems().add(new LineItem("16gb memory card", "P-SDU16GU185GW-GE", "https://www.microcenter.com/product/486146/micro-center-16gb-microsdhc-class-10-flash-memory-card", 4.00, 5.00, hubCnt)); bom.getLineItems().add(new LineItem("8gb Sandisk Industrial memory card", "SDSDQAF3-008G-I", "https://www.amazon.com/gp/product/B07BZ5SY18", 4.00, 5.00, hubCnt));
bom.getLineItems().add(new LineItem("40-pin GPIO header", "C169819", "https://lcsc.com/product-detail/Pin-Header-Female-Header_Ckmtw-Shenzhen-Cankemeng-C169819_C169819.html", 0.36, 0.80, hubCnt));
bom.getLineItems().add(new LineItem("MCP3008", "MCP3008-I-P", "https://www.digikey.com/en/products/detail/microchip-technology/MCP3008-I-P/319422", 2.41, 4.00, hubCnt*2));
bom.getLineItems().add(new LineItem("10uF 25V 4*5 Capacitor", "C43846", "https://lcsc.com/product-detail/Aluminum-Electrolytic-Capacitors-Leaded_CX-Dongguan-Chengxing-Elec-10uF-25V-4-5_C43846.html", 0.01, 0.10, hubCnt));
bom.getLineItems().add(new LineItem("22uF 25V 4*7 Capacitor", "C43840", "https://lcsc.com/product-detail/Aluminum-Electrolytic-Capacitors-Leaded_CX-Dongguan-Chengxing-Elec-22uF-25V-4-7_C43840.html", 0.01, 0.10, hubCnt));
bom.getLineItems().add(new LineItem("10KΩ Resistor", "C385441", "https://lcsc.com/product-detail/Metal-Film-Resistor-TH_TyoHM-RN1-2WS10K%CE%A9FT-BA1_C385441.html", 0.01, 0.10, hubCnt*2));
bom.getLineItems().add(new LineItem("12KΩ Resistor", "C385449", "https://lcsc.com/product-detail/Metal-Film-Resistor-TH_TyoHM-RN1-2WS12K%CE%A9FT-BA1_C385449.html", 0.01, 0.10, hubCnt));
bom.getLineItems().add(new LineItem("180KΩ Resistor", "C385460", "https://lcsc.com/product-detail/Metal-Film-Resistor-TH_TyoHM-RN1-2WS180K%CE%A9FT-BA1_C385460.html", 0.01, 0.10, hubCnt));
bom.getLineItems().add(new LineItem("33KΩ Resistor", "C385498", "https://lcsc.com/product-detail/Metal-Film-Resistor-TH_TyoHM-RN1-2WS33K%CE%A9FT-BA1_C385498.html", 0.01, 0.10, hubCnt));
bom.getLineItems().add(new LineItem("68KΩ Resistor", "C385541", "https://lcsc.com/product-detail/Metal-Film-Resistor-TH_TyoHM-RN1-2WS68K%CE%A9FT-BA1_C385541.html", 0.01, 0.10, hubCnt));
bom.getLineItems().add(new LineItem("3.5mm Headphone Jack", "PJ-3583-B", "https://lcsc.com/product-detail/Audio-Video-Connectors_XKB-Enterprise-PJ-3583-B_C397337.html", 0.16, 0.25, hubCnt*16));
bom.getLineItems().add(new LineItem("M2.5x10mm Cap Screw", "A15120300ux0225", "https://www.amazon.com/gp/product/B01B1OD7IK", 0.10, 0.20, hubCnt*8)); bom.getLineItems().add(new LineItem("M2.5x10mm Cap Screw", "A15120300ux0225", "https://www.amazon.com/gp/product/B01B1OD7IK", 0.10, 0.20, hubCnt*8));
bom.getLineItems().add(new LineItem("M2.5x11mm Female x Female Standoff", "", "https://www.ebay.com/itm/50pcs-M2-5-Female-Hex-Screw-Brass-PCB-Standoffs-Hexagonal-Spacers/172746413434", 0.15, 0.25, hubCnt*4)); bom.getLineItems().add(new LineItem("M2.5x11mm Female x Female Standoff", "", "https://www.ebay.com/itm/50pcs-M2-5-Female-Hex-Screw-Brass-PCB-Standoffs-Hexagonal-Spacers/172746413434", 0.15, 0.25, hubCnt*4));
bom.getLineItems().add(new LineItem("M2.5x12mm Female x Male Standoff", "", "https://www.ebay.com/itm/M2-5-2-5mm-Thread-6mm-Brass-Standoff-Spacer-Male-x-Female-20-50pcs-New/283432513974", 0.15, 0.25, hubCnt*4)); bom.getLineItems().add(new LineItem("M2.5x12mm Female x Male Standoff", "", "https://www.ebay.com/itm/M2-5-2-5mm-Thread-6mm-Brass-Standoff-Spacer-Male-x-Female-20-50pcs-New/283432513974", 0.15, 0.25, hubCnt*4));
for (Map.Entry<Integer, AtomicInteger> ctCnt : ctCnts.entrySet()) { for (Map.Entry<Integer, AtomicInteger> ctCnt : ctCnts.entrySet()) {
bom.getLineItems().add(new LineItem(String.format("%d Amp Current Transformer", ctCnt.getKey()), String.format("SCT-013-0%d", ctCnt.getKey()), "N/A", 5.00, 7.00, ctCnt.getValue().get())); bom.getLineItems().add(new LineItem(String.format("%d Amp Current Transformer", ctCnt.getKey()), String.format("SCT-013-%03d", ctCnt.getKey()), ctSizes.get(ctCnt.getKey()), 5.00, 6.00, ctCnt.getValue().get()));
} }
return bom; return bom;
} }
@ -93,4 +79,11 @@ public class BOM {
rows.add(CollectionUtils.asArrayList("Total", "", "", "", "", "", String.format("$%.2f", selfCost), String.format("$%.2f", shippedCost))); rows.add(CollectionUtils.asArrayList("Total", "", "", "", "", "", String.format("$%.2f", selfCost), String.format("$%.2f", shippedCost)));
return new CSV(headers, rows, headers.size()); return new CSV(headers, rows, headers.size());
} }
private boolean isUntrackedBottom(Map<Integer, Breaker> _breakers, Breaker _breaker) {
if (_breaker.getType() != BreakerType.DOUBLE_POLE_BOTTOM)
return false;
Breaker topBreaker = _breakers.get(Breaker.intKey(_breaker.getPanel(), _breaker.getSpaceIndex() - 2));
return topBreaker != null && topBreaker.getType() == BreakerType.DOUBLE_POLE_TOP_ONE_CT;
}
} }

View File

@ -28,6 +28,8 @@ public class BreakerHubSerializer extends AbstractDaoSerializer<BreakerHub>
d.put("hub", _o.getHub()); d.put("hub", _o.getHub());
d.put("voltage_calibration_factor", _o.getRawVoltageCalibrationFactor()); d.put("voltage_calibration_factor", _o.getRawVoltageCalibrationFactor());
d.put("port_calibration_factor", _o.getRawPortCalibrationFactor()); 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("frequency", _o.getFrequency());
d.put("bluetooth_mac", _o.getBluetoothMac()); d.put("bluetooth_mac", _o.getBluetoothMac());
return d; return d;
@ -40,6 +42,8 @@ public class BreakerHubSerializer extends AbstractDaoSerializer<BreakerHub>
o.setHub(DaoSerializer.getInteger(_d, "hub")); o.setHub(DaoSerializer.getInteger(_d, "hub"));
o.setVoltageCalibrationFactor(DaoSerializer.getDouble(_d, "voltage_calibration_factor")); o.setVoltageCalibrationFactor(DaoSerializer.getDouble(_d, "voltage_calibration_factor"));
o.setPortCalibrationFactor(DaoSerializer.getDouble(_d, "port_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.setFrequency(DaoSerializer.getInteger(_d, "frequency"));
o.setBluetoothMac(DaoSerializer.getString(_d, "bluetooth_mac")); o.setBluetoothMac(DaoSerializer.getString(_d, "bluetooth_mac"));
return o; return o;

View File

@ -35,11 +35,13 @@ public class BreakerSerializer extends AbstractDaoSerializer<Breaker>
d.put("name", _o.getName()); d.put("name", _o.getName());
d.put("description", _o.getDescription()); d.put("description", _o.getDescription());
d.put("size_amps", _o.getSizeAmps()); d.put("size_amps", _o.getSizeAmps());
d.put("phase_offset_ns", _o.getPhaseOffsetNs());
d.put("calibration_factor", _o.getCalibrationFactor()); d.put("calibration_factor", _o.getCalibrationFactor());
d.put("low_pass_filter", _o.getLowPassFilter()); d.put("low_pass_filter", _o.getLowPassFilter());
d.put("polarity", DaoSerializer.toEnumName(_o.getPolarity())); d.put("polarity", DaoSerializer.toEnumName(_o.getPolarity()));
d.put("double_power", _o.isDoublePower()); d.put("double_power", _o.isDoublePower());
d.put("type", DaoSerializer.toEnumName(_o.getType())); d.put("type", DaoSerializer.toEnumName(_o.getType()));
d.put("main", _o.isMain());
return d; return d;
} }
@ -55,11 +57,13 @@ public class BreakerSerializer extends AbstractDaoSerializer<Breaker>
o.setName(DaoSerializer.getString(_d, "name")); o.setName(DaoSerializer.getString(_d, "name"));
o.setDescription(DaoSerializer.getString(_d, "description")); o.setDescription(DaoSerializer.getString(_d, "description"));
o.setSizeAmps(DaoSerializer.getInteger(_d, "size_amps")); o.setSizeAmps(DaoSerializer.getInteger(_d, "size_amps"));
o.setPhaseOffsetNs(DaoSerializer.getInteger(_d, "phase_offset_ns"));
o.setCalibrationFactor(DaoSerializer.getDouble(_d, "calibration_factor")); o.setCalibrationFactor(DaoSerializer.getDouble(_d, "calibration_factor"));
o.setLowPassFilter(DaoSerializer.getDouble(_d, "low_pass_filter")); o.setLowPassFilter(DaoSerializer.getDouble(_d, "low_pass_filter"));
o.setPolarity(DaoSerializer.getEnum(_d, "polarity", BreakerPolarity.class)); o.setPolarity(DaoSerializer.getEnum(_d, "polarity", BreakerPolarity.class));
o.setDoublePower(DaoSerializer.getBoolean(_d, "double_power")); o.setDoublePower(DaoSerializer.getBoolean(_d, "double_power"));
o.setType(DaoSerializer.getEnum(_d, "type", BreakerType.class)); o.setType(DaoSerializer.getEnum(_d, "type", BreakerType.class));
o.setMain(DaoSerializer.getBoolean(_d, "main"));
return o; return o;
} }
} }

View File

@ -0,0 +1,48 @@
package com.lanternsoftware.datamodel.currentmonitor.dao;
import com.lanternsoftware.datamodel.currentmonitor.EmailCredentials;
import com.lanternsoftware.datamodel.currentmonitor.EmailProvider;
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 EmailCredentialsSerializer extends AbstractDaoSerializer<EmailCredentials>
{
@Override
public Class<EmailCredentials> getSupportedClass()
{
return EmailCredentials.class;
}
@Override
public List<DaoProxyType> getSupportedProxies() {
return Collections.singletonList(DaoProxyType.MONGO);
}
@Override
public DaoEntity toDaoEntity(EmailCredentials _o)
{
DaoEntity d = new DaoEntity();
d.put("provider", DaoSerializer.toEnumName(_o.getProvider()));
d.put("api_key", _o.getApiKey());
d.put("api_secret", _o.getApiSecret());
d.put("email_from", _o.getEmailFrom());
d.put("server_url_base", _o.getServerUrlBase());
return d;
}
@Override
public EmailCredentials fromDaoEntity(DaoEntity _d)
{
EmailCredentials o = new EmailCredentials();
o.setProvider(DaoSerializer.getEnum(_d, "provider", EmailProvider.class));
o.setApiKey(DaoSerializer.getString(_d, "api_key"));
o.setApiSecret(DaoSerializer.getString(_d, "api_secret"));
o.setEmailFrom(DaoSerializer.getString(_d, "email_from"));
o.setServerUrlBase(DaoSerializer.getString(_d, "server_url_base"));
return o;
}
}

View File

@ -9,7 +9,6 @@ import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoProxyType; import com.lanternsoftware.util.dao.DaoProxyType;
import com.lanternsoftware.util.dao.DaoSerializer; import com.lanternsoftware.util.dao.DaoSerializer;
import java.nio.ByteBuffer;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
@ -38,6 +37,7 @@ public class EnergySummarySerializer extends AbstractDaoSerializer<EnergySummary
d.put("view_mode", DaoSerializer.toEnumName(_o.getViewMode())); d.put("view_mode", DaoSerializer.toEnumName(_o.getViewMode()));
d.put("start", DaoSerializer.toLong(_o.getStart())); d.put("start", DaoSerializer.toLong(_o.getStart()));
d.put("sub_groups", DaoSerializer.toDaoEntities(_o.getSubGroups(), DaoProxyType.MONGO)); d.put("sub_groups", DaoSerializer.toDaoEntities(_o.getSubGroups(), DaoProxyType.MONGO));
d.put("main", _o.isMain());
TimeZone tz = DateUtils.defaultTimeZone(_o.getTimeZone()); TimeZone tz = DateUtils.defaultTimeZone(_o.getTimeZone());
d.put("timezone", tz.getID()); d.put("timezone", tz.getID());
if (_o.getEnergy() != null) if (_o.getEnergy() != null)
@ -61,6 +61,7 @@ public class EnergySummarySerializer extends AbstractDaoSerializer<EnergySummary
o.setViewMode(DaoSerializer.getEnum(_d, "view_mode", EnergyViewMode.class)); o.setViewMode(DaoSerializer.getEnum(_d, "view_mode", EnergyViewMode.class));
o.setStart(DaoSerializer.getDate(_d, "start")); o.setStart(DaoSerializer.getDate(_d, "start"));
o.setSubGroups(DaoSerializer.getList(_d, "sub_groups", EnergySummary.class)); o.setSubGroups(DaoSerializer.getList(_d, "sub_groups", EnergySummary.class));
o.setMain(DaoSerializer.getBoolean(_d, "main"));
o.setTimeZone(DateUtils.fromTimeZoneId(DaoSerializer.getString(_d, "timezone"))); o.setTimeZone(DateUtils.fromTimeZoneId(DaoSerializer.getString(_d, "timezone")));
o.setEnergy(CollectionUtils.toFloatArray(DaoSerializer.getByteArray(_d, "energy"))); o.setEnergy(CollectionUtils.toFloatArray(DaoSerializer.getByteArray(_d, "energy")));
o.setGridEnergy(CollectionUtils.toFloatArray(DaoSerializer.getByteArray(_d, "grid_energy"))); o.setGridEnergy(CollectionUtils.toFloatArray(DaoSerializer.getByteArray(_d, "grid_energy")));

View File

@ -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<PowerSample> 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<PowerSample> getSamples() {
return samples;
}
public void setSamples(List<PowerSample> _samples) {
samples = _samples;
}
}

View File

@ -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<BreakerSample> 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<BreakerSample> getBreakers() {
return breakers;
}
public void setBreakers(List<BreakerSample> _breakers) {
breakers = _breakers;
}
}

View File

@ -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;
}
}

View File

@ -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<BreakerSample>
{
@Override
public Class<BreakerSample> getSupportedClass()
{
return BreakerSample.class;
}
@Override
public List<DaoProxyType> 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;
}
}

View File

@ -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<HubSample>
{
@Override
public Class<HubSample> getSupportedClass()
{
return HubSample.class;
}
@Override
public List<DaoProxyType> 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;
}
}

View File

@ -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<PowerSample>
{
@Override
public Class<PowerSample> getSupportedClass()
{
return PowerSample.class;
}
@Override
public List<DaoProxyType> 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;
}
}

View File

@ -15,6 +15,7 @@ com.lanternsoftware.datamodel.currentmonitor.dao.BreakerPowerSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.BreakerSerializer com.lanternsoftware.datamodel.currentmonitor.dao.BreakerSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.ChargeSummarySerializer com.lanternsoftware.datamodel.currentmonitor.dao.ChargeSummarySerializer
com.lanternsoftware.datamodel.currentmonitor.dao.ChargeTotalSerializer com.lanternsoftware.datamodel.currentmonitor.dao.ChargeTotalSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.EmailCredentialsSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.EnergyBlockSerializer com.lanternsoftware.datamodel.currentmonitor.dao.EnergyBlockSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.EnergySummarySerializer com.lanternsoftware.datamodel.currentmonitor.dao.EnergySummarySerializer
com.lanternsoftware.datamodel.currentmonitor.dao.EnergyTotalSerializer com.lanternsoftware.datamodel.currentmonitor.dao.EnergyTotalSerializer
@ -25,3 +26,6 @@ com.lanternsoftware.datamodel.currentmonitor.dao.MeterSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.NetworkStatusSerializer com.lanternsoftware.datamodel.currentmonitor.dao.NetworkStatusSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.SequenceSerializer com.lanternsoftware.datamodel.currentmonitor.dao.SequenceSerializer
com.lanternsoftware.datamodel.currentmonitor.dao.SignupResponseSerializer 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

View File

@ -1,54 +1,41 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.lanternsoftware.currentmonitor</groupId>
<artifactId>lantern-service-currentmonitor</artifactId> <artifactId>lantern-service-currentmonitor</artifactId>
<packaging>war</packaging> <packaging>war</packaging>
<version>1.0.0</version> <version>1.1.0</version>
<name>lantern-service-currentmonitor</name> <name>lantern-service-currentmonitor</name>
<properties> <parent>
<maven.compiler.source>1.8</maven.compiler.source> <groupId>com.lanternsoftware.currentmonitor</groupId>
<maven.compiler.target>1.8</maven.compiler.target> <artifactId>currentmonitor</artifactId>
</properties> <version>1.1.0</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client-bom</artifactId>
<version>1.32.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.lanternsoftware.currentmonitor</groupId> <groupId>com.lanternsoftware.currentmonitor</groupId>
<artifactId>lantern-dataaccess-currentmonitor</artifactId> <artifactId>lantern-dataaccess-currentmonitor</artifactId>
<version>1.0.0</version> <version>${cm.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.lanternsoftware.util</groupId> <groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-servlet</artifactId> <artifactId>lantern-util-servlet</artifactId>
<version>1.0.0</version> <version>${util.version}</version>
</dependency>
<dependency>
<groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-cloudservices</artifactId>
<version>${util.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.lanternsoftware.util</groupId> <groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-http</artifactId> <artifactId>lantern-util-http</artifactId>
<version>1.0.0</version> <version>${util.version}</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.lanternsoftware.rules</groupId> <groupId>com.lanternsoftware.rules</groupId>
<artifactId>lantern-service-rules</artifactId> <artifactId>lantern-service-rules</artifactId>
<version>1.0.0</version> <version>${rules.version}</version>
</dependency>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax</groupId> <groupId>javax</groupId>
@ -67,9 +54,14 @@
<version>1.2.3</version> <version>1.2.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.sendgrid</groupId> <groupId>com.auth0</groupId>
<artifactId>sendgrid-java</artifactId> <artifactId>java-jwt</artifactId>
<version>4.7.2</version> <version>3.19.1</version>
</dependency>
<dependency>
<groupId>com.mailjet</groupId>
<artifactId>mailjet-client</artifactId>
<version>4.2.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
@ -82,7 +74,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version> <version>3.10.1</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
@ -95,13 +87,13 @@
<optimize>true</optimize> <optimize>true</optimize>
<showDeprecation>true</showDeprecation> <showDeprecation>true</showDeprecation>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
<source>1.8</source> <source>${maven.compiler.source}</source>
<target>1.8</target> <target>${maven.compiler.target}</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-war-plugin</artifactId> <artifactId>maven-war-plugin</artifactId>
<version>2.5</version> <version>3.3.2</version>
<configuration> <configuration>
<webResources> <webResources>
<resource> <resource>

View File

@ -6,8 +6,9 @@ import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubCommands; import com.lanternsoftware.datamodel.currentmonitor.HubCommands;
import com.lanternsoftware.rules.RulesEngine; import com.lanternsoftware.rules.RulesEngine;
import com.lanternsoftware.util.DateUtils; import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.external.LanternFiles;
import com.lanternsoftware.util.dao.mongo.MongoConfig; import com.lanternsoftware.util.dao.mongo.MongoConfig;
import com.lanternsoftware.util.external.LanternFiles;
import com.lanternsoftware.util.http.HttpFactory;
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener; import javax.servlet.ServletContextListener;
@ -17,9 +18,12 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Globals implements ServletContextListener { public class Globals implements ServletContextListener {
public static CurrentMonitorDao dao; public static CurrentMonitorDao dao;
public static ExecutorService opsExecutor;
private static final Map<Integer, Map<Integer, List<HubCommand>>> commands = new HashMap<>(); private static final Map<Integer, Map<Integer, List<HubCommand>>> commands = new HashMap<>();
@Override @Override
@ -27,11 +31,14 @@ public class Globals implements ServletContextListener {
dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg")); dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.CONFIG_PATH + "mongo.cfg"));
RulesEngine.instance().start(); RulesEngine.instance().start();
RulesEngine.instance().schedule(new CommandTask(), 0); RulesEngine.instance().schedule(new CommandTask(), 0);
opsExecutor = Executors.newFixedThreadPool(7);
} }
@Override @Override
public void contextDestroyed(ServletContextEvent sce) { public void contextDestroyed(ServletContextEvent sce) {
opsExecutor.shutdown();
dao.shutdown(); dao.shutdown();
HttpFactory.shutdown();
RulesEngine.shutdown(); RulesEngine.shutdown();
} }

View File

@ -0,0 +1,7 @@
package com.lanternsoftware.currentmonitor.email;
import com.lanternsoftware.datamodel.currentmonitor.EmailCredentials;
public interface IEmailProvider {
int sendTextEmail(EmailCredentials _credentials, String _to, String _subject, String _message);
}

View File

@ -0,0 +1,36 @@
package com.lanternsoftware.currentmonitor.email;
import com.lanternsoftware.datamodel.currentmonitor.EmailCredentials;
import com.mailjet.client.ClientOptions;
import com.mailjet.client.MailjetClient;
import com.mailjet.client.MailjetRequest;
import com.mailjet.client.MailjetResponse;
import com.mailjet.client.resource.Emailv31;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MailJetProvider implements IEmailProvider {
protected static final Logger LOG = LoggerFactory.getLogger(MailJetProvider.class);
@Override
public int sendTextEmail(EmailCredentials _credentials, String _to, String _subject, String _message) {
MailjetClient client;
MailjetRequest request;
MailjetResponse response;
client = new MailjetClient(_credentials.getApiKey(), _credentials.getApiSecret(), new ClientOptions("v3.1"));
request = new MailjetRequest(Emailv31.resource).property(Emailv31.MESSAGES, new JSONArray().put(new JSONObject()
.put(Emailv31.Message.FROM, new JSONObject().put("Email", _credentials.getEmailFrom()).put("Name", "Lantern Power Monitor"))
.put(Emailv31.Message.TO, new JSONArray().put(new JSONObject().put("Email", _to)))
.put(Emailv31.Message.SUBJECT, _subject)
.put(Emailv31.Message.TEXTPART, _message)));
try {
response = client.post(request);
return response.getStatus();
} catch (Exception _e) {
LOG.error("Failed to send email", _e);
return 500;
}
}
}

View File

@ -0,0 +1,26 @@
package com.lanternsoftware.currentmonitor.email;
import com.lanternsoftware.datamodel.currentmonitor.EmailCredentials;
public class SendGridProvider implements IEmailProvider {
@Override
public int sendTextEmail(EmailCredentials _credentials, String _to, String _subject, String _message) {
/* Email to = new Email(email);
Content content = new Content("text/plain", "Reset your password using this link:\nhttps://lanternpowermonitor.com/currentmonitor/resetPassword/" + key);
Mail mail = new Mail(from, subject, to, content);
SendGrid sg = new SendGrid(api_key);
Request request = new Request();
try {
request.setMethod(Method.POST);
request.setEndpoint("mail/send");
request.setBody(mail.build());
Response response = sg.api(request);
LOG.info("Password reset email status: {}\nfrom: {}\nto: {}\nkey: {}\nhost: {}", response.getStatusCode(), from.getEmail(), to.getEmail(), api_key, sg.getHost());
zipBsonResponse(_resp, new DaoEntity("success", response.getStatusCode() == 200));
} catch (IOException ex) {
LOG.error("Failed to send password reset email", ex);
_resp.setStatus(500);
}*/
return 500;
}
}

View File

@ -1,14 +1,15 @@
package com.lanternsoftware.currentmonitor.servlet; package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals; import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.currentmonitor.util.GoogleAuthHelper;
import com.lanternsoftware.util.DateUtils; import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.cloudservices.apple.AppleSSO;
import com.lanternsoftware.util.cloudservices.google.GoogleSSO;
import com.lanternsoftware.util.dao.DaoEntity; import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.external.LanternFiles;
import com.lanternsoftware.util.servlet.BasicAuth; import com.lanternsoftware.util.servlet.BasicAuth;
import com.lanternsoftware.util.servlet.LanternServlet; import com.lanternsoftware.util.servlet.LanternServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.annotation.WebServlet; import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -16,19 +17,27 @@ import javax.servlet.http.HttpServletResponse;
@WebServlet("/auth/*") @WebServlet("/auth/*")
public class AuthServlet extends LanternServlet { public class AuthServlet extends LanternServlet {
private static final Logger logger = LoggerFactory.getLogger(AuthServlet.class); private static final GoogleSSO googleSSO = new GoogleSSO(LanternFiles.CONFIG_PATH + "google_sso.txt");
private static final AppleSSO appleSSO = new AppleSSO(LanternFiles.CONFIG_PATH + "apple_sso.txt");
@Override @Override
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) {
String authCode = _req.getHeader("auth_code"); String authCode = _req.getHeader("auth_code");
if (NullUtils.isEmpty(authCode)) { String idToken = _req.getHeader("id_token");
String email = null;
if (NullUtils.isNotEmpty(idToken))
email = appleSSO.getEmailFromIdToken(idToken);
else if (NullUtils.isNotEmpty(authCode))
authCode = Globals.dao.exchangeAuthCode(authCode, DaoSerializer.toInteger(_req.getHeader("override_account")));
else {
BasicAuth auth = new BasicAuth(_req); BasicAuth auth = new BasicAuth(_req);
if (NullUtils.isEqual(auth.getUsername(), "googlesso")) { if (NullUtils.isEqual(auth.getUsername(), "googlesso"))
logger.info("Attempting google SSO"); email = googleSSO.signin(auth.getPassword());
authCode = GoogleAuthHelper.signin(auth.getPassword(), DateUtils.fromTimeZoneId(_req.getHeader("timezone"))); else
} else
authCode = Globals.dao.authenticateAccount(auth.getUsername(), auth.getPassword()); authCode = Globals.dao.authenticateAccount(auth.getUsername(), auth.getPassword());
} }
if (NullUtils.isNotEmpty(email))
authCode = Globals.dao.getAuthCodeForEmail(email, DateUtils.fromTimeZoneId(_req.getHeader("timezone")));
DaoEntity rep = new DaoEntity("auth_code", authCode).and("timezone", Globals.dao.getTimeZoneForAccount(authCode)); DaoEntity rep = new DaoEntity("auth_code", authCode).and("timezone", Globals.dao.getTimeZoneForAccount(authCode));
if (isPath(_req, 0, "bin")) if (isPath(_req, 0, "bin"))
zipBsonResponse(_rep, rep); zipBsonResponse(_rep, rep);

View File

@ -4,6 +4,7 @@ import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig; import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand; import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic; import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic;
import com.lanternsoftware.rules.RulesEngine;
import com.lanternsoftware.util.dao.auth.AuthCode; import com.lanternsoftware.util.dao.auth.AuthCode;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -40,6 +41,8 @@ public class ConfigServlet extends SecureServiceServlet {
if ((oldConfig == null) || !oldConfig.isIdentical(config)) if ((oldConfig == null) || !oldConfig.isIdentical(config))
Globals.dao.putHubCommand(new HubCommand(config.getAccountId(), HubConfigCharacteristic.ReloadConfig, null)); Globals.dao.putHubCommand(new HubCommand(config.getAccountId(), HubConfigCharacteristic.ReloadConfig, null));
Globals.dao.putConfig(config); Globals.dao.putConfig(config);
zipBsonResponse(_rep, Globals.dao.getMergedConfig(_authCode)); config = Globals.dao.getMergedConfig(_authCode);
RulesEngine.instance().sendFcmMessage(config.getAccountId(), config);
zipBsonResponse(_rep, config);
} }
} }

View File

@ -17,13 +17,14 @@ public class RebuildSummariesServlet extends SecureServiceServlet {
if (_authCode.getAccountId() == 100) { if (_authCode.getAccountId() == 100) {
String[] path = path(_req); String[] path = path(_req);
if (path.length > 0) { 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 { else {
for (String sId : Globals.dao.getProxy().queryForField(Account.class, null, "_id")) { for (String sId : Globals.dao.getProxy().queryForField(Account.class, null, "_id")) {
int id = DaoSerializer.toInteger(sId); int id = DaoSerializer.toInteger(sId);
if (id != 0) if (id != 0) {
Globals.dao.rebuildSummariesAsync(id); Globals.opsExecutor.submit(() -> Globals.dao.rebuildSummaries(id));
}
} }
} }
} }

View File

@ -1,22 +1,19 @@
package com.lanternsoftware.currentmonitor.servlet; package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals; import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.currentmonitor.email.IEmailProvider;
import com.lanternsoftware.currentmonitor.email.MailJetProvider;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.datamodel.currentmonitor.EmailCredentials;
import com.lanternsoftware.util.CollectionUtils; import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.external.LanternFiles;
import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.ResourceLoader; import com.lanternsoftware.util.ResourceLoader;
import com.lanternsoftware.util.dao.DaoEntity; import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer; import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.email.EmailValidator; import com.lanternsoftware.util.email.EmailValidator;
import com.lanternsoftware.util.external.LanternFiles;
import com.lanternsoftware.util.servlet.FreemarkerConfigUtil; import com.lanternsoftware.util.servlet.FreemarkerConfigUtil;
import com.lanternsoftware.util.servlet.FreemarkerServlet; import com.lanternsoftware.util.servlet.FreemarkerServlet;
import com.sendgrid.Method;
import com.sendgrid.Request;
import com.sendgrid.Response;
import com.sendgrid.SendGrid;
import com.sendgrid.helpers.mail.Mail;
import com.sendgrid.helpers.mail.objects.Content;
import com.sendgrid.helpers.mail.objects.Email;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -25,13 +22,13 @@ import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import java.io.IOException;
@WebServlet("/resetPassword/*") @WebServlet("/resetPassword/*")
public class ResetPasswordServlet extends FreemarkerServlet { public class ResetPasswordServlet extends FreemarkerServlet {
protected static final Logger LOG = LoggerFactory.getLogger(ResetPasswordServlet.class); protected static final Logger LOG = LoggerFactory.getLogger(ResetPasswordServlet.class);
protected static final Configuration CONFIG = FreemarkerConfigUtil.createConfig(ResetPasswordServlet.class, "/templates", 100); protected static final Configuration CONFIG = FreemarkerConfigUtil.createConfig(ResetPasswordServlet.class, "/templates", 100);
protected static final String api_key = ResourceLoader.loadFileAsString(LanternFiles.CONFIG_PATH + "sendgrid.txt"); protected static final EmailCredentials credentials = DaoSerializer.parse(ResourceLoader.loadFileAsString(LanternFiles.CONFIG_PATH + "email.json"), EmailCredentials.class);
protected static final IEmailProvider provider = new MailJetProvider();
@Override @Override
protected Configuration getFreemarkerConfig() { protected Configuration getFreemarkerConfig() {
@ -63,25 +60,11 @@ public class ResetPasswordServlet extends FreemarkerServlet {
} else { } else {
DaoEntity payload = getRequestZipBson(_req); DaoEntity payload = getRequestZipBson(_req);
String email = DaoSerializer.getString(payload, "email"); String email = DaoSerializer.getString(payload, "email");
if (EmailValidator.getInstance().isValid(email)) { Account account = Globals.dao.getAccountByUsername(email);
if ((account != null) && EmailValidator.getInstance().isValid(email)) {
String key = Globals.dao.addPasswordResetKey(email); String key = Globals.dao.addPasswordResetKey(email);
Email from = new Email("info@lanternsoftware.com"); int status = provider.sendTextEmail(credentials, email, "Password Reset - Lantern Power Monitor", "Reset your password using this link:\n" + credentials.getServerUrlBase() + "resetPassword/" + key);
String subject = "Password Reset - Lantern Power Monitor"; zipBsonResponse(_resp, new DaoEntity("success", status == 200));
Email to = new Email(email);
Content content = new Content("text/plain", "Reset your password using this link:\nhttps://lanternsoftware.com/currentmonitor/resetPassword/" + key);
Mail mail = new Mail(from, subject, to, content);
SendGrid sg = new SendGrid(api_key);
Request request = new Request();
try {
request.setMethod(Method.POST);
request.setEndpoint("mail/send");
request.setBody(mail.build());
Response response = sg.api(request);
zipBsonResponse(_resp, new DaoEntity("success", response.getStatusCode() == 200));
} catch (IOException ex) {
LOG.error("Failed to send password reset email", ex);
_resp.setStatus(500);
}
} }
else else
_resp.setStatus(400); _resp.setStatus(400);

View File

@ -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);
}
}

View File

@ -14,26 +14,27 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.Locale; import java.util.Locale;
@WebServlet("/signup") @WebServlet("/signup/*")
public class SignupServlet extends LanternServlet { public class SignupServlet extends LanternServlet {
@Override @Override
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) {
boolean binary = isPath(_req, 0, "bin");
BasicAuth auth = new BasicAuth(_req); BasicAuth auth = new BasicAuth(_req);
Account acct = Globals.dao.getAccountByUsername(auth.getUsername().toLowerCase().trim()); Account acct = Globals.dao.getAccountByUsername(auth.getUsername().toLowerCase().trim());
if (acct != null) { if (acct != null) {
jsonResponse(_rep, SignupResponse.error("An account for " + auth.getUsername() + " already exists")); jsonResponse(_rep, SignupResponse.error("An account for " + auth.getUsername() + " already exists"), binary);
return; return;
} }
if (!EmailValidator.getInstance().isValid(auth.getUsername())) { if (!EmailValidator.getInstance().isValid(auth.getUsername())) {
jsonResponse(_rep, SignupResponse.error(auth.getUsername() + " is not a valid email address")); jsonResponse(_rep, SignupResponse.error(auth.getUsername() + " is not a valid email address"), binary);
return; return;
} }
if (NullUtils.length(auth.getPassword()) < 8) { if (NullUtils.length(auth.getPassword()) < 8) {
jsonResponse(_rep, SignupResponse.error("Your password must be at least 8 characters long")); jsonResponse(_rep, SignupResponse.error("Your password must be at least 8 characters long"), binary);
return; return;
} }
if (NullUtils.isEqual("password", auth.getPassword())) { if (NullUtils.isEqual("password", auth.getPassword())) {
jsonResponse(_rep, SignupResponse.error("Seriously? \"password\"? Come on.")); jsonResponse(_rep, SignupResponse.error("Seriously? \"password\"? Come on."), binary);
return; return;
} }
acct = new Account(); acct = new Account();
@ -42,6 +43,6 @@ public class SignupServlet extends LanternServlet {
acct.setTimezone(DateUtils.fromTimeZoneId(_req.getHeader("timezone")).getID()); acct.setTimezone(DateUtils.fromTimeZoneId(_req.getHeader("timezone")).getID());
Globals.dao.putAccount(acct); Globals.dao.putAccount(acct);
String authCode = Globals.dao.authenticateAccount(auth.getUsername(), auth.getPassword()); String authCode = Globals.dao.authenticateAccount(auth.getUsername(), auth.getPassword());
jsonResponse(_rep, SignupResponse.success(authCode, acct.getTimezone())); jsonResponse(_rep, SignupResponse.success(authCode, acct.getTimezone()), binary);
} }
} }

View File

@ -0,0 +1,56 @@
package com.lanternsoftware.currentmonitor.servlet.console;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.auth.AuthCode;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public abstract class AuthenticatedConsoleServlet extends SecureConsoleServlet {
@Override
protected void get(HttpServletRequest _req, HttpServletResponse _rep) {
AuthCode code = getAuthCode(_req, _rep);
if (code != null)
get(code, _req, _rep);
}
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
}
@Override
protected void post(HttpServletRequest _req, HttpServletResponse _rep) {
AuthCode code = getAuthCode(_req, _rep);
if (code != null)
post(code, _req, _rep);
}
private AuthCode getAuthCode(HttpServletRequest _req, HttpServletResponse _rep) {
String sRequestURL = _req.getRequestURL().toString();
String sURL = sRequestURL.replaceFirst("http://", "https://");
if (!sURL.equals(sRequestURL)) {
String sQuery = _req.getQueryString();
if (NullUtils.isNotEmpty(sQuery))
sURL += "?" + sQuery;
redirect(_rep, sURL);
return null;
}
AuthCode authCode = Globals.dao.decryptAuthCode(DaoSerializer.toString(_req.getSession().getAttribute("auth_code")));
if (authCode == null) {
Cookie authCookie = CollectionUtils.filterOne(CollectionUtils.asArrayList(_req.getCookies()), _c-> NullUtils.isEqual(_c.getName(), "auth_code"));
if (authCookie != null)
authCode = Globals.dao.decryptAuthCode(authCookie.getValue());
}
if (authCode == null) {
redirect(_rep, _req.getContextPath() + "/login");
return null;
}
return authCode;
}
protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
}
}

View File

@ -9,7 +9,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@WebServlet("") @WebServlet("")
public class ConsoleServlet extends SecureConsoleServlet { public class ConsoleServlet extends AuthenticatedConsoleServlet {
private static final Logger logger = LoggerFactory.getLogger(ConsoleServlet.class); private static final Logger logger = LoggerFactory.getLogger(ConsoleServlet.class);
@Override @Override

View File

@ -40,7 +40,7 @@ import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@WebServlet("/export/*") @WebServlet("/export/*")
public class ExportServlet extends SecureConsoleServlet { public class ExportServlet extends AuthenticatedConsoleServlet {
private static final Logger logger = LoggerFactory.getLogger(ExportServlet.class); private static final Logger logger = LoggerFactory.getLogger(ExportServlet.class);
@Override @Override

View File

@ -1,8 +1,10 @@
package com.lanternsoftware.currentmonitor.servlet.console; package com.lanternsoftware.currentmonitor.servlet.console;
import com.lanternsoftware.currentmonitor.servlet.FreemarkerCMServlet; import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.currentmonitor.util.GoogleAuthHelper; import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.cloudservices.google.GoogleSSO;
import com.lanternsoftware.util.external.LanternFiles;
import javax.servlet.annotation.WebServlet; import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
@ -10,23 +12,28 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@WebServlet("/gso") @WebServlet("/gso")
public class GsoServlet extends FreemarkerCMServlet { public class GsoServlet extends SecureConsoleServlet {
private static final GoogleSSO googleSSO = new GoogleSSO(LanternFiles.CONFIG_PATH + "google_sso.txt");
@Override @Override
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { protected void get(HttpServletRequest _req, HttpServletResponse _rep) {
render(_rep, "login.ftl", model(_req)); render(_rep, "login.ftl", model(_req));
} }
@Override @Override
protected void doPost(HttpServletRequest _req, HttpServletResponse _rep) { protected void post(HttpServletRequest _req, HttpServletResponse _rep) {
String code = getRequestPayloadAsString(_req); String code = getRequestPayloadAsString(_req);
if (NullUtils.isNotEmpty(code)) { if (NullUtils.isNotEmpty(code)) {
String authCode = GoogleAuthHelper.signin(code, null); String email = googleSSO.signin(code);
if (NullUtils.isNotEmpty(authCode)) { if (NullUtils.isNotEmpty(email)) {
Cookie authCookie = new Cookie("auth_code", authCode); String authCode = Globals.dao.getAuthCodeForEmail(email, DateUtils.fromTimeZoneId(_req.getHeader("timezone")));
authCookie.setMaxAge(157680000); if (NullUtils.isNotEmpty(authCode)) {
authCookie.setSecure(true); Cookie authCookie = new Cookie("auth_code", authCode);
_rep.addCookie(authCookie); authCookie.setMaxAge(157680000);
_req.getSession().setAttribute("auth_code", authCode); authCookie.setSecure(true);
_rep.addCookie(authCookie);
_req.getSession().setAttribute("auth_code", authCode);
}
} }
} }
} }

View File

@ -1,31 +1,22 @@
package com.lanternsoftware.currentmonitor.servlet.console; package com.lanternsoftware.currentmonitor.servlet.console;
import com.lanternsoftware.currentmonitor.context.Globals; import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.currentmonitor.servlet.FreemarkerCMServlet;
import com.lanternsoftware.currentmonitor.util.GoogleAuthHelper;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.util.servlet.LanternServlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet; import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/login") @WebServlet("/login")
public class LoginServlet extends FreemarkerCMServlet { public class LoginServlet extends SecureConsoleServlet {
@Override @Override
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { protected void get(HttpServletRequest _req, HttpServletResponse _rep) {
render(_rep, "login.ftl", model(_req)); render(_rep, "login.ftl", model(_req));
} }
@Override @Override
protected void doPost(HttpServletRequest _req, HttpServletResponse _rep) { protected void post(HttpServletRequest _req, HttpServletResponse _rep) {
String username = _req.getParameter("username"); String username = _req.getParameter("username");
String password = _req.getParameter("password"); String password = _req.getParameter("password");
String authCode = Globals.dao.authenticateAccount(username, password); String authCode = Globals.dao.authenticateAccount(username, password);

View File

@ -1,18 +1,14 @@
package com.lanternsoftware.currentmonitor.servlet.console; package com.lanternsoftware.currentmonitor.servlet.console;
import com.lanternsoftware.currentmonitor.servlet.FreemarkerCMServlet;
import com.lanternsoftware.currentmonitor.util.GoogleAuthHelper;
import com.lanternsoftware.util.NullUtils;
import javax.servlet.annotation.WebServlet; import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@WebServlet("/logout") @WebServlet("/logout")
public class LogoutServlet extends FreemarkerCMServlet { public class LogoutServlet extends AuthenticatedConsoleServlet {
@Override @Override
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { protected void get(HttpServletRequest _req, HttpServletResponse _rep) {
_req.getSession().removeAttribute("auth_code"); _req.getSession().removeAttribute("auth_code");
Cookie authCookie = new Cookie("auth_code", ""); Cookie authCookie = new Cookie("auth_code", "");
authCookie.setMaxAge(0); authCookie.setMaxAge(0);
@ -22,6 +18,6 @@ public class LogoutServlet extends FreemarkerCMServlet {
} }
@Override @Override
protected void doPost(HttpServletRequest _req, HttpServletResponse _rep) { protected void post(HttpServletRequest _req, HttpServletResponse _rep) {
} }
} }

View File

@ -1,48 +1,40 @@
package com.lanternsoftware.currentmonitor.servlet.console; package com.lanternsoftware.currentmonitor.servlet.console;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.currentmonitor.servlet.FreemarkerCMServlet; import com.lanternsoftware.currentmonitor.servlet.FreemarkerCMServlet;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils; import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.auth.AuthCode;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
public abstract class SecureConsoleServlet extends FreemarkerCMServlet { public abstract class SecureConsoleServlet extends FreemarkerCMServlet {
@Override @Override
protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) { protected void doGet(HttpServletRequest _req, HttpServletResponse _rep) {
AuthCode code = getAuthCode(_req, _rep); if (isSecure(_req, _rep))
if (code != null) get(_req, _rep);
get(code, _req, _rep);
} }
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { protected void get(HttpServletRequest _req, HttpServletResponse _rep) {
} }
@Override @Override
protected void doPost(HttpServletRequest _req, HttpServletResponse _rep) { protected void doPost(HttpServletRequest _req, HttpServletResponse _rep) {
AuthCode code = getAuthCode(_req, _rep); if (isSecure(_req, _rep))
if (code != null) post(_req, _rep);
post(code, _req, _rep);
} }
private AuthCode getAuthCode(HttpServletRequest _req, HttpServletResponse _rep) { protected void post(HttpServletRequest _req, HttpServletResponse _rep) {
AuthCode authCode = Globals.dao.decryptAuthCode(DaoSerializer.toString(_req.getSession().getAttribute("auth_code")));
if (authCode == null) {
Cookie authCookie = CollectionUtils.filterOne(CollectionUtils.asArrayList(_req.getCookies()), _c-> NullUtils.isEqual(_c.getName(), "auth_code"));
if (authCookie != null)
authCode = Globals.dao.decryptAuthCode(authCookie.getValue());
}
if (authCode == null) {
redirect(_rep, _req.getContextPath() + "/login");
return null;
}
return authCode;
} }
protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) { private boolean isSecure(HttpServletRequest _req, HttpServletResponse _rep) {
String sRequestURL = _req.getRequestURL().toString();
String sURL = sRequestURL.replaceFirst("http://", "https://");
if (!sURL.equals(sRequestURL)) {
String sQuery = _req.getQueryString();
if (NullUtils.isNotEmpty(sQuery))
sURL += "?" + sQuery;
redirect(_rep, sURL);
return false;
}
return true;
} }
} }

View File

@ -1,42 +0,0 @@
package com.lanternsoftware.currentmonitor.util;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.util.external.LanternFiles;
import com.lanternsoftware.util.ResourceLoader;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.TimeZone;
public class GoogleAuthHelper {
private static final Logger logger = LoggerFactory.getLogger(GoogleAuthHelper.class);
private static final NetHttpTransport transport = new NetHttpTransport();
private static final String googleClientId;
private static final String googleClientSecret;
static {
DaoEntity google = DaoSerializer.parse(ResourceLoader.loadFileAsString(LanternFiles.CONFIG_PATH + "google_sso.txt"));
googleClientId = DaoSerializer.getString(google, "id");
googleClientSecret = DaoSerializer.getString(google, "secret");
}
public static String signin(String _code, TimeZone _tz) {
try {
GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(transport, new GsonFactory(), "https://oauth2.googleapis.com/token", googleClientId, googleClientSecret, _code, "postmessage").execute();
if (tokenResponse != null) {
GoogleIdToken idToken = tokenResponse.parseIdToken();
if (idToken != null)
return Globals.dao.getAuthCodeForEmail(idToken.getPayload().getEmail(), _tz);
}
} catch (Exception _e) {
logger.error("Failed to validate google auth code", _e);
}
return null;
}
}

View File

@ -3,7 +3,7 @@
<property name="log.pattern" value="%date %-5level %logger{0} - %message%n"/> <property name="log.pattern" value="%date %-5level %logger{0} - %message%n"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/opt/tomcat/logs/log.txt</file> <file>/opt/tomcat/log/log.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>/opt/tomcat/log/log.%d{yyyy-MM-dd}.%i.txt</fileNamePattern> <fileNamePattern>/opt/tomcat/log/log.%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<maxFileSize>20MB</maxFileSize> <maxFileSize>20MB</maxFileSize>

View File

@ -1,10 +0,0 @@
package com.lanternsoftware.currentmonitor;
import com.lanternsoftware.util.external.LanternFiles;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
public class CreateMongoConfig {
public static void main(String[] args) {
new MongoConfig("lanternsoftware.com", "*redacted*", "*redacted*", "CURRENT_MONITOR").saveToDisk(LanternFiles.CONFIG_PATH + "mongo.cfg");
}
}

View File

@ -0,0 +1,37 @@
package com.lanternsoftware.currentmonitor;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.EnergySummary;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.http.HttpPool;
import com.lanternsoftware.util.servlet.BasicAuth;
import org.apache.http.client.methods.HttpGet;
import java.util.Date;
import java.util.TimeZone;
public class GetEnergySummary {
public static void main(String[] args) {
HttpPool pool = new HttpPool(10, 10, 10000, 10000, 10000);
HttpGet authRequest = new HttpGet("https://lanternpowermonitor.com/currentmonitor/auth");
authRequest.addHeader("Authorization", BasicAuth.toHeader("<username>", "<password>"));
String authRep = pool.executeToString(authRequest);
String authCode = DaoSerializer.getString(DaoSerializer.parse(authRep), "auth_code");
HttpGet configRequest = new HttpGet("https://lanternpowermonitor.com/currentmonitor/config");
configRequest.addHeader("auth_code", authCode);
String configRep = pool.executeToString(configRequest);
BreakerConfig config = DaoSerializer.parse(configRep, BreakerConfig.class);
Date day = DateUtils.date(6, 5, 2022, TimeZone.getTimeZone("America/Chicago"));
HttpGet summaryRequest = new HttpGet("https://lanternpowermonitor.com/currentmonitor/energy/" + config.getRootGroup().getId() + "/DAY/" + day.getTime());
summaryRequest.addHeader("auth_code", authCode);
EnergySummary summary = DaoSerializer.fromZipBson(pool.executeToByteArray(summaryRequest), EnergySummary.class);
System.out.println(DaoSerializer.toJson(summary));
pool.shutdown();
}
}

View File

@ -4,14 +4,16 @@
<groupId>com.lanternsoftware.currentmonitor</groupId> <groupId>com.lanternsoftware.currentmonitor</groupId>
<artifactId>currentmonitor</artifactId> <artifactId>currentmonitor</artifactId>
<name>currentmonitor</name> <name>currentmonitor</name>
<version>1.0.0</version> <version>1.1.0</version>
<properties> <parent>
<maven.compiler.source>1.8</maven.compiler.source> <groupId>com.lanternsoftware</groupId>
<maven.compiler.target>1.8</maven.compiler.target> <artifactId>LanternPowerMonitor</artifactId>
</properties> <version>1.0.0</version>
</parent>
<modules> <modules>
<module>lantern-config-currentmonitor</module>
<module>lantern-currentmonitor</module> <module>lantern-currentmonitor</module>
<module>lantern-dataaccess-currentmonitor</module> <module>lantern-dataaccess-currentmonitor</module>
<module>lantern-datamodel-currentmonitor</module> <module>lantern-datamodel-currentmonitor</module>

File diff suppressed because one or more lines are too long

View File

@ -1,25 +1,24 @@
G04 Layer: BoardOutline* G04 Layer: BoardOutline*
G04 EasyEDA v6.4.0, 2020-07-14T23:16:01--5:00* G04 EasyEDA v6.4.25, 2022-04-08T13:20:52--5:00*
G04 6dc5d916f8a9456ea10d5ff2c879efc5,9df6f537d2f94b3ba0ed850023b5714d,10*
G04 Gerber Generator version 0.2* G04 Gerber Generator version 0.2*
G04 Scale: 100 percent, Rotated: No, Reflected: No * G04 Scale: 100 percent, Rotated: No, Reflected: No *
G04 Dimensions in millimeters * G04 Dimensions in millimeters *
G04 leading zeros omitted , absolute positions ,3 integer and 3 decimal * G04 leading zeros omitted , absolute positions ,4 integer and 5 decimal *
%FSLAX33Y33*% %FSLAX45Y45*%
%MOMM*% %MOMM*%
G90*
G71D02*
%ADD10C,0.254000*% %ADD10C,0.2540*%
G54D10* D10*
G01X0Y56000D02* X0Y0D02*
G01X64999Y56000D01* G01*
G01X64999Y0D01* X6499994Y0D01*
G01X64999Y0D02* X6499994Y-5600006D01*
G01X0Y0D01* X6499994Y-5600006D02*
G01X0Y0D02* G01*
G01X0Y56000D01* X0Y-5600006D01*
X0Y-5600006D02*
G01*
X0Y0D01*
%LPD*% %LPD*%
M00*
M02* M02*

View File

@ -1,382 +1,568 @@
G04 Layer: BottomLayer* G04 Layer: BottomLayer*
G04 EasyEDA v6.4.0, 2020-07-14T23:16:01--5:00* G04 EasyEDA v6.4.25, 2022-04-08T13:20:52--5:00*
G04 6dc5d916f8a9456ea10d5ff2c879efc5,9df6f537d2f94b3ba0ed850023b5714d,10*
G04 Gerber Generator version 0.2* G04 Gerber Generator version 0.2*
G04 Scale: 100 percent, Rotated: No, Reflected: No * G04 Scale: 100 percent, Rotated: No, Reflected: No *
G04 Dimensions in millimeters * G04 Dimensions in millimeters *
G04 leading zeros omitted , absolute positions ,3 integer and 3 decimal * G04 leading zeros omitted , absolute positions ,4 integer and 5 decimal *
%FSLAX33Y33*% %FSLAX45Y45*%
%MOMM*% %MOMM*%
G90*
G71D02*
%ADD10C,0.254000*% %ADD10C,0.2540*%
%ADD12C,0.609600*% %ADD12C,0.6096*%
%ADD13R,1.799996X1.799996*% %ADD13R,1.8000X1.8000*%
%ADD14C,1.799996*% %ADD14C,1.2000*%
%ADD15C,1.199998*% %ADD15C,1.5240*%
%ADD16C,1.524000*% %ADD16C,1.8000*%
%ADD17C,1.599997*% %ADD17C,1.6000*%
%LPD*% %LPD*%
G54D10* D10*
G01X8369Y53769D02* X1545081Y-4261865D02*
G01X9639Y52499D01* G01*
G01X11674Y52500D01* X2353056Y-4261865D01*
G01X12174Y52000D01* X2435097Y-4180077D01*
G01X12174Y48875D01* X3668999Y-2127001D02*
G01X12999Y48141D01* G01*
G01X12999Y47031D01* X3303000Y-2127001D01*
G01X59499Y21125D02* X3300001Y-2130000D01*
G01X59499Y21250D01* X1299972Y-1927097D02*
G01X58750Y21999D01* G01*
G01X51501Y21999D01* X1299972Y-2769107D01*
G01X50250Y23250D01* X1299997Y-2768983D02*
G01X50250Y34000D01* G01*
G01X46979Y37270D01* X1299997Y-3045007D01*
G01X44310Y37270D01* X2434991Y-4180001D01*
G01X59500Y12124D02* X3199993Y-1754997D02*
G01X59500Y12250D01* G01*
G01X58000Y13750D01* X3317996Y-1873001D01*
G01X51499Y13750D01* X3668999Y-1873001D01*
G01X48749Y16500D01* X3050031Y-1754631D02*
G01X48749Y32500D01* G01*
G01X46519Y34730D01* X3049993Y-1931995D01*
G01X44310Y34730D01* X2831000Y-2127001D01*
G01X38874Y5499D02* X2831084Y-2126995D01*
G01X38874Y6124D01* X3630929Y-223012D02*
G01X46250Y13500D01* G01*
G01X46250Y28750D01* X3757993Y-348000D01*
G01X45349Y29650D01* X3757993Y-659998D01*
G01X44310Y29650D01* X3999991Y-899921D01*
G01X47873Y5499D02* X3999991Y-1249934D01*
G01X47500Y5499D01* X3884929Y-1364995D01*
G01X45999Y7000D01* X3669029Y-1364995D01*
G01X45999Y10749D01* X3669035Y-1618995D02*
G01X47500Y12251D01* G01*
G01X47500Y30749D01* X3668044Y-1618005D01*
G01X46060Y32190D01* X3121990Y-1618005D01*
G01X44310Y32190D01* X3121913Y-1617979D02*
G01X59499Y30125D02* G01*
G01X59499Y30249D01* X3121992Y-2249995D01*
G01X57749Y31999D01* X2987995Y-2381994D01*
G01X55250Y31999D01* X2831084Y-2380995D01*
G01X47439Y39810D01* X3376929Y-477012D02*
G01X44310Y39810D01* G01*
G01X59500Y39124D02* X3377994Y-629998D01*
G01X59500Y39250D01* X3050001Y-940000D01*
G01X57749Y41000D01* X3050001Y-1754728D01*
G01X53750Y41000D01* X5950000Y-787527D02*
G01X52399Y42350D01* G01*
G01X44310Y42350D01* X5950000Y-774999D01*
G01X59500Y48124D02* X5850000Y-674999D01*
G01X59500Y48250D01* X5375000Y-674999D01*
G01X58500Y49250D01* X4939004Y-1110995D01*
G01X53750Y49250D01* X4431004Y-1110995D01*
G01X49390Y44890D01* X5950000Y-1687525D02*
G01X44310Y44890D01* G01*
G01X33769Y51229D02* X5950000Y-1675000D01*
G01X33779Y49700D01* X5774999Y-1499999D01*
G01X30500Y46600D01* X5375000Y-1499999D01*
G01X30500Y38452D01* X5239997Y-1364995D01*
G01X31219Y39820D02* X4431004Y-1364995D01*
G01X31219Y33500D01* X5949950Y-2587497D02*
G01X29879Y32180D01* G01*
G01X28310Y32190D01* X5949950Y-2575052D01*
G01X36690Y39810D02* X5774943Y-2400045D01*
G01X36680Y39820D01* X5525008Y-2400045D01*
G01X31219Y39820D01* X4743958Y-1618995D01*
G01X36309Y53769D02* X4431029Y-1618995D01*
G01X37579Y52520D01* X4787391Y-5050027D02*
G01X37579Y49400D01* G01*
G01X39999Y47000D01* X4750054Y-5050027D01*
G01X39999Y43500D01* X4599940Y-4899913D01*
G01X38849Y42350D01* X4599940Y-4525009D01*
G01X36690Y42350D01* X4750054Y-4374895D01*
G01X30500Y38453D02* X4750054Y-2525013D01*
G01X30499Y36680D01* X4606036Y-2380995D01*
G01X28310Y34730D01* X4431029Y-2380995D01*
G01X28310Y34730D01* X3887470Y-5050027D02*
G01X31999Y38450D02* G01*
G01X33179Y37270D01* X3887470Y-4987543D01*
G01X36689Y37270D01* X4625086Y-4249927D01*
G01X12999Y28310D02* X4625086Y-2724912D01*
G01X12999Y25549D01* X4534915Y-2634995D01*
G01X24349Y14200D01* X4431029Y-2634995D01*
G01X12999Y36729D02* X5950000Y-4387517D02*
G01X12999Y28308D01* G01*
G01X36689Y34730D02* X5950000Y-4375000D01*
G01X33030Y34730D01* X5800001Y-4225000D01*
G01X33000Y34700D01* X5149999Y-4225000D01*
G01X15450Y13381D02* X4874999Y-3949999D01*
G01X23530Y13381D01* X4874999Y-2350000D01*
G01X24350Y14199D01* X4651994Y-2126995D01*
G54D13* X4431004Y-2126995D01*
G01X20690Y44890D03* X5949950Y-3487420D02*
G01X20690Y42350D03* G01*
G01X20690Y34730D03* X5949950Y-3474974D01*
G01X20690Y32190D03* X5875020Y-3400043D01*
G01X20690Y39810D03* X5150104Y-3400043D01*
G01X20690Y37270D03* X5025001Y-3274999D01*
G01X20690Y29650D03* X5025001Y-2200000D01*
G01X20690Y27110D03* X4697984Y-1872995D01*
G01X28310Y27110D03* X4431029Y-1872995D01*
G01X28310Y29650D03* X836929Y-223012D02*
G01X28310Y32190D03* G01*
G01X28310Y34730D03* X963929Y-350012D01*
G01X28310Y37270D03* X1167498Y-349999D01*
G01X28310Y39810D03* X1217498Y-399999D01*
G01X28310Y42350D03* X1217498Y-712497D01*
G01X28310Y44890D03* X1299972Y-785876D01*
G01X44310Y27110D03* X1299972Y-896873D01*
G01X44310Y29650D03* D13*
G01X44310Y37270D03* G01*
G01X44310Y39810D03* X2069007Y-1110995D03*
G01X44310Y32190D03* G01*
G01X44310Y34730D03* X2069007Y-1364995D03*
G01X44310Y42350D03* G01*
G01X44310Y44890D03* X2069007Y-2126995D03*
G01X36690Y44890D03* G01*
G01X36690Y42350D03* X2069007Y-2380995D03*
G01X36690Y39810D03* G01*
G01X36690Y37270D03* X2069007Y-1618995D03*
G01X36690Y34730D03* G01*
G01X36690Y32190D03* X2069007Y-1872995D03*
G01X36690Y29650D03* G01*
G01X36690Y27110D03* X2069007Y-2634995D03*
G01*
X2069007Y-2888995D03*
G01*
X2831007Y-2888995D03*
G01*
X2831007Y-2634995D03*
G01*
X2831007Y-2380995D03*
G01*
X2831007Y-2126995D03*
G01*
X2831007Y-1872995D03*
G01*
X2831007Y-1618995D03*
G01*
X2831007Y-1364995D03*
G01*
X2831007Y-1110995D03*
G01*
X4431004Y-2888995D03*
G01*
X4431004Y-2634995D03*
G01*
X4431004Y-1872995D03*
G01*
X4431004Y-1618995D03*
G01*
X4431004Y-2380995D03*
G01*
X4431004Y-2126995D03*
G01*
X4431004Y-1364995D03*
G01*
X4431004Y-1110995D03*
G01*
X3669004Y-1110995D03*
G01*
X3669004Y-1364995D03*
G01*
X3669004Y-1618995D03*
G01*
X3669004Y-1872995D03*
G01*
X3669004Y-2126995D03*
G01*
X3669004Y-2380995D03*
G01*
X3669004Y-2634995D03*
G01*
X3669004Y-2888995D03*
D14*
G01*
X4999939Y-3699992D03*
G01*
X5150053Y-3699992D03*
D15*
G01*
X1299997Y-1926996D03*
G01*
X1299997Y-896975D03*
G01*
X5253990Y-2861005D03*
G01*
X5253990Y-1830984D03*
G01*
X4224985Y-3210001D03*
G01*
X5255006Y-3210001D03*
G01*
X1299997Y-3799001D03*
G01*
X1299997Y-2768980D03*
D14*
G01*
X1394942Y-4261993D03*
G01*
X1545056Y-4261993D03*
D15*
G01*
X3960012Y-4345025D03*
G01*
X3960012Y-3314979D03*
G01*
X2434996Y-4180001D03*
G01*
X3465017Y-4180001D03*
G36* G36*
G01X57529Y52130D02* X5752998Y-386999D02*
G01X55729Y52130D01* G01*
G01X55729Y50330D01* X5572998Y-386999D01*
G01X57529Y50330D01* X5572998Y-566999D01*
G01X57529Y52130D01* X5752998Y-566999D01*
G37* G37*
G54D14* D16*
G01X56630Y53769D03* G01*
G01X54090Y51229D03* X5663006Y-223012D03*
G01X54090Y53769D03* G01*
G01X51550Y51229D03* X5409006Y-477012D03*
G01X51550Y53769D03* G01*
G01X49010Y51229D03* X5409006Y-223012D03*
G01X49010Y53769D03* G01*
G01X46470Y51229D03* X5155006Y-477012D03*
G01X46470Y53769D03* G01*
G01X43930Y51229D03* X5155006Y-223012D03*
G01X43930Y53769D03* G01*
G01X41390Y51229D03* X4901006Y-477012D03*
G01X41390Y53769D03* G01*
G01X38850Y51229D03* X4901006Y-223012D03*
G01X38850Y53769D03* G01*
G01X36310Y51229D03* X4647006Y-477012D03*
G01X36310Y53769D03* G01*
G01X33770Y51229D03* X4647006Y-223012D03*
G01X33770Y53769D03* G01*
G01X31230Y51229D03* X4393006Y-477012D03*
G01X31230Y53769D03* G01*
G01X28690Y51229D03* X4393006Y-223012D03*
G01X28690Y53769D03* G01*
G01X26150Y51229D03* X4139006Y-477012D03*
G01X26150Y53769D03* G01*
G01X23610Y51229D03* X4139006Y-223012D03*
G01X23610Y53769D03* G01*
G01X21070Y51229D03* X3885006Y-477012D03*
G01X21070Y53769D03* G01*
G01X18530Y51229D03* X3885006Y-223012D03*
G01X18530Y53769D03* G01*
G01X15990Y51229D03* X3631006Y-477012D03*
G01X15990Y53769D03* G01*
G01X13450Y51229D03* X3631006Y-223012D03*
G01X13450Y53769D03* G01*
G01X10910Y51229D03* X3377006Y-477012D03*
G01X10910Y53769D03* G01*
G01X8370Y51229D03* X3377006Y-223012D03*
G01X8370Y53769D03* G01*
G54D15* X3123006Y-477012D03*
G01X49999Y19000D03* G01*
G01X51500Y19000D03* X3123006Y-223012D03*
G54D16* G01*
G01X12999Y36730D03* X2869006Y-477012D03*
G01X12999Y47030D03* G01*
G01X52539Y27390D03* X2869006Y-223012D03*
G01X52539Y37690D03* G01*
G01X42249Y23900D03* X2615006Y-477012D03*
G01X52550Y23900D03* G01*
G01X12999Y18010D03* X2615006Y-223012D03*
G01X12999Y28310D03* G01*
G54D15* X2361006Y-477012D03*
G01X13949Y13380D03* G01*
G01X15450Y13380D03* X2361006Y-223012D03*
G54D16* G01*
G01X39600Y12549D03* X2107006Y-477012D03*
G01X39600Y22850D03* G01*
G01X24349Y14200D03* X2107006Y-223012D03*
G01X34650Y14200D03* G01*
G54D12* X1853006Y-477012D03*
G01X31219Y39820D03* G01*
G01X32999Y34700D03* X1853006Y-223012D03*
G01X31999Y38450D03* G01*
G01X30500Y38452D03* X1599006Y-477012D03*
G01X36699Y25000D03* G01*
G54D17* X1599006Y-223012D03*
G01X32050Y22424D02* G01*
G01X32950Y22424D01* X1345006Y-477012D03*
G01X28600Y21225D02* G01*
G01X28600Y22125D01* X1345006Y-223012D03*
G01X30000Y18624D02* G01*
G01X30000Y17724D01* X1091006Y-477012D03*
G01X35000Y18624D02* G01*
G01X35000Y17724D01* X1091006Y-223012D03*
G01X36399Y22125D02* G01*
G01X36399Y21225D01* X837006Y-477012D03*
G01X5050Y48124D02* G01*
G01X5950Y48124D01* X837006Y-223012D03*
G01X1600Y46924D02* D12*
G01X1600Y47824D01* G01*
G01X3000Y44324D02* X3121990Y-1618005D03*
G01X3000Y43424D01* G01*
G01X8000Y44324D02* X3299993Y-2129993D03*
G01X8000Y43424D01* G01*
G01X9400Y47824D02* X3199993Y-1754987D03*
G01X9400Y46924D01* G01*
G01X5050Y39124D02* X3050006Y-1754733D03*
G01X5950Y39124D01* G01*
G01X1600Y37924D02* X3669995Y-3099993D03*
G01X1600Y38824D01* D17*
G01X3000Y35324D02* X3205007Y-3357524D02*
G01X3000Y34424D01* G01*
G01X8000Y35324D02* X3295007Y-3357524D01*
G01X8000Y34424D01* X2860014Y-3477503D02*
G01X9400Y38824D02* G01*
G01X9400Y37924D01* X2860014Y-3387506D01*
G01X5050Y30124D02* X3000019Y-3737518D02*
G01X5950Y30124D01* G01*
G01X1600Y28925D02* X3000019Y-3827515D01*
G01X1600Y29825D01* X3500018Y-3737518D02*
G01X3000Y26324D02* G01*
G01X3000Y25424D01* X3500018Y-3827515D01*
G01X8000Y26324D02* X3639997Y-3387506D02*
G01X8000Y25424D01* G01*
G01X9400Y29825D02* X3639997Y-3477503D01*
G01X9400Y28925D01* X505012Y-787527D02*
G01X5050Y21124D02* G01*
G01X5950Y21124D01* X595012Y-787527D01*
G01X1600Y19925D02* X160020Y-907508D02*
G01X1600Y20825D01* G01*
G01X3000Y17324D02* X160020Y-817509D01*
G01X3000Y16424D01* X300024Y-1167521D02*
G01X8000Y17324D02* G01*
G01X8000Y16424D01* X300024Y-1257520D01*
G01X9400Y20825D02* X800023Y-1167521D02*
G01X9400Y19925D01* G01*
G01X5050Y12124D02* X800023Y-1257520D01*
G01X5950Y12124D01* X940003Y-817509D02*
G01X1600Y10925D02* G01*
G01X1600Y11825D01* X940003Y-907508D01*
G01X3000Y8324D02* X505012Y-1687525D02*
G01X3000Y7424D01* G01*
G01X8000Y8324D02* X595012Y-1687525D01*
G01X8000Y7424D01* X160020Y-1807507D02*
G01X9400Y11825D02* G01*
G01X9400Y10925D01* X160020Y-1717507D01*
G01X59050Y48124D02* X300024Y-2067519D02*
G01X59950Y48124D01* G01*
G01X55600Y46924D02* X300024Y-2157519D01*
G01X55600Y47824D01* X800023Y-2067519D02*
G01X57000Y44324D02* G01*
G01X57000Y43424D01* X800023Y-2157519D01*
G01X62000Y44324D02* X940003Y-1717507D02*
G01X62000Y43424D01* G01*
G01X63399Y47824D02* X940003Y-1807507D01*
G01X63399Y46924D01* X505012Y-2587523D02*
G01X59050Y39124D02* G01*
G01X59950Y39124D01* X595012Y-2587523D01*
G01X55600Y37924D02* X160020Y-2707505D02*
G01X55600Y38824D01* G01*
G01X57000Y35324D02* X160020Y-2617505D01*
G01X57000Y34424D01* X300024Y-2967517D02*
G01X62000Y35324D02* G01*
G01X62000Y34424D01* X300024Y-3057517D01*
G01X63399Y38824D02* X800023Y-2967517D02*
G01X63399Y37924D01* G01*
G01X59050Y30124D02* X800023Y-3057517D01*
G01X59950Y30124D01* X940003Y-2617505D02*
G01X55600Y28925D02* G01*
G01X55600Y29825D01* X940003Y-2707505D01*
G01X57000Y26324D02* X505012Y-3487521D02*
G01X57000Y25424D01* G01*
G01X62000Y26324D02* X595012Y-3487521D01*
G01X62000Y25424D01* X160020Y-3607503D02*
G01X63399Y29825D02* G01*
G01X63399Y28925D01* X160020Y-3517503D01*
G01X59050Y21124D02* X300024Y-3867515D02*
G01X59950Y21124D01* G01*
G01X55600Y19925D02* X300024Y-3957515D01*
G01X55600Y20825D01* X800023Y-3867515D02*
G01X57000Y17324D02* G01*
G01X57000Y16424D01* X800023Y-3957515D01*
G01X62000Y17324D02* X940003Y-3517503D02*
G01X62000Y16424D01* G01*
G01X63399Y20825D02* X940003Y-3607503D01*
G01X63399Y19925D01* X505012Y-4387519D02*
G01X59050Y12124D02* G01*
G01X59950Y12124D01* X595012Y-4387519D01*
G01X55600Y10925D02* X160020Y-4507501D02*
G01X55600Y11825D01* G01*
G01X57000Y8324D02* X160020Y-4417501D01*
G01X57000Y7424D01* X300024Y-4767513D02*
G01X62000Y8324D02* G01*
G01X62000Y7424D01* X300024Y-4857513D01*
G01X63399Y11825D02* X800023Y-4767513D02*
G01X63399Y10925D01* G01*
G01X11875Y5050D02* X800023Y-4857513D01*
G01X11875Y5950D01* X940003Y-4417501D02*
G01X13075Y1600D02* G01*
G01X12175Y1600D01* X940003Y-4507501D01*
G01X15675Y3000D02* X5905002Y-787527D02*
G01X16575Y3000D01* G01*
G01X15675Y8000D02* X5995001Y-787527D01*
G01X16575Y8000D01* X5560009Y-907508D02*
G01X12175Y9399D02* G01*
G01X13075Y9399D01* X5560009Y-817509D01*
G01X20875Y5050D02* X5700013Y-1167521D02*
G01X20875Y5950D01* G01*
G01X22075Y1600D02* X5700013Y-1257520D01*
G01X21175Y1600D01* X6200013Y-1167521D02*
G01X24675Y3000D02* G01*
G01X25575Y3000D01* X6200013Y-1257520D01*
G01X24675Y8000D02* X6339992Y-817509D02*
G01X25575Y8000D01* G01*
G01X21175Y9399D02* X6339992Y-907508D01*
G01X22075Y9399D01* X5905002Y-1687525D02*
G01X29875Y5050D02* G01*
G01X29875Y5950D01* X5995001Y-1687525D01*
G01X31075Y1600D02* X5560009Y-1807507D02*
G01X30175Y1600D01* G01*
G01X33675Y3000D02* X5560009Y-1717507D01*
G01X34575Y3000D01* X5700013Y-2067519D02*
G01X33675Y8000D02* G01*
G01X34575Y8000D01* X5700013Y-2157519D01*
G01X30175Y9399D02* X6200013Y-2067519D02*
G01X31075Y9399D01* G01*
G01X38875Y5050D02* X6200013Y-2157519D01*
G01X38875Y5950D01* X6339992Y-1717507D02*
G01X40075Y1600D02* G01*
G01X39175Y1600D01* X6339992Y-1807507D01*
G01X42675Y3000D02* X5905002Y-2587523D02*
G01X43575Y3000D01* G01*
G01X42675Y8000D02* X5995001Y-2587523D01*
G01X43575Y8000D01* X5560009Y-2707505D02*
G01X39175Y9399D02* G01*
G01X40075Y9399D01* X5560009Y-2617505D01*
G01X47875Y5050D02* X5700013Y-2967517D02*
G01X47875Y5950D01* G01*
G01X49074Y1600D02* X5700013Y-3057517D01*
G01X48175Y1600D01* X6200013Y-2967517D02*
G01X51675Y3000D02* G01*
G01X52575Y3000D01* X6200013Y-3057517D01*
G01X51675Y8000D02* X6339992Y-2617505D02*
G01X52575Y8000D01* G01*
G01X48175Y9399D02* X6339992Y-2707505D01*
G01X49074Y9399D01* X5905002Y-3487521D02*
M00* G01*
X5995001Y-3487521D01*
X5560009Y-3607503D02*
G01*
X5560009Y-3517503D01*
X5700013Y-3867515D02*
G01*
X5700013Y-3957515D01*
X6200013Y-3867515D02*
G01*
X6200013Y-3957515D01*
X6339992Y-3517503D02*
G01*
X6339992Y-3607503D01*
X5905002Y-4387519D02*
G01*
X5995001Y-4387519D01*
X5560009Y-4507501D02*
G01*
X5560009Y-4417501D01*
X5700013Y-4767513D02*
G01*
X5700013Y-4857513D01*
X6200013Y-4767513D02*
G01*
X6200013Y-4857513D01*
X6339992Y-4417501D02*
G01*
X6339992Y-4507501D01*
X1187526Y-5095003D02*
G01*
X1187526Y-5005003D01*
X1307505Y-5439994D02*
G01*
X1217508Y-5439994D01*
X1567520Y-5299989D02*
G01*
X1657517Y-5299989D01*
X1567520Y-4799990D02*
G01*
X1657517Y-4799990D01*
X1217508Y-4660011D02*
G01*
X1307505Y-4660011D01*
X2087524Y-5095003D02*
G01*
X2087524Y-5005003D01*
X2207503Y-5439994D02*
G01*
X2117506Y-5439994D01*
X2467518Y-5299989D02*
G01*
X2557515Y-5299989D01*
X2467518Y-4799990D02*
G01*
X2557515Y-4799990D01*
X2117506Y-4660011D02*
G01*
X2207503Y-4660011D01*
X2987522Y-5095003D02*
G01*
X2987522Y-5005003D01*
X3107502Y-5439994D02*
G01*
X3017504Y-5439994D01*
X3367516Y-5299989D02*
G01*
X3457514Y-5299989D01*
X3367516Y-4799990D02*
G01*
X3457514Y-4799990D01*
X3017504Y-4660011D02*
G01*
X3107502Y-4660011D01*
X3887520Y-5095003D02*
G01*
X3887520Y-5005003D01*
X4007500Y-5439994D02*
G01*
X3917502Y-5439994D01*
X4267514Y-5299989D02*
G01*
X4357512Y-5299989D01*
X4267514Y-4799990D02*
G01*
X4357512Y-4799990D01*
X3917502Y-4660011D02*
G01*
X4007500Y-4660011D01*
X4787518Y-5095003D02*
G01*
X4787518Y-5005003D01*
X4907498Y-5439994D02*
G01*
X4817501Y-5439994D01*
X5167513Y-5299989D02*
G01*
X5257510Y-5299989D01*
X5167513Y-4799990D02*
G01*
X5257510Y-4799990D01*
X4817501Y-4660011D02*
G01*
X4907498Y-4660011D01*
M02* M02*

View File

@ -1,84 +1,88 @@
G04 Layer: BottomSilkLayer* G04 Layer: BottomSilkLayer*
G04 EasyEDA v6.4.0, 2020-07-14T23:16:01--5:00* G04 EasyEDA v6.4.25, 2022-04-08T13:20:52--5:00*
G04 6dc5d916f8a9456ea10d5ff2c879efc5,9df6f537d2f94b3ba0ed850023b5714d,10*
G04 Gerber Generator version 0.2* G04 Gerber Generator version 0.2*
G04 Scale: 100 percent, Rotated: No, Reflected: No * G04 Scale: 100 percent, Rotated: No, Reflected: No *
G04 Dimensions in millimeters * G04 Dimensions in millimeters *
G04 leading zeros omitted , absolute positions ,3 integer and 3 decimal * G04 leading zeros omitted , absolute positions ,4 integer and 5 decimal *
%FSLAX33Y33*% %FSLAX45Y45*%
%MOMM*% %MOMM*%
G90*
G71D02*
%ADD10C,0.254000*% %ADD10C,0.2540*%
%ADD18C,0.152400*% %ADD18C,0.1524*%
%LPD*% %LPD*%
G54D10* D18*
G01X57899Y49960D02* X2956062Y-674636D02*
G01X57899Y55040D01* G01*
G01X7099Y55040D01* X2961259Y-664245D01*
G01X7099Y49960D01* X2971650Y-653854D01*
G01X57899Y49960D01* X2982038Y-648660D01*
G01X57899Y52500D02* X3002821Y-648660D01*
G01X55359Y52500D01* X3013212Y-653854D01*
G01X55359Y49960D01* X3023603Y-664245D01*
G54D18* X3028800Y-674636D01*
G01X29560Y49253D02* X3033994Y-690224D01*
G01X29612Y49357D01* X3033994Y-716201D01*
G01X29716Y49461D01* X3028800Y-731786D01*
G01X29820Y49513D01* X3023603Y-742177D01*
G01X30028Y49513D01* X3013212Y-752568D01*
G01X30132Y49461D01* X3002821Y-757763D01*
G01X30236Y49357D01* X2982038Y-757763D01*
G01X30288Y49253D01* X2971650Y-752568D01*
G01X30339Y49097D01* X2961259Y-742177D01*
G01X30339Y48838D01* X2956062Y-731786D01*
G01X30288Y48682D01* X2956062Y-716201D01*
G01X30236Y48578D01* X2982038Y-716201D02*
G01X30132Y48474D01* G01*
G01X30028Y48422D01* X2956062Y-716201D01*
G01X29820Y48422D01* X2921772Y-648660D02*
G01X29716Y48474D01* G01*
G01X29612Y48578D01* X2921772Y-757763D01*
G01X29560Y48682D01* X2921772Y-648660D02*
G01X29560Y48838D01* G01*
G01X29820Y48838D02* X2875013Y-648660D01*
G01X29560Y48838D01* X2859427Y-653854D01*
G01X29217Y49513D02* X2854231Y-659051D01*
G01X29217Y48422D01* X2849036Y-669442D01*
G01X29217Y49513D02* X2849036Y-685027D01*
G01X28750Y49513D01* X2854231Y-695418D01*
G01X28594Y49461D01* X2859427Y-700613D01*
G01X28542Y49409D01* X2875013Y-705810D01*
G01X28490Y49305D01* X2921772Y-705810D01*
G01X28490Y49149D01* X2814746Y-648660D02*
G01X28542Y49045D01* G01*
G01X28594Y48993D01* X2814746Y-757763D01*
G01X28750Y48941D01* X2749283Y-648660D02*
G01X29217Y48941D01* G01*
G01X28147Y49513D02* X2759674Y-653854D01*
G01X28147Y48422D01* X2770065Y-664245D01*
G01X27492Y49513D02* X2775259Y-674636D01*
G01X27596Y49461D01* X2780456Y-690224D01*
G01X27700Y49357D01* X2780456Y-716201D01*
G01X27752Y49253D01* X2775259Y-731786D01*
G01X27804Y49097D01* X2770065Y-742177D01*
G01X27804Y48838D01* X2759674Y-752568D01*
G01X27752Y48682D01* X2749283Y-757763D01*
G01X27700Y48578D01* X2728501Y-757763D01*
G01X27596Y48474D01* X2718109Y-752568D01*
G01X27492Y48422D01* X2707718Y-742177D01*
G01X27285Y48422D01* X2702524Y-731786D01*
G01X27181Y48474D01* X2697330Y-716201D01*
G01X27077Y48578D01* X2697330Y-690224D01*
G01X27025Y48682D01* X2702524Y-674636D01*
G01X26973Y48838D01* X2707718Y-664245D01*
G01X26973Y49097D01* X2718109Y-653854D01*
G01X27025Y49253D01* X2728501Y-648660D01*
G01X27077Y49357D01* X2749283Y-648660D01*
G01X27181Y49461D01* D10*
G01X27285Y49513D01* X5789998Y-603999D02*
G01X27492Y49513D01* G01*
M00* X5789998Y-95999D01*
X709998Y-95999D01*
X709998Y-603999D01*
X5789998Y-603999D01*
X5789998Y-349999D02*
G01*
X5535998Y-349999D01*
X5535998Y-603999D01*
M02* M02*

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +1,38 @@
;Layer: Drill NPTH
;EasyEDA v6.4.0, 2020-07-14T23:16:01--5:00
;6dc5d916f8a9456ea10d5ff2c879efc5,9df6f537d2f94b3ba0ed850023b5714d,10
;Gerber Generator version 0.2
M48 M48
METRIC,LZ,000.000 METRIC,LZ,000.000
;Holesize 1 = 1.301 METRIC ;FILE_FORMAT=3:3
;TYPE=NON_PLATED
;Layer: Drill NPTH
;EasyEDA v6.4.25, 2022-04-08T13:20:52--5:00
;10
;Gerber Generator version 0.2
;Holesize 1 = 1.301 mm
T01C1.301 T01C1.301
;Holesize 2 = 2.751 METRIC ;Holesize 2 = 2.751 mm
T02C2.751 T02C2.751
% %
G05 G05
G90 G90
T01 T01
X+032500Y+017424 X032500Y-038575
X+005500Y+043124 X005500Y-012875
X+005500Y+034124 X005500Y-021875
X+005500Y+025124 X005500Y-030875
X+005500Y+016124 X005500Y-039875
X+005500Y+007124 X005500Y-048875
X+059500Y+043124 X059500Y-012875
X+059500Y+034124 X059500Y-021875
X+059500Y+025124 X059500Y-030875
X+059500Y+016124 X059500Y-039875
X+059500Y+007124 X059500Y-048875
X+016875Y+005500 X016875Y-050500
X+025875Y+005500 X025875Y-050500
X+034875Y+005500 X034875Y-050500
X+043875Y+005500 X043875Y-050500
X+052875Y+005500 X052875Y-050500
T02 T02
X+061500Y+052499 X061500Y-003500
X+061500Y+003500 X061500Y-052500
X+003500Y+052499 X003500Y-003500
X+003500Y+003500 X003500Y-052500
M30 M30

View File

@ -1,198 +1,200 @@
;Layer: Drill PTH
;EasyEDA v6.4.0, 2020-07-14T23:16:01--5:00
;6dc5d916f8a9456ea10d5ff2c879efc5,9df6f537d2f94b3ba0ed850023b5714d,10
;Gerber Generator version 0.2
M48 M48
METRIC,LZ,000.000 METRIC,LZ,000.000
;Holesize 1 = 0.305 METRIC ;FILE_FORMAT=3:3
;TYPE=PLATED
;Layer: Drill PTH
;EasyEDA v6.4.25, 2022-04-08T13:20:52--5:00
;10
;Gerber Generator version 0.2
;Holesize 1 = 0.305 mm
T01C0.305 T01C0.305
;Holesize 2 = 0.800 METRIC ;Holesize 2 = 0.800 mm
T02C0.800 T02C0.800
;Holesize 3 = 1.000 METRIC ;Holesize 3 = 1.000 mm
T03C1.000 T03C1.000
;Holesize 4 = 1.101 METRIC ;Holesize 4 = 1.101 mm
T04C1.101 T04C1.101
;Holesize 5 = 1.200 METRIC ;Holesize 5 = 1.200 mm
T05C1.200 T05C1.200
% %
G05 G05
G90 G90
T01 T01
X+031219Y+039820 X031220Y-016180
X+032999Y+034700 X033000Y-021300
X+031999Y+038450 X032000Y-017550
X+030500Y+038452 X030500Y-017547
X+036699Y+025000 X036700Y-031000
T02 T02
X+032950Y+022425G85X+032049Y+022425 X032950Y-033575G85X032050Y-033575
X+028600Y+022125G85X+028600Y+021225 X028600Y-033875G85X028600Y-034775
X+030000Y+017725G85X+030000Y+018625 X030000Y-038275G85X030000Y-037375
X+035000Y+017725G85X+035000Y+018625 X035000Y-038275G85X035000Y-037375
X+036400Y+021225G85X+036400Y+022125 X036400Y-034775G85X036400Y-033875
X+049999Y+019000 X049999Y-037000
X+051500Y+019000 X051501Y-037000
X+013949Y+013380 X013949Y-042620
X+015450Y+013380 X015451Y-042620
X+005950Y+048125G85X+005049Y+048125 X005950Y-007875G85X005050Y-007875
X+001599Y+047825G85X+001599Y+046925 X001600Y-008175G85X001600Y-009075
X+002999Y+043425G85X+002999Y+044325 X003000Y-012575G85X003000Y-011675
X+007999Y+043425G85X+007999Y+044325 X008000Y-012575G85X008000Y-011675
X+009399Y+046925G85X+009399Y+047825 X009400Y-009075G85X009400Y-008175
X+005950Y+039125G85X+005049Y+039125 X005950Y-016875G85X005050Y-016875
X+001599Y+038825G85X+001599Y+037925 X001600Y-017175G85X001600Y-018075
X+002999Y+034425G85X+002999Y+035325 X003000Y-021575G85X003000Y-020675
X+007999Y+034425G85X+007999Y+035325 X008000Y-021575G85X008000Y-020675
X+009399Y+037925G85X+009399Y+038825 X009400Y-018075G85X009400Y-017175
X+005950Y+030125G85X+005049Y+030125 X005950Y-025875G85X005050Y-025875
X+001599Y+029825G85X+001599Y+028925 X001600Y-026175G85X001600Y-027075
X+002999Y+025425G85X+002999Y+026325 X003000Y-030575G85X003000Y-029675
X+007999Y+025425G85X+007999Y+026325 X008000Y-030575G85X008000Y-029675
X+009399Y+028925G85X+009399Y+029825 X009400Y-027075G85X009400Y-026175
X+005950Y+021125G85X+005049Y+021125 X005950Y-034875G85X005050Y-034875
X+001599Y+020825G85X+001599Y+019925 X001600Y-035175G85X001600Y-036075
X+002999Y+016425G85X+002999Y+017325 X003000Y-039575G85X003000Y-038675
X+007999Y+016425G85X+007999Y+017325 X008000Y-039575G85X008000Y-038675
X+009399Y+019925G85X+009399Y+020825 X009400Y-036075G85X009400Y-035175
X+005950Y+012125G85X+005049Y+012125 X005950Y-043875G85X005050Y-043875
X+001599Y+011825G85X+001599Y+010925 X001600Y-044175G85X001600Y-045075
X+002999Y+007425G85X+002999Y+008325 X003000Y-048575G85X003000Y-047675
X+007999Y+007425G85X+007999Y+008325 X008000Y-048575G85X008000Y-047675
X+009399Y+010925G85X+009399Y+011825 X009400Y-045075G85X009400Y-044175
X+059950Y+048125G85X+059049Y+048125 X059950Y-007875G85X059050Y-007875
X+055600Y+047825G85X+055600Y+046925 X055600Y-008175G85X055600Y-009075
X+057000Y+043425G85X+057000Y+044325 X057000Y-012575G85X057000Y-011675
X+062000Y+043425G85X+062000Y+044325 X062000Y-012575G85X062000Y-011675
X+063400Y+046925G85X+063400Y+047825 X063400Y-009075G85X063400Y-008175
X+059950Y+039125G85X+059049Y+039125 X059950Y-016875G85X059050Y-016875
X+055600Y+038825G85X+055600Y+037925 X055600Y-017175G85X055600Y-018075
X+057000Y+034425G85X+057000Y+035325 X057000Y-021575G85X057000Y-020675
X+062000Y+034425G85X+062000Y+035325 X062000Y-021575G85X062000Y-020675
X+063400Y+037925G85X+063400Y+038825 X063400Y-018075G85X063400Y-017175
X+059950Y+030125G85X+059049Y+030125 X059950Y-025875G85X059050Y-025875
X+055600Y+029825G85X+055600Y+028925 X055600Y-026175G85X055600Y-027075
X+057000Y+025425G85X+057000Y+026325 X057000Y-030575G85X057000Y-029675
X+062000Y+025425G85X+062000Y+026325 X062000Y-030575G85X062000Y-029675
X+063400Y+028925G85X+063400Y+029825 X063400Y-027075G85X063400Y-026175
X+059950Y+021125G85X+059049Y+021125 X059950Y-034875G85X059050Y-034875
X+055600Y+020825G85X+055600Y+019925 X055600Y-035175G85X055600Y-036075
X+057000Y+016425G85X+057000Y+017325 X057000Y-039575G85X057000Y-038675
X+062000Y+016425G85X+062000Y+017325 X062000Y-039575G85X062000Y-038675
X+063400Y+019925G85X+063400Y+020825 X063400Y-036075G85X063400Y-035175
X+059950Y+012125G85X+059049Y+012125 X059950Y-043875G85X059050Y-043875
X+055600Y+011825G85X+055600Y+010925 X055600Y-044175G85X055600Y-045075
X+057000Y+007425G85X+057000Y+008325 X057000Y-048575G85X057000Y-047675
X+062000Y+007425G85X+062000Y+008325 X062000Y-048575G85X062000Y-047675
X+063400Y+010925G85X+063400Y+011825 X063400Y-045075G85X063400Y-044175
X+011874Y+005950G85X+011874Y+005050 X011875Y-050050G85X011875Y-050950
X+012174Y+001600G85X+013074Y+001600 X012175Y-054400G85X013075Y-054400
X+016574Y+003000G85X+015674Y+003000 X016575Y-053000G85X015675Y-053000
X+016574Y+008000G85X+015674Y+008000 X016575Y-048000G85X015675Y-048000
X+013074Y+009400G85X+012174Y+009400 X013075Y-046600G85X012175Y-046600
X+020874Y+005950G85X+020874Y+005050 X020875Y-050050G85X020875Y-050950
X+021174Y+001600G85X+022074Y+001600 X021175Y-054400G85X022075Y-054400
X+025575Y+003000G85X+024674Y+003000 X025575Y-053000G85X024675Y-053000
X+025575Y+008000G85X+024674Y+008000 X025575Y-048000G85X024675Y-048000
X+022074Y+009400G85X+021174Y+009400 X022075Y-046600G85X021175Y-046600
X+029874Y+005950G85X+029874Y+005050 X029875Y-050050G85X029875Y-050950
X+030174Y+001600G85X+031074Y+001600 X030175Y-054400G85X031075Y-054400
X+034575Y+003000G85X+033674Y+003000 X034575Y-053000G85X033675Y-053000
X+034575Y+008000G85X+033674Y+008000 X034575Y-048000G85X033675Y-048000
X+031074Y+009400G85X+030174Y+009400 X031075Y-046600G85X030175Y-046600
X+038874Y+005950G85X+038874Y+005050 X038875Y-050050G85X038875Y-050950
X+039174Y+001600G85X+040074Y+001600 X039175Y-054400G85X040075Y-054400
X+043574Y+003000G85X+042674Y+003000 X043575Y-053000G85X042675Y-053000
X+043574Y+008000G85X+042674Y+008000 X043575Y-048000G85X042675Y-048000
X+040074Y+009400G85X+039174Y+009400 X040075Y-046600G85X039175Y-046600
X+047874Y+005950G85X+047874Y+005050 X047875Y-050050G85X047875Y-050950
X+048174Y+001600G85X+049074Y+001600 X048175Y-054400G85X049075Y-054400
X+052575Y+003000G85X+051674Y+003000 X052575Y-053000G85X051675Y-053000
X+052575Y+008000G85X+051674Y+008000 X052575Y-048000G85X051675Y-048000
X+049074Y+009400G85X+048174Y+009400 X049075Y-046600G85X048175Y-046600
T03 T03
X+012999Y+036730 X013000Y-019270
X+012999Y+047030 X013000Y-008970
X+052539Y+027390 X052540Y-028610
X+052539Y+037690 X052540Y-018310
X+042250Y+023900 X042250Y-032100
X+052550Y+023900 X052550Y-032100
X+012999Y+018010 X013000Y-037990
X+012999Y+028310 X013000Y-027690
X+039600Y+012550 X039600Y-043450
X+039600Y+022850 X039600Y-033150
X+024350Y+014200 X024350Y-041800
X+034650Y+014200 X034650Y-041800
T04 T04
X+020690Y+044890 X020690Y-011110
X+020690Y+042350 X020690Y-013650
X+020690Y+034730 X020690Y-021270
X+020690Y+032190 X020690Y-023810
X+020690Y+039810 X020690Y-016190
X+020690Y+037270 X020690Y-018730
X+020690Y+029650 X020690Y-026350
X+020690Y+027110 X020690Y-028890
X+028310Y+027110 X028310Y-028890
X+028310Y+029650 X028310Y-026350
X+028310Y+032190 X028310Y-023810
X+028310Y+034730 X028310Y-021270
X+028310Y+037270 X028310Y-018730
X+028310Y+039810 X028310Y-016190
X+028310Y+042350 X028310Y-013650
X+028310Y+044890 X028310Y-011110
X+044309Y+027110 X044310Y-028890
X+044309Y+029650 X044310Y-026350
X+044309Y+037270 X044310Y-018730
X+044309Y+039810 X044310Y-016190
X+044309Y+032190 X044310Y-023810
X+044309Y+034730 X044310Y-021270
X+044309Y+042350 X044310Y-013650
X+044309Y+044890 X044310Y-011110
X+036689Y+044890 X036690Y-011110
X+036689Y+042350 X036690Y-013650
X+036689Y+039810 X036690Y-016190
X+036689Y+037270 X036690Y-018730
X+036689Y+034730 X036690Y-021270
X+036689Y+032190 X036690Y-023810
X+036689Y+029650 X036690Y-026350
X+036689Y+027110 X036690Y-028890
T05 T05
X+056630Y+051230 X056630Y-004770
X+056630Y+053770 X056630Y-002230
X+054090Y+051230 X054090Y-004770
X+054090Y+053770 X054090Y-002230
X+051550Y+051230 X051550Y-004770
X+051550Y+053770 X051550Y-002230
X+049010Y+051230 X049010Y-004770
X+049010Y+053770 X049010Y-002230
X+046470Y+051230 X046470Y-004770
X+046470Y+053770 X046470Y-002230
X+043930Y+051230 X043930Y-004770
X+043930Y+053770 X043930Y-002230
X+041390Y+051230 X041390Y-004770
X+041390Y+053770 X041390Y-002230
X+038850Y+051230 X038850Y-004770
X+038850Y+053770 X038850Y-002230
X+036310Y+051230 X036310Y-004770
X+036310Y+053770 X036310Y-002230
X+033770Y+051230 X033770Y-004770
X+033770Y+053770 X033770Y-002230
X+031230Y+051230 X031230Y-004770
X+031230Y+053770 X031230Y-002230
X+028690Y+051230 X028690Y-004770
X+028690Y+053770 X028690Y-002230
X+026150Y+051230 X026150Y-004770
X+026150Y+053770 X026150Y-002230
X+023610Y+051230 X023610Y-004770
X+023610Y+053770 X023610Y-002230
X+021070Y+051230 X021070Y-004770
X+021070Y+053770 X021070Y-002230
X+018530Y+051230 X018530Y-004770
X+018530Y+053770 X018530Y-002230
X+015990Y+051230 X015990Y-004770
X+015990Y+053770 X015990Y-002230
X+013450Y+051230 X013450Y-004770
X+013450Y+053770 X013450Y-002230
X+010910Y+051230 X010910Y-004770
X+010910Y+053770 X010910Y-002230
X+008370Y+051230 X008370Y-004770
X+008370Y+053770 X008370Y-002230
M30 M30

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

BIN
pcb/Gerber/LPMPCB.zip Normal file

Binary file not shown.

View File

@ -0,0 +1,68 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>lantern-pigpio</artifactId>
<packaging>jar</packaging>
<version>1.0.0</version>
<name>lantern-pigpio</name>
<parent>
<groupId>com.lanternsoftware.pigpio</groupId>
<artifactId>pigpio</artifactId>
<version>1.0.0</version>
</parent>
<dependencies>
<dependency>
<groupId>com.lanternsoftware.util</groupId>
<artifactId>lantern-util-common</artifactId>
<version>${util.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<executions>
<execution>
<goals>
<goal>testCompile</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
<configuration>
<optimize>true</optimize>
<showDeprecation>true</showDeprecation>
<encoding>UTF-8</encoding>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,52 @@
package com.lanternsoftware.pigpio;
import com.lanternsoftware.util.ResourceLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PIGPIO {
protected static final Logger LOG = LoggerFactory.getLogger(PIGPIO.class);
private PIGPIO() {
}
static {
try {
String osArch = System.getProperty("os.arch").toLowerCase();
if (osArch.equals("arm"))
osArch = "armhf";
String path = "/lib/" + osArch + "/lantern-pigpio.so";
byte[] file = ResourceLoader.getByteArrayResource(PIGPIO.class, path);
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);
}
}
public static native int gpioInitialise();
public static native void gpioTerminate();
public static native int gpioSetMode(int gpio, int mode);
public static native int gpioGetMode(int gpio);
public static native int gpioSetPullUpDown(int gpio, int pud);
public static native int gpioRead(int gpio);
public static native int gpioWrite(int gpio, int level);
public static native int spiOpen(int spiChan, int baud, int spiFlags);
public static native int spiClose(int handle);
public static native int spiRead(int handle, byte[] buf, int offset, int count);
public static native int spiWrite(int handle, byte[] buf, int offset, int count);
public static native int spiXfer(int handle, byte[] txBuf, int txOffset, byte[] rxBuf, int rxOffset, int count);
}

View File

@ -0,0 +1,53 @@
package com.lanternsoftware.pigpio;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
public class PiGpioFactory {
private static final Logger LOG = LoggerFactory.getLogger(PIGPIO.class);
private static final Map<Integer, Spi> spiHandles = new HashMap<>();
private static boolean initialized = false;
public static Spi getSpiChannel(int _channel, int _baud, boolean _auxiliary) {
if (!ensureInitialized())
return null;
int channelId = (0xff & _channel);
if (_auxiliary)
channelId |= 0x100;
Spi handle = spiHandles.get(channelId);
if (handle != null)
return handle;
int h = PIGPIO.spiOpen(_channel, _baud, _auxiliary ? 0x100 : 0);
if (h >= 0) {
handle = new Spi(h);
spiHandles.put(channelId, handle);
return handle;
}
LOG.error("Failed to get SPI handle");
return null;
}
private static boolean ensureInitialized() {
if (initialized)
return true;
int init = PIGPIO.gpioInitialise();
LOG.info("GPIO init: {}", init);
if (init < 0) {
LOG.error("Failed to initialize PiGpio");
return false;
}
initialized = true;
return true;
}
public static void shutdown() {
for (Spi handle : spiHandles.values()) {
PIGPIO.spiClose(handle.getHandle());
}
spiHandles.clear();
PIGPIO.gpioTerminate();
}
}

View File

@ -0,0 +1,37 @@
package com.lanternsoftware.pigpio;
public class Spi {
private final int handle;
public Spi(int _handle) {
handle = _handle;
}
public int getHandle() {
return handle;
}
public int read(byte[] buf) {
return read(buf, 0, buf.length);
}
public int read(byte[] buf, int offset, int count) {
return PIGPIO.spiRead(handle, buf, offset, count);
}
public int write(byte[] buf) {
return write(buf, 0, buf.length);
}
public int write(byte[] buf, int offset, int count) {
return PIGPIO.spiWrite(handle, buf, offset, count);
}
public int transfer(byte[] txBuf, byte[] rxBuf) {
return transfer(txBuf, 0, rxBuf, 0, rxBuf.length);
}
public int transfer(byte[] txBuf, int txOffset, byte[] rxBuf, int rxOffset, int count) {
return PIGPIO.spiXfer(handle, txBuf, txOffset, rxBuf, rxOffset, count);
}
}

View File

@ -0,0 +1,54 @@
ARCH := armhf
DEBUG = -O3
CC = $(CROSS_PREFIX)gcc
AR = $(CROSS_PREFIX)ar
RANLIB = $(CROSS_PREFIX)ranlib
SIZE = $(CROSS_PREFIX)size
STRIP = $(CROSS_PREFIX)strip
SHLIB = $(CC) -shared
STRIPLIB = $(STRIP) --strip-unneeded
INCLUDE = -I. -Ipigpio \
-I/$(JAVA_HOME)/include \
-I/$(JAVA_HOME)/include/linux \
-I/usr/local/include -I/usr/local/include/linux
CFLAGS := $(DEBUG) -Wall $(INCLUDE) -Winline -pipe $(CARGS) -fPIC
LIBS = -L lib/$(ARCH) -L pigpio -lpigpio -lrt
TARGET=lantern-pigpio.so
###############################################################################
SRC = com_lanternsoftware_pigpio_PIGPIO.c
OBJ = $(SRC:.c=.o)
all: $(OBJ)
@echo [LINK with DYNAMICALLY linked libraries]
@$(CC) $(OBJ) -shared -o $(TARGET) $(INCLUDE) $(LIBS)
.c.o:
@echo [COMPILE] $<
@$(CC) -c $(CFLAGS) $< -o $@
clean:
rm -f $(OBJ) $(TARGET) *~ core tags Makefile.bak
tags: $(SRC)
@echo [ctags]
@ctags $(SRC)
depend:
makedepend -Y $(SRC)
install: $(TARGET)
@echo [install]
install -m 0755 -d /usr/local/lib
install -m 0755 -d /usr/local/include
install -m 0644 $(TARGET) /usr/local/lib
uninstall:
@echo [uninstall]
rm -f /usr/local/lib/$(TARGET)
com_lanternsoftware_pigpio_PIGPIO.o: com_lanternsoftware_pigpio_PIGPIO.h

View File

@ -0,0 +1,117 @@
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <pigpio.h>
#include "com_lanternsoftware_pigpio_PIGPIO.h"
JavaVM *callback_jvm;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *env;
if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2))
{
return JNI_ERR;
}
callback_jvm = jvm;
return JNI_VERSION_1_2;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *jvm, void *reserved)
{
return;
}
JNIEXPORT jint JNICALL Java_com_lanternsoftware_pigpio_PIGPIO_gpioInitialise
(JNIEnv *env, jclass class)
{
gpioCfgSetInternals (gpioCfgGetInternals () | PI_CFG_NOSIGHANDLER);
return gpioInitialise();
}
JNIEXPORT void JNICALL Java_com_lanternsoftware_pigpio_PIGPIO_gpioTerminate(JNIEnv *env, jclass class)
{
return gpioTerminate();
}
JNIEXPORT jint JNICALL Java_com_lanternsoftware_pigpio_PIGPIO_gpioSetMode
(JNIEnv *env, jclass class, jint gpio, jint mode)
{
return gpioSetMode((unsigned)gpio, (unsigned)mode);
}
JNIEXPORT jint JNICALL Java_com_lanternsoftware_pigpio_PIGPIO_gpioGetMode
(JNIEnv *env, jclass class, jint gpio)
{
return gpioGetMode((unsigned)gpio);
}
JNIEXPORT jint JNICALL Java_com_lanternsoftware_pigpio_PIGPIO_gpioSetPullUpDown
(JNIEnv *env, jclass class, jint gpio, jint pud)
{
return gpioSetPullUpDown((unsigned)gpio, (unsigned)pud);
}
JNIEXPORT jint JNICALL Java_com_lanternsoftware_pigpio_PIGPIO_gpioRead
(JNIEnv *env, jclass class, jint gpio)
{
return gpioRead((unsigned)gpio);
}
JNIEXPORT jint JNICALL Java_com_lanternsoftware_pigpio_PIGPIO_gpioWrite
(JNIEnv *env, jclass class, jint gpio, jint level)
{
return gpioWrite((unsigned)gpio, (unsigned)level);
}
JNIEXPORT jint JNICALL Java_com_lanternsoftware_pigpio_PIGPIO_spiOpen
(JNIEnv *env, jclass class, jint spiChan, jint baud, jint spiFlags)
{
return spiOpen((unsigned)spiChan, (unsigned)baud, (unsigned)spiFlags);
}
JNIEXPORT jint JNICALL Java_com_lanternsoftware_pigpio_PIGPIO_spiClose
(JNIEnv *env, jclass class, jint handle)
{
return spiClose((unsigned)handle);
}
JNIEXPORT jint JNICALL Java_com_lanternsoftware_pigpio_PIGPIO_spiRead
(JNIEnv *env, jclass class, jint handle, jbyteArray data, jint offset, jint count)
{
jbyte *buffer = (*env)->GetByteArrayElements(env, data, 0);
jsize max_length = (*env)->GetArrayLength(env, data) - offset;
int length = (count > max_length) ? max_length : count;
jbyte *offsetBuffer = buffer + offset;
jint result = spiRead((unsigned)handle, (char *)offsetBuffer, (unsigned)length);
(*env)->ReleaseByteArrayElements(env, data, buffer, 0);
return result;
}
JNIEXPORT jint JNICALL Java_com_lanternsoftware_pigpio_PIGPIO_spiWrite
(JNIEnv *env, jclass class, jint handle, jbyteArray data, jint offset, jint count)
{
jbyte *buffer = (*env)->GetByteArrayElements(env, data, 0);
jsize max_length = (*env)->GetArrayLength(env, data) - offset;
int length = (count > max_length) ? max_length : count;
jbyte *offsetBuffer = buffer + offset;
jint result = spiWrite((unsigned)handle, (char *)offsetBuffer, (unsigned)length);
(*env)->ReleaseByteArrayElements(env, data, buffer, JNI_ABORT);
return result;
}
JNIEXPORT jint JNICALL Java_com_lanternsoftware_pigpio_PIGPIO_spiXfer
(JNIEnv *env, jclass class, jint handle, jbyteArray writeData, jint writeOffset, jbyteArray readData, jint readOffset, jint count)
{
jbyte *writeBuffer = (*env)->GetByteArrayElements(env, writeData, 0);
jbyte *readBuffer = (*env)->GetByteArrayElements(env, readData, 0);
jsize max_length = (*env)->GetArrayLength(env, writeData) - writeOffset;
int length = (count > max_length) ? max_length : count;
jbyte *offsetWriteBuffer = writeBuffer + writeOffset;
jbyte *offsetReadBuffer = readBuffer + readOffset;
jint result = spiXfer((unsigned)handle, (char *)offsetWriteBuffer, (char *)offsetReadBuffer, (unsigned)length);
(*env)->ReleaseByteArrayElements(env, writeData, writeBuffer, JNI_ABORT);
(*env)->ReleaseByteArrayElements(env, readData, readBuffer, 0);
return result;
}

Some files were not shown because too many files have changed in this diff Show More