Messung des Stromverbrauchs mit Hilfe von Schaltsteckdosen.
Der Stromverbrauch von Geräten wird von Herstellern meist sehr allgemein angegeben. Wieviel Energie tatsächlich verbraucht wird, lässt sich oft nur mit einer Messung feststellen. Schaltbare Steckdosen ermöglichen es, den Stromverbrauch von Geräten festzustellen und erleichtern mit ihren Funktionen eine Messung über längere Zeiträume.
Stromverbrauch
Der Bedarf an elektrischer Energie, auch der Strombedarf, ist die Menge an elektrischer Energie, die Elektrogeräte für ihren Betrieb benötigen. Die tatsächliche Umsetzung im Betrieb während eines definierten Zeitabschnitts wird als Stromverbrauch bezeichnet.
Die umgewandelte elektrische Energie W (von englisch work = Arbeit) wird gemessen in Wattsekunden oder auch Kilowattstunden. Sie ist die gesamte elektrische Arbeit, die während des betrachteten Zeitraums t bei der betrachteten Leistungsaufnahme P fließt. Bei einem gleich bleibenden Bedarf ist die umgewandelte Energie W das Produkt der elektrischen Leistung P mit der verstrichenen Zeit t:
W = Pt
Beispiel: Ein Haartrockner nimmt 2000 Watt (2 kW) elektrische Leistung auf. Wird der Haartrockner eine halbe Stunde (0,5 h) lang betrieben, beträgt der Bedarf an elektrischer Energie 2 kW x 0,5 h = 1 kWh (eine Kilowattstunde).
(Quelle: Wikipedia [2])
Messung
Die Definition des Stromverbrauchs lässt erkennen, dass eine fortlaufende Messung des Stromverbrauchs nötig ist, um die gesamte Arbeit festzustellen, die während des betrachteten Zeitraums bei der Leistungsaufnahme fließt. Die Shelly Plug S tut genau das: Sie misst den Stromverbrauch fortlaufend. Der Stand der fortlaufenden Messung wird in einem kumulierten Gesamtwert festgehalten und fortgeschrieben. Zudem wird die Arbeit über jeweils die letzen drei vollen Minuten fortlaufend festgehalten (Wattminuten).
http://192.168.178.78/meter/0
Die IP-Adresse in obigem Aufruf ist die Adresse der Steckdose im Heimnetz. Mit Java lässt sich der obige Aufruf wie folgt ausführen.
private String getMessung(String ip) {
StringBuilder sb = new StringBuilder();
try {
URL url = new URL("http", ip, "/meter/0");
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
int responseCode = c.getResponseCode();
if (responseCode == 200) {
BufferedReader in = new BufferedReader(new InputStreamReader(c.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
sb.append(inputLine);
}
in.close();
} else {
sb.append("Messung fehlgeschlagen, ").append(responseCode);
}
} catch (IOException ex) {
sb.append("Messung fehlgeschlagen, ").append(ex.getLocalizedMessage());
}
return sb.toString();
}
Die Antwort des Abrufs liefert eine JSON-Struktur wie folgt:
{
"power":14.02,
"overpower":0.00,
"is_valid":true,
"timestamp":1671551931,
"counters":[
13.984,
12.823,
12.839],
"total":426009
}
Die obige JSON-Struktur kann in ein Java-Objekt überführt werden. Dazu genügt eine Java-Klasse mit folgendem Inhalt.
public class ShellyPlugSStromMessung {
public double power;
public double overpower;
public boolean is_valid;
public long timestamp;
public double[] counters;
public int total;
}
Mit Einsatz von Google Gson [6] genügt folgender Java-Code, um die Messdaten zu lesen.
private ShellyPlugSStromMessung getMessObjekt(String json) {
Gson gson = new Gson();
return gson.fromJson(json, ShellyPlugSStromMessung.class);
}
So gewonnene Daten können nun in Java weiterverarbeitet werden.
Messreihe
Zur Feststellung des Stromverbrauchs für einen bestimmten Zeitraum müssen über den entsprechenden Zeitraum hinweg immer wieder Messungen gemacht werden. Da eine Schaltsteckdose des Typs Shelly Plug S die Wattminuten der letzten drei vollen Minuten erfasst, bietet sich eine Messung alle drei Minuten als Intervall an. Die so alle drei Minuten entstehenden Messungen bilden eine Messreihe.
Zur Modellierung einer Messreihe dient die folgende auszugsweise dargestellte gleichnamige Java-Klasse.
public class Messreihe {
private transient Connection c;
private transient final Timer timer;
public Messreihe() {
this.timer = new Timer();
}
public Timer getTimer() {
return timer;
}
// hier folgen noch weitere Elemente,
// die weiter unten betrachtet werden
}
Die Klasse Messreihe
dient der Erfassung von Informationen zu einer Messreihe. Zentral ist zudem die Referenz zu einem Objekt der Klasse Timer
, das zur Steuerung der Messungen verwendet wird, sowie die Referenz zur Verbindung zu einer Datenbank.
Datenbank
Um die gewonnenen Daten zur späteren Verwendung festhalten und auswerten zu können, bietet sich die Nutzung einer relationalen Datenbank an. Die Datenstruktur zur Aufnahme der Daten ist nachfolgend beschrieben.
Tabellen
Die Datenbanktabelle Messung dient zur Aufnahme eines Messergebnisses in der Struktur, wie sie Schaltsteckdosen des Typs Shelly Plug S liefern.
CREATE TABLE APP.MESSUNG
(
MID INTEGER NOT NULL,
MRID INTEGER NOT NULL,
PW DOUBLE NOT NULL,
OP DOUBLE NOT NULL,
IV BOOLEAN,
TS TIMESTAMP NOT NULL,
W1 DOUBLE,
W2 DOUBLE,
W3 DOUBLE,
TTL INTEGER
);
Die Tabelle Messreihe nimmt die Angaben einzelner Messreihen auf.
CREATE TABLE APP.MESSREIHE
(
MRID INTEGER NOT NULL,
MRSTATUS INTEGER NOT NULL,
MRNAME VARCHAR(250) NOT NULL,
MRIP VARCHAR(15) NOT NULL,
MRSTART TIMESTAMP,
MREND TIMESTAMP
);
In der Tabelle WMin
wird der Stromverbrauch für jede Minute einzeln gespeichert. Auf diese Weise sind Auswertungen über längere Zeiträume einfacher bis auf einzelne Minuten herunterzubrechen.
CREATE TABLE APP.WMIN
(
MID INTEGER NOT NULL,
WMNR INTEGER NOT NULL,
TS TIMESTAMP NOT NULL,
WMIN DOUBLE
);
DTO
Die Klasse Messreihe
wird mit Gettern, Settern und Annotationen versehen, die Objekte der Klasse als Data Transfer Objects (DTO) [11] zum Lesen aus der und Schreiben in die Datenbank verwendbar werden lassen.
@DBTable(name="app.messreihe")
@DBPrimaryKey({"mrid"})
public class Messreihe {
private transient Connection c;
private transient final Timer timer;
public Messreihe() {
this.timer = new Timer();
}
public Connection getConnection() {
return c;
}
public void setConnection(Connection c) {
this.c = c;
}
public Timer getTimer() {
return timer;
}
/* ----- DTO ----- */
public static final int STATUS_RUNNING = 1;
public static final int STATUS_ENDED = 2;
private int mrid;
private int mrstatus;
private String mrname;
private String ip;
private Timestamp mrstart;
private Timestamp mrend;
public void setmrid(int wert) {
mrid = wert;
}
@DBColumn(name = "mrid")
public int getmrid() {
return mrid;
}
public void setmrname(String wert) {
mrname = wert;
}
@DBColumn(name = "mrname")
public String getmrname() {
return mrname;
}
// hier folgen noch weitere getter und setter
}
Mit der Klasse Messung
wird ebenso verfahren.
@DBTable(name="app.messung")
@DBPrimaryKey({"mid"})
public class Messung {
private int mid;
private double pw;
private double op;
private boolean iv;
private String ts;
private double w1;
private double w2;
private double w3;
private int ttl;
public void setmid(int wert) {
mid = wert;
}
@DBColumn(name = "mid")
public int getmid() {
return mid;
}
public void setpw(double wert) {
pw = wert;
}
@DBColumn(name = "pw")
public double getpw() {
return pw;
}
// hier weitere getter und setter
}
Und auch die Klasse WattMinute
dient als DTO.
@DBTable(name="app.wmin")
@DBPrimaryKey({"mid","wmnr"})
public class WattMinute {
private int mid;
private int wmnr;
private java.sql.Timestamp ts;
private double wmin;
public void setmid(int wert) {
mid = wert;
}
@DBColumn(name = "mid")
public int getmid() {
return mid;
}
public void setwmnr(int wert) {
wmnr = wert;
}
@DBColumn(name = "wmnr")
public int getwmnr() {
return wmnr;
}
public void setts(java.sql.Timestamp wert) {
ts = wert;
}
@DBColumn(name = "ts")
public java.sql.Timestamp getts() {
return ts;
}
public void setwmin(double wert) {
wmin = wert;
}
@DBColumn(name = "wmin")
public double getwmin() {
return wmin;
}
}
In Verbindung mit einem Derby Network Server [7] ist nun alles bereit zur Durchführung und Steuerung von Messreihen.
Timestamp
Im Zusammenhang mit Messreihen ist der Zeitpunkt einzelner Messungen wichtig. Ein Zeit-Ausdruck wird mit Hilfe der Package java.sql
mit dem Datentyp Timestamp [14] in Datenbanken gespeichert. Derby hat hierfür den gleichnamigen Datentyp [15].
Mit den Methoden getTime
[16] und setTime
[17] kann ein Java SQL Zeitstempel in einen Unix Zeitstempel verwandelt werden und umgekehrt. Die Klasse java.util.Date
wiederum kann einen Unix Zeitstempel in ein Java-Objekt mit dem entsprechenden Datum und der entsprechenden Zeit fassen.
Die Methode valueOf
[18] verwandelt einen String des Formats yyyy-[m]m-[d]d hh:mm:ss[.f…]
in einen Java-SQL-Timestamp. Die Angabe von 'fractional' Sekunden ist optional und kann weggelassen werden, wenn Sekundenbruchteile für den Anwednungsfall nicht nötig sind. Die führenden Nullen bei Monaten und Tagen können ebenfalls weggelassen werden.
Für Zeitangaben kann auf der Seite eines Webclients das String-Format 2022-12-28 17:00:00
genutzt werden, um beispielsweise beim Abruf von Ergebnissen einen über die Bedienoberfläche gewählten Start- oder Ende-Zeitpunkt anzugeben.
BaseLink
Zur Arbeit mit der zuvor beschriebenen Datenbank kommt die Klassenbibliothek BaseLink
[10] zum Einsatz, die als zentralen Funktionsbaustein die Klasse PersistenceManager
sowie Objekte der Klasse GenericRecord
für das objektrelationale Mapping [9] vorsieht. Zudem unterstützt BaseLink die Verwendung von Konfigurationsdateien für die Angabe von SQL-Statements [13].
Deshalb sind Klassen im Kontext dieses Beitrag als Subklassen der Klasse DbActor
angelegt, wenn sie auf die Datenbank zugreifen. DbActor
vereinheitlicht für ihre Subklassen die Verwendung von BaseLink für Zugriffe auf die Datenbank.
In DbActor
ist auch die in den Code-Beispielen dieses Beitrags verschiedentlich auftauchende Methode getNextId
enthalten, wie sie in der Anleitung zu BaseLink für eigene Schlüssel [12] beschrieben ist.
Steuerung
Zur Steuerung von Messungen in Messreihen dient ein Objekt der Klasse MessTreiber
sowie Objekte von dessen innerer Klasse MessTask
. Ein MessTreiber
hält je ein Objekt der Klasse GenericRecord
für jede Klasse bereit, die vom MessTask
für Datenbankzugriffe benötigt wird. Zudem wird im Konstruktor der Klasse ein PersistenceManager
und ein Verzeichnis der benötigten SQL-Kommandos erzeugt.
public class MessTreiber extends DbActor {
public static final String SQL_KEY_MESSREIHE = "mrid";
public static final int START_FAILED = -1;
public static final long MESS_INTERVALL = 1000 * 58 * 3; // prod: 2 Minuten 54 Sekunden
private final Record mrMapper = new GenericRecord(Messreihe.class);
private final Record msMapper = new GenericRecord(ShellyPlugSStromMessung.class);
private final Record wminMapper = new GenericRecord(WattMinute.class);
private final Map<Integer, Messreihe> mrMap;
public MessTreiber(String dbDriverName, String dbUrl) throws ClassNotFoundException, IOException {
super(dbDriverName, dbUrl);
mrMap = new HashMap();
}
// weitere Methoden
}
Messreihe starten
Eine Messreihe wird mit der Methode MessTreiber.start
begonnen.
public static final long MESS_INTERVALL = 1000 * 58 * 3; // 2 Minuten 54 Sekunden
public int start(String ip, String mrName) {
Messreihe mr = new Messreihe();
Connection c = db.getConnection();
mr.setConnection(c);
int mrid = getNextId(c, SQL_KEY_MESSREIHE);
mr.setmrid(mrid);
mr.setmrname(mrName);
mr.setIp(ip);
mr.setMrstatus(Messreihe.STATUS_RUNNING);
mr.setMrstart(new Timestamp(new Date().getTime()));
mr.setMrend(mr.getMrstart());
mrMap.put(mrid, mr);
db.insert(c, mr, mrMapper);
MessTask mt = new MessTask();
mt.setMrId(mrid);
mr.getTimer().scheduleAtFixedRate(mt, 0, MESS_INTERVALL);
return mrid;
}
In der Methode MessTreiber.start
wird zuallererst eine neue Messreihe erzeugt und vom PersistenceManager
eine Datenbankverbindung abgerufen, die für die Dauer der Messreihe dieser für Datenbankzugriffe übergeben wird. Der MessTreiber
'merkt' sich die Messreihe zusammen mit allen laufenden Messreihen in einer Map
und schreibt die Angaben zur erzeugten Messreihe in die Datenbank.
Zuletzt wird eine neue Instanz eines MessTasks erzeugt und mit dem Intervall MESS_INTERVALL
gestartet, das mit 2 Minuten und 54 Sekunden angelegt ist. Mit diesem Intervall wird erreicht, dass durch eventuelle Schwankungen und Ungenauigkeiten bei der Zeitsteuerung keine der drei vollen Minuten verpasst wird, die von einer Schaltsteckdose aufgezeichnet werden.
Messungen
Messungen werden während des Laufs einer Messreihe alle 2 Minuten und 54 Sekunden vom Timer ausgelöst, der in der Methode MessTask.start
gestartet wird.
public class MessTask extends TimerTask {
public static final String SQL_KEY_MESSUNG = "mid";
private int mrid;
public void setMrId(int mrid) {
this.mrid = mrid;
}
// weitrere Methoden
}
Die innere Klasse MessTask
der Klasse MessTreiber
überschreibt als Subklasse der Klasse TimerTask
hierfür deren Methode run
. Sie wird von Timer
-Objekten immer dann aufgerufen, wenn die Zeit für die Ausführung eines Tasks gekommen ist.
public void run() {
Messreihe mr = mrMap.get(mrid);
Connection c = mr.getConnection();
String messungJson = getMessung(mr.getIp());
ShellyPlugSStromMessung sm = getMessObjekt(messungJson);
if (sm instanceof ShellyPlugSStromMessung) {
/*
der Shelly-Timestamp ist in Sekunden,
java.sql.Timestamp will die Zeit aber in Millisekunden
*/
sm.timestamp = sm.timestamp * 1000;
/*
der Shelly-Timestamp ist aus unklaren Gruenden der
Zeit eine Stunde voraus
*/
sm.timestamp -= 1000 * 60 * 60;
sm.setmid(getNextId(c, SQL_KEY_MESSUNG));
sm.setmrid(mr.getmrid());
db.insert(c, sm, msMapper);
db.insert(getWattMinute(sm, 1, sm.getw1()), wminMapper);
db.insert(getWattMinute(sm, 2, sm.getw2()), wminMapper);
db.insert(getWattMinute(sm, 3, sm.getw3()), wminMapper);
} // der else-zweig ist schon in der Subroutine abgedeckt
}
Bei der Messung kommen die zu Beginn des Beitrags beschriebenen Methoden zur Anwendung. Mit getMessung
wird die Messung abgerufen. getMessObjekt
überträgt das Ergebnis in ein Java-Objekt der Klasse ShellyPlugSStromMessung
das dann in der Datenbank gespeichert wird.
Zur Vereinfachung der Auswertung werden die gemessenen Wattminuten der letzten drei vollen Minuten zusätzlich in einzelne Datensätze zerlegt und separat in der Datenbank gespeichert. Hierzu dient die Methode getWattMinute
der Klasse MessTask
.
private WattMinute getWattMinute(ShellyPlugSStromMessung sm, int nr, double wert) {
long millis = sm.getts().getTime();
millis -= 1000 * 60 * nr;
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(new Date(millis));
gc.set(Calendar.SECOND, 0);
WattMinute wmin = new WattMinute();
wmin.setmid(sm.getmid());
wmin.setwmnr(nr);
wmin.setwmin(wert);
wmin.setts(new Timestamp(gc.getTimeInMillis()));
return wmin;
}
Messreihe stoppen
Zum Stoppen dient die Methode MessTreiber.stop
.
public boolean stop(int mrid) {
Messreihe mr = mrMap.get(mrid);
if (mr instanceof Messreihe) {
Connection c = mr.getConnection();
mr.getTimer().cancel();
mr.setMrstatus(Messreihe.STATUS_ENDED);
mr.setMrend(new Timestamp(new Date().getTime()));
db.update(c, mr, mrMapper);
db.closeConnectionFinally(c);
mrMap.remove(mrid);
return true;
} else {
// keine gueltige ID, nichts zu tun
return false;
}
}
Hier wird zunächst der Timer der Messreihe gestoppt, woraufhin aus dieser Messreihe keine weiteren Messungen mehr erfolgen. Dann wird der Eintrag zur Messreihe in der Datenbank als beendet aktualisiert, die Datenbankverbindung dieser Messreihe geschlossen und schließlich das Messreihen-Objekt aus der Liste der Klasse MessTreiber
entfernt.
An dieser Stelle wurden alle Mittel zur Messung des Stromverbrauchs und des Speicherns von Messergebnissen betrachtet.
Bedienung
Der Aufwand zur Herstellung einer grafischen Bedienoberfläche ist für eine Anwendung wie die hier vorgestellte vergleichsweise hoch. Eine einfachere Alternative ist, die in diesem Beitrag vorgestellten Funktionen über eine HTTP-Schnittstelle zugänglich und damit z.B. mit einem Browser bedienbar zu machen.
Hierzu wird mit Hilfe der Klasse HttpServer
der Java Ablaufumgebung ein eingebetteter HTTP-Server in das Messprogramm eingebaut und eine Programmschnittstelle (Application Programming Interface, API) auf deren Grundlage geschaffen, die via HTTP aufrufbare Kommandos bereitstellt.
Die Klasse HttpServer
erlaubt das Erstellen von Objekten der Klasse HttpContext
, die bestimmte URLs wie z.B. ew/strg/mr
repäsentieren. Ausgeführt auf der lokalen Maschine localhost
bspw. auf Port 8989
würde damit ein Objekt der Klasse HttpServer
Aufrufe an http://localhost:8989/ew/strg/mr
verarbeiten.
ew
wäre hierbei das Kürzel für ein Messprogramm namens eWächter
, wie es in diesem Beitrag beschrieben ist. strg
wäre die Kennung des Handlers für die Steuerung und mr
stünde für die Steuerung von Messreihen.
Zu einem solchen Kontext kann dann eine Klasse entworfen werden, die die Schnittstelle HttpHandler
implementiert und in dieser Implementierung z.B. das Starten und Stoppen von Messreihen sowie das Auflisten laufender Messreihen realisiert.
API: Messreihen
Zum Starten und Stoppen von Messreihen sowie zum Auflisten laufender Messreihen jeweils über HTTP kann eine Klasse MessSteuerung
dienen, die mit ihrer Methode handle
die Schnittstelle HttpHandler
implementiert.
public class MessSteuerung implements HttpHandler {
public static final int ITEM = 3;
public static final int CMD = 4;
public static final int IP = 5;
public static final int MRID = 5;
public static final int NAME = 6;
public static final String START = "start";
public static final String STOP = "stop";
public static final String LISTE = "liste";
public static final String MESSREIHE = "mr";
@Override
public void handle(HttpExchange exchange) throws IOException {
String[] elem = exchange.getRequestURI().getPath().split("/");
if (elem[ITEM].equals(MESSREIHE)) {
switch (elem[CMD]) {
case START:
startMessreihe(exchange, elem[IP], elem[NAME]);
break;
case STOP:
stopMessreihe(exchange, Integer.parseInt(elem[MRID]));
break;
case LISTE:
messreihenAuflisten(exchange);
break;
default:
// unbekanntes Kommando
break;
}
} else {
// unbekanntes Element
}
}
// weitere Methoden hier
}
In der Methode handle
werden HTTP-Aufrufe verarbeitet, die vom HttpServer
an diesen HttpHandler
geleitet werden. Im Objekt der Klasse HttpExchange
sind alle Bestandteile des jeweiligen HTTP-Aufrufs beschrieben. Ein Objekt der Klasse MessSteuerung
zerlegt den URL-String jedes HTTP-Aufrufs und prüft, ob an der vorgesehenen Stelle die Kommandos start
, stop
oder liste
enthalten sind. Folgende URLs werden auf diese Weise verarbeitet.
-
Messreihe starten:
HTTP GET
an/ew/strg/mr/start/[IP-Adresse]/[Name der Messreihe]
-
Messreihe stoppen:
HTTP GET
an/ew/strg/mr/stop/[Messreihen-ID]
-
Messreihen auflisten:
HTTP GET
an/ew/strg/mr/liste
Für die obigen Kommandos wirkt die Methode handle
der Klasse MessSteuerung
als Verteiler, der zum HTTP-Kommando die passende Methode aufruft. Trifft auf diese Weise zum Beispiel das Kommando start
ein, wird die Methode MessSteuerung.startMessreihe
aufgerufen.
private void startMessreihe(HttpExchange exchange, String ip, String name) throws ClassNotFoundException, IOException {
MessTreiber mt = getMessTreiber(exchange.getHttpContext());
int id = mt.start(ip, name);
antwortSenden(exchange, 200, "Messreihe laeuft, ID: " + id);
}
Beim Start einer Messreihe ziehen Objekte der Klasse MessSteuerung
mit Hilfe der Methode getMessTreiber
den in Verwendung befindlichen Messtreiber heran und starten mit dessen Methode start
eine neue Messreihe. Die eindeutige Nummer der neuen Messreihe wird als Antwort zurückgegeben.
private MessTreiber getMessTreiber(HttpContext ctx) throws ClassNotFoundException, IOException {
Object o = ctx.getAttributes().get(MESS_TREIBER);
if (o instanceof MessTreiber) {
return (MessTreiber) o;
} else {
AppProperties einst = (AppProperties) eobj;
MessTreiber mr = new MessTreiber("drivername", "dburl");
ctx.getAttributes().put(MESS_TREIBER, mr);
return mr;
}
}
Die Methode getMessTreiber
entnimmt den aktuell verwendeten Messtreiber den Attributen des HttpContext
. Ist dort noch kein Messtreiber vorhanden, wird ein Messtreiber erstellt, dort hinterlegt und dieser Messtreiber zurückgegeben.
Die Methode antwortSenden
ist dafür zuständig, die HTTP-Antwort zu formulieren und zu senden.
protected void antwortSenden(HttpExchange exchange, int code, String antwort) {
byte[] bytes = antwort.getBytes();
exchange.sendResponseHeaders(code, bytes.length);
OutputStream os = exchange.getResponseBody();
os.write(bytes);
os.flush();
os.close();
}
Die Methoden stopMessreihe
und messreihenAuflisten
verwenden analog der Methode startMessreihe
den Messtreiber und sind zur Vervollständigung der Beschreibung zur Klasse MessSteuerung
nachfolgend aufgeführt.
private void stopMessreihe(HttpExchange exchange, int id) throws ClassNotFoundException, IOException {
HttpContext ctx = exchange.getHttpContext();
Object o = ctx.getAttributes().get(MESS_TREIBER);
if (o instanceof MessTreiber) {
MessTreiber mt = (MessTreiber) o;
if(mt.stop(id)) {
clear(ctx, mt);
antwortSenden(exchange, "Messreihe ID " + id + " beendet");
} else {
antwortSenden(exchange, "Messreihe ID " + id + " nicht gefunden");
}
} else {
antwortSenden(exchange, "Keine Messreihen in Betrieb");
}
}
private void messreihenAuflisten(HttpExchange exchange) {
HttpContext ctx = exchange.getHttpContext();
Object o = ctx.getAttributes().get(MESS_TREIBER);
if (o instanceof MessTreiber) {
MessTreiber mt = (MessTreiber) o;
Collection<Messreihe> reihen = mt.getMessreihen();
Gson gson = new GsonBuilder().setPrettyPrinting().create();
antwortSenden(exchange, gson.toJson(reihen));
} else {
antwortSenden(exchange, "Keine Messreihen.");
}
}
Server
Damit eine wie im vorangegangenen Abschnitt beschriebene Steuerung von Messungen via HTTP erfolgen kann, wird eine Instanz der Klasse HttpServer
erzeugt und gestartet.
HttpServer server = HttpServer.create(new InetSocketAddress(8989), 0);
server.createContext("/ew/strg/mr", new MessSteuerung());
server.setExecutor(Executors.newFixedThreadPool(10));
server.start();
Mit dem obigen Code läuft ein in eine Java-Anwendung eingebetter HTTP-Server und verarbeitet HTTP-Anfragen an /ew/strg/mr
, wie es der vorige Abschnitt beschreibt.
Ergebnisse auswerten
Ebenso, wie es mit der Klasse MessSteuerung
gemacht wurde, lässt sich eine Klasse entwerfen, mit der die in der Datenbank gesammelten Ergebnisse ausgewertet werden können. Auswertungen sind in aller Regel zur jeweiligen Fragestellung passende SQL-Abfragen. Hier ein SQL-Statement zum Abruf der Wattminuten einer Messreihe über einen bestimmten Zeitraum.
select wm.ts,wm.wmin
from app.messreihe as mr,app.messung as m,app.wmin as wm
where mr.mrid=m.mrid
and m.mid=wm.mid
and wm.ts <= ?
and wm.ts >= ?
and mr.mrid = ?
order by wm.ts asc
Dieser SQL-Code kann in einer Properties-Datei hinterlegt und verwendet werden, wie es die Anleitung von BaseLink beschreibt [13]. Die Fragezeichen im SQL-Ausdruck ersetzt BaseLink [10] zur Laufzeit mit Hilfe von Prepared Statements durch die entsprechenden Parameter.
- Wichtig
-
Wird ein SQL-Ausdruck wie der obige in einer Properties-Datei hinterlegt, die als XML-Struktur kodiert ist, müssen manche Zeichen maskiert werden, damit sie verarbeitet werden können:
>
für<
und<
für>
Mit folgendem Beispielcode kann der Inhalt mit diesem SQL-Ausdruck mit BaseLink aus der Datenbank gelesen werden.
PersistenceManager db = new PersistenceManager();
db.setDriverName("drivername");
db.setDatabase("DB-URL");
List<List<String>> ergebnisse = db.select(sql, false, "2023-01-20 00:00:00", "2023-01-20 23:59:59", 16);
Der Inhalt von ergebnisse
in obigem Code wird einfach zu Text umgewandelt wie z.B. in folgendem Beispiel.
private String zuText(List<List<String>> dbAuszug) {
StringBuilder sb = new StringBuilder();
Iterator<List<String>> i = dbAuszug.iterator();
while(i.hasNext()) {
List rec = i.next();
Iterator<String> field = rec.iterator();
while(field.hasNext()) {
sb.append(field.next());
sb.append(" ");
}
sb.append("\n");
}
return sb.toString();
}
Mit diesen wenigen Code-Beispielen ist bereits schnell ein HttpHandler
nach dem zuvor geschilderten Baumuster erstellt, der gesteuert über HTTP-Aufrufe Messergebnisse in Textform ausgibt. Natürlich lassen sich zahlreiche andere Ausprägungen von Auswertungen denken, die zwar nicht minder einfach zu bewerkstelligen sind, aber den Rahmen dieses Beitrags sprengen würden.
Fazit
Schaltsteckdosen des Typs Shelly Plug S liefern hilfreiche Funktionen für die Messung des Stromverbrauchs über längere Zeiträume.
Der Beitrag zeigt, dass sich schnell und einfach die Messung in Messreihen nebst Steuerung über HTTP-Anfragen und den Browser realisieren und sich ebenso einfach ein Einblick in die gewonnenen Messdaten erzielen lässt.
Die Programmiersprache Java liefert ein universell verwendbares Werkzeug, das mit den ebenfalls in Java vorliegenden Hilfsmitteln Derby, BaseLink und Gson alles enthält, was für eine maschinelle Datenerfassung nötig ist.
Verweise
[6] Google Gson
[7] Apache Derby
[10] BaseLink
[14] Timestamp (SQL)
[15] Timestamp (Derby)
[16] getTime (Java SQL)
[17] setTime (Java SQL)
[18] valueOf (Java SQL)