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 2019/12/17 20:13:47 UTC

[plc4x] 02/02: - Continued porting the S7 driver to fully generated drivers.

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

cdutz pushed a commit to branch next-gen-core
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit ff91b0504084471612499fc478ac8721968bc210
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Tue Dec 17 21:13:37 2019 +0100

    - Continued porting the S7 driver to fully generated drivers.
---
 .../language/java/JavaLanguageTemplateHelper.java  |  24 +
 .../main/resources/templates/java/io-template.ftlh |  13 +-
 plc4j/examples/hello-world-plc4x/pom.xml           |   7 +
 plc4j/pom.xml                                      |   1 +
 .../org/apache/plc4x/java/utils/ReadBuffer.java    |   8 +-
 .../s7/src/main/resources/protocols/s7/s7.mspec    |  38 +-
 .../server/s7/protocol/S7Step7ServerProtocol.java  |   2 +-
 sandbox/test-java-s7-driver/pom.xml                |  23 +
 .../apache/plc4x/java/s7/readwrite/S7Driver.java   |  74 +++
 .../java/s7/readwrite/connection/S7Connection.java | 224 ++++++++
 .../s7/readwrite/events/IsoTPConnectedEvent.java   |  22 +
 .../java/s7/readwrite/events/S7ConnectedEvent.java |  22 +
 .../java/s7/readwrite/protocol/Plc4xProtocol.java  | 619 +++++++++++++++++++++
 .../java/s7/readwrite/protocol/S7Protocol.java     |  67 +++
 .../java/s7/readwrite/types/S7ControllerType.java  |  30 +
 .../plc4x/java/s7/readwrite/utils/S7Field.java     | 265 +++++++++
 .../java/s7/readwrite/utils/S7PlcFieldHandler.java | 569 +++++++++++++++++++
 .../java/s7/readwrite/utils/S7TsapIdEncoder.java   |  48 ++
 .../services/org.apache.plc4x.java.spi.PlcDriver   |  19 +
 19 files changed, 2056 insertions(+), 19 deletions(-)

diff --git a/build-utils/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java b/build-utils/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
index 2804d4b..307471b 100644
--- a/build-utils/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
+++ b/build-utils/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
@@ -193,6 +193,30 @@ public class JavaLanguageTemplateHelper implements FreemarkerLanguageTemplateHel
         return "Hurz";
     }
 
