Improve 3A+ case, making it easier to take the pi out. Improve the fit of the Z2 case.

Make it possible for a hub to reload a config automatically when it changes without being restarted.
Prevent the auto-calibration on first install from being stomped by the app.
Allow updating the hub software via the app.
This commit is contained in:
MarkBryanMilligan
2022-01-13 14:33:21 -06:00
parent 88ed044ef7
commit ed75ab1f05
38 changed files with 659 additions and 261 deletions

View File

@@ -2,20 +2,31 @@ package com.lanternsoftware.currentmonitor.context;
import com.lanternsoftware.dataaccess.currentmonitor.CurrentMonitorDao;
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubCommands;
import com.lanternsoftware.rules.RulesEngine;
import com.lanternsoftware.util.DateUtils;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.dao.mongo.MongoConfig;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
public class Globals implements ServletContextListener {
public static CurrentMonitorDao dao;
private static final Map<Integer, Map<Integer, List<HubCommand>>> commands = new HashMap<>();
@Override
public void contextInitialized(ServletContextEvent sce) {
dao = new MongoCurrentMonitorDao(MongoConfig.fromDisk(LanternFiles.OPS_PATH + "mongo.cfg"));
RulesEngine.instance().start();
RulesEngine.instance().schedule(new CommandTask(), 0);
}
@Override
@@ -23,4 +34,38 @@ public class Globals implements ServletContextListener {
dao.shutdown();
RulesEngine.shutdown();
}
public static HubCommands getCommandsForHub(int _accountId, int _hub) {
List<HubCommand> c = null;
synchronized (commands) {
Map<Integer, List<HubCommand>> hubCommands = commands.get(_accountId);
if (hubCommands != null)
c = hubCommands.remove(_hub);
}
if (c != null) {
for (HubCommand command : c) {
dao.deleteHubCommand(command.getId());
}
return new HubCommands(c);
}
return null;
}
private static final class CommandTask extends TimerTask {
@Override
public void run() {
List<HubCommand> c = Globals.dao.getAllHubCommands();
Date stale = DateUtils.addMinutes(new Date(), -5);
synchronized (commands) {
commands.clear();
for (HubCommand command : c) {
if (DateUtils.isBefore(command.getCreated(), stale))
dao.deleteHubCommand(command.getId());
else
commands.computeIfAbsent(command.getAccountId(), _t -> new HashMap<>()).computeIfAbsent(command.getHub(), _h->new ArrayList<>()).add(command);
}
}
RulesEngine.instance().schedule(new CommandTask(), 1000);
}
}
}

View File

@@ -44,11 +44,8 @@ public class AuthServlet extends LanternServlet {
GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(transport, new GsonFactory(), "https://oauth2.googleapis.com/token", googleClientId, googleClientSecret, auth.getPassword(), "").execute();
if (tokenResponse != null) {
GoogleIdToken idToken = tokenResponse.parseIdToken();
if (idToken != null) {
logger.info("Successfully received google id token");
if (idToken != null)
authCode = Globals.dao.getAuthCodeForEmail(idToken.getPayload().getEmail(), DateUtils.fromTimeZoneId(_req.getHeader("timezone")));
logger.info("Auth code for google user is valid: " + (authCode != null));
}
}
} catch (Exception _e) {
logger.error("Failed to validate google auth code", _e);

View File

@@ -1,49 +1,19 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.LanternFiles;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.ResourceLoader;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.DaoSerializer;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@WebServlet("/command")
public class CommandServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
File folder = new File(LanternFiles.OPS_PATH + _authCode.getAccountId());
List<String> commands = new ArrayList<>();
if (folder.exists() && folder.isDirectory()) {
for (File command : CollectionUtils.asArrayList(folder.listFiles())) {
if (command.isDirectory())
continue;
String c = command.getName();
String extension = NullUtils.after(c, ".");
if (NullUtils.isNotEmpty(extension))
c = c.replace("." + extension, "");
commands.add(c);
}
}
zipBsonResponse(_rep, new DaoEntity("commands", commands));
}
@Override
protected void post(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
DaoEntity payload = getRequestZipBson(_req);
if (payload == null)
return;
String command = DaoSerializer.getString(payload, "command");
String path = LanternFiles.OPS_PATH + _authCode.getAccountId() + File.separator + "payload" + File.separator;
new File(path).mkdirs();
ResourceLoader.writeFile(path+ command + ".txt", DaoSerializer.getString(payload, "payload"));
}
}

View File

@@ -2,6 +2,8 @@ package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.BreakerConfig;
import com.lanternsoftware.datamodel.currentmonitor.HubCommand;
import com.lanternsoftware.datamodel.currentmonitor.HubConfigCharacteristic;
import com.lanternsoftware.util.dao.auth.AuthCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,6 +36,10 @@ public class ConfigServlet extends SecureServlet {
return;
}
logger.info("Received config for account {}", config.getAccountId());
BreakerConfig oldConfig = Globals.dao.getConfig(config.getAccountId());
if ((oldConfig == null) || !oldConfig.isIdentical(config))
Globals.dao.putHubCommand(new HubCommand(config.getAccountId(), HubConfigCharacteristic.ReloadConfig, null));
Globals.dao.putConfig(config);
zipBsonResponse(_rep, Globals.dao.getMergedConfig(_authCode));
}
}

