test(modbus): add parser tests for 4-byte and legacy-wrap decoding #1

Merged
mariusz.pacek merged 1 commits from parsers-and-tests into master 2025-12-27 10:48:25 +00:00
@@ -0,0 +1,89 @@
package home.automation.modbus;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import static org.junit.jupiter.api.Assertions.*;
class ModbusParserTests {
private ModbusRtuTcpRepository createRepo() {
return new ModbusRtuTcpRepository(new Connection() {
@Override
public com.serotonin.modbus4j.ModbusMaster getConnection() {
throw new UnsupportedOperationException("not used in parser tests");
}
}, java.util.Collections.emptyList());
}
private String invokeDecode(ModbusRtuTcpRepository repo, short[] data, String dataType, Double scale, Boolean legacy) throws Exception {
Method m = ModbusRtuTcpRepository.class.getDeclaredMethod("decodeValueFromShorts", short[].class, String.class, Double.class, Boolean.class);
m.setAccessible(true);
return (String) m.invoke(repo, (Object) data, dataType, scale, legacy);
}
@Test
void decodeFourByteFloat() throws Exception {
ModbusRtuTcpRepository repo = createRepo();
float value = 12.34f;
int bits = Float.floatToIntBits(value);
short high = (short) ((bits >>> 16) & 0xFFFF);
short low = (short) (bits & 0xFFFF);
String decoded = invokeDecode(repo, new short[]{high, low}, "FOUR_BYTE_FLOAT", 1.0, null);
assertEquals("12.34", decoded);
}
@Test
void decodeFourByteFloatSwapped() throws Exception {
ModbusRtuTcpRepository repo = createRepo();
float value = -5.5f;
int bits = Float.floatToIntBits(value);
short high = (short) ((bits >>> 16) & 0xFFFF);
short low = (short) (bits & 0xFFFF);
// swapped: pass low first, then high and use SWAPPED dataType
String decoded = invokeDecode(repo, new short[]{low, high}, "FOUR_BYTE_FLOAT_SWAPPED", 1.0, null);
// rounding to 2 decimals expected by implementation
assertEquals("-5.5", decoded);
}
@Test
void decodeFourByteUnsignedInt() throws Exception {
ModbusRtuTcpRepository repo = createRepo();
long unsignedVal = 3000000000L; // > 2^31
int bits = (int) unsignedVal; // low 32 bits
short high = (short) ((bits >>> 16) & 0xFFFF);
short low = (short) (bits & 0xFFFF);
String decoded = invokeDecode(repo, new short[]{high, low}, "FOUR_BYTE_INT_UNSIGNED", 1.0, null);
assertEquals((double) unsignedVal, Double.parseDouble(decoded), 0.0001);
}
@Test
void decodeFourByteSignedInt() throws Exception {
ModbusRtuTcpRepository repo = createRepo();
int signedVal = -12345678;
int bits = signedVal;
short high = (short) ((bits >>> 16) & 0xFFFF);
short low = (short) (bits & 0xFFFF);
String decoded = invokeDecode(repo, new short[]{high, low}, "FOUR_BYTE_INT_SIGNED", 1.0, null);
assertEquals((double) signedVal, Double.parseDouble(decoded), 0.0001);
}
@Test
void decodeTwoByteSignedLegacyWrap() throws Exception {
ModbusRtuTcpRepository repo = createRepo();
short s = (short) (-32760); // near legacy wrap sentinel
String decoded = invokeDecode(repo, new short[]{s}, "TWO_BYTE_INT_SIGNED", 1.0, true);
// calculation: -(s + 32768) * scale -> -( -32760 + 32768) = -8
assertEquals("-8.0", decoded);
}
@Test
void decodeTwoByteNormal() throws Exception {
ModbusRtuTcpRepository repo = createRepo();
short s = (short) (123);
String decoded = invokeDecode(repo, new short[]{s}, "TWO_BYTE_INT_SIGNED", 0.1, false);
// 123 * 0.1 = 12.3
assertEquals("12.3", decoded);
}
}