+    public int getNumBits(SimpleTypeReference simpleTypeReference) {
+        switch (simpleTypeReference.getBaseType()) {
+            case BIT: {
+                return 1;
+            }
+            case UINT:
+            case INT: {
+                IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                return integerTypeReference.getSizeInBits();
+            }
+            case FLOAT: {
+                FloatTypeReference floatTypeReference = (FloatTypeReference) simpleTypeReference;
+                return floatTypeReference.getSizeInBits();
+            }
+            case STRING: {
+                IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference;
+                return integerTypeReference.getSizeInBits();
+            }
+            default: {
+                return 0;
+            }
+        }
+    }
+
     public String getReadBufferReadMethodCall(SimpleTypeReference simpleTypeReference) {
         switch (simpleTypeReference.getBaseType()) {
             case BIT: {
diff --git a/build-utils/language-java/src/main/resources/templates/java/io-template.ftlh b/build-utils/language-java/src/main/resources/templates/java/io-template.ftlh
index aa26563..9866247 100644
--- a/build-utils/language-java/src/main/resources/templates/java/io-template.ftlh
+++ b/build-utils/language-java/src/main/resources/templates/java/io-template.ftlh
@@ -73,10 +73,13 @@ public class ${typeName}IO {
         if(${helper.toDeserializationExpression(field.loopExpression, type.parserArguments)?no_esc} > Integer.MAX_VALUE) {
             throw new ParseException("Array count of " + (${helper.toDeserializationExpression(field.loopExpression, type.parserArguments)?no_esc}) + " exceeds the maximum allowed count of " + Integer.MAX_VALUE);
         }
-        int _${field.name}Count = (int) ${helper.toDeserializationExpression(field.loopExpression, type.parserArguments)?no_esc};
-        ${helper.getLanguageTypeNameForField(field)}[] ${field.name} = new ${helper.getLanguageTypeNameForField(field)}[_${field.name}Count];
-        for(int i = 0; i < _${field.name}Count; i++) {
-            ${field.name}[i] = <#if helper.isSimpleType(field.type)>${helper.getReadBufferReadMethodCall(field.type)?no_esc}<#else>${field.type.name}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>(${helper.getArgumentType(field.type, parserArgument?index)}) (${helper.toDeserializationExpression(parserArgument, type.parserArguments)?no_esc})<#sep>, </#sep></#list></#if>)</#if>;
+        ${helper.getLanguageTypeNameForField( field)}[] ${field.name};
+        {
+            int itemCount = (int) ${helper.toDeserializationExpression(field.loopExpression, type.parserArguments)?no_esc};
+            ${field.name} = new ${helper.getLanguageTypeNameForField(field)}[itemCount];
+            for(int curItem = 0; curItem < itemCount; curItem++) {
+                ${field.name}[curItem] = <#if helper.isSimpleType(field.type)>${helper.getReadBufferReadMethodCall(field.type)?no_esc}<#else>${field.type.name}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>(${helper.getArgumentType(field.type, parserArgument?index)}) (${helper.toDeserializationExpression(parserArgument, type.parserArguments)?no_esc})<#sep>, </#sep></#list></#if>)</#if>;
+            }
         }
         <#-- In all other cases do we have to work with a list, that is later converted to an array -->
         <#else>
@@ -230,7 +233,7 @@ public class ${typeName}IO {
     <#case "padding">
 
         // Padding Field (${field.name})
-        boolean _${field.name}NeedsPadding = (boolean) (${helper.toDeserializationExpression(field.paddingCondition, type.parserArguments)});
+        boolean _${field.name}NeedsPadding = (boolean) ((io.hasMore(${helper.getNumBits(field.type)})) && (${helper.toDeserializationExpression(field.paddingCondition, type.parserArguments)}));
         if(_${field.name}NeedsPadding) {
             // Just read the padding data and ignore it
             ${helper.getReadBufferReadMethodCall(field.type)?no_esc};
diff --git a/plc4j/examples/hello-world-plc4x/pom.xml b/plc4j/examples/hello-world-plc4x/pom.xml
index f2e9aa5..0228ec2 100644
--- a/plc4j/examples/hello-world-plc4x/pom.xml
+++ b/plc4j/examples/hello-world-plc4x/pom.xml
@@ -104,6 +104,12 @@
       <version>0.6.0-SNAPSHOT</version>
       <scope>runtime</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x.sandbox</groupId>
+      <artifactId>test-java-s7-driver</artifactId>
+      <version>0.6.0-SNAPSHOT</version>
+      <scope>runtime</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -120,6 +126,7 @@
             <usedDependency>org.apache.plc4x:plc4j-driver-opcua</usedDependency>
             <usedDependency>org.apache.plc4x:plc4j-driver-s7</usedDependency>
             <usedDependency>org.apache.plc4x:plc4j-driver-simulated</usedDependency>
+            <usedDependency>org.apache.plc4x.sandbox:test-java-s7-driver</usedDependency>
             <usedDependency>org.slf4j:log4j-over-slf4j</usedDependency>
           </usedDependencies>
         </configuration>
diff --git a/plc4j/pom.xml b/plc4j/pom.xml
index 080ce71..1da9579 100644
--- a/plc4j/pom.xml
+++ b/plc4j/pom.xml
@@ -37,6 +37,7 @@
     <module>api</module>
     <module>drivers</module>
     <module>utils</module>
+    <module>protocols</module>
 
     <module>examples</module>
     <module>integrations</module>
diff --git a/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/ReadBuffer.java b/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/ReadBuffer.java
index e4fd993..10db633 100644
--- a/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/ReadBuffer.java
+++ b/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/ReadBuffer.java
@@ -30,6 +30,7 @@ public class ReadBuffer {
 
     private final MyDefaultBitInput bi;
     private final boolean littleEndian;
+    private final long totalBytes;
 
     public ReadBuffer(byte[] input) {
         this(input, true);
@@ -37,14 +38,19 @@ public class ReadBuffer {
 
     public ReadBuffer(byte[] input, boolean littleEndian) {
         ArrayByteInput abi = new ArrayByteInput(input);
-        bi = new MyDefaultBitInput(abi);
+        this.bi = new MyDefaultBitInput(abi);
         this.littleEndian = littleEndian;
+        this.totalBytes = input.length * 8;
     }
 
     public int getPos() {
         return (int) bi.getPos();
     }
 
+    public boolean hasMore(int numBits) {
+        return (numBits / 8) > (totalBytes - getPos());
+    }
+
     public byte peekByte(int offset) throws ParseException {
         // Remember the old index.
         int oldIndex = bi.getDelegate().getIndex();
diff --git a/protocols/s7/src/main/resources/protocols/s7/s7.mspec b/protocols/s7/src/main/resources/protocols/s7/s7.mspec
index 31b588c..9867c69 100644
--- a/protocols/s7/src/main/resources/protocols/s7/s7.mspec
+++ b/protocols/s7/src/main/resources/protocols/s7/s7.mspec
@@ -228,23 +228,23 @@
 
 // This is actually not quite correct as depending pon the transportSize the length is either defined in bits or bytes.
 [type 'S7VarPayloadDataItem'
-    [simple  uint 8             'returnCode']
-    [enum    DataTransportSize  'transportSize']
-    [simple  uint 16            'dataLength']
-    [array   uint 8             'data' count 'dataLength / 8']
-    [padding uint 8             'pad' '0x00' '(dataLength / 8) % 2 == 1']
+    [enum    DataTransportErrorCode 'returnCode']
+    [enum    DataTransportSize      'transportSize']
+    [simple  uint 16                'dataLength']
+    [array   int  8                 'data' count 'dataLength / 8']
+    [padding uint 8                 'pad' '0x00' '(dataLength / 8) % 2 == 1']
 ]
 
 [type 'S7VarPayloadStatusItem'
-    [simple uint 8 'returnCode']
+    [enum DataTransportErrorCode 'returnCode']
 ]
 
 [discriminatedType 'S7PayloadUserDataItem' [uint 4 'cpuFunctionType']
-    [simple   uint 8            'returnCode']
-    [enum     DataTransportSize 'transportSize']
-    [implicit uint 16           'dataLength' 'lengthInBytes - 4']
-    [simple   SzlId             'szlId']
-    [simple   uint 16           'szlIndex']
+    [enum     DataTransportErrorCode 'returnCode']
+    [enum     DataTransportSize      'transportSize']
+    [implicit uint 16                'dataLength' 'lengthInBytes - 4']
+    [simple   SzlId                  'szlId']
+    [simple   uint 16                'szlIndex']
     [typeSwitch 'cpuFunctionType'
         ['0x04' S7PayloadUserDataItemCpuFunctionReadSzlRequest
         ]
@@ -256,7 +256,6 @@
     ]
 ]
 
-
 [enum int 8 'COTPTpduSize' [uint 8 'sizeInBytes']
     ['0x07' SIZE_128 ['128']]
     ['0x08' SIZE_256 ['256']]
@@ -285,6 +284,12 @@
     ['0x09' OCTET_STRING    ['false']]
 ]
 
+[enum int 8 'DeviceGroup'
+    ['0x01' PG_OR_PC]
+    ['0x02' OS      ]
+    ['0x03' OTHERS  ]
+]
+
 [enum int 8 'TransportSize'  [uint 8 'sizeCode', uint 8 'sizeInBytes', TransportSize 'baseType', DataTransportSize 'dataTransportSize']
     ['0x01' BOOL             ['X'              , '1'                 , 'null'                  , 'DataTransportSize.BIT']]
     ['0x02' BYTE             ['B'              , '1'                 , 'null'                  , 'DataTransportSize.BYTE_WORD_DWORD']]
@@ -334,6 +339,15 @@
     ['0x09' OCTET_STRING        ['false']]
 ]
 
+[enum int 8 'DataTransportErrorCode'
+    ['0x00' RESERVED               ]
+    ['0xFF' OK                     ]
+    ['0x03' ACCESS_DENIED          ]
+    ['0x05' INVALID_ADDRESS        ]
+    ['0x06' DATA_TYPE_NOT_SUPPORTED]
+    ['0x0A' NOT_FOUND              ]
+]
+
 [enum int 4 'SzlModuleTypeClass'
     ['0x0' CPU]
     ['0x4' IM]
diff --git a/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/protocol/S7Step7ServerProtocol.java b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/protocol/S7Step7ServerProtocol.java
index a166538..35a3142 100644
--- a/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/protocol/S7Step7ServerProtocol.java
+++ b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/protocol/S7Step7ServerProtocol.java
@@ -177,7 +177,7 @@ public class S7Step7ServerProtocol extends ChannelInboundHandlerAdapter {
 
                                                 S7PayloadUserDataItemCpuFunctionReadSzlResponse readSzlResponsePayload =
                                                     new S7PayloadUserDataItemCpuFunctionReadSzlResponse(
-                                                        (short) 0xFF, DataTransportSize.OCTET_STRING, szlId,
+                                                        DataTransportErrorCode.OK, DataTransportSize.OCTET_STRING, szlId,
                                                         readSzlRequestPayload.getSzlIndex(), items);
 
                                                 S7ParameterUserDataItem[] responseParameterItems =
diff --git a/sandbox/test-java-s7-driver/pom.xml b/sandbox/test-java-s7-driver/pom.xml
index 1cf9911..880f753 100644
--- a/sandbox/test-java-s7-driver/pom.xml
+++ b/sandbox/test-java-s7-driver/pom.xml
@@ -56,15 +56,38 @@
   <dependencies>
     <dependency>
       <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-api</artifactId>
+      <version>0.6.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
       <artifactId>plc4j-utils-driver-base-java</artifactId>
       <version>0.6.0-SNAPSHOT</version>
     </dependency>
     <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-protocol-driver-base</artifactId>
+      <version>0.6.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-protocol-driver-base-tcp</artifactId>
+      <version>0.6.0-SNAPSHOT</version>
+    </dependency>
+
+    <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-annotations</artifactId>
     </dependency>
 
     <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>osgi.cmpn</artifactId>
+      <version>6.0.0</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
       <groupId>commons-codec</groupId>
       <artifactId>commons-codec</artifactId>
       <scope>test</scope>
diff --git a/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/S7Driver.java b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/S7Driver.java
new file mode 100644
index 0000000..6daf337
--- /dev/null
+++ b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/S7Driver.java
@@ -0,0 +1,74 @@
+/*
+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.api.PlcConnection;
+import org.apache.plc4x.java.api.authentication.PlcAuthentication;
+import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
+import org.apache.plc4x.java.s7.readwrite.connection.S7Connection;
+import org.apache.plc4x.java.spi.PlcDriver;
+import org.osgi.service.component.annotations.Component;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Component(service = PlcDriver.class, immediate = true)
+public class S7Driver implements PlcDriver {
+
+    private static final Pattern S7_URI_PATTERN = Pattern.compile("^s7ng://(?<host>.*)(?<params>\\?.*)?");
+
+    @Override
+    public String getProtocolCode() {
+        return "s7ng";
+    }
+
+    @Override
+    public String getProtocolName() {
+        return "Siemens S7 (Basic)";
+    }
+
+    @Override
+    public PlcConnection connect(String url) throws PlcConnectionException {
+        Matcher matcher = S7_URI_PATTERN.matcher(url);
+        if (!matcher.matches()) {
+            throw new PlcConnectionException(
+                "Connection url doesn't match the format 's7ng://{host|ip}'");
+        }
+        String host = matcher.group("host");
+
+        String params = matcher.group("params") != null ? matcher.group("params").substring(1) : null;
+
+        try {
+            InetAddress serverInetAddress = InetAddress.getByName(host);
+            return new S7Connection(serverInetAddress, params);
+        } catch (UnknownHostException e) {
+            throw new PlcConnectionException("Error parsing address", e);
+        } catch (Exception e) {
+            throw new PlcConnectionException("Error connecting to host", e);
+        }
+    }
+
+    @Override
+    public PlcConnection connect(String url, PlcAuthentication authentication) throws PlcConnectionException {
+        throw new PlcConnectionException("Basic S7 connections don't support authentication (NG).");
+    }
+
+}
diff --git a/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/connection/S7Connection.java b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/connection/S7Connection.java
new file mode 100644
index 0000000..8616533
--- /dev/null
+++ b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/connection/S7Connection.java
@@ -0,0 +1,224 @@
+/*
+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.connection;
+
+import io.netty.channel.*;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcReadResponse;
+import org.apache.plc4x.java.api.messages.PlcWriteRequest;
+import org.apache.plc4x.java.api.messages.PlcWriteResponse;
+import org.apache.plc4x.java.base.connection.ChannelFactory;
+import org.apache.plc4x.java.base.connection.NettyPlcConnection;
+import org.apache.plc4x.java.base.events.ConnectEvent;
+import org.apache.plc4x.java.base.events.ConnectedEvent;
+import org.apache.plc4x.java.base.messages.*;
+import org.apache.plc4x.java.s7.readwrite.protocol.Plc4xProtocol;
+import org.apache.plc4x.java.s7.readwrite.protocol.S7Protocol;
+import org.apache.plc4x.java.s7.readwrite.types.COTPTpduSize;
+import org.apache.plc4x.java.s7.readwrite.types.DeviceGroup;
+import org.apache.plc4x.java.s7.readwrite.types.S7ControllerType;
+import org.apache.plc4x.java.s7.readwrite.utils.S7PlcFieldHandler;
+import org.apache.plc4x.java.s7.readwrite.utils.S7TsapIdEncoder;
+import org.apache.plc4x.java.tcp.connection.TcpSocketChannelFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetAddress;
+import java.util.concurrent.CompletableFuture;
+
+public class S7Connection extends NettyPlcConnection implements PlcReader, PlcWriter {
+
+    private static final int ISO_ON_TCP_PORT = 102;
+
+    private static final Logger logger = LoggerFactory.getLogger(S7Connection.class);
+
+    private final short rack;
+    private final short slot;
+    private final COTPTpduSize tpduSize;
+    private final short maxAmqCaller;
+    private final short maxAmqCallee;
+    private final S7ControllerType controllerType;
+
+    public S7Connection(InetAddress address, String params) {
+        this(new TcpSocketChannelFactory(address, ISO_ON_TCP_PORT), params);
+    }
+
+    public S7Connection(ChannelFactory channelFactory, String params) {
+        super(channelFactory, true);
+
+        short curRack = 1;
+        short curSlot = 1;
+        short curParamPduSize = 1024;
+        short curParamMaxAmqCaller = 8;
+        short curParamMaxAmqCallee = 8;
+        S7ControllerType curParamControllerType = S7ControllerType.ANY;
+
+        if (!StringUtils.isEmpty(params)) {
+            for (String param : params.split("&")) {
+                String[] paramElements = param.split("=");
+                String paramName = paramElements[0];
+                if (paramElements.length == 2) {
+                    String paramValue = paramElements[1];
+                    switch (paramName) {
+                        case "rack":
+                            curRack = Short.parseShort(paramValue);
+                            break;
+                        case "slot":
+                            curSlot = Short.parseShort(paramValue);
+                            break;
+                        case "pdu-size":
+                            curParamPduSize = Short.parseShort(paramValue);
+                            break;
+                        case "max-amq-caller":
+                            curParamMaxAmqCaller = Short.parseShort(paramValue);
+                            break;
+                        case "max-amq-callee":
+                            curParamMaxAmqCallee = Short.parseShort(paramValue);
+                            break;
+                        case "controller-type":
+                            curParamControllerType = S7ControllerType.valueOf(paramValue);
+                            break;
+                        default:
+                            logger.debug("Unknown parameter {} with value {}", paramName, paramValue);
+                    }
+                } else {
+                    logger.debug("Unknown no-value parameter {}", paramName);
+                }
+            }
+        }
+
+        // It seems that the LOGO devices are a little picky about the pdu-size.
+        // Instead of handling this out, they just hang up without any error message.
+        // So in case of a LOGO controller, set this to a known working value.
+        if(curParamControllerType == S7ControllerType.LOGO && curParamPduSize == 1024) {
+            curParamPduSize = 480;
+        }
+
+        this.tpduSize = getNearestMatchingTpduSize(curParamPduSize);
+
+        this.rack = curRack;
+        this.slot = curSlot;
+        this.maxAmqCallee = curParamMaxAmqCallee;
+        this.maxAmqCaller = curParamMaxAmqCaller;
+        this.controllerType = curParamControllerType;
+
+        logger.info("Setting up S7 Connection with: rack {}, slot {}, tpdu-size {}, max-amq-caller {}, " +
+                "max-amq-callee {}", rack, slot, tpduSize, maxAmqCaller, maxAmqCallee);
+    }
+
+    @Override
+    public boolean canRead() {
+        return true;
+    }
+
+    @Override
+    public boolean canWrite() {
+        return true;
+    }
+
+    @Override
+    protected ChannelHandler getChannelHandler(CompletableFuture<Void> sessionSetupCompleteFuture) {
+        short calledTsapId = S7TsapIdEncoder.encodeS7TsapId(DeviceGroup.OS, 0, 0);
+        short callingTsapId = S7TsapIdEncoder.encodeS7TsapId(DeviceGroup.PG_OR_PC, rack, slot);
+
+        return new ChannelInitializer() {
+            @Override
+            protected void initChannel(Channel channel) {
+                // Build the protocol stack for communicating with the s7 protocol.
+                ChannelPipeline pipeline = channel.pipeline();
+                pipeline.addLast(new ChannelInboundHandlerAdapter() {
+                    @Override
+                    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+                        if (evt instanceof ConnectedEvent) {
+                            sessionSetupCompleteFuture.complete(null);
+                        } else {
+                            super.userEventTriggered(ctx, evt);
+                        }
+                    }
+                });
+                pipeline.addLast(new S7Protocol());
+                pipeline.addLast(new Plc4xProtocol(callingTsapId, calledTsapId, tpduSize,
+                    maxAmqCaller, maxAmqCallee, controllerType));
+            }
+        };
+    }
+
+    @Override
+    public PlcReadRequest.Builder readRequestBuilder() {
+        return new DefaultPlcReadRequest.Builder(this, new S7PlcFieldHandler());
+    }
+
+    @Override
+    public PlcWriteRequest.Builder writeRequestBuilder() {
+        return new DefaultPlcWriteRequest.Builder(this, new S7PlcFieldHandler());
+    }
+
+    @Override
+    public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) {
+        InternalPlcReadRequest internalReadRequest = checkInternal(readRequest, InternalPlcReadRequest.class);
+        CompletableFuture<InternalPlcReadResponse> future = new CompletableFuture<>();
+        PlcRequestContainer<InternalPlcReadRequest, InternalPlcReadResponse> container =
+            new PlcRequestContainer<>(internalReadRequest, future);
+        channel.writeAndFlush(container).addListener(f -> {
+            if (!f.isSuccess()) {
+                future.completeExceptionally(f.cause());
+            }
+        });
+        return future
+            .thenApply(PlcReadResponse.class::cast);
+    }
+
+    @Override
+    public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
+        InternalPlcWriteRequest internalWriteRequest = checkInternal(writeRequest, InternalPlcWriteRequest.class);
+        CompletableFuture<InternalPlcWriteResponse> future = new CompletableFuture<>();
+        PlcRequestContainer<InternalPlcWriteRequest, InternalPlcWriteResponse> container =
+            new PlcRequestContainer<>(internalWriteRequest, future);
+        channel.writeAndFlush(container).addListener(f -> {
+            if (!f.isSuccess()) {
+                future.completeExceptionally(f.cause());
+            }
+        });
+        return future
+            .thenApply(PlcWriteResponse.class::cast);
+    }
+
+    @Override
+    protected void sendChannelCreatedEvent() {
+        logger.trace("Channel was created, firing ChannelCreated Event");
+        // Send an event to the pipeline telling the Protocol filters what's going on.
+        channel.pipeline().fireUserEventTriggered(new ConnectEvent());
+    }
+
+    /**
+     * Iterate over all values until one is found that the given tpdu size will fit.
+     * @param tpduSizeParameter requested tpdu size.
+     * @return smallest {@link COTPTpduSize} which will fit a given size of tpdu.
+     */
+    protected COTPTpduSize getNearestMatchingTpduSize(short tpduSizeParameter) {
+        for (COTPTpduSize value : COTPTpduSize.values()) {
+            if(value.getSizeInBytes() >= tpduSizeParameter) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/events/IsoTPConnectedEvent.java b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/events/IsoTPConnectedEvent.java
new file mode 100644
index 0000000..e829c5b
--- /dev/null
+++ b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/events/IsoTPConnectedEvent.java
@@ -0,0 +1,22 @@
+/*
+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.events;
+
+public class IsoTPConnectedEvent {
+}
diff --git a/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/events/S7ConnectedEvent.java b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/events/S7ConnectedEvent.java
new file mode 100644
index 0000000..ac22e85
--- /dev/null
+++ b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/events/S7ConnectedEvent.java
@@ -0,0 +1,22 @@
+/*
+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.events;
+
+public class S7ConnectedEvent {
+}
diff --git a/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/Plc4xProtocol.java b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/Plc4xProtocol.java
new file mode 100644
index 0000000..d4e2eff
--- /dev/null
+++ b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/Plc4xProtocol.java
@@ -0,0 +1,619 @@
+/*
+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.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.plc4x.java.api.exceptions.PlcProtocolException;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.messages.PlcResponse;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.base.PlcMessageToMessageCodec;
+import org.apache.plc4x.java.base.events.ConnectEvent;
+import org.apache.plc4x.java.base.events.ConnectedEvent;
+import org.apache.plc4x.java.base.messages.*;
+import org.apache.plc4x.java.base.messages.items.*;
+import org.apache.plc4x.java.s7.readwrite.*;
+import org.apache.plc4x.java.s7.readwrite.events.IsoTPConnectedEvent;
+import org.apache.plc4x.java.s7.readwrite.types.*;
+import org.apache.plc4x.java.s7.readwrite.utils.S7Field;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Array;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+public class Plc4xProtocol extends PlcMessageToMessageCodec<TPKTPacket, PlcRequestContainer> {
+
+    private static final Logger logger = LoggerFactory.getLogger(Plc4xProtocol.class);
+
+    private final int callingTsapId;
+    private int calledTsapId;
+    private COTPTpduSize cotpTpduSize;
+    private int pduSize;
+    private int maxAmqCaller;
+    private int maxAmqCallee;
+    private S7ControllerType controllerType;
+
+    private static final AtomicInteger tpduGenerator = new AtomicInteger(10);
+    private final Map<Integer, PlcRequestContainer> requests;
+
+    public Plc4xProtocol(int callingTsapId, int calledTsapId, COTPTpduSize tpduSize,
+                         int maxAmqCaller, int maxAmqCallee, S7ControllerType controllerType) {
+        this.callingTsapId = callingTsapId;
+        this.calledTsapId = calledTsapId;
+        this.cotpTpduSize = tpduSize;
+        this.pduSize = tpduSize.getSizeInBytes() - 16;
+        this.maxAmqCaller = maxAmqCaller;
+        this.maxAmqCallee = maxAmqCallee;
+        this.controllerType = controllerType;
+
+        requests = new HashMap<>();
+    }
+
+    /**
+     * If the S7 protocol layer is used over Iso TP, then after receiving a {@link IsoTPConnectedEvent} the
+     * corresponding S7 setup communication message has to be sent in order to negotiate the S7 protocol layer.
+     *
+     * @param ctx the current protocol layers context
+     * @param evt the event
+     * @throws Exception throws an exception if something goes wrong internally
+     */
+    @Override
+    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+        // If the connection has just been established, start setting up the connection
+        // by sending a connection request to the plc.
+        if (evt instanceof ConnectEvent) {
+            logger.debug("ISO Transport Protocol Sending Connection Request");
+            // Open the session on ISO Transport Protocol first.
+            COTPPacketConnectionRequest connectionRequest = new COTPPacketConnectionRequest(
+                new COTPParameter[] {
+                    new COTPParameterCalledTsap(calledTsapId),
+                    new COTPParameterCallingTsap(callingTsapId),
+                    new COTPParameterTpduSize(cotpTpduSize)
+                }, null, (short) 0x0000, (short) 0x000F, COTPProtocolClass.CLASS_0);
+            TPKTPacket packet = new TPKTPacket(connectionRequest);
+            ctx.channel().writeAndFlush(packet);
+        }
+        else {
+            super.userEventTriggered(ctx, evt);
+        }
+    }
+
+    @Override
+    protected void encode(ChannelHandlerContext ctx, PlcRequestContainer msg, List<Object> out) throws Exception {
+        if(msg.getRequest() instanceof DefaultPlcReadRequest) {
+            DefaultPlcReadRequest request = (DefaultPlcReadRequest) msg.getRequest();
+            List<S7VarRequestParameterItem> requestItems = new ArrayList<>(request.getNumberOfFields());
+            for (PlcField field : request.getFields()) {
+                requestItems.add(new S7VarRequestParameterItemAddress(toS7Address(field)));
+            }
+            final int tpduId = tpduGenerator.getAndIncrement();
+            out.add(new TPKTPacket(new COTPPacketData(null,
+                new S7MessageRequest(tpduId,
+                    new S7ParameterReadVarRequest(requestItems.toArray(new S7VarRequestParameterItem[0])),
+                    new S7PayloadReadVarRequest()),
+                true, (short) tpduId)));
+            requests.put(tpduId, msg);
+/*        } else if(msg.getRequest() instanceof DefaultPlcWriteRequest) {
+            DefaultPlcWriteRequest request = (DefaultPlcWriteRequest) msg.getRequest();
+            List<S7VarRequestParameterItem> requestItems = new ArrayList<>(request.getNumberOfFields());
+            List<S7VarPayloadDataItem> payloadItems = new ArrayList<>(request.getNumberOfFields());
+            for (PlcField field : request.getFields()) {
+                requestItems.add(new S7VarRequestParameterItemAddress(toS7Address(field)));
+                payloadItems.add((new S7VarPayloadDataItem(0xff, DataTransportSize.BYTE_WORD_DWORD, 1, field)));
+            }
+            final int tpduId = tpduGenerator.getAndIncrement();
+            out.add(new TPKTPacket(new COTPPacketData(null,
+                new S7MessageRequest(3,
+                    new S7ParameterWriteVarRequest(requestItems.toArray(new S7VarRequestParameterItem[0])),
+                    new S7PayloadWriteVarRequest(payloadItems.toArray(new S7VarPayloadDataItem[0]))),
+                true, (short) 0)));
+            requests.put((short) tpduId, msg);*/
+        }
+    }
+
+    @Override
+    protected void decode(ChannelHandlerContext ctx, TPKTPacket msg, List<Object> out) throws Exception {
+        if((msg == null) || (msg.getPayload() == null)) {
+            return;
+        }
+
+        // When getting a response to the connection request on COTP layer, extract some
+        // data and continue logging in on the S7 protocol.
+        if(msg.getPayload() instanceof COTPPacketConnectionResponse) {
+            COTPPacketConnectionResponse cotpPacketConnectionResponse = (COTPPacketConnectionResponse) msg.getPayload();
+            for (COTPParameter parameter : cotpPacketConnectionResponse.getParameters()) {
+                if(parameter instanceof COTPParameterCalledTsap) {
+                    COTPParameterCalledTsap cotpParameterCalledTsap = (COTPParameterCalledTsap) parameter;
+                    calledTsapId = cotpParameterCalledTsap.getTsapId();
+                } else if(parameter instanceof COTPParameterTpduSize) {
+                    COTPParameterTpduSize cotpParameterTpduSize = (COTPParameterTpduSize) parameter;
+                    cotpTpduSize = cotpParameterTpduSize.getTpduSize();
+                } else if(parameter instanceof COTPParameterCallingTsap) {
+                    // Ignore this ...
+                } else {
+                    logger.warn("Got unknown parameter type '" + parameter.getClass().getName() + "'");
+                }
+            }
+
+            // Send an S7 login message.
+            S7ParameterSetupCommunication s7ParameterSetupCommunication =
+                new S7ParameterSetupCommunication(maxAmqCaller, maxAmqCallee, pduSize);
+            S7Message s7Message = new S7MessageRequest(0, s7ParameterSetupCommunication,
+                new S7PayloadSetupCommunication());
+            COTPPacketData cotpPacketData = new COTPPacketData(null, s7Message, true, (short) 1);
+            TPKTPacket tpktPacket = new TPKTPacket(cotpPacketData);
+            ctx.channel().writeAndFlush(tpktPacket);
+        }
+
+        else if(msg.getPayload() instanceof COTPPacketData) {
+            COTPPacketData packetData = (COTPPacketData) msg.getPayload();
+            if(packetData.getPayload() instanceof S7MessageResponse) {
+                S7MessageResponse s7MessageResponse = (S7MessageResponse) packetData.getPayload();
+                final S7Parameter parameter = s7MessageResponse.getParameter();
+                if(parameter instanceof S7ParameterSetupCommunication) {
+                    S7ParameterSetupCommunication setupCommunication = (S7ParameterSetupCommunication) parameter;
+
+                    // Save some data from the response.
+                    maxAmqCaller = setupCommunication.getMaxAmqCaller();
+                    maxAmqCallee = setupCommunication.getMaxAmqCallee();
+                    pduSize = setupCommunication.getPduLength();
+
+                    // Only if the controller type is set to "ANY", then try to identify the PLC type.
+                    if(controllerType == S7ControllerType.ANY) {
+                        // Prepare a message to request the remote to identify itself.
+                        S7MessageUserData identifyRemoteMessage = new S7MessageUserData(1, new S7ParameterUserData(new S7ParameterUserDataItem[] {
+                            new S7ParameterUserDataItemCPUFunctions((short) 0x11, (byte) 0x4, (byte) 0x4, (short) 0x01, (short) 0x00, null, null, null)
+                        }), new S7PayloadUserData( new S7PayloadUserDataItem[] {
+                            new S7PayloadUserDataItemCpuFunctionReadSzlRequest(DataTransportErrorCode.OK, DataTransportSize.OCTET_STRING, new SzlId(SzlModuleTypeClass.CPU, (byte) 0x00, SzlSublist.MODULE_IDENTIFICATION), 0x0000)
+                        }));
+                        COTPPacketData cotpPacketData = new COTPPacketData(null, identifyRemoteMessage, true, (short) 2);
+                        TPKTPacket tpktPacket = new TPKTPacket(cotpPacketData);
+                        ctx.channel().writeAndFlush(tpktPacket);
+                    } else {
+                        // Send an event that connection setup is complete.
+                        ctx.channel().pipeline().fireUserEventTriggered(new ConnectedEvent());
+                    }
+                } else if (parameter instanceof S7ParameterReadVarResponse) {
+                    final PlcRequestContainer requestContainer = requests.remove(s7MessageResponse.getTpduReference());
+                    final PlcResponse response = decodeReadResponse(s7MessageResponse, requestContainer);
+                    requestContainer.getResponseFuture().complete(response);
+                } else if (parameter instanceof S7ParameterWriteVarResponse) {
+                    S7ParameterWriteVarResponse writeResponseParameter = (S7ParameterWriteVarResponse) parameter;
+
+                    System.out.println(writeResponseParameter);
+                }
+            } else if(packetData.getPayload() instanceof S7MessageUserData) {
+                S7MessageUserData messageUserData = (S7MessageUserData) packetData.getPayload();
+                if(messageUserData.getPayload() instanceof S7PayloadUserData) {
+                    S7PayloadUserData payloadUserData = (S7PayloadUserData) messageUserData.getPayload();
+                    for (S7PayloadUserDataItem item : payloadUserData.getItems()) {
+                        if(item instanceof S7PayloadUserDataItemCpuFunctionReadSzlResponse) {
+                            S7PayloadUserDataItemCpuFunctionReadSzlResponse readSzlResponseItem =
+                                (S7PayloadUserDataItemCpuFunctionReadSzlResponse) item;
+                            for (SzlDataTreeItem readSzlResponseItemItem : readSzlResponseItem.getItems()) {
+                                if(readSzlResponseItemItem.getItemIndex() == 0x0001) {
+                                    final String articleNumber = new String(readSzlResponseItemItem.getMlfb());
+                                    controllerType = lookupControllerType(articleNumber);
+
+                                    // Send an event that connection setup is complete.
+                                    ctx.channel().pipeline().fireUserEventTriggered(new ConnectedEvent());
+                                }
+                            }
+                        }
+                    }
+                }
+            } else {
+                System.out.println(packetData);
+            }
+        }
+
+        else {
+            System.out.println(msg);
+        }
+    }
+
+    private PlcResponse decodeReadResponse(S7MessageResponse responseMessage, PlcRequestContainer requestContainer) throws PlcProtocolException {
+        InternalPlcReadRequest plcReadRequest = (InternalPlcReadRequest) requestContainer.getRequest();
+
+        S7PayloadReadVarResponse payload = (S7PayloadReadVarResponse) responseMessage.getPayload();
+
+        // If the numbers of items don't match, we're in big trouble as the only
+        // way to know how to interpret the responses is by aligning them with the
+        // items from the request as this information is not returned by the PLC.
+        if (plcReadRequest.getNumberOfFields() != payload.getItems().length) {
+            throw new PlcProtocolException(
+                "The number of requested items doesn't match the number of returned items");
+        }
+
+        Map<String, Pair<PlcResponseCode, BaseDefaultFieldItem>> values = new HashMap<>();
+        S7VarPayloadDataItem[] payloadItems = payload.getItems();
+        int index = 0;
+        for (String fieldName : plcReadRequest.getFieldNames()) {
+            S7Field field = (S7Field) plcReadRequest.getField(fieldName);
+            S7VarPayloadDataItem payloadItem = payloadItems[index];
+
+            PlcResponseCode responseCode = decodeResponseCode(payloadItem.getReturnCode());
+            BaseDefaultFieldItem fieldItem = null;
+            ByteBuf data = Unpooled.wrappedBuffer(payloadItem.getData());
+            if (responseCode == PlcResponseCode.OK) {
+                try {
+                    switch (field.getDataType()) {
+                        // -----------------------------------------
+                        // Bit
+                        // -----------------------------------------
+                        case BOOL:
+                            fieldItem = decodeReadResponseBitField(field, data);
+                            break;
+                        // -----------------------------------------
+                        // Bit-strings
+                        // -----------------------------------------
+                        case BYTE:  // 1 byte
+                            fieldItem = decodeReadResponseByteBitStringField(field, data);
+                            break;
+                        case WORD:  // 2 byte (16 bit)
+                            fieldItem = decodeReadResponseShortBitStringField(field, data);
+                            break;
+                        case DWORD:  // 4 byte (32 bit)
+                            fieldItem = decodeReadResponseIntegerBitStringField(field, data);
+                            break;
+                        case LWORD:  // 8 byte (64 bit)
+                            fieldItem = decodeReadResponseLongBitStringField(field, data);
+                            break;
+                        // -----------------------------------------
+                        // Integers
+                        // -----------------------------------------
+                        // 8 bit:
+                        case SINT:
+                            fieldItem = decodeReadResponseSignedByteField(field, data);
+                            break;
+                        case USINT:
+                            fieldItem = decodeReadResponseUnsignedByteField(field, data);
+                            break;
+                        // 16 bit:
+                        case INT:
+                            fieldItem = decodeReadResponseSignedShortField(field, data);
+                            break;
+                        case UINT:
+                            fieldItem = decodeReadResponseUnsignedShortField(field, data);
+                            break;
+                        // 32 bit:
+                        case DINT:
+                            fieldItem = decodeReadResponseSignedIntegerField(field, data);
+                            break;
+                        case UDINT:
+                            fieldItem = decodeReadResponseUnsignedIntegerField(field, data);
+                            break;
+                        // 64 bit:
+                        case LINT:
+                            fieldItem = decodeReadResponseSignedLongField(field, data);
+                            break;
+                        case ULINT:
+                            fieldItem = decodeReadResponseUnsignedLongField(field, data);
+                            break;
+                        // -----------------------------------------
+                        // Floating point values
+                        // -----------------------------------------
+                        case REAL:
+                            fieldItem = decodeReadResponseFloatField(field, data);
+                            break;
+                        case LREAL:
+                            fieldItem = decodeReadResponseDoubleField(field, data);
+                            break;
+                        // -----------------------------------------
+                        // Characters & Strings
+                        // -----------------------------------------
+                        case CHAR: // 1 byte (8 bit)
+                            fieldItem = decodeReadResponseFixedLengthStringField(1, false, data);
+                            break;
+                        case WCHAR: // 2 byte
+                            fieldItem = decodeReadResponseFixedLengthStringField(1, true, data);
+                            break;
+                        case STRING:
+                            fieldItem = decodeReadResponseVarLengthStringField(false, data);
+                            break;
+                        case WSTRING:
+                            fieldItem = decodeReadResponseVarLengthStringField(true, data);
+                            break;
+                        // -----------------------------------------
+                        // TIA Date-Formats
+                        // -----------------------------------------
+                        case DATE_AND_TIME:
+                            fieldItem = decodeReadResponseDateAndTime(field, data);
+                            break;
+                        case TIME_OF_DAY:
+                            fieldItem = decodeReadResponseTimeOfDay(field, data);
+                            break;
+                        case DATE:
+                            fieldItem = decodeReadResponseDate(field, data);
+                            break;
+                        default:
+                            throw new PlcProtocolException("Unsupported type " + field.getDataType());
+                    }
+                }
+                catch (Exception e){
+                    logger.warn("Some other error occurred casting field {}, FieldInformation: {}",fieldName, field,e);
+                }
+            }
+            Pair<PlcResponseCode, BaseDefaultFieldItem> result = new ImmutablePair<>(responseCode, fieldItem);
+            values.put(fieldName, result);
+            index++;
+        }
+
+        return new DefaultPlcReadResponse(plcReadRequest, values);
+    }
+
+    private PlcResponseCode decodeResponseCode(DataTransportErrorCode dataTransportErrorCode) {
+        if (dataTransportErrorCode == null) {
+            return PlcResponseCode.INTERNAL_ERROR;
+        }
+        switch (dataTransportErrorCode) {
+            case OK:
+                return PlcResponseCode.OK;
+            case NOT_FOUND:
+                return PlcResponseCode.NOT_FOUND;
+            case INVALID_ADDRESS:
+                return PlcResponseCode.INVALID_ADDRESS;
+            case DATA_TYPE_NOT_SUPPORTED:
+                return PlcResponseCode.INVALID_DATATYPE;
+            default:
+                return PlcResponseCode.INTERNAL_ERROR;
+        }
+    }
+
+    BaseDefaultFieldItem decodeReadResponseBitField(S7Field field, ByteBuf data) {
+        Boolean[] booleans = readAllValues(Boolean.class, field, i -> data.readByte() != 0x00);
+        return new DefaultBooleanFieldItem(booleans);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseByteBitStringField(S7Field field, ByteBuf data) {
+        byte[] bytes = new byte[field.getNumElements()];
+        data.readBytes(bytes);
+        return decodeBitStringField(bytes);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseShortBitStringField(S7Field field, ByteBuf data) {
+        byte[] bytes = new byte[field.getNumElements() * 2];
+        data.readBytes(bytes);
+        return decodeBitStringField(bytes);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseIntegerBitStringField(S7Field field, ByteBuf data) {
+        byte[] bytes = new byte[field.getNumElements() * 4];
+        data.readBytes(bytes);
+        return decodeBitStringField(bytes);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseLongBitStringField(S7Field field, ByteBuf data) {
+        byte[] bytes = new byte[field.getNumElements() * 8];
+        data.readBytes(bytes);
+        return decodeBitStringField(bytes);
+    }
+
+    BaseDefaultFieldItem decodeBitStringField(byte[] bytes) {
+        BitSet bitSet = BitSet.valueOf(bytes);
+        Boolean[] booleanValues = new Boolean[8 * bytes.length];
+        int k = 0;
+        for(int i = bytes.length - 1; i >= 0; i--) {
+            for(int j = 0; j < 8; j++) {
+                booleanValues[k++] = bitSet.get(8 * i + j);
+            }
+        }
+        return new DefaultBooleanFieldItem(booleanValues);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseSignedByteField(S7Field field, ByteBuf data) {
+        Byte[] bytes = readAllValues(Byte.class, field, i -> data.readByte());
+        return new DefaultByteFieldItem(bytes);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseUnsignedByteField(S7Field field, ByteBuf data) {
+        Short[] shorts = readAllValues(Short.class, field, i -> data.readUnsignedByte());
+        return new DefaultShortFieldItem(shorts);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseSignedShortField(S7Field field, ByteBuf data) {
+        Short[] shorts = readAllValues(Short.class, field, i -> data.readShort());
+        return new DefaultShortFieldItem(shorts);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseUnsignedShortField(S7Field field, ByteBuf data) {
+        Integer[] ints = readAllValues(Integer.class, field, i -> data.readUnsignedShort());
+        return new DefaultIntegerFieldItem(ints);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseSignedIntegerField(S7Field field, ByteBuf data) {
+        Integer[] ints = readAllValues(Integer.class, field, i -> data.readInt());
+        return new DefaultIntegerFieldItem(ints);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseUnsignedIntegerField(S7Field field, ByteBuf data) {
+        Long[] longs = readAllValues(Long.class, field, i -> data.readUnsignedInt());
+        return new DefaultLongFieldItem(longs);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseSignedLongField(S7Field field, ByteBuf data) {
+        Long[] longs = readAllValues(Long.class, field, i -> data.readLong());
+        return new DefaultLongFieldItem(longs);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseUnsignedLongField(S7Field field, ByteBuf data) {
+        BigInteger[] bigIntegers = readAllValues(BigInteger.class, field, i -> readUnsigned64BitInteger(data));
+        return new DefaultBigIntegerFieldItem(bigIntegers);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseFloatField(S7Field field, ByteBuf data) {
+        Float[] floats = readAllValues(Float.class, field, i -> data.readFloat());
+        return new DefaultFloatFieldItem(floats);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseDoubleField(S7Field field, ByteBuf data) {
+        Double[] doubles = readAllValues(Double.class, field, i -> data.readDouble());
+        return new DefaultDoubleFieldItem(doubles);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseFixedLengthStringField(int numChars, boolean isUtf16, ByteBuf data) {
+        int numBytes = isUtf16 ? numChars * 2 : numChars;
+        String stringValue = data.readCharSequence(numBytes, StandardCharsets.UTF_8).toString();
+        return new DefaultStringFieldItem(stringValue);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseVarLengthStringField(boolean isUtf16, ByteBuf data) {
+        // Max length ... ignored.
+        data.skipBytes(1);
+
+        //reading out byte and transforming that to an unsigned byte within an integer, otherwise longer strings are failing
+        byte currentLengthByte = data.readByte();
+        int currentLength = currentLengthByte & 0xFF;
+        return decodeReadResponseFixedLengthStringField(currentLength, 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);
+    }
+
+    BaseDefaultFieldItem decodeReadResponseDate(S7Field field,ByteBuf data) {
+        LocalDate[] localTimes = readAllValues(LocalDate.class,field, i -> readDate(data));
+        return new DefaultLocalDateFieldItem(localTimes);
+    }
+
+    private static <T> T[] readAllValues(Class<T> clazz, S7Field field, Function<Integer, T> extract) {
+        try {
+            return IntStream.rangeClosed(1, field.getNumElements())
+                .mapToObj(extract::apply)
+                .collect(Collectors.toList())
+                .toArray((T[]) Array.newInstance(clazz, 0));
+        } catch (IndexOutOfBoundsException e) {
+            throw new PlcRuntimeException("To few bytes in the buffer to read requested type", e);
+        }
+    }
+
+    private static BigInteger readUnsigned64BitInteger(ByteBuf data) {
+        byte[] bytes = new byte[9];
+        // Set the first byte to 0
+        bytes[0] = 0;
+        // Read the next 8 bytes into the rest.
+        data.readBytes(bytes, 1, 8);
+        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);
+    }
+
+    LocalDate readDate(ByteBuf data) {
+        //per definition for Date_And_Time only the first 6 bytes are used
+        int daysSince1990 = data.readUnsignedShort();
+        return LocalDate.now().withYear(1990).withDayOfMonth(1).withMonth(1).plus(daysSince1990, ChronoUnit.DAYS);
+    }
+
+    /**
+     * 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);
+    }
+
+    /**
+     * Little helper method to parse Siemens article numbers and extract the type of controller.
+     * @param articleNumber article number string.
+     * @return type of controller.
+     */
+    private S7ControllerType lookupControllerType(String articleNumber) {
+        if (!articleNumber.startsWith("6ES7 ")) {
+            return S7ControllerType.ANY;
+        }
+        String model = articleNumber.substring(articleNumber.indexOf(' ') + 1, articleNumber.indexOf(' ') + 2);
+        switch (model) {
+            case "2":
+                return S7ControllerType.S7_1200;
+            case "5":
+                return S7ControllerType.S7_1500;
+            case "3":
+                return S7ControllerType.S7_300;
+            case "4":
+                return S7ControllerType.S7_400;
+            default:
+                if (logger.isInfoEnabled()) {
+                    logger.info(String.format("Looking up unknown article number %s", articleNumber));
+                }
+                return S7ControllerType.ANY;
+        }
+    }
+
+    protected S7Address toS7Address(PlcField field) {
+        if(!(field instanceof S7Field)) {
+            throw new RuntimeException("Unsupported address type " + field.getClass().getName());
+        }
+        S7Field s7Field = (S7Field) field;
+        return new S7AddressAny(s7Field.getDataType(), s7Field.getNumElements(), s7Field.getBlockNumber(),
+            s7Field.getMemoryArea(), s7Field.getByteOffset(), s7Field.getBitOffset());
+    }
+
+}
diff --git a/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7Protocol.java b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7Protocol.java
new file mode 100644
index 0000000..edb34e8
--- /dev/null
+++ b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7Protocol.java
@@ -0,0 +1,67 @@
+/*
+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.protocol;
+
+import io.netty.buffer.ByteBuf;
+import org.apache.plc4x.java.base.GeneratedDriverByteToMessageCodec;
+import org.apache.plc4x.java.s7.readwrite.*;
+import org.apache.plc4x.java.s7.readwrite.io.TPKTPacketIO;
+import org.apache.plc4x.java.utils.MessageIO;
+import org.apache.plc4x.java.utils.ParseException;
+import org.apache.plc4x.java.utils.ReadBuffer;
+import org.apache.plc4x.java.utils.WriteBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class S7Protocol extends GeneratedDriverByteToMessageCodec<TPKTPacket> {
+
+    private static final Logger logger = LoggerFactory.getLogger(S7Protocol.class);
+
+    public S7Protocol() {
+        super(new MessageIO<TPKTPacket, TPKTPacket>() {
+            @Override
+            public TPKTPacket parse(ReadBuffer io) throws ParseException {
+                return TPKTPacketIO.parse(io);
+            }
+
+            @Override
+            public void serialize(WriteBuffer io, TPKTPacket value) throws ParseException {
+                TPKTPacketIO.serialize(io, value);
+            }
+        });
+    }
+
+    @Override
+    protected int getPacketSize(ByteBuf byteBuf) {
+        if(byteBuf.readableBytes() >= 4) {
+            return byteBuf.getUnsignedShort(byteBuf.readerIndex() + 2);
+        }
+        return -1;
+    }
+
+    @Override
+    protected void removeRestOfCorruptPackage(ByteBuf byteBuf) {
+        while (byteBuf.getUnsignedByte(0) != TPKTPacket.PROTOCOLID) {
+            // Just consume the bytes till the next possible start position.
+            byteBuf.readByte();
+        }
+    }
+
+}
diff --git a/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/types/S7ControllerType.java b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/types/S7ControllerType.java
new file mode 100644
index 0000000..f2956fe
--- /dev/null
+++ b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/types/S7ControllerType.java
@@ -0,0 +1,30 @@
+/*
+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.types;
+
+public enum S7ControllerType {
+
+    ANY,
+    S7_300,
+    S7_400,
+    S7_1200,
+    S7_1500,
+    LOGO
+
+}
diff --git a/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7Field.java b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7Field.java
new file mode 100644
index 0000000..69b41d5
--- /dev/null
+++ b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7Field.java
@@ -0,0 +1,265 @@
+/*
+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.utils;
+
+import org.apache.commons.lang3.NotImplementedException;
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.s7.readwrite.types.MemoryArea;
+import org.apache.plc4x.java.s7.readwrite.types.TransportSize;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class S7Field implements PlcField {
+
+    //byteOffset theoretically can reach up to 2097151 ... see checkByteOffset() below --> 7digits
+    private static final Pattern ADDRESS_PATTERN =
+        Pattern.compile("^%(?<memoryArea>.)(?<transferSizeCode>[XBWD]?)(?<byteOffset>\\d{1,7})(.(?<bitOffset>[0-7]))?:(?<dataType>[a-zA-Z_]+)(\\[(?<numElements>\\d+)])?");
+
+    //blockNumber usually has its max hat around 64000 --> 5digits
+    private static final Pattern DATA_BLOCK_ADDRESS_PATTERN =
+        Pattern.compile("^%DB(?<blockNumber>\\d{1,5}).DB(?<transferSizeCode>[XBWD]?)(?<byteOffset>\\d{1,7})(.(?<bitOffset>[0-7]))?:(?<dataType>[a-zA-Z_]+)(\\[(?<numElements>\\d+)])?");
+
+    private static final String DATA_TYPE = "dataType";
+    private static final String TRANSFER_SIZE_CODE = "transferSizeCode";
+    private static final String BLOCK_NUMBER = "blockNumber";
+    private static final String BYTE_OFFSET = "byteOffset";
+    private static final String BIT_OFFSET = "bitOffset";
+    private static final String NUM_ELEMENTS = "numElements";
+    private static final String MEMORY_AREA = "memoryArea";
+
+    private final TransportSize dataType;
+    private final MemoryArea memoryArea;
+    private final int blockNumber;
+    private final int byteOffset;
+    private final byte bitOffset;
+    private final int numElements;
+
+    private S7Field(TransportSize dataType, MemoryArea memoryArea, int blockNumber, int byteOffset, byte bitOffset, int numElements) {
+        this.dataType = dataType;
+        this.memoryArea = memoryArea;
+        this.blockNumber = blockNumber;
+        this.byteOffset = byteOffset;
+        this.bitOffset = bitOffset;
+        this.numElements = numElements;
+    }
+
+    public TransportSize getDataType() {
+        return dataType;
+    }
+
+    public MemoryArea getMemoryArea() {
+        return memoryArea;
+    }
+
+    public int getBlockNumber() {
+        return blockNumber;
+    }
+
+    public int getByteOffset() {
+        return byteOffset;
+    }
+
+    public byte getBitOffset() {
+        return bitOffset;
+    }
+
+    public int getNumElements() {
+        return numElements;
+    }
+
+    public static boolean matches(String fieldString) {
+        return DATA_BLOCK_ADDRESS_PATTERN.matcher(fieldString).matches() ||
+            ADDRESS_PATTERN.matcher(fieldString).matches();
+    }
+
+    /**
+     * @return Java type of expected response.
+     *
+     * TODO validate all Methods existing are implemented
+     */
+    @Override
+    public Class<?> getDefaultJavaType() {
+        switch (dataType){
+            case STRING:
+                return String.class;
+            case USINT:
+            case SINT:
+            case UINT:
+            case INT:
+            case DINT:
+                return Integer.class;
+            case UDINT:
+            case ULINT:
+            case LINT:
+                return Long.class;
+            case BOOL:
+                return Boolean.class;
+            case REAL:
+            case LREAL:
+                return Double.class;
+            case DATE_AND_TIME:
+                return LocalDateTime.class;
+            case DATE:
+                return LocalDate.class;
+            case TIME_OF_DAY:
+                return LocalTime.class;
+            default:
+                throw new NotImplementedException("The response type for datatype " + dataType + " is not yet implemented");
+        }
+    }
+
+    public static S7Field of(String fieldString) {
+        Matcher matcher = DATA_BLOCK_ADDRESS_PATTERN.matcher(fieldString);
+        if(matcher.matches()) {
+            TransportSize dataType = TransportSize.valueOf(matcher.group(DATA_TYPE));
+            MemoryArea memoryArea = MemoryArea.DATA_BLOCKS;
+            Short transferSizeCode = getSizeCode(matcher.group(TRANSFER_SIZE_CODE));
+
+            int blockNumber = checkDatablockNumber(Integer.parseInt(matcher.group(BLOCK_NUMBER)));
+
+            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));
+            }
+            numElements = calcNumberOfElementsForIndividualTypes(numElements,dataType);
+            if((transferSizeCode != null) && (dataType.getSizeCode() != transferSizeCode)) {
+                throw new PlcInvalidFieldException("Transfer size code '" + transferSizeCode +
+                    "' doesn't match specified data type '" + dataType.name() + "'");
+            }
+            return new S7Field(dataType, memoryArea, blockNumber, byteOffset, bitOffset, numElements);
+        } else {
+            matcher = ADDRESS_PATTERN.matcher(fieldString);
+            if (matcher.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));
+                }
+                numElements = calcNumberOfElementsForIndividualTypes(numElements,dataType);
+                if((transferSizeCode != null) && (dataType.getSizeCode() != transferSizeCode)) {
+                    throw new PlcInvalidFieldException("Transfer size code '" + transferSizeCode +
+                        "' doesn't match specified data type '" + dataType.name() + "'");
+                }
+                return new S7Field(dataType, memoryArea, (short) 0, byteOffset, bitOffset, numElements);
+            }
+        }
+        throw new PlcInvalidFieldException("Unable to parse address: " + fieldString);
+    }
+
+    /**
+     * checks if DatablockNumber of S7Field is in valid range
+     * @param blockNumber given DatablockNumber
+     * @return given blockNumber if Ok, throws PlcInvalidFieldException otherwise
+     */
+    private static int checkDatablockNumber(int blockNumber){
+        //ToDo check the value or add reference - limit eventually depending on active S7 --> make a case selection
+        if(blockNumber>64000 || blockNumber<1){
+            throw new PlcInvalidFieldException("Datablock numbers larger than 64000 or smaller than 1 are not supported.");
+        }
+        return blockNumber;
+    }
+
+    /**
+     * checks if ByteOffset from S7Field is in valid range
+     * @param byteOffset given byteOffset
+     * @return given byteOffset if Ok, throws PlcInvalidFieldException otherwise
+     */
+    private static int checkByteOffset(int byteOffset){
+        //ToDo check the value or add reference
+        if(byteOffset>2097151 || byteOffset<0){
+            throw new PlcInvalidFieldException("ByteOffset must be smaller than 2097151 and positive.");
+        }
+        return byteOffset;
+    }
+
+    /**
+     * correct the storage of "array"-like variables like STRING
+     * @param numElements auto-detected numElements (1 if no numElements in brackets has been given, x if a specific number has been given)
+     * @param dataType detected Transport-Size that represents the data-type
+     * @return corrected numElements if nessesary
+     */
+    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;
+        }
+        return numElements;
+
+    }
+
+    protected static Short getSizeCode(String value) {
+        if((value == null) || value.isEmpty()) {
+            return null;
+        }
+        if(value.length() > 1) {
+            return null;
+        }
+        return (short) value.getBytes()[0];
+    }
+
+    protected static MemoryArea getMemoryAreaForShortName(String shortName) {
+        for (MemoryArea memoryArea : MemoryArea.values()) {
+            if(memoryArea.getShortName().equals(shortName)) {
+                return memoryArea;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return "S7Field{" +
+            "dataType=" + dataType +
+            ", memoryArea=" + memoryArea +
+            ", blockNumber=" + blockNumber +
+            ", byteOffset=" + byteOffset +
+            ", bitOffset=" + bitOffset +
+            ", numElements=" + numElements +
+            '}';
+    }
+}
diff --git a/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7PlcFieldHandler.java b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7PlcFieldHandler.java
new file mode 100644
index 0000000..0e8f658
--- /dev/null
+++ b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7PlcFieldHandler.java
@@ -0,0 +1,569 @@
+/*
+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.utils;
+
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.base.connection.DefaultPlcFieldHandler;
+import org.apache.plc4x.java.base.messages.items.*;
+
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.util.BitSet;
+import java.util.LinkedList;
+import java.util.List;
+
+public class S7PlcFieldHandler extends DefaultPlcFieldHandler {
+
+    @Override
+    public PlcField createField(String fieldQuery) {
+        if (S7Field.matches(fieldQuery)) {
+            return S7Field.of(fieldQuery);
+        }
+        throw new PlcInvalidFieldException(fieldQuery);
+    }
+
+    @Override
+    public BaseDefaultFieldItem encodeBoolean(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        // All of these types are declared as Bit or Bit-String types.
+        switch (s7Field.getDataType()) {
+            case BOOL:
+            case BYTE:
+            case WORD:
+            case DWORD:
+            case LWORD:
+                return internalEncodeBoolean(field, values);
+            default:
+                throw new PlcRuntimeException("Invalid encoder for type " + s7Field.getDataType().name());
+        }
+    }
+
+    @Override
+    public BaseDefaultFieldItem encodeByte(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        // All of these types are declared as Bit or Bit-String types.
+        switch (s7Field.getDataType()) {
+            case BYTE:
+            case SINT:
+            case USINT:
+            case CHAR:
+                return internalEncodeInteger(field, values);
+            default:
+                throw new PlcRuntimeException("Invalid encoder for type " + s7Field.getDataType().name());
+        }
+    }
+
+    @Override
+    public BaseDefaultFieldItem encodeShort(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        switch (s7Field.getDataType()) {
+            case WORD:
+            case INT:
+            case UINT:
+                return internalEncodeInteger(field, values);
+            default:
+                throw new PlcRuntimeException("Invalid encoder for type " + s7Field.getDataType().name());
+        }
+    }
+
+    @Override
+    public BaseDefaultFieldItem encodeInteger(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        switch (s7Field.getDataType()) {
+            case DWORD:
+            case DINT:
+            case UDINT:
+                return internalEncodeInteger(field, values);
+            default:
+                throw new PlcRuntimeException("Invalid encoder for type " + s7Field.getDataType().name());
+        }
+    }
+
+    @Override
+    public BaseDefaultFieldItem encodeBigInteger(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        switch (s7Field.getDataType()) {
+            case DWORD:
+            case DINT:
+            case UDINT:
+                return internalEncodeInteger(field, values);
+            default:
+                throw new PlcRuntimeException("Invalid encoder for type " + s7Field.getDataType().name());
+        }
+    }
+
+    @Override
+    public BaseDefaultFieldItem encodeLong(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        switch (s7Field.getDataType()) {
+            case LWORD:
+            case LINT:
+            case ULINT:
+                return internalEncodeInteger(field, values);
+            default:
+                throw new PlcRuntimeException("Invalid encoder for type " + s7Field.getDataType().name());
+        }
+    }
+
+    @Override
+    public BaseDefaultFieldItem encodeFloat(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        switch (s7Field.getDataType()) {
+            case REAL:
+                return internalEncodeFloatingPoint(field, values);
+            default:
+                throw new PlcRuntimeException("Invalid encoder for type " + s7Field.getDataType().name());
+        }
+    }
+
+    @Override
+    public BaseDefaultFieldItem encodeDouble(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        switch (s7Field.getDataType()) {
+            case LREAL:
+                return internalEncodeFloatingPoint(field, values);
+            default:
+                throw new PlcRuntimeException("Invalid encoder for type " + s7Field.getDataType().name());
+        }
+    }
+
+    @Override
+    public BaseDefaultFieldItem encodeString(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        switch (s7Field.getDataType()) {
+            case CHAR:
+            case WCHAR:
+            case STRING:
+            case WSTRING:
+                return internalEncodeString(field, values);
+            default:
+                throw new PlcRuntimeException("Invalid encoder for type " + s7Field.getDataType().name());
+        }
+    }
+
+    @Override
+    public BaseDefaultFieldItem encodeTime(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        switch (s7Field.getDataType()) {
+            case TIME:
+                return internalEncodeTemporal(field, values);
+            default:
+                throw new PlcRuntimeException("Invalid encoder for type " + s7Field.getDataType().name());
+        }
+    }
+
+    @Override
+    public BaseDefaultFieldItem encodeDate(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        switch (s7Field.getDataType()) {
+            case DATE:
+                return internalEncodeTemporal(field, values);
+            default:
+                throw new PlcRuntimeException("Invalid encoder for type " + s7Field.getDataType().name());
+        }
+    }
+
+    @Override
+    public BaseDefaultFieldItem encodeDateTime(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        switch (s7Field.getDataType()) {
+            case DATE_AND_TIME:
+                return internalEncodeTemporal(field, values);
+            default:
+                throw new PlcRuntimeException("Invalid encoder for type " + s7Field.getDataType().name());
+        }
+    }
+
+    private BaseDefaultFieldItem internalEncodeBoolean(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        switch (s7Field.getDataType()) {
+            case BOOL:
+            case BYTE:
+            case WORD:
+            case DWORD:
+            case LWORD:
+                break;
+            default:
+                throw new IllegalArgumentException(
+                    "Cannot assign boolean values to " + s7Field.getDataType().name() + " fields.");
+        }
+        List<Boolean> booleanValues = new LinkedList<>();
+        for (Object value : values) {
+            if (value instanceof Boolean) {
+                Boolean booleanValue = (Boolean) value;
+                booleanValues.add(booleanValue);
+            } else if (value instanceof Byte) {
+                Byte byteValue = (Byte) value;
+                BitSet bitSet = BitSet.valueOf(new byte[]{byteValue});
+                for (int i = 0; i < 8; i++) {
+                    booleanValues.add(bitSet.get(i));
+                }
+            } else if (value instanceof Short) {
+                Short shortValue = (Short) value;
+                BitSet bitSet = BitSet.valueOf(new long[]{shortValue});
+                for (int i = 0; i < 16; i++) {
+                    booleanValues.add(bitSet.get(i));
+                }
+            } else if (value instanceof Integer) {
+                Integer integerValue = (Integer) value;
+                BitSet bitSet = BitSet.valueOf(new long[]{integerValue});
+                for (int i = 0; i < 32; i++) {
+                    booleanValues.add(bitSet.get(i));
+                }
+            } else if (value instanceof Long) {
+                long longValue = (Long) value;
+                BitSet bitSet = BitSet.valueOf(new long[]{longValue});
+                for (int i = 0; i < 64; i++) {
+                    booleanValues.add(bitSet.get(i));
+                }
+            } else {
+                throw new IllegalArgumentException(
+                    "Value of type " + value.getClass().getName() +
+                        " is not assignable to " + s7Field.getDataType().name() + " fields.");
+            }
+        }
+        return new DefaultBooleanFieldItem(booleanValues.toArray(new Boolean[0]));
+    }
+
+    private BaseDefaultFieldItem internalEncodeInteger(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+
+        // Initialize the constraints.
+        BigInteger minValue;
+        BigInteger maxValue;
+        Class<? extends BaseDefaultFieldItem> fieldType;
+        Class<?> valueType;
+        Object[] castedValues;
+        switch (s7Field.getDataType()) {
+            case BYTE:
+                minValue = BigInteger.valueOf((long) Byte.MIN_VALUE);
+                maxValue = BigInteger.valueOf((long) Byte.MAX_VALUE);
+                fieldType = DefaultByteFieldItem.class;
+                valueType = Byte[].class;
+                castedValues = new Byte[values.length];
+                break;
+            case WORD:
+                minValue = BigInteger.valueOf((long) Short.MIN_VALUE);
+                maxValue = BigInteger.valueOf((long) Short.MAX_VALUE);
+                fieldType = DefaultShortFieldItem.class;
+                valueType = Short[].class;
+                castedValues = new Short[values.length];
+                break;
+            case DWORD:
+                minValue = BigInteger.valueOf((long) Integer.MIN_VALUE);
+                maxValue = BigInteger.valueOf((long) Integer.MAX_VALUE);
+                fieldType = DefaultIntegerFieldItem.class;
+                valueType = Integer[].class;
+                castedValues = new Integer[values.length];
+                break;
+            case LWORD:
+                minValue = BigInteger.valueOf(Long.MIN_VALUE);
+                maxValue = BigInteger.valueOf(Long.MAX_VALUE);
+                fieldType = DefaultLongFieldItem.class;
+                valueType = Long[].class;
+                castedValues = new Long[values.length];
+                break;
+            case SINT:
+                minValue = BigInteger.valueOf((long) Byte.MIN_VALUE);
+                maxValue = BigInteger.valueOf((long) Byte.MAX_VALUE);
+                fieldType = DefaultByteFieldItem.class;
+                valueType = Byte[].class;
+                castedValues = new Byte[values.length];
+                break;
+            case USINT:
+                minValue = BigInteger.valueOf((long) 0);
+                maxValue = BigInteger.valueOf((long) Byte.MAX_VALUE * 2);
+                fieldType = DefaultShortFieldItem.class;
+                valueType = Short[].class;
+                castedValues = new Short[values.length];
+                break;
+            case INT:
+                minValue = BigInteger.valueOf((long) Short.MIN_VALUE);
+                maxValue = BigInteger.valueOf((long) Short.MAX_VALUE);
+                fieldType = DefaultShortFieldItem.class;
+                valueType = Short[].class;
+                castedValues = new Short[values.length];
+                break;
+            case UINT:
+                minValue = BigInteger.valueOf((long) 0);
+                maxValue = BigInteger.valueOf(((long) Short.MAX_VALUE) * 2);
+                fieldType = DefaultIntegerFieldItem.class;
+                valueType = Integer[].class;
+                castedValues = new Integer[values.length];
+                break;
+            case DINT:
+                minValue = BigInteger.valueOf((long) Integer.MIN_VALUE);
+                maxValue = BigInteger.valueOf((long) Integer.MAX_VALUE);
+                fieldType = DefaultIntegerFieldItem.class;
+                valueType = Integer[].class;
+                castedValues = new Integer[values.length];
+                break;
+            case UDINT:
+                minValue = BigInteger.valueOf((long) 0);
+                maxValue = BigInteger.valueOf(((long) Integer.MAX_VALUE) * 2);
+                fieldType = DefaultLongFieldItem.class;
+                valueType = Long[].class;
+                castedValues = new Long[values.length];
+                break;
+            case LINT:
+                minValue = BigInteger.valueOf(Long.MIN_VALUE);
+                maxValue = BigInteger.valueOf(Long.MAX_VALUE);
+                fieldType = DefaultLongFieldItem.class;
+                valueType = Long[].class;
+                castedValues = new Long[values.length];
+                break;
+            case ULINT:
+                minValue = BigInteger.valueOf((long) 0);
+                maxValue = BigInteger.valueOf(Long.MAX_VALUE).multiply(BigInteger.valueOf((long) 2));
+                fieldType = DefaultBigIntegerFieldItem.class;
+                valueType = BigInteger[].class;
+                castedValues = new BigInteger[values.length];
+                break;
+            default:
+                throw new IllegalArgumentException(
+                    "Cannot assign integer values to " + s7Field.getDataType().name() + " fields.");
+        }
+
+        // Check the constraints
+        for (int i = 0; i < values.length; i++) {
+            BigInteger value;
+            if (values[i] instanceof BigInteger) {
+                value = (BigInteger) values[i];
+            } else if ((values[i] instanceof Byte) || (values[i] instanceof Short) ||
+                (values[i] instanceof Integer) || (values[i] instanceof Long)) {
+                value = BigInteger.valueOf(((Number) values[i]).longValue());
+            } else {
+                throw new IllegalArgumentException(
+                    "Value of type " + values[i].getClass().getName() +
+                        " is not assignable to " + s7Field.getDataType().name() + " fields.");
+            }
+            if (minValue.compareTo(value) > 0) {
+                throw new IllegalArgumentException(
+                    "Value of " + value.toString() + " exceeds allowed minimum for type "
+                        + s7Field.getDataType().name() + " (min " + minValue.toString() + ")");
+            }
+            if (maxValue.compareTo(value) < 0) {
+                throw new IllegalArgumentException(
+                    "Value of " + value.toString() + " exceeds allowed maximum for type "
+                        + s7Field.getDataType().name() + " (max " + maxValue.toString() + ")");
+            }
+            if (valueType == Byte[].class) {
+                castedValues[i] = value.byteValue();
+            } else if (valueType == Short[].class) {
+                castedValues[i] = value.shortValue();
+            } else if (valueType == Integer[].class) {
+                castedValues[i] = value.intValue();
+            } else if (valueType == Long[].class) {
+                castedValues[i] = value.longValue();
+            } else {
+                castedValues[i] = value;
+            }
+        }
+
+        // Create the field item.
+        try {
+            return fieldType.getDeclaredConstructor(valueType).newInstance(new Object[]{castedValues});
+        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+            throw new PlcRuntimeException("Error initializing field class " + fieldType.getSimpleName(), e);
+        }
+    }
+
+    private BaseDefaultFieldItem internalEncodeFloatingPoint(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+
+        // Initialize the constraints.
+        Double minValue;
+        Double maxValue;
+        Class<? extends BaseDefaultFieldItem> fieldType;
+        Class<?> valueType;
+        Object[] castedValues;
+        switch (s7Field.getDataType()) {
+            case REAL:
+                minValue = (double) -Float.MAX_VALUE;
+                maxValue = (double) Float.MAX_VALUE;
+                fieldType = DefaultFloatFieldItem.class;
+                valueType = Float[].class;
+                castedValues = new Float[values.length];
+                break;
+            case LREAL:
+                minValue = -Double.MAX_VALUE;
+                maxValue = Double.MAX_VALUE;
+                fieldType = DefaultDoubleFieldItem.class;
+                valueType = Double[].class;
+                castedValues = new Double[values.length];
+                break;
+            default:
+                throw new IllegalArgumentException(
+                    "Cannot assign floating point values to " + s7Field.getDataType().name() + " fields.");
+        }
+
+        // Check the constraints
+        for (int i = 0; i < values.length; i++) {
+            Double value;
+            if (values[i] instanceof Float) {
+                value = ((Float) values[i]).doubleValue();
+            } else if (values[i] instanceof Double) {
+                value = (Double) values[i];
+            } else {
+                throw new IllegalArgumentException(
+                    "Value of type " + values[i].getClass().getName() +
+                        " is not assignable to " + s7Field.getDataType().name() + " fields.");
+            }
+            if (value < minValue) {
+                throw new IllegalArgumentException(
+                    "Value of " + value + " exceeds allowed minimum for type "
+                        + s7Field.getDataType().name() + " (min " + minValue.toString() + ")");
+            }
+            if (value > maxValue) {
+                throw new IllegalArgumentException(
+                    "Value of " + value + " exceeds allowed maximum for type "
+                        + s7Field.getDataType().name() + " (max " + maxValue.toString() + ")");
+            }
+            if (valueType == Float[].class) {
+                castedValues[i] = value.floatValue();
+            } else {
+                castedValues[i] = value;
+            }
+        }
+
+        // Create the field item.
+        try {
+            return fieldType.getDeclaredConstructor(valueType).newInstance(new Object[]{castedValues});
+        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+            throw new PlcRuntimeException("Error initializing field class " + fieldType.getSimpleName(), e);
+        }
+    }
+
+    private BaseDefaultFieldItem internalEncodeString(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+
+        // Initialize the constraints.
+        int maxLength;
+        boolean encoding16Bit;
+        switch (s7Field.getDataType()) {
+            case CHAR:
+                maxLength = 1;
+                encoding16Bit = false;
+                break;
+            case WCHAR:
+                maxLength = 1;
+                encoding16Bit = true;
+                break;
+            case STRING:
+                maxLength = 254;
+                encoding16Bit = false;
+                break;
+            case WSTRING:
+                maxLength = 254;
+                encoding16Bit = true;
+                break;
+            default:
+                throw new IllegalArgumentException(
+                    "Cannot assign string values to " + s7Field.getDataType().name() + " fields.");
+        }
+
+        // Check the constraints and create the strings.
+        List<String> stringValues = new LinkedList<>();
+        for (Object value : values) {
+            if (value instanceof String) {
+                String stringValue = (String) value;
+                if (stringValue.length() > maxLength) {
+                    throw new IllegalArgumentException(
+                        "String length " + stringValue.length() + " exceeds allowed maximum for type "
+                            + s7Field.getDataType().name() + " (max " + maxLength + ")");
+                }
+                stringValues.add(stringValue);
+            }
+            // All other types just translate to max one String character.
+            else if (value instanceof Byte) {
+                Byte byteValue = (Byte) value;
+                byte[] stringBytes = new byte[]{byteValue};
+                if (encoding16Bit) {
+                    stringValues.add(new String(stringBytes, StandardCharsets.UTF_16));
+                } else {
+                    stringValues.add(new String(stringBytes, StandardCharsets.UTF_8));
+                }
+            } else if (value instanceof Short) {
+                Short shortValue = (Short) value;
+                byte[] stringBytes = new byte[2];
+                stringBytes[0] = (byte) (shortValue >> 8);
+                stringBytes[1] = (byte) (shortValue & 0xFF);
+                if (encoding16Bit) {
+                    stringValues.add(new String(stringBytes, StandardCharsets.UTF_16));
+                } else {
+                    stringValues.add(new String(stringBytes, StandardCharsets.UTF_8));
+                }
+            } else if (value instanceof Integer) {
+                Integer integerValue = (Integer) value;
+                byte[] stringBytes = new byte[4];
+                stringBytes[0] = (byte) ((integerValue >> 24) & 0xFF);
+                stringBytes[1] = (byte) ((integerValue >> 16) & 0xFF);
+                stringBytes[2] = (byte) ((integerValue >> 8) & 0xFF);
+                stringBytes[3] = (byte) (integerValue & 0xFF);
+                if (encoding16Bit) {
+                    stringValues.add(new String(stringBytes, StandardCharsets.UTF_16));
+                } else {
+                    stringValues.add(new String(stringBytes, StandardCharsets.UTF_8));
+                }
+            } else if (value instanceof Long) {
+                Long longValue = (Long) value;
+                byte[] stringBytes = new byte[8];
+                stringBytes[0] = (byte) ((longValue >> 56) & 0xFF);
+                stringBytes[1] = (byte) ((longValue >> 48) & 0xFF);
+                stringBytes[2] = (byte) ((longValue >> 40) & 0xFF);
+                stringBytes[3] = (byte) ((longValue >> 32) & 0xFF);
+                stringBytes[4] = (byte) ((longValue >> 24) & 0xFF);
+                stringBytes[5] = (byte) ((longValue >> 16) & 0xFF);
+                stringBytes[6] = (byte) ((longValue >> 8) & 0xFF);
+                stringBytes[7] = (byte) (longValue & 0xFF);
+                if (encoding16Bit) {
+                    stringValues.add(new String(stringBytes, StandardCharsets.UTF_16));
+                } else {
+                    stringValues.add(new String(stringBytes, StandardCharsets.UTF_8));
+                }
+            } else {
+                throw new IllegalArgumentException(
+                    "Value of type " + value.getClass().getName() +
+                        " is not assignable to " + s7Field.getDataType().name() + " fields.");
+            }
+        }
+
+        // Create the field item.
+        return new DefaultStringFieldItem(stringValues.toArray(new String[0]));
+    }
+
+    private BaseDefaultFieldItem internalEncodeTemporal(PlcField field, Object[] values) {
+        S7Field s7Field = (S7Field) field;
+        switch (s7Field.getDataType()) {
+            case TIME:
+                // TODO: I think I should implement this some time ...
+            case DATE:
+                // TODO: I think I should implement this some time ...
+            case DATE_AND_TIME:
+                return new DefaultLocalDateTimeFieldItem();
+            default:
+                throw new IllegalArgumentException(
+                    "Cannot assign temporal values to " + s7Field.getDataType().name() + " fields.");
+        }
+    }
+
+}
diff --git a/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7TsapIdEncoder.java b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7TsapIdEncoder.java
new file mode 100644
index 0000000..edd8b3c
--- /dev/null
+++ b/sandbox/test-java-s7-driver/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/S7TsapIdEncoder.java
@@ -0,0 +1,48 @@
+/*
+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.utils;
+
+import org.apache.plc4x.java.s7.readwrite.types.DeviceGroup;
+
+public class S7TsapIdEncoder {
+
+    private S7TsapIdEncoder() {
+        // Prevent this from being instantiated.
+    }
+
+    public static short encodeS7TsapId(DeviceGroup deviceGroup, int rack, int slot) {
+        short firstByte = (short) (deviceGroup.getValue() << 8);
+        short secondByte = (short) ((rack << 4) | (slot & 0x0F));
+        return (short) (firstByte | secondByte);
+    }
+
+    public static DeviceGroup decodeDeviceGroup(short tsapId) {
+        byte deviceGroupCode = (byte) ((tsapId >> 8) & (0xFF));
+        return DeviceGroup.valueOf(deviceGroupCode);
+    }
+
+    public static int decodeRack(short tsapId) {
+        return (tsapId >> 4) & 0xF;
+    }
+
+    public static int decodeSlot(short tsapId) {
+        return tsapId & 0xF;
+    }
+
+}
diff --git a/sandbox/test-java-s7-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.spi.PlcDriver b/sandbox/test-java-s7-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.spi.PlcDriver
new file mode 100644
index 0000000..728a544
--- /dev/null
+++ b/sandbox/test-java-s7-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.spi.PlcDriver
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+org.apache.plc4x.java.s7.readwrite.S7Driver