Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[modbus] Modbus register array backed by bytes and other simplifications #8862

Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@
*/
package org.openhab.binding.modbus.e3dc.internal.dto;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.BitSet;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.io.transport.modbus.ModbusBitUtilities;
import org.openhab.io.transport.modbus.ValueBuffer;

/**
* The {@link DataConverter} Helper class to convert bytes from modbus into desired data format
Expand All @@ -25,56 +26,19 @@
*/
@NonNullByDefault
public class DataConverter {
private static final long MAX_INT32 = (long) Math.pow(2, Integer.SIZE);

/**
* Get unit16 value from 2 bytes
*
* @param wrap
* @return int
*/
public static int getUInt16Value(ByteBuffer wrap) {
return Short.toUnsignedInt(wrap.getShort());
}

/**
* Get unit32 value from 4 bytes
*
* @param wrap
* @return long
*/
public static long getLongValue(ByteBuffer wrap) {
return Integer.toUnsignedLong(wrap.getInt());
}

/**
* Get double value from 2 bytes with correction factor
*
* @param wrap
* @return double
*/
public static double getUDoubleValue(ByteBuffer wrap, double factor) {
return round(getUInt16Value(wrap) * factor, 2);
}

/**
* Conversion done according to E3DC Modbus Specification V1.7
*
* @param wrap
* @return decoded long value, Long.MIN_VALUE otherwise
*/
public static long getInt32Swap(ByteBuffer wrap) {
long a = getUInt16Value(wrap);
long b = getUInt16Value(wrap);
if (b < 32768) {
return b * 65536 + a;
} else {
return (MAX_INT32 - b * 65536 - a) * -1;
}
public static double getUDoubleValue(ValueBuffer wrap, double factor) {
return round(wrap.getUInt16() * factor, 2);
}

public static String getString(byte[] bArray) {
return new String(bArray, StandardCharsets.US_ASCII).trim();
return ModbusBitUtilities.extractStringFromBytes(bArray, 0, bArray.length, StandardCharsets.US_ASCII).trim();
}

public static int toInt(BitSet bitSet) {
Expand All @@ -93,8 +57,7 @@ public static double round(double value, int places) {
}

long factor = (long) Math.pow(10, places);
value = value * factor;
long tmp = Math.round(value);
long tmp = Math.round(value * factor);
return (double) tmp / factor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@

import static org.openhab.binding.modbus.e3dc.internal.modbus.E3DCModbusConstans.*;

import java.nio.ByteBuffer;
import java.util.BitSet;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.StringType;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.io.transport.modbus.ModbusBitUtilities;

/**
* The {@link EmergencyBlock} Data object for E3DC Info Block
Expand Down Expand Up @@ -55,7 +55,7 @@ public class EmergencyBlock implements Data {
*/
public EmergencyBlock(byte[] bArray) {
// uint16 status register 40084 - possible Status Strings are defined in Constants above
int status = DataConverter.getUInt16Value(ByteBuffer.wrap(bArray));
int status = ModbusBitUtilities.extractUInt16(bArray, 0);
if (status >= 0 && status < 5) {
epStatus = EP_STATUS_ARRAY[status];
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@
*/
package org.openhab.binding.modbus.e3dc.internal.dto;

import java.nio.ByteBuffer;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.library.types.StringType;
import org.eclipse.smarthome.core.util.HexUtils;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.io.transport.modbus.ValueBuffer;

/**
* The {@link InfoBlock} Data object for E3DC Info Block
Expand All @@ -43,7 +42,7 @@ public class InfoBlock implements Data {
*/
public InfoBlock(byte[] bArray) {
// index handling to calculate the correct start index
ByteBuffer wrapper = ByteBuffer.wrap(bArray);
ValueBuffer wrapper = ValueBuffer.wrap(bArray);

// first uint16 = 2 bytes - decode magic byte
byte[] magicBytes = new byte[2];
Expand All @@ -52,11 +51,11 @@ public InfoBlock(byte[] bArray) {
// first uint16 = 2 bytes - decode magic byte

// unit8 (Modbus Major Version) + uint8 Modbus minor Version
String modbusVersion = wrapper.get() + "." + wrapper.get();
String modbusVersion = wrapper.getSInt8() + "." + wrapper.getSInt8();
this.modbusVersion = new StringType(modbusVersion);

// unit16 - supported registers
short supportedRegisters = wrapper.getShort();
int supportedRegisters = wrapper.getSInt16();
this.supportedRegisters = new DecimalType(supportedRegisters);

byte[] buffer = new byte[32];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@
*/
package org.openhab.binding.modbus.e3dc.internal.dto;

import java.nio.ByteBuffer;

import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Power;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.smarthome.core.library.types.QuantityType;
import org.eclipse.smarthome.core.library.unit.SmartHomeUnits;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.io.transport.modbus.ValueBuffer;

/**
* The {@link PowerBlock} Data object for E3DC Info Block
Expand Down Expand Up @@ -49,18 +48,18 @@ public class PowerBlock implements Data {
*/
public PowerBlock(byte[] bArray) {
// index handling to calculate the correct start index
ByteBuffer wrap = ByteBuffer.wrap(bArray);
ValueBuffer wrap = ValueBuffer.wrap(bArray);

// int32_swap value = 4 byte
long pvPowerSupplyL = DataConverter.getInt32Swap(wrap);
long pvPowerSupplyL = wrap.getSInt32Swap();

/*
* int32_swap value don't provide negative values!
* Positive value - Battery is charging = Power consumer
* Negative value - Battery is discharging = Power supplier
*/
pvPowerSupply = QuantityType.valueOf(pvPowerSupplyL, SmartHomeUnits.WATT);
long batteryPower = DataConverter.getInt32Swap(wrap);
long batteryPower = wrap.getSInt32Swap();
if (batteryPower > 0) {
// Battery is charging so Power is consumed by Battery
batteryPowerSupply = QuantityType.valueOf(0, SmartHomeUnits.WATT);
Expand All @@ -72,15 +71,15 @@ public PowerBlock(byte[] bArray) {
}

// int32_swap value = 4 byte
long householdPowerConsumptionL = DataConverter.getInt32Swap(wrap);
long householdPowerConsumptionL = wrap.getSInt32Swap();
householdPowerConsumption = QuantityType.valueOf(householdPowerConsumptionL, SmartHomeUnits.WATT);

/*
* int32_swap value don't provide negative values!
* Positive value - Power provided towards Grid = Power consumer
* Negative value - Power requested from Grid = Power supplier
*/
long gridPower = DataConverter.getInt32Swap(wrap);
long gridPower = wrap.getSInt32Swap();
if (gridPower > 0) {
// Power is provided by Grid
gridPowerSupply = QuantityType.valueOf(gridPower, SmartHomeUnits.WATT);
Expand All @@ -92,19 +91,19 @@ public PowerBlock(byte[] bArray) {
}

// int32_swap value = 4 byte
externalPowerSupply = QuantityType.valueOf(DataConverter.getInt32Swap(wrap), SmartHomeUnits.WATT);
externalPowerSupply = QuantityType.valueOf(wrap.getSInt32Swap(), SmartHomeUnits.WATT);

// int32_swap value = 4 byte
wallboxPowerConsumption = QuantityType.valueOf(DataConverter.getInt32Swap(wrap), SmartHomeUnits.WATT);
wallboxPowerConsumption = QuantityType.valueOf(wrap.getSInt32Swap(), SmartHomeUnits.WATT);

// int32_swap value = 4 byte
wallboxPVPowerConsumption = QuantityType.valueOf(DataConverter.getInt32Swap(wrap), SmartHomeUnits.WATT);
wallboxPVPowerConsumption = QuantityType.valueOf(wrap.getSInt32Swap(), SmartHomeUnits.WATT);

// unit8 + uint8 - one register with split value for Autarky & Self Consumption
autarky = QuantityType.valueOf(wrap.get(), SmartHomeUnits.PERCENT);
selfConsumption = QuantityType.valueOf(wrap.get(), SmartHomeUnits.PERCENT);
autarky = QuantityType.valueOf(wrap.getSInt8(), SmartHomeUnits.PERCENT);
selfConsumption = QuantityType.valueOf(wrap.getSInt8(), SmartHomeUnits.PERCENT);

// uint16 for Battery State of Charge
batterySOC = QuantityType.valueOf(wrap.getShort(), SmartHomeUnits.PERCENT);
batterySOC = QuantityType.valueOf(wrap.getSInt16(), SmartHomeUnits.PERCENT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
*/
package org.openhab.binding.modbus.e3dc.internal.dto;

import java.nio.ByteBuffer;

import javax.measure.quantity.ElectricCurrent;
import javax.measure.quantity.ElectricPotential;
import javax.measure.quantity.Power;
Expand All @@ -22,6 +20,7 @@
import org.eclipse.smarthome.core.library.types.QuantityType;
import org.eclipse.smarthome.core.library.unit.SmartHomeUnits;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
import org.openhab.io.transport.modbus.ValueBuffer;

/**
* The {@link StringBlock} Data object for E3DC Info Block
Expand All @@ -46,17 +45,17 @@ public class StringBlock implements Data {
* @param bArray - Modbus Registers as bytes from 40096 to 40104
*/
public StringBlock(byte[] bArray) {
ByteBuffer wrap = ByteBuffer.wrap(bArray);
ValueBuffer wrap = ValueBuffer.wrap(bArray);
// straight forward - for each String the values Volt, Ampere and then Watt. All unt16 = 2 bytes values
string1Volt = QuantityType.valueOf(DataConverter.getUInt16Value(wrap), SmartHomeUnits.VOLT);
string2Volt = QuantityType.valueOf(DataConverter.getUInt16Value(wrap), SmartHomeUnits.VOLT);
string3Volt = QuantityType.valueOf(DataConverter.getUInt16Value(wrap), SmartHomeUnits.VOLT);
string1Volt = QuantityType.valueOf(wrap.getUInt16(), SmartHomeUnits.VOLT);
string2Volt = QuantityType.valueOf(wrap.getUInt16(), SmartHomeUnits.VOLT);
string3Volt = QuantityType.valueOf(wrap.getUInt16(), SmartHomeUnits.VOLT);
// E3DC Modbus Spec chapter 3.1.2, page 16 - Ampere values shall be handled with factor 0.01
string1Ampere = QuantityType.valueOf(DataConverter.getUDoubleValue(wrap, 0.01), SmartHomeUnits.AMPERE);
string2Ampere = QuantityType.valueOf(DataConverter.getUDoubleValue(wrap, 0.01), SmartHomeUnits.AMPERE);
string3Ampere = QuantityType.valueOf(DataConverter.getUDoubleValue(wrap, 0.01), SmartHomeUnits.AMPERE);
string1Watt = QuantityType.valueOf(DataConverter.getUInt16Value(wrap), SmartHomeUnits.WATT);
string2Watt = QuantityType.valueOf(DataConverter.getUInt16Value(wrap), SmartHomeUnits.WATT);
string3Watt = QuantityType.valueOf(DataConverter.getUInt16Value(wrap), SmartHomeUnits.WATT);
string1Watt = QuantityType.valueOf(wrap.getUInt16(), SmartHomeUnits.WATT);
string2Watt = QuantityType.valueOf(wrap.getUInt16(), SmartHomeUnits.WATT);
string3Watt = QuantityType.valueOf(wrap.getUInt16(), SmartHomeUnits.WATT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.openhab.binding.modbus.e3dc.internal.dto.WallboxArray;
import org.openhab.binding.modbus.e3dc.internal.modbus.Data.DataType;
import org.openhab.io.transport.modbus.AsyncModbusReadResult;
import org.openhab.io.transport.modbus.ModbusRegister;
import org.openhab.io.transport.modbus.ModbusRegisterArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -59,17 +58,10 @@ public Parser(DataType type) {
}

public void handle(AsyncModbusReadResult result) {
byte[] newArray = new byte[size];
long startTime = System.currentTimeMillis();
Optional<ModbusRegisterArray> opt = result.getRegisters();
if (opt.isPresent()) {
ModbusRegisterArray registers = opt.get();
int i = 0;
for (ModbusRegister reg : registers) {
System.arraycopy(reg.getBytes(), 0, newArray, i, 2);
i += 2;
}
setArray(newArray);
setArray(opt.get().getBytes());

long duration = System.currentTimeMillis() - startTime;
avgDuration += duration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,26 @@
*/
public class DataBlockTest {
private Parser mc;
private Parser mcNegativePVSupply;

@Before
public void setup() {
byte[] dataBlock = new byte[] { 0, -14, 0, 0, -2, -47, -1, -1, 2, 47, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 99, 99, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
125, 2, 21, 0, 0, 0, 27, 0, 26, 0, 0, 0, 103, 0, -117, 0, 0 };
mc = new Parser(DataType.DATA);
mc.setArray(dataBlock);
{
byte[] dataBlock = new byte[] { 0, -14, 0, 0, -2, -47, -1, -1, 2, 47, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 99, 99, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 125, 2, 21, 0, 0, 0, 27, 0, 26, 0, 0, 0, 103, 0, -117, 0, 0 };
mc = new Parser(DataType.DATA);
mc.setArray(dataBlock);
}
{
// 65098 bytes [-2, 74]
// 65535 bytes [-1, -1]
byte[] dataBlock = new byte[] { -2, -74, -1, -1, -2, -47, -1, -1, 2, 47, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 99, 99, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 125, 2, 21, 0, 0, 0, 27, 0, 26, 0, 0, 0, 103, 0, -117, 0, 0 };
mcNegativePVSupply = new Parser(DataType.DATA);
mcNegativePVSupply.setArray(dataBlock);
}
}

@Test
Expand All @@ -56,6 +68,17 @@ public void testValidPowerBlock() {
assertEquals("Battery Supply", "303.0 W", b.batteryPowerSupply.toString());
}

@Test
public void testValidPowerBlockNegativePVSupply() {
Optional<Data> dataOpt = mcNegativePVSupply.parse(DataType.POWER);
assertTrue(dataOpt.isPresent());
PowerBlock b = (PowerBlock) dataOpt.get();
assertEquals("PV Supply", "-330.0 W", b.pvPowerSupply.toString());
assertEquals("Grid Supply", "14.0 W", b.gridPowerSupply.toString());
assertEquals("Grid Consumption", "0.0 W", b.gridPowerConsumpition.toString());
assertEquals("Battery Supply", "303.0 W", b.batteryPowerSupply.toString());
}

@Test
public void testValidWallboxBlock() {
Optional<Data> wba = mc.parse(DataType.WALLBOX);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import static org.mockito.Mockito.*;

import java.nio.ByteBuffer;
import java.util.HashMap;

import org.eclipse.jdt.annotation.NonNullByDefault;
Expand All @@ -31,7 +30,6 @@
import org.openhab.io.transport.modbus.AsyncModbusFailure;
import org.openhab.io.transport.modbus.AsyncModbusReadResult;
import org.openhab.io.transport.modbus.ModbusReadRequestBlueprint;
import org.openhab.io.transport.modbus.ModbusRegister;
import org.openhab.io.transport.modbus.ModbusRegisterArray;

/**
Expand Down Expand Up @@ -92,26 +90,16 @@ private AsyncModbusReadResult getInfoResult() {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 78, 73, 78, 73, 84, 73, 65, 76, 73, 90, 69,
68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 49, 48, 95, 50, 48, 50, 48, 95, 48, 52,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
ByteBuffer infoWrap = ByteBuffer.wrap(infoBlockBytes);
ModbusRegister[] infoBlock = new ModbusRegister[infoBlockBytes.length / 2];
for (int i = 0; i < infoBlock.length; i++) {
infoBlock[i] = new ModbusRegister(infoWrap.get(), infoWrap.get());
}
ModbusReadRequestBlueprint readRequest = mock(ModbusReadRequestBlueprint.class);
return new AsyncModbusReadResult(readRequest, new ModbusRegisterArray(infoBlock));
return new AsyncModbusReadResult(readRequest, new ModbusRegisterArray(infoBlockBytes));
}

private AsyncModbusReadResult getDataResult() {
byte[] dataBlockBytes = new byte[] { 0, -14, 0, 0, -2, -47, -1, -1, 2, 47, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 99, 99, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 125, 2, 21, 0, 0, 0, 27, 0, 26, 0, 0, 0, 103, 0, -117, 0, 0 };
ByteBuffer dataWrap = ByteBuffer.wrap(dataBlockBytes);
ModbusRegister[] dataBlock = new ModbusRegister[dataBlockBytes.length / 2];
for (int i = 0; i < dataBlock.length; i++) {
dataBlock[i] = new ModbusRegister(dataWrap.get(), dataWrap.get());
}
ModbusReadRequestBlueprint readRequest = mock(ModbusReadRequestBlueprint.class);
return new AsyncModbusReadResult(readRequest, new ModbusRegisterArray(dataBlock));
return new AsyncModbusReadResult(readRequest, new ModbusRegisterArray(dataBlockBytes));
}

private AsyncModbusFailure<ModbusReadRequestBlueprint> getFailResult() {
Expand Down
Loading