You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by tm...@apache.org on 2019/05/06 19:37:04 UTC

[plc4x] 02/02: added DateAndTime and TimeOfDay to acquirable Datataypes

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

tmitsch pushed a commit to branch scraper_refactoring_and_improvement
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 8385dd1e824eba7d768a7d1d7578a655290de6b7
Author: Tim Mitsch <t....@pragmaticindustries.de>
AuthorDate: Mon May 6 21:36:56 2019 +0200

    added DateAndTime and TimeOfDay to acquirable Datataypes
---
 .../org/apache/plc4x/java/s7/model/S7Field.java    | 25 +++----
 .../plc4x/java/s7/netty/Plc4XS7Protocol.java       | 80 ++++++++++++++++++++++
 .../org/apache/plc4x/java/s7/netty/S7Protocol.java | 18 ++++-
 .../java/s7/netty/model/types/TransportSize.java   |  6 +-
 .../strategies/DefaultS7MessageProcessor.java      |  4 ++
 5 files changed, 117 insertions(+), 16 deletions(-)

diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/model/S7Field.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/model/S7Field.java
index 7bced35..52cc084 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/model/S7Field.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/model/S7Field.java
@@ -141,7 +141,7 @@ public class S7Field implements PlcField {
             if(matcher.group(NUM_ELEMENTS) != null) {
                 numElements = Integer.parseInt(matcher.group(NUM_ELEMENTS));
             }
-            numElements = calcNumberOfElementsForStringTypes(numElements,dataType);
+            numElements = calcNumberOfElementsForIndividualTypes(numElements,dataType);
             if(!transferSizeCode.isEmpty() && !dataType.getSizeCode().equals(transferSizeCode)) {
                 throw new PlcInvalidFieldException("Transfer size code '" + transferSizeCode +
                     "' doesn't match specified data type '" + dataType.name() + "'");
@@ -166,7 +166,7 @@ public class S7Field implements PlcField {
                 if(matcher.group(NUM_ELEMENTS) != null) {
                     numElements = Integer.parseInt(matcher.group(NUM_ELEMENTS));
                 }
-                numElements = calcNumberOfElementsForStringTypes(numElements,dataType);
+                numElements = calcNumberOfElementsForIndividualTypes(numElements,dataType);
                 if(!transferSizeCode.isEmpty() && !dataType.getSizeCode().equals(transferSizeCode)) {
                     throw new PlcInvalidFieldException("Transfer size code '" + transferSizeCode +
                         "' doesn't match specified data type '" + dataType.name() + "'");
@@ -209,17 +209,18 @@ public class S7Field implements PlcField {
      * @param dataType detected Transport-Size that represents the data-type
      * @return corrected numElements if nessesary
      */
-    private static int calcNumberOfElementsForStringTypes(int numElements,TransportSize dataType){
-        //if no String nothing has to be done
-        if(!dataType.equals(TransportSize.STRING)){
-            return numElements;
-        }
-        //on valid String-length add two byte because of S7-representation of Strings
-        if(numElements>1 && numElements<=254){
-            return numElements+2;
+    private static int calcNumberOfElementsForIndividualTypes(int numElements, TransportSize dataType){
+
+        if(dataType.equals(TransportSize.STRING)){
+            //on valid String-length add two byte because of S7-representation of Strings
+            if(numElements>1 && numElements<=254){
+                return numElements+2;
+            }
+            //connection String usage with "STRING" only --> numElements=1 --> enter default value
+            return 256;
         }
-        //connection String usage with "STRING" only --> numElements=1 --> enter default value
-        return 256;
+        return numElements;
+
     }
 
 }
diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/Plc4XS7Protocol.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/Plc4XS7Protocol.java
index d64ed82..6e50468 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/Plc4XS7Protocol.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/Plc4XS7Protocol.java
@@ -51,7 +51,11 @@ import java.lang.reflect.Array;
 import java.math.BigInteger;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.temporal.ChronoUnit;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -524,6 +528,15 @@ public class Plc4XS7Protocol extends PlcMessageToMessageCodec<S7Message, PlcRequ
                     case WSTRING:
                         fieldItem = decodeReadResponseVarLengthStringField(true, data);
                         break;
+                    // -----------------------------------------
+                    // Characters & Strings
+                    // -----------------------------------------
+                    case DATE_AND_TIME:
+                        fieldItem = decodeReadResponseDateAndTime(field,data);
+                        break;
+                    case TIME_OF_DAY:
+                        fieldItem = decodeReadResponseTimeOfDay(field,data);
+                        break;
                     default:
                         throw new PlcProtocolException("Unsupported type " + field.getDataType());
                 }
@@ -640,6 +653,29 @@ public class Plc4XS7Protocol extends PlcMessageToMessageCodec<S7Message, PlcRequ
         return decodeReadResponseFixedLengthStringField(actualLength, isUtf16, data);
     }
 
+    BaseDefaultFieldItem decodeReadResponseDateAndTime(S7Field field,ByteBuf data) {
+        LocalDateTime[] localDateTimes = readAllValues(LocalDateTime.class,field, i -> readDateAndTime(data));
+        return new DefaultLocalDateTimeFieldItem(localDateTimes);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseTimeOfDay(S7Field field,ByteBuf data) {
+        LocalTime[] localTimes = readAllValues(LocalTime.class,field, i -> readTimeOfDay(data));
+        return new DefaultLocalTimeFieldItem(localTimes);
+    }
+
+    // Returns a 32 bit unsigned value : from 0 to 4294967295 (2^32-1)
+    public static int getUDIntAt(byte[] buffer, int pos) {
+        int result;
+        result = buffer[pos] & 0x0FF;
+        result <<= 8;
+        result += buffer[pos + 1] & 0x0FF;
+        result <<= 8;
+        result += buffer[pos + 2] & 0x0FF;
+        result <<= 8;
+        result += buffer[pos + 3] & 0x0FF;
+        return result;
+    }
+
     private static <T> T[] readAllValues(Class<T> clazz, S7Field field, Function<Integer, T> extract) {
         try {
             return IntStream.rangeClosed(1, field.getNumElements())
@@ -730,4 +766,48 @@ public class Plc4XS7Protocol extends PlcMessageToMessageCodec<S7Message, PlcRequ
         return new BigInteger(bytes);
     }
 
+    LocalDateTime readDateAndTime(ByteBuf data) {
+        //per definition for Date_And_Time only the first 6 bytes are used
+
+        int year=convertByteToBcd(data.readByte());
+        int month=convertByteToBcd(data.readByte());
+        int day=convertByteToBcd(data.readByte());
+        int hour=convertByteToBcd(data.readByte());
+        int minute=convertByteToBcd(data.readByte());
+        int second=convertByteToBcd(data.readByte());
+        //skip the last 2 bytes no information present
+        data.readByte();
+        data.readByte();
+
+        //data-type ranges from 1990 up to 2089
+        if(year>=90){
+            year+=1900;
+        }
+        else{
+            year+=2000;
+        }
+
+        return LocalDateTime.of(year,month,day,hour,minute,second);
+    }
+
+    LocalTime readTimeOfDay(ByteBuf data) {
+        //per definition for Date_And_Time only the first 6 bytes are used
+
+        int millisSinsMidnight = data.readInt();
+
+
+        return LocalTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0).plus(millisSinsMidnight, ChronoUnit.MILLIS);
+
+    }
+
+    /**
+     * converts incoming byte to an integer regarding used BCD format
+     * @param incomingByte
+     * @return converted BCD number
+     */
+    private static int convertByteToBcd(byte incomingByte) {
+        int dec = (incomingByte >> 4) * 10;
+        return dec + (incomingByte & 0x0f);
+    }
+
 }
diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/S7Protocol.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/S7Protocol.java
index 02eb677..55a33b7 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/S7Protocol.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/S7Protocol.java
@@ -393,7 +393,8 @@ public class S7Protocol extends ChannelDuplexHandler {
         buf.writeByte((byte) 0x0a);
         buf.writeByte(s7AnyRequestItem.getAddressingMode().getCode());
         buf.writeByte(s7AnyRequestItem.getDataType().getTypeCode());
-        buf.writeShort(s7AnyRequestItem.getNumElements());
+
+        buf.writeShort(encodeNumElements(s7AnyRequestItem));
         buf.writeShort(s7AnyRequestItem.getDataBlockNumber());
         buf.writeByte(s7AnyRequestItem.getMemoryArea().getCode());
         // A S7 address is 3 bytes long. Unfortunately the byte-offset is NOT located in
@@ -407,6 +408,21 @@ public class S7Protocol extends ChannelDuplexHandler {
                 | (s7AnyRequestItem.getBitOffset() & 0x07)));
     }
 
+    /**
+     * this is a workaround for the date and time types, as native requests with the datatypes are
+     * @return
+     */
+    private short encodeNumElements(S7AnyVarParameterItem s7AnyVarParameterItem){
+        switch (s7AnyVarParameterItem.getDataType()){
+            case DATE_AND_TIME:
+            case TIME_OF_DAY:
+                return (short) (s7AnyVarParameterItem.getNumElements()*s7AnyVarParameterItem.getDataType().getSizeInBytes());
+            default:
+                return (short) s7AnyVarParameterItem.getNumElements();
+        }
+
+    }
+
     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     // Decoding
     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/model/types/TransportSize.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/model/types/TransportSize.java
index 98f35d6..f5e03f1 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/model/types/TransportSize.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/model/types/TransportSize.java
@@ -84,18 +84,18 @@ public enum TransportSize {
     // -----------------------------------------
     // IEC date (yyyy-m-d)
     // TODO: Find the code
-    DATE(0x00, "X", 2, null, null, S7ControllerType.ANY),
+    DATE(0x02, "X", 4, null, DataTransportSize.BYTE_WORD_DWORD, S7ControllerType.ANY),
 
     // -----------------------------------------
     // Time of day
     // -----------------------------------------
     // Time (hh:mm:ss.S)
-    TIME_OF_DAY(0x0A, "X", 4, null, null, S7ControllerType.ANY),
+    TIME_OF_DAY(0x02, "X", 4, null, DataTransportSize.BYTE_WORD_DWORD,S7ControllerType.ANY),
 
     // -----------------------------------------
     // Date and time of day
     // -----------------------------------------
-    DATE_AND_TIME(0x0F, "X", 8, null, null, S7ControllerType.ANY),
+    DATE_AND_TIME(0x02, "X", 8,null, null, S7ControllerType.S7_1500, S7ControllerType.S7_300, S7ControllerType.S7_400),
 
     // -----------------------------------------
     // ASCII Strings
diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/strategies/DefaultS7MessageProcessor.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/strategies/DefaultS7MessageProcessor.java
index a89bd93..97425d8 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/strategies/DefaultS7MessageProcessor.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/strategies/DefaultS7MessageProcessor.java
@@ -405,6 +405,10 @@ public class DefaultS7MessageProcessor implements S7MessageProcessor {
                 if(requestItem.getNumElements() != responseParameterItem.getNumElements()) {
                     int itemSizeInBytes = requestItem.getDataType().getSizeInBytes();
                     int totalSizeInBytes = requestItem.getNumElements() * itemSizeInBytes;
+                    if(requestItem.getDataType().equals(TransportSize.DATE_AND_TIME)){
+                        totalSizeInBytes = requestItem.getNumElements() * itemSizeInBytes;
+                    }
+
                     if(varParameter.getType() == ParameterType.READ_VAR) {
                         byte[] data = new byte[totalSizeInBytes];
                         System.arraycopy(responsePayloadItem.getData(), 0, data, 0, responsePayloadItem.getData().length);