This is an automated email from the ASF dual-hosted git repository. sruehl pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-plc4x.git
commit 14d558505a519cadfa7045a6b0b9e6b097362530 Author: Sebastian Rühl <sru...@apache.org> AuthorDate: Thu Jul 26 11:13:58 2018 +0200 added BigInteger support on modbus and ads --- .../ads/protocol/util/LittleEndianDecoder.java | 12 ++++++ .../ads/protocol/util/LittleEndianEncoder.java | 18 ++++++++ .../base/protocol/Plc4XSupportedDataTypes.java | 14 ++++--- .../java/modbus/netty/Plc4XModbusProtocol.java | 48 ++++++++++++++++++++++ .../java/modbus/netty/Plc4XModbusProtocolTest.java | 16 +++++++- 5 files changed, 101 insertions(+), 7 deletions(-) diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianDecoder.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianDecoder.java index 3539be0..dcea26f 100644 --- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianDecoder.java +++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianDecoder.java @@ -18,6 +18,7 @@ */ package org.apache.plc4x.java.ads.protocol.util; +import org.apache.commons.lang3.ArrayUtils; import org.apache.plc4x.java.ads.api.commands.types.Length; import org.apache.plc4x.java.ads.api.commands.types.TimeStamp; import org.apache.plc4x.java.api.exceptions.PlcProtocolException; @@ -97,6 +98,10 @@ public class LittleEndianDecoder { } else if (datatype == Integer.class) { decodeInteger(safeLengthAdsData, i, result); i += 4; + } else if (datatype == BigInteger.class) { + decodeBigInteger(safeLengthAdsData, result); + // A big integer can consume the whole stream + i = length; } else if (datatype == Float.class) { decodeFloat(safeLengthAdsData, i, result); i += 4; @@ -143,6 +148,13 @@ public class LittleEndianDecoder { result.add((byteOne & 0xff) | ((byteTwo & 0xff) << 8) | ((byteThree & 0xff) << 16) | (byteFour & 0xff) << 24); } + private static void decodeBigInteger(byte[] adsData, List<Object> result) { + byte[] clone = ArrayUtils.clone(adsData); + ArrayUtils.reverse(clone); + byte[] bigIntegerByteArray = ArrayUtils.insert(0, clone, (byte) 0x0); + result.add(new BigInteger(bigIntegerByteArray)); + } + private static void decodeFloat(byte[] adsData, int i, List<Object> result) { byte byteOne = adsData[i]; byte byteTwo = adsData[i + 1]; diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianEncoder.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianEncoder.java index 5c75a61..6b147c5 100644 --- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianEncoder.java +++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianEncoder.java @@ -39,6 +39,7 @@ public class LittleEndianEncoder { // Utility class } + // TODO: add bound checking public static byte[] encodeData(Class<?> valueType, Object... values) throws PlcProtocolException { if (values.length == 0) { return new byte[]{}; @@ -52,6 +53,8 @@ public class LittleEndianEncoder { result = encodeShort(Arrays.stream(values).map(Short.class::cast)); } else if (valueType == Integer.class) { result = encodeInteger(Arrays.stream(values).map(Integer.class::cast)); + } else if (valueType == BigInteger.class) { + result = encodeBigInteger(Arrays.stream(values).map(BigInteger.class::cast)); } else if (valueType == Calendar.class || Calendar.class.isAssignableFrom(valueType)) { result = encodeCalendar(Arrays.stream(values).map(Calendar.class::cast)); } else if (valueType == Float.class) { @@ -128,6 +131,21 @@ public class LittleEndianEncoder { }); } + private static Stream<byte[]> encodeBigInteger(Stream<BigInteger> bigIntegerStream) { + return bigIntegerStream + .map(bigIntValue -> { + byte[] bytes = bigIntValue.toByteArray(); + if (bytes[0] == 0x0) { + byte[] subArray = ArrayUtils.subarray(bytes, 1, bytes.length); + ArrayUtils.reverse(subArray); + return subArray; + } else { + ArrayUtils.reverse(bytes); + return bytes; + } + }); + } + private static Stream<byte[]> encodeCalendar(Stream<Calendar> calendarStream) { return calendarStream .map(Calendar.class::cast) diff --git a/plc4j/protocols/driver-bases/test/src/main/java/org/apache/plc4x/java/base/protocol/Plc4XSupportedDataTypes.java b/plc4j/protocols/driver-bases/test/src/main/java/org/apache/plc4x/java/base/protocol/Plc4XSupportedDataTypes.java index a8dc32c..f84e532 100644 --- a/plc4j/protocols/driver-bases/test/src/main/java/org/apache/plc4x/java/base/protocol/Plc4XSupportedDataTypes.java +++ b/plc4j/protocols/driver-bases/test/src/main/java/org/apache/plc4x/java/base/protocol/Plc4XSupportedDataTypes.java @@ -23,6 +23,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import java.io.Serializable; +import java.math.BigInteger; import java.util.*; import java.util.stream.Stream; @@ -41,11 +42,12 @@ public class Plc4XSupportedDataTypes { littleEndianMap.put(Boolean.class, ImmutablePair.of(Boolean.TRUE, new byte[]{0x01})); littleEndianMap.put(Byte.class, ImmutablePair.of(Byte.valueOf("1"), new byte[]{0x1})); littleEndianMap.put(Short.class, ImmutablePair.of(Short.valueOf("1"), new byte[]{0x1, 0x0})); - littleEndianMap.put(Calendar.class, ImmutablePair.of(calenderInstance, new byte[]{0x0, (byte) 0x80, 0x3E, 0x15, (byte) 0xAB, 0x47, (byte) 0xFC, 0x28})); - littleEndianMap.put(GregorianCalendar.class, littleEndianMap.get(Calendar.class)); littleEndianMap.put(Float.class, ImmutablePair.of(Float.valueOf("1"), new byte[]{0x0, 0x0, (byte) 0x80, 0x3F})); - littleEndianMap.put(Double.class, ImmutablePair.of(Double.valueOf("1"), new byte[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, (byte) 0xF0, 0x3F})); littleEndianMap.put(Integer.class, ImmutablePair.of(Integer.valueOf("1"), new byte[]{0x1, 0x0, 0x0, 0x0})); + littleEndianMap.put(Double.class, ImmutablePair.of(Double.valueOf("1"), new byte[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, (byte) 0xF0, 0x3F})); + littleEndianMap.put(BigInteger.class, ImmutablePair.of(BigInteger.valueOf(1), new byte[]{0x1, 0x0, 0x0, 0x0})); + littleEndianMap.put(Calendar.class, ImmutablePair.of(calenderInstance, new byte[]{0x0, (byte) 0x80, 0x3E, 0x15, (byte) 0xAB, 0x47, (byte) 0xFC, 0x28})); + littleEndianMap.put(GregorianCalendar.class, littleEndianMap.get(Calendar.class)); littleEndianMap.put(String.class, ImmutablePair.of(String.valueOf("Hello World!"), new byte[]{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x00})); bigEndianMap = new HashMap<>(); littleEndianMap.forEach((clazz, pair) -> { @@ -66,11 +68,13 @@ public class Plc4XSupportedDataTypes { return Stream.of( Boolean.class, Byte.class, + // TODO: add byte[] Short.class, - Calendar.class, Float.class, - Double.class, Integer.class, + Double.class, + BigInteger.class, + Calendar.class, String.class ); } diff --git a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java index 9a050f4..b4914a8 100644 --- a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java +++ b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java @@ -37,6 +37,7 @@ import org.apache.plc4x.java.modbus.model.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.math.BigInteger; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -260,6 +261,7 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, && !(value instanceof byte[]) && !(value instanceof Short) && !(value instanceof Integer) + && !(value instanceof BigInteger) ) { throw new PlcRuntimeException("Unsupported datatype detected " + value.getClass()); } @@ -311,6 +313,8 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, throw new PlcProtocolException("Value to high to fit into Byte: " + value); } coilSet = (int) value == 1; + } else if (value.getClass() == BigInteger.class) { + coilSet = value.equals(BigInteger.ONE); } byte coilToSet = (coilSet ? (byte) 1 : (byte) 0); actualCoil = (byte) (actualCoil | coilToSet << i); @@ -344,12 +348,41 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, } buffer.writeBytes(bytes); } else if (value.getClass() == Short.class) { + if ((short) value < 0) { + throw new PlcProtocolException("Only positive values are supported for Short: " + value); + } buffer.writeShort((short) value); } else if (value.getClass() == Integer.class) { if ((int) value > Integer.MAX_VALUE) { throw new PlcProtocolException("Value to high to fit into register: " + value); } + if ((int) value < 0) { + throw new PlcProtocolException("Only positive values are supported for Integer: " + value); + } buffer.writeShort((int) value); + } else if (value.getClass() == BigInteger.class) { + if (((BigInteger) value).compareTo(BigInteger.ZERO) < 0) { + throw new PlcProtocolException("Only positive values are supported for BigInteger: " + value); + } + if (((BigInteger) value).compareTo(BigInteger.valueOf(0XFFFF_FFFFL)) > 0) { + throw new PlcProtocolException("Value to high to fit into register: " + value); + } + // TODO: for now we can't support big values as we only write one register at once + if (((BigInteger) value).compareTo(BigInteger.valueOf(0XFFFFL)) > 0) { + throw new PlcProtocolException("Value to high to fit into register: " + value); + } + // TODO: Register has 2 bytes so we trim to 2 instead of 4 like the second if above + int maxBytes = 2; + byte[] bigIntegerBytes = ((BigInteger) value).toByteArray(); + byte[] bytes = new byte[maxBytes]; + int lengthToCopy = Math.min(bigIntegerBytes.length, maxBytes); + int srcPosition = Math.max(bigIntegerBytes.length - maxBytes, 0); + int destPosition = maxBytes - lengthToCopy; + System.arraycopy(bigIntegerBytes, srcPosition, bytes, destPosition, lengthToCopy); + + // TODO: check if this is a good representation. + // TODO: can a big integer span multiple registers? + buffer.writeBytes(bytes); } } byte[] result = new byte[buffer.writerIndex()]; @@ -393,6 +426,10 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, @SuppressWarnings("unchecked") T itemToBeAdded = (T) Integer.valueOf(coilFlag); data.add(itemToBeAdded); + } else if (dataType == BigInteger.class) { + @SuppressWarnings("unchecked") + T itemToBeAdded = (T) BigInteger.valueOf(coilFlag); + data.add(itemToBeAdded); } } return data; @@ -431,9 +468,20 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, T itemToBeAdded = (T) Short.valueOf((short) intValue); data.add(itemToBeAdded); } else if (dataType == Integer.class) { + if (intValue < 0) { + throw new PlcProtocolException("Integer underflow: " + intValue); + } @SuppressWarnings("unchecked") T itemToBeAdded = (T) Integer.valueOf(intValue); data.add(itemToBeAdded); + } else if (dataType == BigInteger.class) { + if (intValue < 0) { + throw new PlcProtocolException("BigInteger underflow: " + intValue); + } + // TODO: can a big integer span multiple registers? + @SuppressWarnings("unchecked") + T itemToBeAdded = (T) new BigInteger(register); + data.add(itemToBeAdded); } } return data; diff --git a/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocolTest.java b/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocolTest.java index 42b882e..f28cb69 100644 --- a/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocolTest.java +++ b/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocolTest.java @@ -43,6 +43,7 @@ import org.slf4j.LoggerFactory; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.math.BigInteger; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; @@ -167,7 +168,7 @@ public class Plc4XModbusProtocolTest { .builder() .addItem((Class) pair.getLeft().getClass(), RegisterModbusAddress.of("register:1"), pair.getLeft(), pair.getLeft(), pair.getLeft()) .build(), new CompletableFuture<>()), - new ModbusTcpPayload((short) 0, (short) 0, new WriteMultipleCoilsResponse(1, 3)) + new ModbusTcpPayload((short) 0, (short) 0, new WriteMultipleRegistersResponse(1, 3)) ), ImmutablePair.of( new PlcRequestContainer<>( @@ -175,7 +176,7 @@ public class Plc4XModbusProtocolTest { .builder() .addItem(RegisterModbusAddress.of("register:1"), pair.getLeft()) .build(), new CompletableFuture<>()), - new ModbusTcpPayload((short) 0, (short) 0, new WriteSingleCoilResponse(1, pair.getRight()[0])) + new ModbusTcpPayload((short) 0, (short) 0, new WriteSingleRegisterResponse(1, pair.getRight()[0])) ) )) .flatMap(stream -> stream) @@ -228,6 +229,9 @@ public class Plc4XModbusProtocolTest { } else if (payloadClazzName.equals(Integer.class.getSimpleName())) { assertThat(andMask, equalTo(1)); assertThat(orMask, equalTo(2)); + } else if (payloadClazzName.equals(BigInteger.class.getSimpleName())) { + assertThat(andMask, equalTo(1)); + assertThat(orMask, equalTo(2)); } else if (payloadClazzName.equals(String.class.getSimpleName())) { assertThat(andMask, equalTo(1)); assertThat(orMask, equalTo(2)); @@ -275,6 +279,8 @@ public class Plc4XModbusProtocolTest { assertByteEquals(new byte[]{0x0}, bytes); } else if (payloadClazzName.equals(Integer.class.getSimpleName())) { assertByteEquals(new byte[]{0x0}, bytes); + } else if (payloadClazzName.equals(BigInteger.class.getSimpleName())) { + assertByteEquals(new byte[]{0x0}, bytes); } else if (payloadClazzName.equals(String.class.getSimpleName())) { assertByteEquals(new byte[]{0x0}, bytes); } @@ -297,6 +303,8 @@ public class Plc4XModbusProtocolTest { assertByteEquals(new byte[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, bytes); } else if (payloadClazzName.equals(Integer.class.getSimpleName())) { assertByteEquals(new byte[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, bytes); + } else if (payloadClazzName.equals(BigInteger.class.getSimpleName())) { + assertByteEquals(new byte[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, bytes); } else if (payloadClazzName.equals(String.class.getSimpleName())) { assertByteEquals(new byte[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, bytes); } @@ -318,6 +326,8 @@ public class Plc4XModbusProtocolTest { assertThat(coilValue, equalTo(true)); } else if (payloadClazzName.equals(Integer.class.getSimpleName())) { assertThat(coilValue, equalTo(true)); + } else if (payloadClazzName.equals(BigInteger.class.getSimpleName())) { + assertThat(coilValue, equalTo(true)); } else if (payloadClazzName.equals(String.class.getSimpleName())) { assertThat(coilValue, equalTo(true)); } @@ -338,6 +348,8 @@ public class Plc4XModbusProtocolTest { assertThat(value, equalTo(1)); } else if (payloadClazzName.equals(Integer.class.getSimpleName())) { assertThat(value, equalTo(1)); + } else if (payloadClazzName.equals(BigInteger.class.getSimpleName())) { + assertThat(value, equalTo(1)); } else if (payloadClazzName.equals(String.class.getSimpleName())) { assertThat(value, equalTo(1)); }