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 0eaa9f953c1f0cf123b7b7f18f262b29d1a844ad Author: Sebastian Rühl <sru...@apache.org> AuthorDate: Wed May 30 15:10:32 2018 +0200 added tests for LittleEndianDecoder/Encoder and fixed some bugs. --- .../ads/protocol/util/LittleEndianDecoder.java | 35 +++++++++- .../ads/protocol/util/LittleEndianEncoder.java | 34 ++++++--- .../ads/protocol/util/LittleEndianDecoderTest.java | 80 ++++++++++++++++++++++ .../ads/protocol/util/LittleEndianEncoderTest.java | 57 +++++++++++++++ 4 files changed, 195 insertions(+), 11 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 c66a353..3539be0 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 @@ -38,6 +38,7 @@ public class LittleEndianDecoder { lengthMap.put(Short.class, 2L); lengthMap.put(Integer.class, 4L); lengthMap.put(Float.class, 4L); + lengthMap.put(Double.class, 8L); lengthMap.put(Calendar.class, 8L); LENGTH_MAP = Collections.unmodifiableMap(lengthMap); } @@ -70,7 +71,10 @@ public class LittleEndianDecoder { } else if (datatype == Float.class && length < 4) { safeLengthAdsData = new byte[4]; System.arraycopy(adsData, 0, safeLengthAdsData, 0, length); - } else if (datatype == Calendar.class || Calendar.class.isAssignableFrom(datatype) && length < 8) { + } else if (datatype == Double.class && length < 8) { + safeLengthAdsData = new byte[8]; + System.arraycopy(adsData, 0, safeLengthAdsData, 0, length); + } else if ((datatype == Calendar.class || Calendar.class.isAssignableFrom(datatype)) && length < 8) { safeLengthAdsData = new byte[8]; System.arraycopy(adsData, 0, safeLengthAdsData, 0, length); } else { @@ -96,6 +100,9 @@ public class LittleEndianDecoder { } else if (datatype == Float.class) { decodeFloat(safeLengthAdsData, i, result); i += 4; + } else if (datatype == Double.class) { + decodeDouble(safeLengthAdsData, i, result); + i += 8; } else if (datatype == Calendar.class || Calendar.class.isAssignableFrom(datatype)) { extractCalendar(safeLengthAdsData, i, result); i += 8; @@ -106,10 +113,14 @@ public class LittleEndianDecoder { return (List<T>) result; } - private static int decodeString(byte[] adsData, int i, int length, List<Object> result) { + private static int decodeString(byte[] adsData, int i, int length, List<Object> result) throws PlcProtocolException { + if (length < 2) { + throw new PlcProtocolException("String must be a null terminated byte array"); + } int pos = i; StringBuilder builder = new StringBuilder(); - while (adsData[pos] != (byte) 0x0 && pos < length) { + // TODO: check if we have at least a 0x0 space + while (pos < length && adsData[pos] != (byte) 0x0) { builder.append((char) adsData[pos]); pos++; } @@ -145,6 +156,24 @@ public class LittleEndianDecoder { result.add(Float.intBitsToFloat(intValue)); } + private static void decodeDouble(byte[] adsData, int i, List<Object> result) { + byte byteOne = adsData[i]; + byte byteTwo = adsData[i + 1]; + byte byteThree = adsData[i + 2]; + byte byteFour = adsData[i + 3]; + byte byteFive = adsData[i + 4]; + byte byteSix = adsData[i + 5]; + byte byteSeven = adsData[i + 6]; + byte byteEigth = adsData[i + 7]; + // TODO: check how ads expects this data + // Description of the Real number format: + // https://www.sps-lehrgang.de/zahlenformate-step7/#c144 + // https://de.wikipedia.org/wiki/IEEE_754 + long longValue = (long) (byteOne & 0xff) | ((long) (byteTwo & 0xff) << 8) | ((long) (byteThree & 0xff) << 16) | ((long) (byteFour & 0xff) << 24) + | (long) (byteFive & 0xff) << 32 | ((long) (byteSix & 0xff) << 40) | ((long) (byteSeven & 0xff) << 48) | ((long) (byteEigth & 0xff) << 56); + result.add(Double.longBitsToDouble(longValue)); + } + private static void extractCalendar(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 7ee8cbe..5c75a61 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,7 +39,7 @@ public class LittleEndianEncoder { // Utility class } - public static byte[] encodeData(Class<?> valueType, Object[] values) throws PlcProtocolException { + public static byte[] encodeData(Class<?> valueType, Object... values) throws PlcProtocolException { if (values.length == 0) { return new byte[]{}; } @@ -56,6 +56,8 @@ public class LittleEndianEncoder { result = encodeCalendar(Arrays.stream(values).map(Calendar.class::cast)); } else if (valueType == Float.class) { result = encodeFloat(Arrays.stream(values).map(Float.class::cast)); + } else if (valueType == Double.class) { + result = encodeDouble(Arrays.stream(values).map(Double.class::cast)); } else if (valueType == String.class) { result = encodeString(Arrays.stream(values).map(String.class::cast)); } else { @@ -80,7 +82,7 @@ public class LittleEndianEncoder { } } - public static Stream<byte[]> encodeString(Stream<String> stringStream) { + private static Stream<byte[]> encodeString(Stream<String> stringStream) { // TODO: what do we do with utf-8 values with 2 bytes? what is the charset here? return stringStream .map(s -> s.getBytes(Charset.defaultCharset())) @@ -88,7 +90,7 @@ public class LittleEndianEncoder { .map(bytes -> ArrayUtils.add(bytes, (byte) 0x0)); } - public static Stream<byte[]> encodeFloat(Stream<Float> floatStream) { + private static Stream<byte[]> encodeFloat(Stream<Float> floatStream) { return floatStream // TODO: check how ads expects this data .map(Float::floatToIntBits) @@ -100,7 +102,23 @@ public class LittleEndianEncoder { }); } - public static Stream<byte[]> encodeInteger(Stream<Integer> integerStream) { + private static Stream<byte[]> encodeDouble(Stream<Double> doubleStream) { + return doubleStream + // TODO: check how ads expects this data + .map(Double::doubleToLongBits) + .map(longValue -> new byte[]{ + (byte) (longValue & 0x00000000_000000ffL), + (byte) ((longValue & 0x00000000_0000ff00L) >> 8), + (byte) ((longValue & 0x00000000_00ff0000L) >> 16), + (byte) ((longValue & 0x00000000_ff000000L) >> 24), + (byte) ((longValue & 0x000000ff_00000000L) >> 32), + (byte) ((longValue & 0x0000ff00_00000000L) >> 40), + (byte) ((longValue & 0x00ff0000_00000000L) >> 48), + (byte) ((longValue & 0xff000000_00000000L) >> 56), + }); + } + + private static Stream<byte[]> encodeInteger(Stream<Integer> integerStream) { return integerStream .map(intValue -> new byte[]{ (byte) (intValue & 0x000000ff), @@ -110,7 +128,7 @@ public class LittleEndianEncoder { }); } - public static Stream<byte[]> encodeCalendar(Stream<Calendar> calendarStream) { + private static Stream<byte[]> encodeCalendar(Stream<Calendar> calendarStream) { return calendarStream .map(Calendar.class::cast) .map(Calendar::getTime) @@ -132,7 +150,7 @@ public class LittleEndianEncoder { } - public static Stream<byte[]> encodeShort(Stream<Short> shortStream) { + private static Stream<byte[]> encodeShort(Stream<Short> shortStream) { return shortStream .map(shortValue -> new byte[]{ (byte) (shortValue & 0x00ff), @@ -140,12 +158,12 @@ public class LittleEndianEncoder { }); } - public static Stream<byte[]> encodeByte(Stream<Byte> byteStream) { + private static Stream<byte[]> encodeByte(Stream<Byte> byteStream) { return byteStream .map(aByte -> new byte[]{aByte}); } - public static Stream<byte[]> encodeBoolean(Stream<Boolean> booleanStream) { + private static Stream<byte[]> encodeBoolean(Stream<Boolean> booleanStream) { return booleanStream .map(booleanValue -> new byte[]{booleanValue ? (byte) 0x01 : (byte) 0x00}); } diff --git a/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianDecoderTest.java b/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianDecoderTest.java new file mode 100644 index 0000000..c14d5cf --- /dev/null +++ b/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianDecoderTest.java @@ -0,0 +1,80 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ +package org.apache.plc4x.java.ads.protocol.util; + +import org.apache.plc4x.java.ads.api.commands.types.Length; +import org.junit.Test; + +import java.util.Calendar; +import java.util.Date; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.junit.Assert.assertEquals; + +public class LittleEndianDecoderTest { + + @Test + public void getLengthFor() { + assertEquals(LittleEndianDecoder.getLengthFor(Boolean.class, 0), Length.of(1)); + assertEquals(LittleEndianDecoder.getLengthFor(Byte.class, 0), Length.of(1)); + assertEquals(LittleEndianDecoder.getLengthFor(Short.class, 0), Length.of(2)); + assertEquals(LittleEndianDecoder.getLengthFor(Integer.class, 0), Length.of(4)); + assertEquals(LittleEndianDecoder.getLengthFor(Float.class, 0), Length.of(4)); + assertEquals(LittleEndianDecoder.getLengthFor(Double.class, 0), Length.of(8)); + assertEquals(LittleEndianDecoder.getLengthFor(Calendar.class, 0), Length.of(8)); + assertEquals(LittleEndianDecoder.getLengthFor(LittleEndianDecoderTest.class, 666), Length.of(666)); + } + + @Test + public void decodeData() throws Exception { + assertEquals(asList(true, false), LittleEndianDecoder.decodeData(Boolean.class, new byte[]{0x1, 0x0})); + + assertEquals(asList((byte) 0x1, (byte) 0x0), LittleEndianDecoder.decodeData(Byte.class, new byte[]{0x1, 0x0})); + + assertEquals(singletonList((short) 1), LittleEndianDecoder.decodeData(Short.class, new byte[]{0x1})); + assertEquals(singletonList((short) 256), LittleEndianDecoder.decodeData(Short.class, new byte[]{0x0, 0x1})); + assertEquals(asList((short) 256, (short) 256), LittleEndianDecoder.decodeData(Short.class, new byte[]{0x0, 0x1, 0x0, 0x1})); + + assertEquals(singletonList(1), LittleEndianDecoder.decodeData(Integer.class, new byte[]{0x1})); + assertEquals(singletonList(16777216), LittleEndianDecoder.decodeData(Integer.class, new byte[]{0x0, 0x0, 0x0, 0x1})); + assertEquals(asList(16777216, 16777216), LittleEndianDecoder.decodeData(Integer.class, new byte[]{0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1})); + + assertEquals(singletonList(1.4E-45f), LittleEndianDecoder.decodeData(Float.class, new byte[]{0x1})); + assertEquals(singletonList(2.3509887E-38f), LittleEndianDecoder.decodeData(Float.class, new byte[]{0x0, 0x0, 0x0, 0x1})); + assertEquals(asList(2.3509887E-38f, 2.3509887E-38f), LittleEndianDecoder.decodeData(Float.class, new byte[]{0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1})); + + assertEquals(singletonList(4.9E-324), LittleEndianDecoder.decodeData(Double.class, new byte[]{0x1})); + assertEquals(singletonList(7.2911220195563975E-304), LittleEndianDecoder.decodeData(Double.class, new byte[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1})); + assertEquals(asList(7.2911220195563975E-304, 7.2911220195563975E-304), LittleEndianDecoder.decodeData(Double.class, new byte[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1})); + + Calendar calendar1 = Calendar.getInstance(); + calendar1.setTime(new Date(-11644473600000L)); + assertEquals(singletonList(calendar1), LittleEndianDecoder.decodeData(Calendar.class, new byte[]{0x1})); + Calendar calendar0x0001 = Calendar.getInstance(); + calendar0x0001.setTime(new Date(-4438714196208L)); + assertEquals(singletonList(calendar0x0001), LittleEndianDecoder.decodeData(Calendar.class, new byte[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1})); + assertEquals(asList(calendar0x0001, calendar0x0001), LittleEndianDecoder.decodeData(Calendar.class, new byte[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1})); + + assertEquals(singletonList("plc4x"), LittleEndianDecoder.decodeData(String.class, new byte[]{0x70, 0x6c, 0x63, 0x34, 0x78, 0x0})); + assertEquals(singletonList("plc4xplc4x"), LittleEndianDecoder.decodeData(String.class, new byte[]{0x70, 0x6c, 0x63, 0x34, 0x78, 0x70, 0x6c, 0x63, 0x34, 0x78, 0x0})); + assertEquals(asList("plc4x", "plc4x"), LittleEndianDecoder.decodeData(String.class, new byte[]{0x70, 0x6c, 0x63, 0x34, 0x78, 0x0, 0x70, 0x6c, 0x63, 0x34, 0x78, 0x0})); + } + +} \ No newline at end of file diff --git a/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianEncoderTest.java b/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianEncoderTest.java new file mode 100644 index 0000000..110fd7c --- /dev/null +++ b/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/protocol/util/LittleEndianEncoderTest.java @@ -0,0 +1,57 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ +package org.apache.plc4x.java.ads.protocol.util; + +import org.junit.Test; + +import java.util.Calendar; +import java.util.Date; + +import static org.apache.plc4x.java.ads.util.Assert.assertByteEquals; + +public class LittleEndianEncoderTest { + + @Test + public void encodeData() throws Exception { + assertByteEquals(new byte[]{0x01, 0x00, 0x01, 0x00}, LittleEndianEncoder.encodeData(Boolean.class, true, false, true, false)); + + assertByteEquals(new byte[]{0x12, 0x03, 0x05, 0x7f}, LittleEndianEncoder.encodeData(Byte.class, (byte) 0x12, (byte) 0x03, (byte) 0x05, (byte) 0x7f)); + + assertByteEquals(new byte[]{0x1, 0x00}, LittleEndianEncoder.encodeData(Short.class, (short) 1)); + assertByteEquals(new byte[]{0x0e, 0x00, 0x50, 0x00}, LittleEndianEncoder.encodeData(Short.class, (short) 14, (short) 80)); + + assertByteEquals(new byte[]{0x5a, 0x0a, 0x00, 0x00}, LittleEndianEncoder.encodeData(Integer.class, 2650)); + assertByteEquals(new byte[]{0x5a, 0x0a, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00}, LittleEndianEncoder.encodeData(Integer.class, 2650, 80)); + + assertByteEquals(new byte[]{(byte) 0xc3, (byte) 0xf5, 0x48, 0x40}, LittleEndianEncoder.encodeData(Float.class, 3.14f)); + assertByteEquals(new byte[]{(byte) 0xc3, (byte) 0xf5, 0x48, 0x40, 0x14, (byte) 0xae, 0x07, 0x40}, LittleEndianEncoder.encodeData(Float.class, 3.14f, 2.12f)); + + assertByteEquals(new byte[]{0x1F, (byte) 0x85, (byte) 0xEB, 0x51, (byte) 0xB8, 0x1E, 0x09, 0x40}, LittleEndianEncoder.encodeData(Double.class, 3.14)); + assertByteEquals(new byte[]{0x1F, (byte) 0x85, (byte) 0xEB, 0x51, (byte) 0xB8, 0x1E, 0x09, 0x40, (byte) 0xF6, 0x28, 0x5C, (byte) 0x8F, (byte) 0xC2, (byte) 0xF5, 0x00, 0x40}, LittleEndianEncoder.encodeData(Double.class, 3.14, 2.12)); + + Calendar calendar1 = Calendar.getInstance(); + //calendar1.set(2003, Calendar.DECEMBER, 23, 13, 3, 0); + calendar1.setTime(new Date(1072180980436L)); + assertByteEquals(new byte[]{(byte) 0x40, (byte) 0x79, (byte) 0xFB, (byte) 0xB5, (byte) 0x4C, (byte) 0xC9, (byte) 0xC3, (byte) 0x01}, LittleEndianEncoder.encodeData(Calendar.class, calendar1)); + + assertByteEquals(new byte[]{0x70, 0x6c, 0x63, 0x34, 0x78, 0x00}, LittleEndianEncoder.encodeData(String.class, "plc4x")); + assertByteEquals(new byte[]{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x00}, LittleEndianEncoder.encodeData(String.class, "HelloWorld!")); + assertByteEquals(new byte[]{0x70, 0x6c, 0x63, 0x34, 0x78, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x00}, LittleEndianEncoder.encodeData(String.class, "plc4x", "HelloWorld!")); + } +} \ No newline at end of file -- To stop receiving notification emails like this one, please contact sru...@apache.org.