You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cg...@apache.org on 2024/03/15 02:45:40 UTC

(plc4x) branch fix/s7async updated: Fixed time base handling for cyclical subscriptions. Subscription routine for changes is added experimentally.

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

cgarcia pushed a commit to branch fix/s7async
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/fix/s7async by this push:
     new f8ca0f6226 Fixed time base handling for cyclical subscriptions. Subscription routine for changes is added experimentally.
f8ca0f6226 is described below

commit f8ca0f622680c2c99875331a183b31ec179619a9
Author: Cesar Garcia <ce...@ceos.com.ve>
AuthorDate: Thu Mar 14 22:45:06 2024 -0400

    Fixed time base handling for cyclical subscriptions. Subscription routine for changes is added experimentally.
---
 .../apache/plc4x/java/s7/events/S7CyclicEvent.java | 131 ++++++++-------------
 .../readwrite/protocol/S7ProtocolEventLogic.java   |  10 ++
 .../s7/readwrite/protocol/S7ProtocolLogic.java     |  28 ++++-
 .../readwrite/utils/S7PlcSubscriptionRequest.java  | 100 +++++++++++++++-
 .../org/apache/plc4x/java/spi/values/PlcBINT.java  |   5 +
 .../org/apache/plc4x/java/spi/values/PlcBOOL.java  |   5 +
 .../org/apache/plc4x/java/spi/values/PlcBREAL.java |   5 +
 .../org/apache/plc4x/java/spi/values/PlcBYTE.java  |   5 +
 .../org/apache/plc4x/java/spi/values/PlcCHAR.java  |   5 +
 .../org/apache/plc4x/java/spi/values/PlcDINT.java  |   5 +
 .../org/apache/plc4x/java/spi/values/PlcDWORD.java |   5 +
 .../org/apache/plc4x/java/spi/values/PlcINT.java   |   5 +
 .../org/apache/plc4x/java/spi/values/PlcLINT.java  |   5 +
 .../org/apache/plc4x/java/spi/values/PlcLREAL.java |   5 +
 .../org/apache/plc4x/java/spi/values/PlcLWORD.java |   5 +
 .../org/apache/plc4x/java/spi/values/PlcREAL.java  |   5 +
 .../plc4x/java/spi/values/PlcRawByteArray.java     |   5 +
 .../org/apache/plc4x/java/spi/values/PlcSINT.java  |   5 +
 .../org/apache/plc4x/java/spi/values/PlcUBINT.java |   5 +
 .../org/apache/plc4x/java/spi/values/PlcUDINT.java |   5 +
 .../org/apache/plc4x/java/spi/values/PlcUINT.java  |   5 +
 .../org/apache/plc4x/java/spi/values/PlcULINT.java |   5 +
 .../org/apache/plc4x/java/spi/values/PlcUSINT.java |   5 +
 .../org/apache/plc4x/java/spi/values/PlcWCHAR.java |   5 +
 .../org/apache/plc4x/java/spi/values/PlcWORD.java  |   5 +
 25 files changed, 280 insertions(+), 94 deletions(-)

diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7CyclicEvent.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7CyclicEvent.java
index b1ad3ff3ec..42e96ce34a 100644
--- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7CyclicEvent.java
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7CyclicEvent.java
@@ -106,34 +106,10 @@ public class S7CyclicEvent implements S7Event {
             int i = n[0];
             map.put(Fields.RETURNCODE_.name() + i, event.getItems().get(i).getReturnCode().getValue());
             map.put(Fields.TRANSPORTSIZE_.name() + i, event.getItems().get(i).getTransportSize().getValue());
-//            byte[] buffer = new byte[event.getItems().get(i).getData().size()];
-//            j = 0;
-                       
-//            event.getItems().get(i).getData().forEach(s -> {
-//                buffer[j] = s.byteValue();
-//                j++;
-//            });
             map.put(tagname, DataToPlcValue(tagname, request, event.getItems().get(i).getData()));
-            n[0]++;
-            
-
-            
-            
+            n[0]++;                                    
         });
 
