You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2020/10/25 22:37:49 UTC

[plc4x] branch feature/plc4go updated: - Addressed some issues in the reading of values via S7 protocol

This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch feature/plc4go
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/feature/plc4go by this push:
     new a1720ad  - Addressed some issues in the reading of values via S7 protocol
a1720ad is described below

commit a1720ad594a7927384954e6e1b90318a42de1fee
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Sun Oct 25 23:37:32 2020 +0100

    - Addressed some issues in the reading of values via S7 protocol
---
 .../plc4x/java/s7/readwrite/field/S7Field.java     | 57 ++++++++--------
 .../s7/readwrite/protocol/S7ProtocolLogic.java     |  2 +-
 .../apache/plc4x/java/s7/utils/StaticHelper.java   | 12 ++++
 .../plc4x/java/s7/readwrite/DatatypesTest.java     | 79 ++++++++++++++++++++++
 .../plc4x/java/spi/generation/ReadBuffer.java      |  2 +-
 .../s7/src/main/resources/protocols/s7/s7.mspec    | 10 +--
 6 files changed, 127 insertions(+), 35 deletions(-)

diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/field/S7Field.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/field/S7Field.java
index cc2a679..80eff10 100644
--- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/field/S7Field.java
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/field/S7Field.java
@@ -120,12 +120,13 @@ public class S7Field implements PlcField {
     }
 
     public static boolean matches(String fieldString) {
-        return ADDRESS_PATTERN.matcher(fieldString).matches() ||
+        return
             DATA_BLOCK_STRING_ADDRESS_PATTERN.matcher(fieldString).matches() ||
             DATA_BLOCK_STRING_SHORT_PATTERN.matcher(fieldString).matches() ||
             DATA_BLOCK_ADDRESS_PATTERN.matcher(fieldString).matches() ||
             DATA_BLOCK_SHORT_PATTERN.matcher(fieldString).matches() ||
-            PLC_PROXY_ADDRESS_PATTERN.matcher(fieldString).matches();
+            PLC_PROXY_ADDRESS_PATTERN.matcher(fieldString).matches() ||
+            ADDRESS_PATTERN.matcher(fieldString).matches();
     }
 
     /**
@@ -166,32 +167,7 @@ public class S7Field implements PlcField {
 
     public static S7Field of(String fieldString) {
         Matcher matcher;
-        if ((matcher = ADDRESS_PATTERN.matcher(fieldString)).matches()) {
-            TransportSize dataType = TransportSize.valueOf(matcher.group(DATA_TYPE));
-            MemoryArea memoryArea = getMemoryAreaForShortName(matcher.group(MEMORY_AREA));
-            Short transferSizeCode = getSizeCode(matcher.group(TRANSFER_SIZE_CODE));
-            int byteOffset = checkByteOffset(Integer.parseInt(matcher.group(BYTE_OFFSET)));
-            byte bitOffset = 0;
-            if (matcher.group(BIT_OFFSET) != null) {
-                bitOffset = Byte.parseByte(matcher.group(BIT_OFFSET));
-            } else if (dataType == TransportSize.BOOL) {
-                throw new PlcInvalidFieldException("Expected bit offset for BOOL parameters.");
-            }
-            int numElements = 1;
-            if (matcher.group(NUM_ELEMENTS) != null) {
-                numElements = Integer.parseInt(matcher.group(NUM_ELEMENTS));
-            }
-
-            if ((transferSizeCode != null) && (dataType.getSizeCode() != transferSizeCode)) {
-                throw new PlcInvalidFieldException("Transfer size code '" + transferSizeCode +
-                    "' doesn't match specified data type '" + dataType.name() + "'");
-            }
-            if ((dataType != TransportSize.BOOL) && bitOffset != 0) {
-                throw new PlcInvalidFieldException("A bit offset other than 0 is only supported for type BOOL");
-            }
-
-            return new S7Field(dataType, memoryArea, (short) 0, byteOffset, bitOffset, numElements);
-        } else if ((matcher = DATA_BLOCK_STRING_ADDRESS_PATTERN.matcher(fieldString)).matches()) {
+        if ((matcher = DATA_BLOCK_STRING_ADDRESS_PATTERN.matcher(fieldString)).matches()) {
             TransportSize dataType = TransportSize.STRING;
             int stringLength = Integer.parseInt(matcher.group(STRING_LENGTH));
             MemoryArea memoryArea = getMemoryAreaForShortName(matcher.group(MEMORY_AREA));
@@ -309,6 +285,31 @@ public class S7Field implements PlcField {
             } catch (ParseException | DecoderException e) {
                 throw new PlcInvalidFieldException("Unable to parse address: " + fieldString);
             }
+        } else if ((matcher = ADDRESS_PATTERN.matcher(fieldString)).matches()) {
+            TransportSize dataType = TransportSize.valueOf(matcher.group(DATA_TYPE));
+            MemoryArea memoryArea = getMemoryAreaForShortName(matcher.group(MEMORY_AREA));
+            Short transferSizeCode = getSizeCode(matcher.group(TRANSFER_SIZE_CODE));
+            int byteOffset = checkByteOffset(Integer.parseInt(matcher.group(BYTE_OFFSET)));
+            byte bitOffset = 0;
+            if (matcher.group(BIT_OFFSET) != null) {
+                bitOffset = Byte.parseByte(matcher.group(BIT_OFFSET));
+            } else if (dataType == TransportSize.BOOL) {
+                throw new PlcInvalidFieldException("Expected bit offset for BOOL parameters.");
+            }
+            int numElements = 1;
+            if (matcher.group(NUM_ELEMENTS) != null) {
+                numElements = Integer.parseInt(matcher.group(NUM_ELEMENTS));
+            }
+
+            if ((transferSizeCode != null) && (dataType.getSizeCode() != transferSizeCode)) {
+                throw new PlcInvalidFieldException("Transfer size code '" + transferSizeCode +
+                    "' doesn't match specified data type '" + dataType.name() + "'");
+            }
+            if ((dataType != TransportSize.BOOL) && bitOffset != 0) {
+                throw new PlcInvalidFieldException("A bit offset other than 0 is only supported for type BOOL");
+            }
+
+            return new S7Field(dataType, memoryArea, (short) 0, byteOffset, bitOffset, numElements);
         }
         throw new PlcInvalidFieldException("Unable to parse address: " + fieldString);
     }
diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java
index 133cffc..92135d8 100644
--- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java
@@ -606,7 +606,7 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> {
         }
         if(transportSize == TransportSize.STRING) {
             transportSize = TransportSize.CHAR;
-            int stringLength = ((S7StringField) s7Field).getStringLength();
+            int stringLength = (s7Field instanceof S7StringField) ? ((S7StringField) s7Field).getStringLength() : 254;
             numElements = numElements * (stringLength + 2);
         }
         return new S7AddressAny(transportSize, numElements, s7Field.getBlockNumber(),
diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/utils/StaticHelper.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/utils/StaticHelper.java
index e76539c..a489955 100644
--- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/utils/StaticHelper.java
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/utils/StaticHelper.java
@@ -106,6 +106,8 @@ public class StaticHelper {
             int minute = io.readByte(8);
             int second = io.readByte(8);
             int nanosecond = io.readUnsignedInt(24);
+            // Skip day-of-week
+            io.readByte(8);
 
             return LocalDateTime.of(year, month, day, hour, minute, second, nanosecond);
         } catch (Exception e) {
@@ -117,6 +119,11 @@ public class StaticHelper {
         throw new NotImplementedException("Serializing DATE_AND_TIME not implemented");
     }
 
+    public static String parseS7Char(ReadBuffer io, Object encoding) {
+        // Read the full size of the string.
+        return io.readString(8, (String) encoding);
+    }
+
     public static String parseS7String(ReadBuffer io, int stringLength, Object encoding) {
         try {
             // This is the maximum number of bytes a string can be long.
@@ -132,6 +139,11 @@ public class StaticHelper {
         }
     }
 
+    public static void serializeS7Char(WriteBuffer io, PlcValue value, Object encoding) {
+        // TODO: Need to implement the serialization or we can't write strings
+        throw new NotImplementedException("Serializing STRING not implemented");
+    }
+
     public static void serializeS7String(WriteBuffer io, PlcValue value, int stringLength, Object encoding) {
         // TODO: Need to implement the serialization or we can't write strings
         throw new NotImplementedException("Serializing STRING not implemented");
diff --git a/plc4j/drivers/s7/src/test/java/org/apache/plc4x/java/s7/readwrite/DatatypesTest.java b/plc4j/drivers/s7/src/test/java/org/apache/plc4x/java/s7/readwrite/DatatypesTest.java
new file mode 100644
index 0000000..f382b37
--- /dev/null
+++ b/plc4j/drivers/s7/src/test/java/org/apache/plc4x/java/s7/readwrite/DatatypesTest.java
@@ -0,0 +1,79 @@
+/*
+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.s7.readwrite;
+
+import org.apache.plc4x.java.PlcDriverManager;
+import org.apache.plc4x.java.api.PlcConnection;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcReadResponse;
+
+public class DatatypesTest {
+
+    public static void main(String[] args) throws Exception {
+        try (PlcConnection connection = new PlcDriverManager().getConnection("s7://192.168.23.30")) {
+            final PlcReadRequest.Builder builder = connection.readRequestBuilder();
+            builder.addItem("bool-value-1", "%DB2:0.0:BOOL"); // true
+            builder.addItem("bool-value-2", "%DB2:2.1:BOOL"); // false
+            // It seems S7 PLCs ignores the array notation for BOOL
+            //builder.addItem("bool-array", "%DB2:2.1:BOOL[4]");
+            builder.addItem("byte-value", "%DB2:2:BYTE");
+            builder.addItem("byte-array", "%DB2:2:BYTE[2]");
+            builder.addItem("word-value", "%DB2:2:WORD");
+            builder.addItem("word-array", "%DB2:2:WORD[2]");
+            builder.addItem("dword-value", "%DB2:2:DWORD");
+            builder.addItem("dword-array", "%DB2:2:DWORD[2]");
+            builder.addItem("sint-value", "%DB2:12:SINT"); // 7
+            builder.addItem("sint-array", "%DB2:14:SINT[2]"); // 1, -2
+            builder.addItem("int-value", "%DB2:18:INT"); // 23
+            builder.addItem("int-array", "%DB2:20:INT[2]"); // 123, -142
+            builder.addItem("dint-value", "%DB2:24:DINT"); // 24
+            builder.addItem("dint-array", "%DB2:28:DINT[2]"); // 1234, -2345
+            builder.addItem("usint-value", "%DB2:36:USINT"); // 42
+            builder.addItem("usint-array", "%DB2:38:USINT[2]"); // 3, 4
+            builder.addItem("uint-value", "%DB2:40:UINT"); // 3
+            builder.addItem("uint-array", "%DB2:42:UINT[2]"); // 242, 223
+            builder.addItem("udint-value", "%DB2:46:UDINT"); // 815
+            builder.addItem("udint-array", "%DB2:50:UDINT[2]"); // 12345, 23456
+            builder.addItem("real-value", "%DB2:58:REAL"); // 3.14159
+            builder.addItem("real-array", "%DB2:62:REAL[2]"); // 12.345, 12.345
+            builder.addItem("lreal-value", "%DB2:70:LREAL"); // 3.14159265358979
+            builder.addItem("lreal-array", "%DB2:78:LREAL[2]"); // 1.2345, -1.2345
+            builder.addItem("string-value", "%DB2:94:STRING(10)"); // "Hurz"
+            // When reading a sized STRING string array, this has to be translated into multiple items
+            //builder.addItem("string-array", "%DB2:350:STRING(10)[2]"); // "Wolf", "Lamm"
+            builder.addItem("time-value", "%DB2:862:TIME"); // 1234ms
+            builder.addItem("time-array", "%DB2:866:TIME[2]"); // 123ms, 234ms
+            builder.addItem("date-value", "%DB2:874:DATE"); // D#2020-08-20
+            builder.addItem("date-array", "%DB2:876:DATE[2]"); // D#1990-03-28, D#2020-10-25
+            builder.addItem("time-of-day-value", "%DB2:880:TIME_OF_DAY"); // TOD#12:34:56
+            builder.addItem("time-of-day-array", "%DB2:884:TIME_OF_DAY[2]"); // TOD#16:34:56, TOD#08:15:00
+            builder.addItem("date-and-time-value", "%DB2:892:DATE_AND_TIME"); // DTL#1978-03-28-12:34:56
+            builder.addItem("date-and-time-array", "%DB2:904:DATE_AND_TIME[2]"); // DTL#1978-03-28-12:34:56, DTL#1978-03-28-12:34:56
+            builder.addItem("char-value", "%DB2:928:CHAR"); // "H"
+            builder.addItem("char-array", "%DB2:930:CHAR[4]"); // "H", "u", "r", "z"
+            final PlcReadRequest readRequest = builder.build();
+
+            final PlcReadResponse readResponse = readRequest.execute().get();
+
+            System.out.println(readResponse);
+
+        }
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java
index c856c6c..d890f7b 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java
@@ -61,7 +61,7 @@ public class ReadBuffer {
     }
 
     public boolean hasMore(int numBits) {
-        return (numBits / 8) < (totalBytes - getPos());
+        return (numBits / 8) <= (totalBytes - getPos());
     }
 
     public byte peekByte(int offset) throws ParseException {
diff --git a/protocols/s7/src/main/resources/protocols/s7/s7.mspec b/protocols/s7/src/main/resources/protocols/s7/s7.mspec
index 914c53e..5e1d329 100644
--- a/protocols/s7/src/main/resources/protocols/s7/s7.mspec
+++ b/protocols/s7/src/main/resources/protocols/s7/s7.mspec
@@ -333,11 +333,11 @@
         // -----------------------------------------
         // Characters & Strings
         // -----------------------------------------
-        ['41' STRING
-            [manual string 'UTF-8' 'value' 'STATIC_CALL("org.apache.plc4x.java.s7.utils.StaticHelper.parseS7String", io, stringLength, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.s7.utils.StaticHelper.serializeS7String", io, _value, '1', _type.encoding)' '1']
+        ['41' CHAR
+            [manual string 'UTF-8' 'value' 'STATIC_CALL("org.apache.plc4x.java.s7.utils.StaticHelper.parseS7Char", io, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.s7.utils.StaticHelper.serializeS7Char", io, _value, _type.encoding)' '1']
         ]
-        ['42' STRING
-            [manual string 'UTF-16' 'value' 'STATIC_CALL("org.apache.plc4x.java.s7.utils.StaticHelper.parseS7String", io, stringLength, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.s7.utils.StaticHelper.serializeS7String", io, _value, '1', _type.encoding)' '2']
+        ['42' CHAR
+            [manual string 'UTF-16' 'value' 'STATIC_CALL("org.apache.plc4x.java.s7.utils.StaticHelper.parseS7Char", io, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.s7.utils.StaticHelper.serializeS7Char", io, _value, _type.encoding)' '2']
         ]
         ['43' STRING
             [manual string 'UTF-8' 'value' 'STATIC_CALL("org.apache.plc4x.java.s7.utils.StaticHelper.parseS7String", io, stringLength, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.s7.utils.StaticHelper.serializeS7String", io, _value, stringLength, _type.encoding)' '_value.length + 2']
@@ -433,7 +433,7 @@
     ['0x03' CHAR             ['B'              , '1'                 , 'null'                  , 'DataTransportSize.BYTE_WORD_DWORD'  , '41'                   , 'true'                , 'true'                , 'true'                 , 'true'                 , 'true'              ]]
     ['0x13' WCHAR            ['X'              , '2'                 , 'null'                  , 'null'                               , '42'                   , 'false'               , 'false'               , 'true'                 , 'true'                 , 'true'              ]]
     ['0x03' STRING           ['X'              , '1'                 , 'null'                  , 'DataTransportSize.BYTE_WORD_DWORD'  , '43'                   , 'true'                , 'true'                , 'true'                 , 'true'                 , 'true'              ]]
-    ['0x00' WSTRING          ['X'              , '1'                 , 'null'                  , 'null'                               , '44'                   , 'false'               , 'false'               , 'true'                 , 'true'                 , 'true'              ]]
+    ['0x00' WSTRING          ['X'              , '2'                 , 'null'                  , 'null'                               , '44'                   , 'false'               , 'false'               , 'true'                 , 'true'                 , 'true'              ]]
 
     // Dates and time values (Please note that we seem to have to reqite queries for these types to reading bytes or we'll get "Data type not supported" errors)
     ['0x0B' TIME             ['X'              , '4'                 , 'null'                  , 'null'                               , '51'                   , 'true'                , 'true'                , 'true'                 , 'true'                 , 'true'              ]]