You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2018/11/01 11:19:38 UTC
[incubator-plc4x] branch master updated: [plc4j-ads] fixed encoder
and decoder and enabled round-trip test
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
The following commit(s) were added to refs/heads/master by this push:
new f452493 [plc4j-ads] fixed encoder and decoder and enabled round-trip test
f452493 is described below
commit f452493ca25dc43055434c765c5e0e1249cde29e
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Thu Nov 1 12:19:22 2018 +0100
[plc4j-ads] fixed encoder and decoder and enabled round-trip test
---
.../apache/plc4x/java/ads/model/AdsDataType.java | 72 ++++++++++++----------
.../plc4x/java/ads/model/AdsPlcFieldHandler.java | 65 ++++++-------------
.../ads/protocol/util/LittleEndianDecoder.java | 34 ++++------
.../ads/protocol/util/LittleEndianEncoder.java | 62 +++++++++++++------
.../protocol/util/EncoderDecoderRoundTripTest.java | 56 ++++++++++++++---
plc4j/protocols/ads/src/test/resources/logback.xml | 2 +-
.../base/messages/items/BaseDefaultFieldItem.java | 2 +-
7 files changed, 162 insertions(+), 131 deletions(-)
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsDataType.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsDataType.java
index 771ccb7..6470803 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsDataType.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsDataType.java
@@ -23,9 +23,11 @@ import org.apache.commons.lang3.ArrayUtils;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.time.Duration;
+import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
+import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
/**
@@ -37,19 +39,19 @@ import java.util.stream.IntStream;
public enum AdsDataType {
// TODO: maybe this are just types for the plc ide and can be removed
// https://infosys.beckhoff.com/english.php?content=../content/1033/tcsystemmanager/basics/TcSysMgr_DatatypeComparison.htm&id=
- BIT(8),
- BIT8(8),
- BITARR8(8),
- BITARR16(16),
- BITARR32(32),
- INT8(8),
+ BIT(0, 1, 8),
+ BIT8((short) 0x00, (short) 0xFF, 8),
+ BITARR8((short) 0x00, (short) 0xFF, 8),
+ BITARR16(0x0000, 0xFFFF, 16),
+ BITARR32(0x0000_0000L, 0xFFFF_FFFFL, 32),
+ INT8(Byte.MIN_VALUE, Byte.MAX_VALUE, 8),
INT16(Short.MIN_VALUE, Short.MAX_VALUE, 16),
INT32(Integer.MIN_VALUE, Integer.MAX_VALUE, 32),
INT64(Long.MIN_VALUE, Long.MAX_VALUE, 64),
- UINT8(0, Short.MAX_VALUE, 8),
- UINT16(0, Integer.MAX_VALUE, 16),
- UINT32(0, produceUnsignedMaxValue(32), 32),
- UINT64(0, produceUnsignedMaxValue(64), 64),
+ UINT8((short) 0, (short) 0xFF, 8),
+ UINT16(0, 0xFFFF, 16),
+ UINT32(0L, 0xFFFF_FFFFL, 32),
+ UINT64(BigInteger.ZERO, produceUnsignedMaxValue(8), 64),
FLOAT(-Float.MAX_VALUE, Float.MAX_VALUE, 32),
DOUBLE(-Double.MAX_VALUE, Double.MAX_VALUE, 64),
// https://infosys.beckhoff.com/english.php?content=../content/1033/tcplccontrol/html/tcplcctrl_plc_data_types_overview.htm&id
@@ -67,7 +69,7 @@ public enum AdsDataType {
* <p>
* The boolean variable is in the same memory range as the byte variable.
*/
- BOOL(8),
+ BOOL((byte) 0, (byte) 1, 8),
/**
* BYTE
* <p>
@@ -76,7 +78,7 @@ public enum AdsDataType {
* Type Lower bound Upper bound Memory use
* BYTE 0 255 8 Bit
*/
- BYTE(0, 255, 8),
+ BYTE((short) 0, (short) 255, 8),
/**
* WORD
* Integer data type.
@@ -92,7 +94,7 @@ public enum AdsDataType {
* Type Lower bound Upper bound Memory use
* DWORD 0 4294967295 32 Bit
*/
- DWORD(0, 4294967295L, 32),
+ DWORD(0L, 4294967295L, 32),
/**
* SINT
* (Short) signed integer data type.
@@ -100,7 +102,7 @@ public enum AdsDataType {
* Type Lower bound Upper bound Memory use
* SINT -128 127 8 Bit
*/
- SINT(-128, 127, 8),
+ SINT(Byte.MIN_VALUE, Byte.MAX_VALUE, 8),
/**
* USINT
* Unsigned (short) integer data type.
@@ -108,7 +110,7 @@ public enum AdsDataType {
* Type Lower bound Upper bound Memory use
* USINT 0 255 8 Bit
*/
- USINT(0, 255, 8),
+ USINT((short) 0, (short) 0xFF, 8),
/**
* INT
* Signed integer data type.
@@ -116,7 +118,7 @@ public enum AdsDataType {
* Type Lower bound Upper bound Memory use
* INT -32768 32767 16 Bit
*/
- INT(-32768, 32767, 16),
+ INT(Short.MIN_VALUE, Short.MAX_VALUE, 16),
/**
* UINT
* Unsigned integer data type.
@@ -140,15 +142,15 @@ public enum AdsDataType {
* Type Lower bound Upper bound Memory use
* UDINT 0 4294967295 32 Bit
*/
- UDINT(0, 4294967295L, 32),
+ UDINT(0L, 4294967295L, 32),
/**
* LINT (64 bit integer, currently not supported by TwinCAT)
*/
- LINT(64),
+ LINT(Long.MIN_VALUE, Long.MAX_VALUE, 64),
/**
* ULINT (Unsigned 64 bit integer, currently not supported by TwinCAT)
*/
- ULINT(64),
+ ULINT(BigInteger.ZERO, produceUnsignedMaxValue(8), 64),
/**
* REAL
* 32 Bit floating point data type. It is required to represent rational numbers.
@@ -185,7 +187,7 @@ public enum AdsDataType {
* Type Lower bound Upper bound Memory use
* TIME T#0ms T#71582m47s295ms 32 Bit
*/
- TIME(0, Duration.ofMinutes(71582).plusSeconds(47).plusMillis(295).toMillis(), 32),
+ TIME(0L, Duration.ofMinutes(71582).plusSeconds(47).plusMillis(295).toMillis(), 32),
/**
* TIME_OF_DAY
* TOD
@@ -197,7 +199,7 @@ public enum AdsDataType {
* <p>
* TOD#00:00 TOD#1193:02:47.295 32 Bit
*///TODO: strange maximum
- TIME_OF_DAY(0, ChronoUnit.MILLIS.between(LocalTime.of(0, 0), LocalTime.of(23, 59, 59)), 32),
+ TIME_OF_DAY(0L, ChronoUnit.MILLIS.between(LocalTime.of(0, 0), LocalTime.of(23, 59, 59)), 32),
/**
* DATE
* Date. The most significant digit is one second. The data type is handled internally like DWORD.
@@ -205,7 +207,7 @@ public enum AdsDataType {
* Type Lower bound Upper bound Memory use
* DATE D#1970-01-01 D#2106-02-06 32 Bit
*/
- DATE(0, ChronoUnit.SECONDS.between(LocalDateTime.of(1970, 1, 1, 0, 0), LocalDateTime.of(2106, 2, 6, 0, 0)), 32),
+ DATE(0L, TimeUnit.DAYS.toSeconds(ChronoUnit.DAYS.between(LocalDate.of(1970, 1, 1), LocalDate.of(2106, 2, 6))), 32),
/**
* DATE_AND_TIME
* DT
@@ -216,8 +218,8 @@ public enum AdsDataType {
* DT
* <p>
* DT#1970-01-01-00:00 DT#2106-02-06-06:28:15 32 Bit
- *////TODO: calculate max
- DATE_AND_TIME(0, -1, 32),
+ */
+ DATE_AND_TIME(0L, ChronoUnit.SECONDS.between(LocalDateTime.of(1970, 1, 1, 0, 0), LocalDateTime.of(2106, 2, 6, 6, 28, 15)), 32),
//User-defined Data Types
/**
* Arrays
@@ -495,9 +497,13 @@ public enum AdsDataType {
private final String typeName;
- private final double lowerBound;
+ private final Number lowerBound;
+
+ private final double lowerBoundDouble;
+
+ private final Number upperBound;
- private final double upperBound;
+ private final double upperBoundDouble;
private final int memoryUse;
@@ -508,9 +514,11 @@ public enum AdsDataType {
this(0, Byte.MAX_VALUE, memoryUse);
}
- AdsDataType(double lowerBound, double upperBound, int memoryUse) {
+ AdsDataType(Number lowerBound, Number upperBound, int memoryUse) {
this.lowerBound = lowerBound;
+ this.lowerBoundDouble = lowerBound.doubleValue();
this.upperBound = upperBound;
+ this.upperBoundDouble = upperBound.doubleValue();
this.typeName = name();
this.memoryUse = memoryUse;
this.targetByteSize = this.memoryUse / 8;
@@ -520,11 +528,11 @@ public enum AdsDataType {
return typeName;
}
- public double getLowerBound() {
+ public Number getLowerBound() {
return lowerBound;
}
- public double getUpperBound() {
+ public Number getUpperBound() {
return upperBound;
}
@@ -537,7 +545,7 @@ public enum AdsDataType {
}
public boolean withinBounds(double other) {
- return other >= lowerBound && other <= upperBound;
+ return other >= lowerBoundDouble && other <= upperBoundDouble;
}
@Override
@@ -551,7 +559,7 @@ public enum AdsDataType {
"} " + super.toString();
}
- private static double produceUnsignedMaxValue(int numberOfBytes) {
+ private static BigInteger produceUnsignedMaxValue(int numberOfBytes) {
return new BigInteger(
ArrayUtils.insert(
0,
@@ -564,6 +572,6 @@ public enum AdsDataType {
)
.toByteArray(),
(byte) 0x0)
- ).doubleValue();
+ );
}
}
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsPlcFieldHandler.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsPlcFieldHandler.java
index c6811e5..70d18df 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsPlcFieldHandler.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsPlcFieldHandler.java
@@ -716,8 +716,6 @@ public class AdsPlcFieldHandler extends DefaultPlcFieldHandler {
private BaseDefaultFieldItem internalEncodeInteger(PlcField field, Object[] values) {
AdsField adsField = (AdsField) field;
- BigDecimal minValue = BigDecimal.valueOf(adsField.getAdsDataType().getLowerBound());
- BigDecimal maxValue = BigDecimal.valueOf(adsField.getAdsDataType().getUpperBound());
Class<? extends BaseDefaultFieldItem> fieldType;
switch (adsField.getAdsDataType()) {
case BYTE:
@@ -772,16 +770,10 @@ public class AdsPlcFieldHandler extends DefaultPlcFieldHandler {
"Value of type " + values[i].getClass().getName() +
" is not assignable to " + adsField.getAdsDataType().name() + " fields.");
}
- BigDecimal value = BigDecimal.valueOf(((Number) values[i]).longValue());
- if (minValue.compareTo(value) > 0) {
- throw new IllegalArgumentException(
- "Value of " + value.toString() + " exceeds allowed minimum for type "
- + adsField.getAdsDataType().name() + " (min " + minValue.toString() + ")");
- }
- if (maxValue.compareTo(value) < 0) {
- throw new IllegalArgumentException(
- "Value of " + value.toString() + " exceeds allowed maximum for type "
- + adsField.getAdsDataType().name() + " (max " + maxValue.toString() + ")");
+
+ Number value = (Number) values[i];
+ if (!adsField.getAdsDataType().withinBounds(value.doubleValue())) {
+ throw new IllegalArgumentException("Value " + values[i] + " ist not within bounds of " + adsField.getAdsDataType());
}
longValues[i] = value.longValue();
}
@@ -800,15 +792,8 @@ public class AdsPlcFieldHandler extends DefaultPlcFieldHandler {
"Value of type " + values[i].getClass().getName() +
" is not assignable to " + adsField.getAdsDataType().name() + " fields.");
}
- if (minValue.compareTo(value) > 0) {
- throw new IllegalArgumentException(
- "Value of " + value.toString() + " exceeds allowed minimum for type "
- + adsField.getAdsDataType().name() + " (min " + minValue.toString() + ")");
- }
- if (maxValue.compareTo(value) < 0) {
- throw new IllegalArgumentException(
- "Value of " + value.toString() + " exceeds allowed maximum for type "
- + adsField.getAdsDataType().name() + " (max " + maxValue.toString() + ")");
+ if (!adsField.getAdsDataType().withinBounds(value.doubleValue())) {
+ throw new IllegalArgumentException("Value " + values[i] + " ist not within bounds of " + adsField.getAdsDataType());
}
bigIntegerValues[i] = value.toBigInteger();
}
@@ -818,8 +803,6 @@ public class AdsPlcFieldHandler extends DefaultPlcFieldHandler {
private BaseDefaultFieldItem internalEncodeFloatingPoint(PlcField field, Object[] values) {
AdsField adsField = (AdsField) field;
- BigDecimal minValue = BigDecimal.valueOf(adsField.getAdsDataType().getLowerBound());
- BigDecimal maxValue = BigDecimal.valueOf(adsField.getAdsDataType().getUpperBound());
Class<? extends BaseDefaultFieldItem> fieldType;
switch (adsField.getAdsDataType()) {
case REAL:
@@ -845,15 +828,9 @@ public class AdsPlcFieldHandler extends DefaultPlcFieldHandler {
" is not assignable to " + adsField.getAdsDataType().name() + " fields.");
}
- if (minValue.compareTo(new BigDecimal(floatingPointValues[i])) > 0) {
- throw new IllegalArgumentException(
- "Value of " + floatingPointValues[i] + " exceeds allowed minimum for type "
- + adsField.getAdsDataType().name() + " (min " + minValue.toString() + ")");
- }
- if (maxValue.compareTo(new BigDecimal(floatingPointValues[i])) < 0) {
- throw new IllegalArgumentException(
- "Value of " + floatingPointValues[i] + " exceeds allowed maximum for type "
- + adsField.getAdsDataType().name() + " (max " + maxValue.toString() + ")");
+ Number value = (Number) values[i];
+ if (!adsField.getAdsDataType().withinBounds(value.doubleValue())) {
+ throw new IllegalArgumentException("Value " + values[i] + " ist not within bounds of " + adsField.getAdsDataType());
}
}
return new DefaultDoubleFieldItem(floatingPointValues);
@@ -868,15 +845,9 @@ public class AdsPlcFieldHandler extends DefaultPlcFieldHandler {
" is not assignable to " + adsField.getAdsDataType().name() + " fields.");
}
- if (minValue.compareTo(new BigDecimal(floatingPointValues[i])) > 0) {
- throw new IllegalArgumentException(
- "Value of " + floatingPointValues[i] + " exceeds allowed minimum for type "
- + adsField.getAdsDataType().name() + " (min " + minValue.toString() + ")");
- }
- if (maxValue.compareTo(new BigDecimal(floatingPointValues[i])) < 0) {
- throw new IllegalArgumentException(
- "Value of " + floatingPointValues[i] + " exceeds allowed maximum for type "
- + adsField.getAdsDataType().name() + " (max " + maxValue.toString() + ")");
+ Number value = (Number) values[i];
+ if (!adsField.getAdsDataType().withinBounds(value.doubleValue())) {
+ throw new IllegalArgumentException("Value " + values[i] + " ist not within bounds of " + adsField.getAdsDataType());
}
}
return new DefaultFloatFieldItem(floatingPointValues);
@@ -885,7 +856,7 @@ public class AdsPlcFieldHandler extends DefaultPlcFieldHandler {
private BaseDefaultFieldItem internalEncodeString(PlcField field, Object[] values) {
AdsField adsField = (AdsField) field;
- double maxLength = adsField.getAdsDataType().getUpperBound();
+ Number maxLength = adsField.getAdsDataType().getUpperBound();
//boolean encoding16Bit;
switch (adsField.getAdsDataType()) {
case STRING:
@@ -899,7 +870,7 @@ public class AdsPlcFieldHandler extends DefaultPlcFieldHandler {
for (Object value : values) {
if (value instanceof String) {
String stringValue = (String) value;
- if (stringValue.length() > maxLength) {
+ if (stringValue.length() > maxLength.intValue()) {
throw new IllegalArgumentException(
"String length " + stringValue.length() + " exceeds allowed maximum for type "
+ adsField.getAdsDataType().name() + " (max " + maxLength + ")");
@@ -913,7 +884,7 @@ public class AdsPlcFieldHandler extends DefaultPlcFieldHandler {
/*if (encoding16Bit) {
stringValues.add(new String(stringBytes, StandardCharsets.UTF_16));
} else {*/
- stringValues.add(new String(stringBytes, StandardCharsets.UTF_8));
+ stringValues.add(new String(stringBytes, StandardCharsets.UTF_8));
/*}*/
} else if (value instanceof Short) {
Short shortValue = (Short) value;
@@ -923,7 +894,7 @@ public class AdsPlcFieldHandler extends DefaultPlcFieldHandler {
/*if (encoding16Bit) {
stringValues.add(new String(stringBytes, StandardCharsets.UTF_16));
} else {*/
- stringValues.add(new String(stringBytes, StandardCharsets.UTF_8));
+ stringValues.add(new String(stringBytes, StandardCharsets.UTF_8));
/*}*/
} else if (value instanceof Integer) {
Integer integerValue = (Integer) value;
@@ -935,7 +906,7 @@ public class AdsPlcFieldHandler extends DefaultPlcFieldHandler {
/*if (encoding16Bit) {
stringValues.add(new String(stringBytes, StandardCharsets.UTF_16));
} else {*/
- stringValues.add(new String(stringBytes, StandardCharsets.UTF_8));
+ stringValues.add(new String(stringBytes, StandardCharsets.UTF_8));
/*}*/
} else if (value instanceof Long) {
Long longValue = (Long) value;
@@ -951,7 +922,7 @@ public class AdsPlcFieldHandler extends DefaultPlcFieldHandler {
/*if (encoding16Bit) {
stringValues.add(new String(stringBytes, StandardCharsets.UTF_16));
} else {*/
- stringValues.add(new String(stringBytes, StandardCharsets.UTF_8));
+ stringValues.add(new String(stringBytes, StandardCharsets.UTF_8));
/*}*/
} else {
throw new IllegalArgumentException(
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 950e4b4..4bd92d3 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
@@ -104,19 +104,16 @@ public class LittleEndianDecoder {
case INT32: {
LinkedList<Integer> values = new LinkedList<>();
while (wrappedBuffer.isReadable()) {
- int aLong = wrappedBuffer.readIntLE();
- values.offer(aLong);
+ int intLE = wrappedBuffer.readIntLE();
+ values.offer(intLE);
}
return new DefaultIntegerFieldItem(values.toArray(new Integer[0]));
}
case INT64: {
LinkedList<Long> values = new LinkedList<>();
while (wrappedBuffer.isReadable()) {
- byte[] bytes = new byte[8];
- wrappedBuffer.readBytes(bytes);
- BigInteger bigInteger = new BigInteger(bytes);
- // TODO: potential dataloss here.
- values.offer(bigInteger.longValue());
+ long longLE = wrappedBuffer.readLongLE();
+ values.offer(longLE);
}
return new DefaultLongFieldItem(values.toArray(new Long[0]));
}
@@ -144,16 +141,17 @@ public class LittleEndianDecoder {
}
return new DefaultLongFieldItem(values.toArray(new Long[0]));
}
+ case ULINT:
case UINT64: {
- LinkedList<Long> values = new LinkedList<>();
+ LinkedList<BigInteger> values = new LinkedList<>();
while (wrappedBuffer.isReadable()) {
- byte[] bytes = new byte[64];
+ byte[] bytes = new byte[8];
wrappedBuffer.readBytes(bytes);
- BigInteger bigInteger = new BigInteger(ArrayUtils.add(bytes, (byte) 0x0));
- // TODO: potential dataloss here.
- values.offer(bigInteger.longValue());
+ ArrayUtils.reverse(bytes);
+ BigInteger bigInteger = new BigInteger(ArrayUtils.insert(0, bytes, (byte) 0x0));
+ values.offer(bigInteger);
}
- return new DefaultLongFieldItem(values.toArray(new Long[0]));
+ return new DefaultBigIntegerFieldItem(values.toArray(new BigInteger[0]));
}
case FLOAT: {
LinkedList<Float> values = new LinkedList<>();
@@ -259,16 +257,6 @@ public class LittleEndianDecoder {
}
return new DefaultLongFieldItem(values.toArray(new Long[0]));
}
- case ULINT: {
- LinkedList<BigInteger> values = new LinkedList<>();
- while (wrappedBuffer.isReadable()) {
- byte[] bytes = new byte[64];
- wrappedBuffer.readBytes(bytes);
- BigInteger bigInteger = new BigInteger(ArrayUtils.add(bytes, (byte) 0x0));
- values.offer(bigInteger);
- }
- return new DefaultBigIntegerFieldItem(values.toArray(new BigInteger[0]));
- }
case REAL: {
LinkedList<Float> values = new LinkedList<>();
while (wrappedBuffer.isReadable()) {
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 de41485..272d451 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
@@ -21,6 +21,7 @@ package org.apache.plc4x.java.ads.protocol.util;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.plc4x.java.ads.model.AdsDataType;
import org.apache.plc4x.java.api.exceptions.PlcProtocolException;
+import org.apache.plc4x.java.api.exceptions.PlcProtocolPayloadTooBigException;
import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
import org.apache.plc4x.java.api.exceptions.PlcUnsupportedDataTypeException;
@@ -102,17 +103,28 @@ public class LittleEndianEncoder {
return stringStream
.map(s -> s.getBytes(Charset.defaultCharset()))
// TODO: this 0 termination is from s7 but might be completly wrong in ads. Guess its a terminator
- .map(bytes -> ArrayUtils.add(bytes, (byte) 0x0));
+ .map(bytes -> ArrayUtils.add(bytes, (byte) 0x0))
+ .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
}
private static Stream<byte[]> encodeByteArray(AdsDataType adsDataType, Stream<byte[]> byteArrayStream) {
// TODO: add boundchecks and add optional extension
- return byteArrayStream;
+ return byteArrayStream
+ .peek(bytes -> {
+ if (bytes.length > adsDataType.getTargetByteSize()) {
+ throw new PlcRuntimeException(new PlcProtocolPayloadTooBigException("ads", adsDataType.getTargetByteSize(), bytes.length, bytes));
+ }
+ });
}
private static Stream<byte[]> encodeBigByteArray(AdsDataType adsDataType, Stream<Byte[]> byteArrayStream) {
// TODO: add boundchecks and add optional extension
- return byteArrayStream.map(ArrayUtils::toPrimitive);
+ return byteArrayStream.map(ArrayUtils::toPrimitive)
+ .peek(bytes -> {
+ if (bytes.length > adsDataType.getTargetByteSize()) {
+ throw new PlcRuntimeException(new PlcProtocolPayloadTooBigException("ads", adsDataType.getTargetByteSize(), bytes.length, bytes));
+ }
+ });
}
private static Stream<byte[]> encodeFloat(AdsDataType adsDataType, Stream<Float> floatStream) {
@@ -125,7 +137,8 @@ public class LittleEndianEncoder {
(byte) ((intValue & 0x0000ff00) >> 8),
(byte) ((intValue & 0x00ff0000) >> 16),
(byte) ((intValue & 0xff000000) >> 24),
- });
+ })
+ .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
}
private static Stream<byte[]> encodeDouble(AdsDataType adsDataType, Stream<Double> doubleStream) {
@@ -142,7 +155,8 @@ public class LittleEndianEncoder {
(byte) ((longValue & 0x0000ff00_00000000L) >> 40),
(byte) ((longValue & 0x00ff0000_00000000L) >> 48),
(byte) ((longValue & 0xff000000_00000000L) >> 56),
- });
+ })
+ .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
}
private static Stream<byte[]> encodeInteger(AdsDataType adsDataType, Stream<Integer> integerStream) {
@@ -153,11 +167,12 @@ public class LittleEndianEncoder {
(byte) ((intValue & 0x0000ff00) >> 8),
(byte) ((intValue & 0x00ff0000) >> 16),
(byte) ((intValue & 0xff000000) >> 24),
- });
+ })
+ .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
}
- private static Stream<byte[]> encodeLong(AdsDataType adsDataType, Stream<Long> integerStream) {
- return integerStream
+ private static Stream<byte[]> encodeLong(AdsDataType adsDataType, Stream<Long> longStream) {
+ return longStream
.peek(value -> checkBound(adsDataType, value))
.map(longValue -> new byte[]{
(byte) (longValue & 0x00000000_000000ffL),
@@ -168,7 +183,8 @@ public class LittleEndianEncoder {
(byte) ((longValue & 0x0000ff00_00000000L) >> 40),
(byte) ((longValue & 0x00ff0000_00000000L) >> 48),
(byte) ((longValue & 0xff000000_00000000L) >> 56),
- });
+ })
+ .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
}
private static Stream<byte[]> encodeBigInteger(AdsDataType adsDataType, Stream<BigInteger> bigIntegerStream) {
@@ -176,15 +192,16 @@ public class LittleEndianEncoder {
return bigIntegerStream
.map(bigIntValue -> {
byte[] bytes = bigIntValue.toByteArray();
- if (bytes[0] == 0x0) {
- byte[] subArray = ArrayUtils.subarray(bytes, 1, bytes.length);
+ if (bytes.length > 1 && bytes[0] == 0x0) {
+ byte[] subArray = Arrays.copyOf(ArrayUtils.subarray(bytes, 1, bytes.length), adsDataType.getTargetByteSize());
ArrayUtils.reverse(subArray);
return subArray;
} else {
- ArrayUtils.reverse(bytes);
+ ArrayUtils.reverse(Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
return bytes;
}
- });
+ })
+ .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
}
private static Stream<byte[]> encodeLocalTime(AdsDataType adsDataType, Stream<LocalTime> localTimeStream) {
@@ -197,7 +214,8 @@ public class LittleEndianEncoder {
(byte) ((time & 0x00000000_0000ff00L) >> 8),
(byte) ((time & 0x00000000_00ff0000L) >> 16),
(byte) ((time & 0x00000000_ff000000L) >> 24),
- });
+ })
+ .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
}
private static Stream<byte[]> encodeLocalDate(AdsDataType adsDataType, Stream<LocalDate> localDateStream) {
@@ -212,7 +230,8 @@ public class LittleEndianEncoder {
(byte) ((time & 0x00000000_0000ff00L) >> 8),
(byte) ((time & 0x00000000_00ff0000L) >> 16),
(byte) ((time & 0x00000000_ff000000L) >> 24),
- });
+ })
+ .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
}
private static Stream<byte[]> encodeLocalDateTime(AdsDataType adsDataType, Stream<LocalDateTime> localDateTimeStream) {
@@ -231,7 +250,8 @@ public class LittleEndianEncoder {
(byte) ((time & 0x0000ff00_00000000L) >> 40),
(byte) ((time & 0x00ff0000_00000000L) >> 48),
(byte) ((time & 0xff000000_00000000L) >> 56),
- });
+ })
+ .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
}
@@ -241,19 +261,21 @@ public class LittleEndianEncoder {
.map(shortValue -> new byte[]{
(byte) (shortValue & 0x00ff),
(byte) ((shortValue & 0xff00) >> 8),
- });
+ })
+ .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
}
private static Stream<byte[]> encodeByte(AdsDataType adsDataType, Stream<Byte> byteStream) {
return byteStream
.peek(value -> checkBound(adsDataType, value))
- .map(aByte -> new byte[]{aByte});
+ .map(aByte -> new byte[]{aByte})
+ .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
}
private static Stream<byte[]> encodeBoolean(AdsDataType adsDataType, Stream<Boolean> booleanStream) {
- // TODO: add boundchecks and add optional extension
return booleanStream
- .map(booleanValue -> new byte[]{booleanValue ? (byte) 0x01 : (byte) 0x00});
+ .map(booleanValue -> new byte[]{booleanValue ? (byte) 0x01 : (byte) 0x00})
+ .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
}
private static void checkBound(AdsDataType adsDataType, double other) {
diff --git a/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/protocol/util/EncoderDecoderRoundTripTest.java b/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/protocol/util/EncoderDecoderRoundTripTest.java
index a7f693f..f0c38aa 100644
--- a/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/protocol/util/EncoderDecoderRoundTripTest.java
+++ b/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/protocol/util/EncoderDecoderRoundTripTest.java
@@ -20,39 +20,81 @@
package org.apache.plc4x.java.ads.protocol.util;
import org.apache.plc4x.java.ads.model.AdsDataType;
-import org.apache.plc4x.java.base.messages.items.BaseDefaultFieldItem;
+import org.apache.plc4x.java.base.messages.items.*;
+import org.apache.plc4x.java.base.util.HexUtil;
import org.assertj.core.api.WithAssertions;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneOffset;
import java.util.Arrays;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
-@Disabled("check if the encoder/decoder can be used like this.")
public class EncoderDecoderRoundTripTest implements WithAssertions {
+ private static Set<AdsDataType> untestedDataTypes = Stream.of(
+ AdsDataType.STRING,
+ AdsDataType.ARRAY,
+ AdsDataType.POINTER,
+ AdsDataType.ENUM,
+ AdsDataType.STRUCT,
+ AdsDataType.ALIAS,
+ AdsDataType.SUB_RANGE_DATA_TYPE,
+ AdsDataType.UNKNOWN
+ ).collect(Collectors.toSet());
+
@ParameterizedTest
@MethodSource("generateData")
- public void testMin(AdsDataType adsDataType, Double min, Double max) throws Exception {
+ public void testMin(AdsDataType adsDataType, Number min, Number unused) throws Exception {
byte[] bytes = LittleEndianEncoder.encodeData(adsDataType, min);
+ assertThat(bytes).isNotEmpty();
+ assertThat(bytes).hasSize(adsDataType.getTargetByteSize());
BaseDefaultFieldItem baseDefaultFieldItem = LittleEndianDecoder.decodeData(adsDataType, bytes);
- assertThat(baseDefaultFieldItem.getDouble(0)).isEqualTo(min);
+ if (baseDefaultFieldItem instanceof DefaultBooleanFieldItem) {
+ assertThat(baseDefaultFieldItem.getBoolean(0)).isEqualTo(false);
+ } else if (baseDefaultFieldItem instanceof DefaultLocalTimeFieldItem) {
+ assertThat(baseDefaultFieldItem.getObject(0)).isEqualTo(LocalTime.ofNanoOfDay(TimeUnit.MILLISECONDS.toNanos((Long) min)));
+ } else if (baseDefaultFieldItem instanceof DefaultLocalDateFieldItem) {
+ assertThat(baseDefaultFieldItem.getObject(0)).isEqualTo(LocalDate.ofEpochDay(TimeUnit.SECONDS.toDays((Long) min)));
+ } else if (baseDefaultFieldItem instanceof DefaultLocalDateTimeFieldItem) {
+ assertThat(baseDefaultFieldItem.getObject(0)).isEqualTo(LocalDateTime.ofEpochSecond((Long) min, 0, ZoneOffset.UTC));
+ } else {
+ assertThat(baseDefaultFieldItem.getObject(0)).as("Min of %s using %s is equals %s\n%s", baseDefaultFieldItem, adsDataType, min, HexUtil.toHex(bytes)).isEqualTo(min);
+ }
}
@ParameterizedTest
@MethodSource("generateData")
- public void testMax(AdsDataType adsDataType, Double min, Double max) throws Exception {
+ public void testMax(AdsDataType adsDataType, Number unused, Number max) throws Exception {
byte[] bytes = LittleEndianEncoder.encodeData(adsDataType, max);
+ assertThat(bytes).isNotEmpty();
+ assertThat(bytes).hasSize(adsDataType.getTargetByteSize());
BaseDefaultFieldItem baseDefaultFieldItem = LittleEndianDecoder.decodeData(adsDataType, bytes);
- assertThat(baseDefaultFieldItem.getDouble(0)).isEqualTo(max);
+ if (baseDefaultFieldItem instanceof DefaultBooleanFieldItem) {
+ assertThat(baseDefaultFieldItem.getBoolean(0)).isEqualTo(true);
+ } else if (baseDefaultFieldItem instanceof DefaultLocalTimeFieldItem) {
+ assertThat(baseDefaultFieldItem.getObject(0)).isEqualTo(LocalTime.ofNanoOfDay(TimeUnit.MILLISECONDS.toNanos((Long) max)));
+ } else if (baseDefaultFieldItem instanceof DefaultLocalDateFieldItem) {
+ assertThat(baseDefaultFieldItem.getObject(0)).isEqualTo(LocalDate.ofEpochDay(TimeUnit.SECONDS.toDays((Long) max)));
+ } else if (baseDefaultFieldItem instanceof DefaultLocalDateTimeFieldItem) {
+ assertThat(baseDefaultFieldItem.getObject(0)).isEqualTo(LocalDateTime.ofEpochSecond((Long) max, 0, ZoneOffset.UTC));
+ } else {
+ assertThat(baseDefaultFieldItem.getObject(0)).as("Max of %s using %s is equals %s\n%s", baseDefaultFieldItem, adsDataType, max, HexUtil.toHex(bytes)).isEqualTo(max);
+ }
}
public static Stream<Arguments> generateData() {
return Arrays.stream(AdsDataType.values())
+ .filter(adsDataType -> !untestedDataTypes.contains(adsDataType))
.map(adsDataType -> Arguments.of(adsDataType, adsDataType.getLowerBound(), adsDataType.getUpperBound()));
}
}
diff --git a/plc4j/protocols/ads/src/test/resources/logback.xml b/plc4j/protocols/ads/src/test/resources/logback.xml
index 5bfdbdc..dd243bd 100644
--- a/plc4j/protocols/ads/src/test/resources/logback.xml
+++ b/plc4j/protocols/ads/src/test/resources/logback.xml
@@ -31,7 +31,7 @@
</encoder>
</appender>
- <root level="debug">
+ <root level="info">
<appender-ref ref="STDOUT" />
</root>
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/messages/items/BaseDefaultFieldItem.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/messages/items/BaseDefaultFieldItem.java
index e41b2e5..0574a10 100644
--- a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/messages/items/BaseDefaultFieldItem.java
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/messages/items/BaseDefaultFieldItem.java
@@ -183,7 +183,7 @@ public abstract class BaseDefaultFieldItem<T> {
@Override
public String toString() {
- return "FieldItem{" +
+ return getClass().getSimpleName() + "{" +
"values=" + Arrays.toString(values) +
'}';
}