-
-//        for (int i=0; i<event.getItemsCount(); i++){
-//            //map.put(Fields.RETURNCODE_.name()+i, event.getItems()[i].getReturnCode().getValue());
-//            map.put(Fields.RETURNCODE_.name()+i, event.getItems().get(i).getReturnCode().getValue());
-//            map.put(Fields.TRANSPORTSIZE_.name()+i, event.getItems().get(i).getTransportSize().getValue());
-//            byte[] buffer = new byte[event.getItems().get(i).getData().size()];
-//            j = 0;
-//            event.getItems().get(i).getData().forEach(s->{
-//                    buffer[j] = s.byteValue();
-//                    j ++;                
-//                });
-//            map.put(Fields.DATA_.name()+i, buffer);  
-//        }
     }
 
     public S7CyclicEvent(PlcSubscriptionRequest request, short jobid, S7PayloadUserDataItemCyclicServicesChangeDrivenPush event) {
@@ -145,30 +121,15 @@ public class S7CyclicEvent implements S7Event {
         map.put(Fields.JOBID.name(), jobid);
         map.put(Fields.ITEMSCOUNT.name(), event.getItemsCount());
         int[] n = new int[1];
+        
         request.getTagNames().forEach(tagname -> {
             int i = n[0];
             map.put(Fields.RETURNCODE_.name() + i, event.getItems().get(i).getReturnCode().getValue());
             map.put(Fields.TRANSPORTSIZE_.name() + i, event.getItems().get(i).getTransportSize().getValue());
-            byte[] buffer = new byte[event.getItems().get(i).getData().size()];
-            j = 0;
-            event.getItems().get(i).getData().forEach(s -> {
-                buffer[j] = s.byteValue();
-                j++;
-            });
-            map.put(tagname, buffer);
-            n[0]++;
+            map.put(tagname, DataToPlcValue(tagname, request, event.getItems().get(i).getData()));
+            n[0]++;                                    
         });
-//        for (int i=0; i<event.getItemsCount(); i++){
-//            map.put(Fields.RETURNCODE_.name()+i, event.getItems().get(i).getReturnCode().getValue());
-//            map.put(Fields.TRANSPORTSIZE_.name()+i, event.getItems().get(i).getTransportSize().getValue());
-//            byte[] buffer = new byte[event.getItems().get(i).getData().size()];
-//            j = 0;
-//            event.getItems().get(i).getData().forEach(s->{
-//                    buffer[j] = s.byteValue();
-//                    j ++;                
-//                });
-//            map.put(Fields.DATA_.name()+i, buffer);  
-//        }
+        
     }
 
     public S7CyclicEvent(PlcSubscriptionRequest request, short jobid, S7PayloadUserDataItemCyclicServicesSubscribeResponse event) {
@@ -180,30 +141,14 @@ public class S7CyclicEvent implements S7Event {
         map.put(Fields.JOBID.name(), jobid);
         map.put(Fields.ITEMSCOUNT.name(), event.getItemsCount());
         int[] n = new int[1];
+
         request.getTagNames().forEach(tagname -> {
             int i = n[0];
             map.put(Fields.RETURNCODE_.name() + i, event.getItems().get(i).getReturnCode().getValue());
             map.put(Fields.TRANSPORTSIZE_.name() + i, event.getItems().get(i).getTransportSize().getValue());
-            byte[] buffer = new byte[event.getItems().get(i).getData().size()];
-            j = 0;
-            event.getItems().get(i).getData().forEach(s -> {
-                buffer[j] = s.byteValue();
-                j++;
-            });
-            map.put(tagname, buffer);
-            n[0]++;
-        });
-//        for (int i=0; i<event.getItemsCount(); i++){
-//            map.put(Fields.RETURNCODE_.name()+i, event.getItems().get(i).getReturnCode().getValue());
-//            map.put(Fields.TRANSPORTSIZE_.name()+i, event.getItems().get(i).getTransportSize().getValue());
-//            byte[] buffer = new byte[event.getItems().get(i).getData().size()];
-//            j = 0;
-//            event.getItems().get(i).getData().forEach(s->{
-//                    buffer[j] = s.byteValue();
-//                    j ++;                
-//                });
-//            map.put(Fields.DATA_.name()+i, buffer); 
-//        }            
+            map.put(tagname, DataToPlcValue(tagname, request, event.getItems().get(i).getData()));
+            n[0]++;                                    
+        });       
     }
 
     public S7CyclicEvent(PlcSubscriptionRequest request, short jobid, S7PayloadUserDataItemCyclicServicesChangeDrivenSubscribeResponse event) {
@@ -215,30 +160,14 @@ public class S7CyclicEvent implements S7Event {
         map.put(Fields.JOBID.name(), jobid);
         map.put(Fields.ITEMSCOUNT.name(), event.getItemsCount());
         int[] n = new int[1];
+        
         request.getTagNames().forEach(tagname -> {
             int i = n[0];
             map.put(Fields.RETURNCODE_.name() + i, event.getItems().get(i).getReturnCode().getValue());
             map.put(Fields.TRANSPORTSIZE_.name() + i, event.getItems().get(i).getTransportSize().getValue());
-            byte[] buffer = new byte[event.getItems().get(i).getData().size()];
-            j = 0;
-            event.getItems().get(i).getData().forEach(s -> {
-                buffer[j] = s.byteValue();
-                j++;
-            });
-            map.put(tagname, buffer);
-            n[0]++;
-        });
-//        for (int i=0; i<event.getItemsCount(); i++){
-//            map.put(Fields.RETURNCODE_.name()+i, event.getItems().get(i).getReturnCode().getValue());
-//            map.put(Fields.TRANSPORTSIZE_.name()+i, event.getItems().get(i).getTransportSize().getValue());
-//            byte[] buffer = new byte[event.getItems().get(i).getData().size()];
-//            j = 0;
-//            event.getItems().get(i).getData().forEach(s->{
-//                    buffer[j] = s.byteValue();
-//                    j ++;                
-//                });
-//            map.put(Fields.DATA_.name()+i, buffer); 
-//        }            
+            map.put(tagname, DataToPlcValue(tagname, request, event.getItems().get(i).getData()));
+            n[0]++;                                    
+        });       
     }
 
     @Override