View File

@@ -1,12 +1,17 @@
package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.dataaccess.currentmonitor.MongoCurrentMonitorDao;
import com.lanternsoftware.datamodel.currentmonitor.HubCommands;
import com.lanternsoftware.util.dao.DaoEntity;
import com.lanternsoftware.util.dao.auth.AuthCode;
import com.lanternsoftware.datamodel.currentmonitor.BreakerPower;
import com.lanternsoftware.datamodel.currentmonitor.HubPowerMinute;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
@@ -15,6 +20,8 @@ import java.util.List;
@WebServlet("/power/*")
public class PowerServlet extends SecureServlet {
private static final Logger logger = LoggerFactory.getLogger(MongoCurrentMonitorDao.class);
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
String[] path = path(_req);
@@ -32,19 +39,29 @@ public class PowerServlet extends SecureServlet {
String[] path = path(_req);
if ((path.length > 0) && NullUtils.isEqual(CollectionUtils.get(path, 0), "hub")) {
HubPowerMinute m = getRequestPayload(_req, HubPowerMinute.class);
if (m == null)
return;
logger.info("Hub Power from ip {}, account {}, hub {}", _req.getRemoteAddr(), m.getAccountId(), m.getHub());
m.setAccountId(_authCode.getAccountId());
Globals.dao.putHubPowerMinute(m);
return;
}
if ((path.length > 0) && NullUtils.isEqual(CollectionUtils.get(path, 0), "batch")) {
List<BreakerPower> powers = DaoSerializer.getList(getRequestZipBson(_req), "readings", BreakerPower.class);
DaoEntity payload = getRequestZipBson(_req);
List<BreakerPower> powers = DaoSerializer.getList(payload, "readings", BreakerPower.class);
if (!powers.isEmpty()) {
CollectionUtils.edit(powers, _p->_p.setAccountId(_authCode.getAccountId()));
Globals.dao.getProxy().save(powers);
int hub = DaoSerializer.getInteger(payload, "hub");
HubCommands commands = Globals.getCommandsForHub(_authCode.getAccountId(), hub);
if (commands != null)
zipBsonResponse(_rep, commands);
}
return;
}
BreakerPower power = getRequestPayload(_req, BreakerPower.class);
if (power == null)
return;
power.setAccountId(_authCode.getAccountId());
Globals.dao.putBreakerPower(power);
}

View File

@@ -2,6 +2,8 @@ package com.lanternsoftware.currentmonitor.servlet;
import com.lanternsoftware.currentmonitor.context.Globals;
import com.lanternsoftware.datamodel.currentmonitor.Account;
import com.lanternsoftware.util.CollectionUtils;
import com.lanternsoftware.util.NullUtils;
import com.lanternsoftware.util.dao.DaoSerializer;
import com.lanternsoftware.util.dao.auth.AuthCode;
@@ -9,15 +11,21 @@ import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/rebuildSummaries")
@WebServlet("/rebuildSummaries/*")
public class RebuildSummariesServlet extends SecureServlet {
@Override
protected void get(AuthCode _authCode, HttpServletRequest _req, HttpServletResponse _rep) {
if (_authCode.getAccountId() == 100) {
for (String sId : Globals.dao.getProxy().queryForField(Account.class, null, "_id")) {
int id = DaoSerializer.toInteger(sId);
if (id != 0)
Globals.dao.rebuildSummariesAsync(id);
String[] path = path(_req);
if (path.length > 0) {
Globals.dao.rebuildSummariesAsync(DaoSerializer.toInteger(CollectionUtils.get(path, 0)));
}
else {
for (String sId : Globals.dao.getProxy().queryForField(Account.class, null, "_id")) {
int id = DaoSerializer.toInteger(sId);
if (id != 0)
Globals.dao.rebuildSummariesAsync(id);
}
}
}
else

View File

@@ -2,16 +2,21 @@
<configuration>
<property name="log.pattern" value="%date %-5level %logger{0} - %message%n"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/opt/tomcat/logs/log.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>/opt/tomcat/log/log.%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<maxFileSize>20MB</maxFileSize>
<maxHistory>20</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<logger name="com.lanternsoftware" level="DEBUG"/>
<root level="OFF">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
</configuration>

View File

@@ -2,16 +2,21 @@
<configuration>
<property name="log.pattern" value="%date %-5level %logger{0} - %message%n"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/opt/tomcat/logs/log.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>/opt/tomcat/log/log.%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<maxFileSize>20MB</maxFileSize>
<maxHistory>20</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<logger name="com.lanternsoftware" level="DEBUG"/>
<root level="OFF">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
</configuration>