Compare commits
3 Commits
c560160dd0
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2dbba91efa | |||
| 124330db9a | |||
| 9bf9b9a66f |
@@ -81,6 +81,7 @@ main.class=kebaagent.KebaAgent
|
||||
manifest.file=manifest.mf
|
||||
meta.inf.dir=${src.dir}/META-INF
|
||||
mkdist.disabled=false
|
||||
no.dependencies=true
|
||||
platform.active=default_platform
|
||||
run.classpath=\
|
||||
${javac.classpath}:\
|
||||
|
||||
@@ -26,7 +26,11 @@ class JsonHandler implements HttpHandler
|
||||
this.agent = agent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* GET-Request bearbeiten
|
||||
* @param httpExchange
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void handle(HttpExchange httpExchange) throws IOException
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -26,7 +27,7 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
/**
|
||||
*
|
||||
* In der Konfigurationsdatei können mehrere Wallbox-Ports aka alias definiert werden. Über die wird die Konfig separiert.
|
||||
* @author holz
|
||||
*/
|
||||
public class KebaAgent
|
||||
@@ -35,8 +36,18 @@ public class KebaAgent
|
||||
|
||||
|
||||
private Properties prop = new Properties();
|
||||
/**
|
||||
* Liste von Instanzen über Namen
|
||||
*/
|
||||
private final Map<String, KebaModbus> kebaModbusMap = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Instanz erstellen und properties einlesen
|
||||
* @see #init()
|
||||
* @param args
|
||||
*/
|
||||
public KebaAgent(String[] args)
|
||||
{
|
||||
String propertiesFilename = "kemaagent.properties";
|
||||
@@ -80,6 +91,10 @@ public class KebaAgent
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Webserver Port aus config ermitteln und liefern
|
||||
* @return
|
||||
*/
|
||||
int getPort()
|
||||
{
|
||||
int defPort = WEBSERVER_PORT;
|
||||
@@ -97,39 +112,11 @@ public class KebaAgent
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Liste von Instanzen über Namen
|
||||
*/
|
||||
private final Map<String, KebaModbus> kebaModbusMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
|
||||
KebaAgent agent = new KebaAgent(args);
|
||||
agent.init();
|
||||
|
||||
|
||||
Webserver webserver = new Webserver(agent.getPort());
|
||||
try
|
||||
{
|
||||
webserver.start(agent);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
System.getLogger(KebaAgent.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
|
||||
}
|
||||
System.out.println("webserver gestartet");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* alle wichtigen modbus-Daten beschaffen
|
||||
* @param params
|
||||
* @param params URI-Parameter des GET-Requests (werden aktuell ignoriert)
|
||||
* @return JSON-Daten
|
||||
*/
|
||||
String getDataAsJson(Map<String, String> params)
|
||||
@@ -139,6 +126,8 @@ public class KebaAgent
|
||||
JSONObject joRoot = new JSONObject();
|
||||
JSONArray jaStations = new JSONArray();
|
||||
|
||||
System.out.printf("Req at %s\n", LocalDateTime.now().withNano(0));
|
||||
// über alle Wallboxen ...
|
||||
getAliases()
|
||||
.forEach(name ->
|
||||
{
|
||||
@@ -147,58 +136,67 @@ public class KebaAgent
|
||||
|
||||
JSONObject joPart = new JSONObject();
|
||||
|
||||
int val;
|
||||
int intVal;
|
||||
try
|
||||
{
|
||||
if (kebaModbus.initFailed)
|
||||
{
|
||||
joPart.put("error", "initError");
|
||||
jaStations.put(joPart);
|
||||
return;
|
||||
}
|
||||
|
||||
joPart.put("alias", name);
|
||||
int pause_ms = 600;
|
||||
|
||||
sleep(600);
|
||||
// nach keba-Doku muss zwischen den Abfragen 500ms Pause sein und es kann immer nur ein Register gelesen werden!
|
||||
// sleep(600);
|
||||
String
|
||||
jName = "chargingState";
|
||||
int register = 1000;
|
||||
val = kebaModbus.read(jName, register, slaveId);
|
||||
intVal = kebaModbus.read(jName, register, slaveId, pause_ms);
|
||||
// val = readInt32BigEndian(kebaModbus.getModbusMaster(), slaveId, register);
|
||||
System.out.printf("Wert für reg %d: %d (0x%04x)\n", register, val, val);
|
||||
joPart.put(jName, val);
|
||||
System.out.printf("Wert für reg %d: %d (0x%04x)\n", register, intVal, intVal);
|
||||
joPart.put(jName, intVal);
|
||||
|
||||
sleep(600);
|
||||
// cache-Zeit für statische Daten (S/N etc)
|
||||
int maxAgeMinutes = 60;
|
||||
|
||||
// sleep(600);
|
||||
jName = "s/n";
|
||||
register = 1014;
|
||||
val = kebaModbus.read(jName, register, slaveId);
|
||||
System.out.printf("Wert für reg %d: %d (0x%04x)\n", register, val, val);
|
||||
joPart.put(jName, val);
|
||||
intVal = kebaModbus.readCached(jName, register, slaveId, pause_ms, maxAgeMinutes);
|
||||
System.out.printf("Wert für reg %d: %d (0x%04x)\n", register, intVal, intVal);
|
||||
joPart.put(jName, intVal);
|
||||
|
||||
sleep(600);
|
||||
// akt. Leistung
|
||||
// sleep(600);
|
||||
jName = "power";
|
||||
register = 1020;
|
||||
val = kebaModbus.read(jName, register, slaveId);
|
||||
intVal = kebaModbus.read(jName, register, slaveId, pause_ms);
|
||||
// val = readInt32BigEndian(kebaModbus.getModbusMaster(), slaveId, register);
|
||||
System.out.printf("Wert für reg %d (active Power): %1.1f kW (0x%04x)\n", register, val/1e6, val);
|
||||
joPart.put(jName, val/1e6);
|
||||
System.out.printf("Wert für reg %d (active Power): %1.1f kW (0x%04x)\n", register, intVal/1e6, intVal);
|
||||
joPart.put(jName, intVal/1e6);
|
||||
|
||||
sleep(600);
|
||||
// sleep(600);
|
||||
jName = "RFID";
|
||||
register = 1500;
|
||||
val = kebaModbus.read(jName, register, slaveId);
|
||||
intVal = kebaModbus.read(jName, register, slaveId, pause_ms);
|
||||
// val = readInt32BigEndian(kebaModbus.getModbusMaster(), slaveId, register);
|
||||
System.out.printf("Wert für reg %d (RFID-Card): %d (0x%04x)\n", register, val, val);
|
||||
joPart.put(jName, val);
|
||||
System.out.printf("Wert für reg %d (RFID-Card): %d (0x%04x)\n", register, intVal, intVal);
|
||||
joPart.put(jName, intVal);
|
||||
|
||||
sleep(600);
|
||||
// sleep(600);
|
||||
jName = "chargedEnergy";
|
||||
// angeblich Wh, aber in Wirklichkeit Wh/10
|
||||
register = 1502;
|
||||
val = kebaModbus.read(jName, register, slaveId);
|
||||
intVal = kebaModbus.read(jName, register, slaveId, pause_ms);
|
||||
// val = readInt32BigEndian(kebaModbus.getModbusMaster(), slaveId, register);
|
||||
System.out.printf("Wert für reg %d (charged energy [kWh]): %1.1f (0x%04x)\n", register, val/10000f, val);
|
||||
joPart.put(jName, val/10000f);
|
||||
System.out.printf("Wert für reg %d (charged energy [kWh]): %1.1f (0x%04x)\n", register, intVal/10000f, intVal);
|
||||
joPart.put(jName, intVal/10000f);
|
||||
|
||||
|
||||
joPart.put("ts", LocalDateTime.now().withNano(0).toString());
|
||||
jaStations.put(joPart);
|
||||
}
|
||||
catch (ModbusProtocolException | ModbusNumberException | ModbusIOException ex)
|
||||
@@ -229,7 +227,9 @@ public class KebaAgent
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private void init()
|
||||
{
|
||||
getAliases().forEach(alias ->
|
||||
@@ -246,7 +246,7 @@ public class KebaAgent
|
||||
}
|
||||
catch (UnknownHostException | ModbusIOException | NumberFormatException ex)
|
||||
{
|
||||
System.getLogger(KebaAgent.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
|
||||
System.getLogger(KebaAgent.class.getName()).log(System.Logger.Level.ERROR, "init-Fehler", ex);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -295,4 +295,34 @@ public class KebaAgent
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
|
||||
KebaAgent agent = new KebaAgent(args);
|
||||
agent.init();
|
||||
|
||||
|
||||
Webserver webserver = new Webserver(agent.getPort());
|
||||
try
|
||||
{
|
||||
webserver.start(agent);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
System.getLogger(KebaAgent.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
|
||||
}
|
||||
System.out.println("webserver gestartet");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+108
-44
@@ -14,6 +14,8 @@ import com.intelligt.modbus.jlibmodbus.msg.response.ReadHoldingRegistersResponse
|
||||
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
//import oracle.jrockit.jfr.tools.ConCatRepository;
|
||||
@@ -34,8 +36,18 @@ public class KebaModbus
|
||||
|
||||
private final Map<String, Integer> valueMap = new HashMap();
|
||||
|
||||
private static int timeout_ms = 10000;
|
||||
|
||||
record RCacheEntry(String name, LocalDateTime ts, Integer value){};
|
||||
|
||||
private Map<String, RCacheEntry> cache = new HashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws UnknownHostException
|
||||
* @throws ModbusIOException
|
||||
*/
|
||||
void init() throws UnknownHostException, ModbusIOException
|
||||
{
|
||||
TcpParameters tcpParameters = new TcpParameters();
|
||||
@@ -72,11 +84,20 @@ public class KebaModbus
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ModbusMaster getModbusMaster()
|
||||
{
|
||||
return modbusMaster;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<String, Integer> getValueMap()
|
||||
{
|
||||
return valueMap;
|
||||
@@ -84,7 +105,11 @@ public class KebaModbus
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Keba-Wallbox über MODBUS verbinden
|
||||
* @param ip
|
||||
* @param port
|
||||
*/
|
||||
public KebaModbus(String ip, int port)
|
||||
{
|
||||
this.ip = ip;
|
||||
@@ -93,9 +118,12 @@ public class KebaModbus
|
||||
|
||||
|
||||
|
||||
private static int timeout_ms = 10000;
|
||||
|
||||
|
||||
/**
|
||||
* Verbindung herstellen
|
||||
* @param tcpParameters
|
||||
* @param responeTimeout_ms
|
||||
* @return
|
||||
*/
|
||||
public static ModbusMaster connect(TcpParameters tcpParameters, int responeTimeout_ms)
|
||||
{
|
||||
// if you would like to set connection parameters separately,
|
||||
@@ -110,7 +138,63 @@ public class KebaModbus
|
||||
|
||||
|
||||
|
||||
|
||||
private static void usage()
|
||||
{
|
||||
System.err.println("Reading MODBUS-Register from IPAdr\n\nusage:\nSwgModbus <IPAdr> <AdrOffset> <count> [port]\n");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 4 Bytes lesen. Das geht hier nur mit dem Abruf von 2 16-Bit-Registern
|
||||
* @param slaveId
|
||||
* @param register Startregister
|
||||
* @return
|
||||
*/
|
||||
static int readInt32BigEndian(ModbusMaster modbusMaster, int slaveId, int register) throws ModbusProtocolException, ModbusNumberException, ModbusIOException
|
||||
{
|
||||
int[] registerValues = modbusMaster.readHoldingRegisters(slaveId, register, 2);
|
||||
return registerValues[0] << 16 | registerValues[1];
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void sleep(int dur_ms)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(dur_ms);
|
||||
}
|
||||
catch (InterruptedException ex)
|
||||
{
|
||||
System.getLogger(KebaModbus.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MODBUS-Register lesen und Wert in Map speichern
|
||||
* @param name Name, unter dem der Wert gespeichert wird
|
||||
* @param register
|
||||
* @param slaveId
|
||||
* @param pause_ms Pause vor Abfrage
|
||||
* @return gelesener Wert
|
||||
* @throws ModbusProtocolException
|
||||
* @throws ModbusNumberException
|
||||
* @throws ModbusIOException
|
||||
*/
|
||||
int read(String name, int register, int slaveId, int pause_ms) throws ModbusProtocolException, ModbusNumberException, ModbusIOException
|
||||
{
|
||||
sleep(pause_ms);
|
||||
var val = readInt32BigEndian(getModbusMaster(), slaveId, register);
|
||||
valueMap.put(name, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Testcode
|
||||
* @param args the command line arguments: IP Startadr len [port]
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
@@ -289,53 +373,33 @@ public class KebaModbus
|
||||
|
||||
|
||||
|
||||
private static void usage()
|
||||
{
|
||||
System.err.println("Reading MODBUS-Register from IPAdr\n\nusage:\nSwgModbus <IPAdr> <AdrOffset> <count> [port]\n");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 4 Bytes lesen. Das geht hier nur mit dem Abruf von 2 16-Bit-Registern
|
||||
* @param slaveId
|
||||
* @param register Startregister
|
||||
* @return
|
||||
*/
|
||||
static int readInt32BigEndian(ModbusMaster modbusMaster, int slaveId, int register) throws ModbusProtocolException, ModbusNumberException, ModbusIOException
|
||||
{
|
||||
int[] registerValues = modbusMaster.readHoldingRegisters(slaveId, register, 2);
|
||||
return registerValues[0] << 16 | registerValues[1];
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void sleep(int dur_ms)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(dur_ms);
|
||||
}
|
||||
catch (InterruptedException ex)
|
||||
{
|
||||
System.getLogger(KebaModbus.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lesen und in Map speichern
|
||||
* @param status
|
||||
* MODBUS-Wert aus cache liefern oder neu abfragen, wenn maxAgeMinutes überschritten ist
|
||||
* @param jName
|
||||
* @param register
|
||||
* @param slaveId
|
||||
* @param pause_ms
|
||||
* @param maxAgeMinutes
|
||||
* @return
|
||||
* @throws ModbusIOException
|
||||
* @throws ModbusProtocolException
|
||||
* @throws ModbusNumberException
|
||||
* @throws ModbusIOException
|
||||
*/
|
||||
int read(String name, int register, int slaveId) throws ModbusProtocolException, ModbusNumberException, ModbusIOException
|
||||
int readCached(String jName, int register, int slaveId, int pause_ms, int maxAgeMinutes) throws ModbusIOException, ModbusProtocolException, ModbusNumberException
|
||||
{
|
||||
var val = readInt32BigEndian(getModbusMaster(), slaveId, register);
|
||||
valueMap.put(name, val);
|
||||
return val;
|
||||
// test, ob cache existiert
|
||||
if (cache.containsKey(jName))
|
||||
{
|
||||
// noch gültig?
|
||||
if (cache.get(jName).ts.plusMinutes(maxAgeMinutes).isAfter(LocalDateTime.now()))
|
||||
return cache.get(jName).value;
|
||||
}
|
||||
|
||||
int intVal = read(jName, register, slaveId, pause_ms);
|
||||
cache.put(jName, new RCacheEntry(jName, LocalDateTime.now(), intVal));
|
||||
return intVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user