@@ -263,7 +192,12 @@ public class S7CyclicEvent implements S7Event {
 
     @Override
     public PlcValue getPlcValue(String name) {
-        throw new UnsupportedOperationException("Not supported yet.");
+        if (request.getTagNames().contains(name)) {
+            PlcValue plcvalue = (PlcValue) map.get(name);
+            plcvalue.getRaw();
+            return plcvalue;
+        }
+        return null;
     }
 
     @Override
@@ -813,7 +747,32 @@ public class S7CyclicEvent implements S7Event {
         throw new UnsupportedOperationException("Not supported yet.");
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final S7CyclicEvent other = (S7CyclicEvent) obj;
+        
+        for (String tag:request.getTagNames()) {
+            final PlcValue othervalue = other.getPlcValue(tag);
+            if (othervalue == null) return false;
+            final PlcValue localvalue = (PlcValue) getPlcValue(tag);
+            if (Arrays.equals(localvalue.getRaw(), othervalue.getRaw()) == false){
+                return false;
+            }
+        };
+        
+        return true;
+    }
     
+        
     private static PlcValue DataToPlcValue(String tagname, PlcSubscriptionRequest request, List<Short> data){
         
         int[] i = new int[1];
@@ -930,6 +889,8 @@ public class S7CyclicEvent implements S7Event {
         return plcValue;
         
     };
+
+
     
     
 }
diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolEventLogic.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolEventLogic.java
index 0191660ceb..3288a2d77b 100644
--- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolEventLogic.java
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolEventLogic.java
@@ -114,6 +114,8 @@ public class S7ProtocolEventLogic implements PlcSubscriber {
         mapConsumers.remove(registration);
     }
 
+    
+    //TODO: Replace with disruptor
     private class ObjectProcessor implements Runnable {
 
         private final BlockingQueue<S7Event> eventQueue;
@@ -154,6 +156,7 @@ public class S7ProtocolEventLogic implements PlcSubscriber {
         }
     }
 
+    //TODO: Replace with disruptor    
     private class EventDispatcher implements Runnable {
         private final BlockingQueue<S7Event> dispatchQueue;
         private boolean shutdown = false;
@@ -173,6 +176,10 @@ public class S7ProtocolEventLogic implements PlcSubscriber {
             while (!shutdown) {
                 try {
                     S7Event s7Event = dispatchQueue.poll(DEFAULT_DELAY, TimeUnit.MILLISECONDS);
+                    if ((s7Event == null) && (cycDelayedObject != null)) {
+                        s7Event = cycDelayedObject;
+                        cycDelayedObject = null;
+                    }
                     if (s7Event != null) {
                         if (s7Event instanceof S7ModeEvent) {
                             S7ModeEvent modeEvent = (S7ModeEvent) s7Event;
@@ -202,10 +209,13 @@ public class S7ProtocolEventLogic implements PlcSubscriber {
                             S7CyclicEvent cyclicEvent = (S7CyclicEvent) s7Event;
                             if (mapIndex.containsKey(EventType.CYC)) {
                                 Map<PlcConsumerRegistration, Consumer<PlcSubscriptionEvent>> mapConsumers = mapIndex.get(EventType.CYC);
+
                                 if (cycDelayedObject != null) {
                                     mapConsumers.forEach((x, y) -> y.accept(cycDelayedObject));
                                     cycDelayedObject = null;
                                 }
+                                if (mapConsumers.isEmpty()) cycDelayedObject = s7Event;
+                                
                                 mapConsumers.forEach((x, y) -> {
                                     S7PlcSubscriptionHandle sh = (S7PlcSubscriptionHandle) x.getSubscriptionHandles().get(0);
                                     Short id = Short.parseShort(sh.getEventId());
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 e758d2fb85..b161770abf 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
@@ -63,6 +63,7 @@ import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
+import org.apache.plc4x.java.api.types.PlcSubscriptionType;
 import org.apache.plc4x.java.s7.events.S7AlarmEvent;
 import org.apache.plc4x.java.s7.events.S7ModeEvent;
 import org.apache.plc4x.java.s7.events.S7SysEvent;
@@ -114,7 +115,14 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> {
      * the values sent PUSH from the PLC to the driver refer to this JobID.
      */
     private final Map<Short, PlcSubscriptionRequest> cycRequests = new HashMap<>();
-
+    
+    /*
+    * This data structure stores the last value associated with a cyclic 
+    * subscription request. In each event received, the values of the internal 
+    * PlcValue are compared and if any of them are different, the new value is 
+    * transferred to the event stack and the value is updated in this HashMap.
+    */
+    private final Map<Short, S7CyclicEvent> cycChangeValueEvents = new HashMap<>();
 
     private S7DriverContext s7DriverContext;
     private RequestTransactionManager tm;
@@ -800,6 +808,10 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> {
             S7CyclicEvent cycEvent = new S7CyclicEvent(plcSubscriptionRequest,
                 msgParameter.getSequenceNumber(),
                 (S7PayloadUserDataItemCyclicServicesSubscribeResponse) payloadItems.get(0));
+            
+            if (plcSubscriptionRequest.getTags().get(0).getPlcSubscriptionType() == PlcSubscriptionType.CHANGE_OF_STATE) {
+                cycChangeValueEvents.put(msgParameter.getSequenceNumber(), cycEvent);
+            }
 
             eventQueue.add(cycEvent);
 
@@ -1526,7 +1538,7 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> {
                                                 
                     } else if ((myParameter.getCpuFunctionType() == 0x00) && (myParameter.getCpuSubfunction() == 0x13)) {
                         //TODO: Requires reverse engineering.
-                    } else if ((myParameter.getCpuFunctionGroup() == 0x02) && (myParameter.getCpuFunctionType() == 0x00) && (myParameter.getCpuSubfunction() == 0x01)) { //(05)
+                    } else if (((myParameter.getCpuFunctionGroup() == 0x02) && (myParameter.getCpuFunctionType() == 0x00) && (myParameter.getCpuSubfunction() == 0x01))) { //(05)
 
                         S7ParameterUserDataItemCPUFunctions parameterItem =
                             (S7ParameterUserDataItemCPUFunctions)
@@ -1539,7 +1551,17 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> {
                         S7CyclicEvent cycEvent = new S7CyclicEvent(cycRequests.get(parameterItem.getSequenceNumber()),
                             parameterItem.getSequenceNumber(),
                             payloadItem);
-                        eventQueue.add(cycEvent);
+                        
+                        if (cycChangeValueEvents.containsKey(parameterItem.getSequenceNumber())){
+                            S7CyclicEvent lastCycEvent = cycChangeValueEvents.get(parameterItem.getSequenceNumber());
+                            if (cycEvent.equals(lastCycEvent ) == false) {
+                                cycChangeValueEvents.replace(parameterItem.getSequenceNumber(), cycEvent);
+                                eventQueue.add(cycEvent);                                
+                            }
+                            
+                        } else {
+                            eventQueue.add(cycEvent);
+                        }
 
                     } else if ((myParameter.getCpuFunctionGroup() == 0x02) && (myParameter.getCpuFunctionType() == 0x00) && (myParameter.getCpuSubfunction() == 0x05)) { //(06)
                         
diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7PlcSubscriptionRequest.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7PlcSubscriptionRequest.java
index 16d0f59096..a0c06cb6cf 100644
--- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7PlcSubscriptionRequest.java
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7PlcSubscriptionRequest.java
@@ -41,6 +41,7 @@ import static org.apache.plc4x.java.s7.readwrite.TimeBase.B01SEC;
 import org.apache.plc4x.java.s7.readwrite.tag.S7SubscriptionTag;
 import org.apache.plc4x.java.s7.readwrite.tag.S7Tag;
 import org.apache.plc4x.java.s7.readwrite.types.S7SubscriptionType;
+import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionRequest;
 import org.apache.plc4x.java.spi.messages.PlcSubscriber;
 
 public class S7PlcSubscriptionRequest implements PlcSubscriptionRequest, Serializable {
@@ -123,6 +124,27 @@ public class S7PlcSubscriptionRequest implements PlcSubscriptionRequest, Seriali
             this.preRegisteredConsumers = new LinkedHashMap<>();
         }
 
+        /*
+        * This method receives a String that describes an S7Tag and the 
+        * interval required for its sampling.
+        * The value of the "pollingInterval" parameter is adapted to the 
+        * cyclical subscription requirements of an S7-300/S7-400, 
+        * for which multiples of the time base given by TimeBase 
+        * must be handled. To say:
+        *
+        * . B01SEC -> 100, 200, 300, 400, 500, 600, 700, 800, 900 msec
+        * . B1SEC  ->   1,   2,   3,   4,   5,   6,   7,   8,   9 sec
+        * . B10SEC ->  10,  20,  30,  40,  50,  60,  70,  80,  90 sec
+        *
+        * As you can see there are no intermediate values, for example 513 msec,
+        * it will actually be 500 msec, or its nearest rounding.
+        * 
+        * @param name Name of the subscription Tag.
+        * @param tagAddress String representing an S7Tag
+        * @param pollingInterval Required sampling rate based on the "TimeBase"  
+        * @return PlcSubscriptionRequest.Builder S7SubscriptonTag type constructor        
+        * 
+        */
         @Override
         public PlcSubscriptionRequest.Builder addCyclicTagAddress(String name, String tagAddress, Duration pollingInterval) {
             if (tags.containsKey(name)) {
@@ -136,6 +158,16 @@ public class S7PlcSubscriptionRequest implements PlcSubscriptionRequest, Seriali
             return this;
         }
 
+        /*
+        * This method receives an S7Tag built by the user, he is responsible 
+        * for the construction of the object, so no additional verification 
+        * is included.
+        *
+        * @param name Name of the subscription Tag.
+        * @param tag    Tag of S7SubscriptionTag type.
+        * @param pollingInterval Required sampling rate based on the "TimeBase"
+        * @return PlcSubscriptionRequest.Builder S7SubscriptonTag type constructor
+        */
         @Override
         public PlcSubscriptionRequest.Builder addCyclicTag(String name, PlcTag tag, Duration pollingInterval) {
             if (tags.containsKey(name)) {
@@ -148,16 +180,63 @@ public class S7PlcSubscriptionRequest implements PlcSubscriptionRequest, Seriali
             return this;
         }
 
+        /*
+        *
+        */
         @Override
         public PlcSubscriptionRequest.Builder addChangeOfStateTagAddress(String name, String tagAddress) {
-            throw new PlcRuntimeException("Feature currently not supported.");
+            if (tags.containsKey(name)) {
+                throw new PlcRuntimeException("Duplicate tag definition '" + name + "'");
+            }
+            S7Tag[] s7tags = new S7Tag[]{S7Tag.of(tagAddress)};   
+            S7SubscriptionTag tag = new S7SubscriptionTag(S7SubscriptionType.CYCLIC_SUBSCRIPTION, s7tags, TimeBase.B01SEC, (short) 1);            
+            tags.put(name, new BuilderItem(() -> tag, PlcSubscriptionType.CHANGE_OF_STATE));
+            return this;
         }
 
+        /*
+        *
+        */        
         @Override
         public PlcSubscriptionRequest.Builder addChangeOfStateTag(String name, PlcTag tag) {
-            throw new PlcRuntimeException("Feature currently not supported.");
+            if (tags.containsKey(name)) {
+                throw new PlcRuntimeException("Duplicate tag definition '" + name + "'");
+            }
+            if ((tag instanceof S7SubscriptionTag) == false){
+                throw new PlcRuntimeException("Tag is not of type S7SubcriptionTag");                
+            }              
+            tags.put(name, new BuilderItem(() -> tag, PlcSubscriptionType.CHANGE_OF_STATE));
+            return this;
         }
 
+        /*
+        * This method is responsible for the subscription to Events associated 
+        * with the PLC as well as the preliminary version of cyclical 
+        * subscription of values.
+        *
+        * The type of function performed by the tag is given by the definition 
+        * of the "tagAddress", for example:
+        *
+        * "ACK:16#12345678"
+        *
+        * Represents an acknowledgment of an alarm whose ID is 16#12345678.
+        * The following functions are defined:
+        *
+        * . MODE
+        * . SYS
+        * . USR
+        * . ALM
+        * . ACK
+        * . QUERY
+        * . CYC
+        * . CANCEL
+        * 
+        * Go to the driver manual for a complete description.
+        * 
+        * @param name Name of the subscription Tag.
+        * @param tag    Tag of S7SubscriptionTag type.        
+        * @return PlcSubscriptionRequest.Builder S7SubscriptonTag type constructor
+        */
         @Override
         public PlcSubscriptionRequest.Builder addEventTagAddress(String name, String tagAddress) {
             if (tags.containsKey(name)) {
@@ -171,6 +250,15 @@ public class S7PlcSubscriptionRequest implements PlcSubscriptionRequest, Seriali
             return this;
         }
 
+        /*
+        * This method receives an S7Tag built by the user, he is responsible 
+        * for the construction of the object, so no additional verification 
+        * is included.
+        *
+        * @param name Name of the subscription Tag.
+        * @param tag    Tag of S7SubscriptionTag type.
+        * @return PlcSubscriptionRequest.Builder S7SubscriptonTag type constructor        
+        */
         @Override
         public PlcSubscriptionRequest.Builder addEventTag(String name, PlcTag tag) {
             if (tags.containsKey(name)) {
@@ -228,15 +316,15 @@ public class S7PlcSubscriptionRequest implements PlcSubscriptionRequest, Seriali
                 throw new PlcRuntimeException("Subscription time cannot be zero.");                
             }
             long millis = duration.toMillis();
-            if (millis <= 25500) {
+            if (millis < 1000) {
                 return TimeBase.B01SEC;
-            }  if (millis <= 255000) {
+            }  if (millis < 10000) {
                 return TimeBase.B1SEC;                
-            }   if (millis <= 2550000) {
+            }   if (millis < 100000) {
                 return TimeBase.B10SEC;  
             }
             
-            throw new PlcRuntimeException("The maximum subscription time of 2550 sec.");             
+            throw new PlcRuntimeException("The maximum subscription time is 90 sec.");             
         }
         
         //TODO: Chek multiplier is 1-99 in BCD??
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBINT.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBINT.java
index cd8b739a96..e3295121f8 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBINT.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBINT.java
@@ -210,6 +210,11 @@ public class PlcBINT extends PlcIECValue<BigInteger> {
     public String toString() {
         return value.toString();
     }
+    
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
 
     public byte[] getBytes() {
         byte[] tmp = value.toByteArray();
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBOOL.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBOOL.java
index b1f900a9ad..229c805de9 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBOOL.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBOOL.java
@@ -253,6 +253,11 @@ public class PlcBOOL extends PlcIECValue<Boolean> {
         return toString();
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }
+        
     public byte[] getBytes() {
         return ((value != null) && value) ? new byte[]{0x01} : new byte[]{0x00};
     }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBREAL.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBREAL.java
index 6b67ea4f3c..6f73a709e6 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBREAL.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBREAL.java
@@ -211,6 +211,11 @@ public class PlcBREAL extends PlcIECValue<BigDecimal> {
         return value.toString();
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         // TODO: Not sure if this is correct ...
         byte[] tmp = value.unscaledValue().toByteArray();
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBYTE.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBYTE.java
index 76f67e75c1..72370dee5a 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBYTE.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBYTE.java
@@ -266,6 +266,11 @@ public class PlcBYTE extends PlcIECValue<Short> {
     public String toString() {
         return Short.toString(value);
     }
+    
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
 
     public byte[] getBytes() {
         byte[] bytes = new byte[1];
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcCHAR.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcCHAR.java
index 7f24576d0d..19a4727e0f 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcCHAR.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcCHAR.java
@@ -300,6 +300,11 @@ public class PlcCHAR extends PlcIECValue<Short> {
         return Character.toString((char) ((short) value));
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         byte[] bytes = new byte[1];
         bytes[0] = (byte) (value & 0xff);
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcDINT.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcDINT.java
index b0f2abd170..dc587043a9 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcDINT.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcDINT.java
@@ -240,6 +240,11 @@ public class PlcDINT extends PlcIECValue<Integer> {
         return Integer.toString(value);
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         return new byte[]{
             (byte) ((value >> 24) & 0xff),
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcDWORD.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcDWORD.java
index 64ac2e0575..e33a6f9f25 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcDWORD.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcDWORD.java
@@ -267,6 +267,11 @@ public class PlcDWORD extends PlcIECValue<Long> {
         return Long.toString(value);
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         byte[] bytes = new byte[4];
         bytes[0] = (byte) ((value >> 24) & 0xff);
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcINT.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcINT.java
index 874ad6fb2a..93caa2ce75 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcINT.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcINT.java
@@ -244,6 +244,11 @@ public class PlcINT extends PlcIECValue<Short> {
         return Integer.toString(value);
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         return new byte[]{
             (byte) ((value >> 8) & 0xff),
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLINT.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLINT.java
index 3b6e4d5ebf..1e1ecfac0f 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLINT.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLINT.java
@@ -237,6 +237,11 @@ public class PlcLINT extends PlcIECValue<Long> {
         return Long.toString(value);
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         return new byte[]{
             (byte) ((value >> 56) & 0xff),
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLREAL.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLREAL.java
index a407405f10..2d440d847b 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLREAL.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLREAL.java
@@ -227,6 +227,11 @@ public class PlcLREAL extends PlcIECValue<Double> {
         return Double.toString(value);
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         long longBits = Double.doubleToRawLongBits(value);
         return new byte[]{
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLWORD.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLWORD.java
index acba299118..c0b055a6a0 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLWORD.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcLWORD.java
@@ -277,6 +277,11 @@ public class PlcLWORD extends PlcIECValue<BigInteger> {
         return value.toString();
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         byte[] tmp = value.toByteArray();
         byte[] bytes = new byte[8];
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcREAL.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcREAL.java
index bef0e744c5..f919cacecb 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcREAL.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcREAL.java
@@ -229,6 +229,11 @@ public class PlcREAL extends PlcIECValue<Float> {
     public String toString() {
         return Float.toString(value);
     }
+    
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
 
     public byte[] getBytes() {
         int intBits = Float.floatToIntBits(value);
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcRawByteArray.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcRawByteArray.java
index cb1fc54cee..65605d8a47 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcRawByteArray.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcRawByteArray.java
@@ -46,6 +46,11 @@ public class PlcRawByteArray extends PlcIECValue<byte[]> {
     public String toString() {
         return Hex.encodeHexString(value);
     }
+    
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
 
     public byte[] getBytes() {
         return value;
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcSINT.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcSINT.java
index 852da57090..162f430caa 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcSINT.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcSINT.java
@@ -246,6 +246,11 @@ public class PlcSINT extends PlcIECValue<Byte> {
         return Byte.toString(value);
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         byte[] bytes = new byte[1];
         bytes[0] = (byte) (value & 0xff);
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUBINT.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUBINT.java
index 772314204c..e688f9075c 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUBINT.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUBINT.java
@@ -266,6 +266,11 @@ public class PlcUBINT extends PlcIECValue<BigInteger> {
         return value.toString();
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         byte[] tmp = value.toByteArray();
         byte[] bytes = new byte[8];
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUDINT.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUDINT.java
index 861fd75a32..ede2fa8a03 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUDINT.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUDINT.java
@@ -259,6 +259,11 @@ public class PlcUDINT extends PlcIECValue<Long> {
         return Long.toString(value);
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         byte[] bytes = new byte[4];
         bytes[0] = (byte) ((value >> 24) & 0xff);
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUINT.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUINT.java
index 8a82ea2359..ee7a66c4f5 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUINT.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUINT.java
@@ -253,6 +253,11 @@ public class PlcUINT extends PlcIECValue<Integer> {
         return Integer.toString(value);
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         return new byte[]{
             (byte) ((value >> 8) & 0xff),
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcULINT.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcULINT.java
index 589f06114c..f9107815ae 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcULINT.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcULINT.java
@@ -267,6 +267,11 @@ public class PlcULINT extends PlcIECValue<BigInteger> {
         return value.toString();
     }
 
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
+    
     public byte[] getBytes() {
         byte[] tmp = value.toByteArray();
         byte[] bytes = new byte[8];
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUSINT.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUSINT.java
index 23346642c7..d6f16e8b2b 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUSINT.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcUSINT.java
@@ -255,6 +255,11 @@ public class PlcUSINT extends PlcIECValue<Short> {
     public String toString() {
         return Short.toString(value);
     }
+    
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
 
     public byte[] getBytes() {
         byte[] bytes = new byte[1];
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcWCHAR.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcWCHAR.java
index d72ac5b042..5e787a3db5 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcWCHAR.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcWCHAR.java
@@ -274,6 +274,11 @@ public class PlcWCHAR extends PlcIECValue<Integer> {
     public String toString() {
         return Character.toString((char) ((int) value));
     }
+    
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
 
     public byte[] getBytes() {
         return new byte[]{
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcWORD.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcWORD.java
index a227dc41e0..9003db7753 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcWORD.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcWORD.java
@@ -262,6 +262,11 @@ public class PlcWORD extends PlcIECValue<Integer> {
     public String toString() {
         return Integer.toString(value);
     }
+    
+    @Override
+    public byte[] getRaw() {
+        return getBytes();
+    }    
 
     public byte[] getBytes() {
         return new byte[]{