diff --git a/src/main/java/de/hottis/mbusMaster/ADataObject.java b/src/main/java/de/hottis/mbusMaster/ADataObject.java index 65ea2c3..e7e206d 100644 --- a/src/main/java/de/hottis/mbusMaster/ADataObject.java +++ b/src/main/java/de/hottis/mbusMaster/ADataObject.java @@ -9,17 +9,17 @@ import org.apache.logging.log4j.Logger; public abstract class ADataObject implements Serializable { private static final long serialVersionUID = 1L; - static final String KIND_KEY = "kind"; + static final String KIND_KEY = "kind"; final protected Logger logger = LogManager.getRootLogger(); private String name; private Map values; - private String kind; + private String kind; public ADataObject(String name, String kind) { this.name = name; - this.kind = kind; + this.kind = kind; } public void setValues(Map values) { @@ -31,11 +31,19 @@ public abstract class ADataObject implements Serializable { public Map getValues() { return values; } - + + public boolean hasKey(String k) { + return this.values.containsKey(k); + } + public String getName() { return name; } - + + public String getKind() { + return this.kind; + } + public String toString() { StringBuffer sb = new StringBuffer(); sb.append("{\"name\":\""); @@ -54,11 +62,11 @@ public abstract class ADataObject implements Serializable { sb.append(entry.getKey()); sb.append("\":"); Object value = entry.getValue(); - if (! (value instanceof Double)) { + if (! ((value instanceof Double) || (value instanceof Integer))) { sb.append("\""); } sb.append(value); - if (! (value instanceof Double)) { + if (! ((value instanceof Double) || (value instanceof Integer))) { sb.append("\""); } } diff --git a/src/main/java/de/hottis/mbusMaster/ConfigProperties.java b/src/main/java/de/hottis/mbusMaster/ConfigProperties.java index 00c57bb..4c3f647 100644 --- a/src/main/java/de/hottis/mbusMaster/ConfigProperties.java +++ b/src/main/java/de/hottis/mbusMaster/ConfigProperties.java @@ -16,6 +16,9 @@ public class ConfigProperties extends Properties { static final String PROPS_VERBOSE = "verbose"; static final String PROPS_MAINCONFIGFILE = "mainConfigFile"; static final String PROPS_ERRORRATIOTHRESHOLD = "errorRatioThreshold"; + static final String PROPS_ERRORRATIOCHECKTHRESHOLD = "errorRatioCheckThreshold"; + static final String PROPS_LOOPSHUTDOWNDELAY = "loopShutdownDelay"; + static final String PROPS_DEVICES = "mbus.device"; static final Logger logger = LogManager.getRootLogger(); @@ -80,4 +83,45 @@ public class ConfigProperties extends Properties { public boolean isVerbose() { return this.getBooleanProperty("verbose", false) || this.overwriteVerbose; } + + + public double getDoubleProperty(String key) throws ConfigPropertiesException { + String v = this.getStringProperty(key); + try { + double o = Double.parseDouble(v); + return o; + } catch (NullPointerException | NumberFormatException e) { + throw new ConfigPropertiesException("Error in getDoubleProperty", e); + } + } + + public double getDoubleProperty(String key, double def) { + double returnValue; + try { + returnValue = this.getDoubleProperty(key); + } catch (ConfigPropertiesException e) { + returnValue = def; + } + return returnValue; + } + + public int getIntProperty(String key) throws ConfigPropertiesException { + String v = this.getStringProperty(key); + try { + int o = Integer.parseInt(v); + return o; + } catch (NullPointerException | NumberFormatException e) { + throw new ConfigPropertiesException("Error in getIntProperty", e); + } + } + + public int getIntProperty(String key, int def) { + int returnValue; + try { + returnValue = this.getIntProperty(key); + } catch (ConfigPropertiesException e) { + returnValue = def; + } + return returnValue; + } } \ No newline at end of file diff --git a/src/main/java/de/hottis/mbusMaster/DummyDequeuer.java b/src/main/java/de/hottis/mbusMaster/DummyDequeuer.java index b196b2a..4472ea4 100644 --- a/src/main/java/de/hottis/mbusMaster/DummyDequeuer.java +++ b/src/main/java/de/hottis/mbusMaster/DummyDequeuer.java @@ -6,6 +6,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class DummyDequeuer extends Thread { + static final String ERROR_RATIO_KEY = "errorRatio"; + static final Logger logger = LogManager.getRootLogger(); public static final String ANSI_RESET = "\u001B[0m"; @@ -29,12 +31,15 @@ public class DummyDequeuer extends Thread { while(true) { try { ADataObject o = this.queue.take(); - if (((Double)o.getValues().get("errorRatio")) == 0.0) { + if (o.hasKey(ERROR_RATIO_KEY) && ((Double)o.getValues().get(ERROR_RATIO_KEY)) < 0.001) { System.out.print(ANSI_GREEN); } - if (((Double)o.getValues().get("errorRatio")) > 0.25) { + if (o.hasKey(ERROR_RATIO_KEY) && ((Double)o.getValues().get(ERROR_RATIO_KEY)) > 0.25) { System.out.print(ANSI_RED); } + if ("Statistics".equals(o.getKind())) { + System.out.print(ANSI_CYAN); + } System.out.print("DummyDequeuer: " + o.toString()); System.out.println(ANSI_RESET); } catch (InterruptedException e) { diff --git a/src/main/java/de/hottis/mbusMaster/FinderOnePhasePowerMeter.java b/src/main/java/de/hottis/mbusMaster/FinderOnePhasePowerMeter.java index 321d824..41fad25 100644 --- a/src/main/java/de/hottis/mbusMaster/FinderOnePhasePowerMeter.java +++ b/src/main/java/de/hottis/mbusMaster/FinderOnePhasePowerMeter.java @@ -1,6 +1,13 @@ package de.hottis.mbusMaster; + + public class FinderOnePhasePowerMeter extends MbusDevice { + + public FinderOnePhasePowerMeter(String name, Byte address, Integer queryPeriod) { + this(name, address.byteValue(), queryPeriod.intValue()); + } + public FinderOnePhasePowerMeter(String name, byte address, int queryPeriod) { super(name, address, queryPeriod); this.dataPoints.add(new DataPoint("energy", 0)); diff --git a/src/main/java/de/hottis/mbusMaster/FinderThreePhasePowerMeter.java b/src/main/java/de/hottis/mbusMaster/FinderThreePhasePowerMeter.java index a33e075..9e147ae 100644 --- a/src/main/java/de/hottis/mbusMaster/FinderThreePhasePowerMeter.java +++ b/src/main/java/de/hottis/mbusMaster/FinderThreePhasePowerMeter.java @@ -1,6 +1,11 @@ package de.hottis.mbusMaster; public class FinderThreePhasePowerMeter extends MbusDevice { + + public FinderThreePhasePowerMeter(String name, Byte address, Integer queryPeriod) { + this(name, address.byteValue(), queryPeriod.intValue()); + } + public FinderThreePhasePowerMeter(String name, byte address, int queryPeriod) { super(name, address, queryPeriod); this.dataPoints.add(new DataPoint("energy", 0)); diff --git a/src/main/java/de/hottis/mbusMaster/MbusDevice.java b/src/main/java/de/hottis/mbusMaster/MbusDevice.java index 101ad3c..67696f8 100644 --- a/src/main/java/de/hottis/mbusMaster/MbusDevice.java +++ b/src/main/java/de/hottis/mbusMaster/MbusDevice.java @@ -39,6 +39,10 @@ abstract public class MbusDevice { protected ArrayList dataPoints; + protected MbusDevice(String name, Byte address, Integer queryPeriod) { + this(name, address.byteValue(), queryPeriod.intValue()); + } + protected MbusDevice(String name, byte address, int queryPeriod) { this.name = name; this.address = address; diff --git a/src/main/java/de/hottis/mbusMaster/MbusMaster.java b/src/main/java/de/hottis/mbusMaster/MbusMaster.java index 66338ce..5d7ee31 100644 --- a/src/main/java/de/hottis/mbusMaster/MbusMaster.java +++ b/src/main/java/de/hottis/mbusMaster/MbusMaster.java @@ -34,6 +34,7 @@ public class MbusMaster { MbusScheduledQuerier querier = new MbusScheduledQuerier(config, queue); + querier.loadDevices(); querier.start(); DummyDequeuer ddq = new DummyDequeuer(queue); diff --git a/src/main/java/de/hottis/mbusMaster/MbusScheduledQuerier.java b/src/main/java/de/hottis/mbusMaster/MbusScheduledQuerier.java index a4e7aa6..d036742 100644 --- a/src/main/java/de/hottis/mbusMaster/MbusScheduledQuerier.java +++ b/src/main/java/de/hottis/mbusMaster/MbusScheduledQuerier.java @@ -3,8 +3,12 @@ package de.hottis.mbusMaster; import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + import java.util.ArrayList; import java.util.concurrent.BlockingQueue; +import java.util.Enumeration; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -12,6 +16,8 @@ import org.apache.logging.log4j.Logger; public class MbusScheduledQuerier extends Thread { static final double DEFAULT_ERRORRATIOTHRESHOLD = 0.5; + static final int DEFAULT_ERRORRATIOCHECKTHRESHOLD = 100; + static final int DEFAULT_LOOPSHUTDOWNDELAY = 300; // seconds static final Logger logger = LogManager.getRootLogger(); @@ -28,20 +34,53 @@ public class MbusScheduledQuerier extends Thread { this.queue = queue; this.devices = new ArrayList<>(); - this.devices.add(new FinderThreePhasePowerMeter("Total Electricity", (byte)80, 0)); - this.devices.add(new FinderOnePhasePowerMeter("Dryer", (byte)81, 0)); - this.devices.add(new FinderOnePhasePowerMeter("Laundry", (byte)82, 0)); - this.devices.add(new FinderOnePhasePowerMeter("Dishwasher", (byte)83, 0)); - this.devices.add(new FinderOnePhasePowerMeter("Light", (byte)84, 0)); - this.devices.add(new FinderOnePhasePowerMeter("Computer", (byte)85, 0)); - this.devices.add(new FinderOnePhasePowerMeter("Freezer", (byte)86, 0)); - this.devices.add(new FinderOnePhasePowerMeter("Fridge", (byte)87, 0)); + } + + public void loadDevices() throws MbusException { + String deviceClassName = null; + try { + @SuppressWarnings("unchecked") + Enumeration propNames = (Enumeration) config.propertyNames(); + while (propNames.hasMoreElements()) { + String propName = propNames.nextElement(); + if (propName.startsWith(ConfigProperties.PROPS_DEVICES)) { + String[] devicesConfigElements = config.getProperty(propName).split(","); + String name = devicesConfigElements[0]; + byte addr = Byte.parseByte(devicesConfigElements[1]); + deviceClassName = devicesConfigElements[2]; + int period = Integer.parseInt(devicesConfigElements[3]); + + Class klass = Class.forName(deviceClassName); + Constructor constructor = klass.getConstructor(String.class, Byte.class, Integer.class); + MbusDevice device = (MbusDevice) constructor.newInstance(name, addr, period); + + this.devices.add(device); + logger.info("Device " + name + " with class " + deviceClassName + ", address " + addr + ", period " + period + " loaded"); + } + } + } catch (ClassNotFoundException e) { + String msg = "Device class " + deviceClassName + " not found when loading devices"; + logger.error(msg); + throw new MbusException(msg, e); + } catch (NoSuchMethodException e) { + String msg = "Required constructor in device class " + deviceClassName + " not found when loading devices"; + logger.error(msg); + throw new MbusException(msg, e); + } catch (InvocationTargetException | IllegalAccessException | InstantiationException e) { + String msg = "Error when instantiating device class " + deviceClassName + " when loading devices"; + logger.error(msg); + throw new MbusException(msg, e); + } } public void setStopSignal() { this.stopSignal = true; } + private int getCurrentTime() { + return (int) (System.currentTimeMillis() / 1000); + } + public void run() { try { MbusgwChild mbusgw = new MbusgwChild(config); @@ -51,13 +90,16 @@ public class MbusScheduledQuerier extends Thread { int errCnt = 0; int successCnt = 0; int shutdownCnt = 0; - double maxErrorRatio = 0; + int lastShutdown = this.getCurrentTime(); int timeSinceLastLoopShutdown = 0; + int shutdownTimeSum = 0; int meantimeBetweenLoopShutdowns = 0; while (! this.stopSignal) { cnt++; + double maxErrorRatio = 0; + for (MbusDevice device : this.devices) { logger.info("Querying " + device.getName() + " meter"); try { @@ -75,12 +117,23 @@ public class MbusScheduledQuerier extends Thread { errCnt++; logger.error("Error " + e.toString() + " in Meterbus dialog for device " + device.shortString()); } - this.maxErrorRatio = (this.maxErrorRatio > device.getErrorRatio()) ? this.maxErrorRatio : device.getErrorRatio(); + maxErrorRatio = (maxErrorRatio > device.getErrorRatio()) ? maxErrorRatio : device.getErrorRatio(); } + + logger.info("CycleCnt: " + cnt + ", SuccessCnt: " + successCnt + ", ErrCnt: " + errCnt + + ", MaxErrorRatio: " + maxErrorRatio + ", MeanErrorRatio: " + ((double)errCnt / (double)(errCnt+successCnt)) + + ", ShutdownCnt: " + shutdownCnt + + ", TimeSinceLastLoopShutdown: " + timeSinceLastLoopShutdown + ", MeantimeBetweenLoopShutdowns: " + meantimeBetweenLoopShutdowns); + this.queue.add(new MbusStatisticsDataObject("MbusgwChild", cnt, errCnt, successCnt, shutdownCnt, + maxErrorRatio, ((double)errCnt / (double)(errCnt+successCnt)), + timeSinceLastLoopShutdown, meantimeBetweenLoopShutdowns)); + + if ((maxErrorRatio > config.getDoubleProperty(ConfigProperties.PROPS_ERRORRATIOTHRESHOLD, DEFAULT_ERRORRATIOTHRESHOLD)) && + (cnt % config.getIntProperty(ConfigProperties.PROPS_ERRORRATIOCHECKTHRESHOLD, DEFAULT_ERRORRATIOCHECKTHRESHOLD) == 0)) { + logger.error("maxErrorRatio exceeds threshold and cycleCnt exceeds checkThreshold, request loop shutdown"); - if (this.maxErrorRatio > config.getDoubleProperties(ConfigProperties.PROPS_ERRORRATIOTHRESHOLD, DEFAULT_ERRORRATIOTHRESHOLD)) { // disable loop - shutdownCnt++; + mbusgw.loopShutdown(); // reset counters in devices for (MbusDevice device : this.devices) { @@ -89,17 +142,26 @@ public class MbusScheduledQuerier extends Thread { // reset local counters errCnt = 0; successCnt = 0; + shutdownCnt++; // remember time of loop shutdown // calculate time since last shutdown // calculate mean time between shutdowns + int currentTime = this.getCurrentTime(); + timeSinceLastLoopShutdown = currentTime - lastShutdown; + lastShutdown = currentTime; + shutdownTimeSum += timeSinceLastLoopShutdown; + meantimeBetweenLoopShutdowns = shutdownTimeSum / shutdownCnt; + + // delay + int delay = config.getIntProperty(ConfigProperties.PROPS_LOOPSHUTDOWNDELAY, DEFAULT_LOOPSHUTDOWNDELAY); + logger.error("delaying for " + delay + "s"); + try { + Thread.sleep(delay * 1000); + } catch (InterruptedException e) { + } } - - logger.info("CycleCnt: " + cnt + ", SuccessCnt: " + successCnt + ", ErrCnt: " + errCnt); - this.queue.add(new MbusStatisticsDataObject("MbusgwChild", errCnt, successCnt, shutdownCnt, - ((double)errCnt / (double)(errCnt+successCnt))), - timeSinceLastLoopShutdown, meantimeBetweenLoopShutdowns); try { Thread.sleep(5*1000); diff --git a/src/main/java/de/hottis/mbusMaster/MbusStatisticsDataObject.java b/src/main/java/de/hottis/mbusMaster/MbusStatisticsDataObject.java index 882d4fe..3efe1c9 100644 --- a/src/main/java/de/hottis/mbusMaster/MbusStatisticsDataObject.java +++ b/src/main/java/de/hottis/mbusMaster/MbusStatisticsDataObject.java @@ -6,20 +6,24 @@ public class MbusStatisticsDataObject extends ADataObject { private static final long serialVersionUID = 1L; static final String ERROR_CNT_KEY = "error"; static final String SUCCESS_CNT_KEY = "success"; - static final String ERROR_RATIO_KEY = "errorRatio"; + static final String CYCLE_CNT_KEY = "cycle"; + static final String MEAN_ERROR_RATIO_KEY = "meanErrorRatio"; + static final String MAX_ERROR_RATIO_KEY = "maxErrorRatio"; static final String TIME_SINCE_LAST_SHUTDOWN_KEY = "timeSinceLastShutdown"; static final String MEANTIME_BETWEEN_SHUTDOWNS_KEY = "meantimeBetweenShutdowns"; static final String SHUTDOWNS_KEY = "shutdowns"; static final String TABLE_NAME = "Statistics"; static final String KIND_NAME = "Statistics"; - public MbusStatisticsDataObject(String name, int error, int success, int shutdowns, - double errorRatio, int timeSinceLastShutdown, int meantimeBetweenShutdowns) { + public MbusStatisticsDataObject(String name, int cycle, int error, int success, int shutdowns, + double maxErrorRatio, double meanErrorRatio, int timeSinceLastShutdown, int meantimeBetweenShutdowns) { super(name, KIND_NAME); HashMap values = new HashMap(); + values.put(CYCLE_CNT_KEY, cycle); values.put(ERROR_CNT_KEY, error); values.put(SUCCESS_CNT_KEY, success); - values.put(ERROR_RATIO_KEY, errorRatio); + values.put(MAX_ERROR_RATIO_KEY, maxErrorRatio); + values.put(MEAN_ERROR_RATIO_KEY, meanErrorRatio); values.put(TIME_SINCE_LAST_SHUTDOWN_KEY, timeSinceLastShutdown); values.put(MEANTIME_BETWEEN_SHUTDOWNS_KEY, meantimeBetweenShutdowns); values.put(SHUTDOWNS_KEY, shutdowns); diff --git a/src/main/java/de/hottis/mbusMaster/MbusgwChild.java b/src/main/java/de/hottis/mbusMaster/MbusgwChild.java index 089b71e..4698709 100644 --- a/src/main/java/de/hottis/mbusMaster/MbusgwChild.java +++ b/src/main/java/de/hottis/mbusMaster/MbusgwChild.java @@ -77,6 +77,11 @@ public class MbusgwChild { logger.info("Thread joined"); } + public void loopShutdown() throws IOException { + logger.info("Requesting loop shutdown"); + this.sendRequest((byte)0, (byte)1); + } + public InputStream getProcessInputStream() { return this.processInput; }