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/02/07 21:30:07 UTC

[incubator-plc4x] 01/02: - Continued working on the dynamic s7 driver

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

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

commit 66aebb38a350411a25dfa149ce996a992924905f
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Thu Feb 7 15:22:50 2019 +0100

    - Continued working on the dynamic s7 driver
    
    (In it's current state it is able to connect to a remote PLC and identify the type)
---
 .../apache/plc4x/protocols/s7/protocol.dfdl.xsd    |   2 +-
 .../apache/plc4x/protocols/s7/protocol.scxml.xml   | 248 +++++++++------------
 .../org/apache/plc4x/protocols/s7/protocol.tdml    |   4 +-
 sandbox/dynamic-driver-s7/pom.xml                  |  32 +++
 .../java/org/apache/plc4x/sandbox/java/s7/Poc.java |  32 ++-
 .../java/s7/actions/BaseConnectedAction.java       |  34 +++
 ...asePlc4xAction.java => BaseDaffodilAction.java} |  22 +-
 .../sandbox/java/s7/actions/BasePlc4xAction.java   |  58 ++---
 .../sandbox/java/s7/actions/ConnectAction.java     |  23 +-
 .../sandbox/java/s7/actions/InitContextAction.java |  61 ++---
 .../sandbox/java/s7/actions/ReceiveAction.java     | 191 +++++++++++++---
 .../plc4x/sandbox/java/s7/actions/SendAction.java  |  25 +--
 .../java/s7/actions/s7/S7DecodeArticleNumber.java  |  91 ++++++++
 .../s7/utils/W3CDOMTemplateInfosetInputter.scala   |  42 ++++
 14 files changed, 571 insertions(+), 294 deletions(-)

diff --git a/protocols/s7/src/main/resources/org/apache/plc4x/protocols/s7/protocol.dfdl.xsd b/protocols/s7/src/main/resources/org/apache/plc4x/protocols/s7/protocol.dfdl.xsd
index 07715ab..b0ec9f8 100644
--- a/protocols/s7/src/main/resources/org/apache/plc4x/protocols/s7/protocol.dfdl.xsd
+++ b/protocols/s7/src/main/resources/org/apache/plc4x/protocols/s7/protocol.dfdl.xsd
@@ -177,7 +177,7 @@
             </xs:choice>
             <xs:element name="userData" type="s7:S7MessageType" minOccurs="0"
                         dfdl:occursCountKind="expression"
-                        dfdl:occursCount="{if((../../length - (../headerLength + 1)) gt 0) then 1 else 0}"/>
+                        dfdl:occursCount="{if((../../length - (../headerLength + 5)) gt 0) then 1 else 0}"/>
         </xs:sequence>
     </xs:complexType>
 
diff --git a/protocols/s7/src/main/resources/org/apache/plc4x/protocols/s7/protocol.scxml.xml b/protocols/s7/src/main/resources/org/apache/plc4x/protocols/s7/protocol.scxml.xml
index e272f77..bfab15c 100644
--- a/protocols/s7/src/main/resources/org/apache/plc4x/protocols/s7/protocol.scxml.xml
+++ b/protocols/s7/src/main/resources/org/apache/plc4x/protocols/s7/protocol.scxml.xml
@@ -25,25 +25,42 @@
           datamodel="jexl"
           xsi:schemaLocation="http://www.w3.org/2005/07/scxml http://www.w3.org/2011/04/SCXML/scxml.xsd">
 
+  <!-- Define all the variables we're going to use -->
   <sc:datamodel>
-    <sc:data id="args"/>
+    <sc:data id="protocolDaffodilSchema"/>
+    <sc:data id="cotpLocalReference"/>
+    <sc:data id="cotpCalledTsap"/>
+    <sc:data id="cotpCallingTsap"/>
+    <sc:data id="cotpTpduSize"/>
+    <sc:data id="s7MaxAmqCaller"/>
+    <sc:data id="s7MaxAmqCallee"/>
+    <sc:data id="s7PduLength"/>
+    <sc:data id="s7ArticleNumber"/>
+    <sc:data id="plcType"/>
   </sc:datamodel>
 
+  <!--
+    Setup the initial state ... this usually just initializes the Daffodil subsystem.
+  -->
   <sc:state id="init">
     <sc:onentry>
       <!-- Setup the initial content of the connection context (Callback in the driver) -->
-      <plc4x:initContext/>
+      <plc4x:initContext protocolDaffodilSchemaName="protocolDaffodilSchema"/>
     </sc:onentry>
     <sc:transition event="success" target="connect">
       <sc:assign location="args" expr="_event.data"/>
     </sc:transition>
   </sc:state>
 
+  <!--
+    Sub-Statemachine handling the connection establishment.
+  -->
   <sc:state id="connect">
     <sc:initial>
       <sc:transition target="establishNetworkConnection"/>
     </sc:initial>
 
+    <!-- This step establishes the physical connection to the remote -->
     <sc:state id="establishNetworkConnection">
       <sc:onentry>
         <!-- Initialize the network connection to the remote host using the tcp adapter with a given host and port -->
@@ -53,9 +70,20 @@
       <sc:transition event="failure" target="error"/>
     </sc:state>
 
+    <!--
+      First we have to connect on COTP level, so send CTOP connection request first.
+      Some parameters we are sending are a suggestion from our side, the remote will
+      respond with values it sees more fitting.
+
+      For example the "called-tsap" we just make up an id and the remote will respond
+      with its real id.
+
+      The pdu size is the one is the maximum PDU size we can live with, the remote
+      will respond with a size that is at most this big (usually it's smaller).
+    -->
     <sc:state id="sendCotpConnectionRequest">
       <sc:onentry>
-        <plc4x:send socketParameterName="connection">
+        <plc4x:send>
           <s7:TpktMessage>
             <magicByte>3</magicByte>
             <reserved>0</reserved>
@@ -65,29 +93,29 @@
               <type>224</type>
               <s7:CotpTpduConnectionRequest>
                 <destinationReference>0</destinationReference>
-                <!-- Insert the value for "cotp-local-reference" as short here -->
-                <sourceReference>15</sourceReference><!--plc4x:insert type="s7:short" name="cotp-local-reference"/-->
+                <!-- Insert the value for "cotpLocalReference" as short here -->
+                <sourceReference>${cotpLocalReference}</sourceReference>
                 <protocolClass>0</protocolClass>
                 <s7:parameters>
                   <parameter>
                     <type>194</type>
                     <parameterLength>2</parameterLength>
                     <s7:CotpParameterCalledTsap>
-                      <tsapId>512</tsapId><!--plc4x:insert type="s7:short" name="cotp-called-tsap"/-->
+                      <tsapId>${cotpCalledTsap}</tsapId>
                     </s7:CotpParameterCalledTsap>
                   </parameter>
                   <parameter>
                     <type>193</type>
                     <parameterLength>2</parameterLength>
                     <s7:CotpParameterCallingTsap>
-                      <tsapId>273</tsapId><!--plc4x:insert type="s7:short" name="cotp-calling-tsap"/-->
+                      <tsapId>${cotpCallingTsap}</tsapId>
                     </s7:CotpParameterCallingTsap>
                   </parameter>
                   <parameter>
                     <type>192</type>
                     <parameterLength>1</parameterLength>
                     <s7:CotpParameterTpduSize>
-                      <tpduSize>10</tpduSize><!--plc4x:insert type="s7:byte" name="cotp-tpdu-size"/-->
+                      <tpduSize>${cotpTpduSize}</tpduSize>
                     </s7:CotpParameterTpduSize>
                   </parameter>
                 </s7:parameters>
@@ -100,23 +128,33 @@
       <sc:transition event="failure" target="error"/>
     </sc:state>
 
+    <!--
+      If everything wen't fine, the remote should respond with a connection response.
+      As mentioned before we now have to update some of the values with the ones the
+      remote responded with.
+    -->
     <sc:state id="receiveCotpConnectionResponse">
       <sc:onentry>
-        <plc4x:receive socketParameterName="connection" timeout="5000">
-          <verification name="cotp-local-reference" xpath="s7:TpktMessage/userData/s7:CotpTpduConnectionResponse/destinationReference"/>
-          <extraction name="cotp-remote-reference" xpath="s7:TpktMessage/userData/s7:CotpTpduConnectionResponse/sourceReference"/>
-          <extraction name="cotp-tpdu-size" xpath="s7:TpktMessage/userData/s7:CotpTpduConnectionResponse/s7:parameters/parameter/s7:CotpParameterTpduSize/tpduSize"/>
-          <extraction name="cotp-calling-tsap" xpath="s7:TpktMessage/userData/s7:CotpTpduConnectionResponse/s7:parameters/parameter/s7:CotpParameterCallingTsap/tsapId"/>
-          <extraction name="cotp-called-tsap" xpath="s7:TpktMessage/userData/s7:CotpTpduConnectionResponse/s7:parameters/parameter/s7:CotpParameterCalledTsap/tsapId"/>
+        <plc4x:receive timeout="5000" packetLengthStartPosition="2" packetLengthSizeInBytes="2">
+          <verification name="cotpLocalReference" xpath-expression="/s7:TpktMessage/userData/s7:CotpTpduConnectionResponse/destinationReference/text()[1]"/>
+          <extraction name="cotpRemoteReference" xpath-expression="/s7:TpktMessage/userData/s7:CotpTpduConnectionResponse/sourceReference/text()[1]"/>
+          <extraction name="cotpTpduSize" xpath-expression="/s7:TpktMessage/userData/s7:CotpTpduConnectionResponse/s7:parameters/parameter/s7:CotpParameterTpduSize/tpduSize/text()[1]"/>
+          <extraction name="cotpCallingTsap" xpath-expression="/s7:TpktMessage/userData/s7:CotpTpduConnectionResponse/s7:parameters/parameter/s7:CotpParameterCallingTsap/tsapId/text()[1]"/>
+          <extraction name="cotpCalledTsap" xpath-expression="/s7:TpktMessage/userData/s7:CotpTpduConnectionResponse/s7:parameters/parameter/s7:CotpParameterCalledTsap/tsapId/text()[1]"/>
         </plc4x:receive>
       </sc:onentry>
       <sc:transition event="success" target="sendS7SetupCommunicationRequest"/>
       <sc:transition event="failure" target="error"/>
     </sc:state>
 
+    <!--
+      After the connection is established on COTP level, we now do the same on the S7
+      protocol level. Again we will be sending values we think are ok and in the
+      response the remote will tell us what it can live with.
+    -->
     <sc:state id="sendS7SetupCommunicationRequest">
       <sc:onentry>
-        <plc4x:send socketParameterName="connection">
+        <plc4x:send>
           <s7:TpktMessage>
             <magicByte>3</magicByte>
             <reserved>0</reserved>
@@ -136,17 +174,17 @@
                   <tpduReference>0</tpduReference>
                   <parametersLength>8</parametersLength>
                   <payloadsLength>0</payloadsLength>
-                  <s7:parameters>
+                  <parameters>
                     <parameter>
                       <type>240</type>
                       <s7:S7GeneralParameterSetupCommunication>
                         <reserved>0</reserved>
-                        <maxAmqCaller>10</maxAmqCaller><!--plc4x:insert type="s7:short" name="s7-max-amq-caller"/-->
-                        <maxAmqCallee>10</maxAmqCallee><!--plc4x:insert type="s7:short" name="s7-max-amq-callee"/-->
-                        <pduLength>1024</pduLength><!--plc4x:insert type="s7:short" name="s7-pdu-length"/-->
+                        <maxAmqCaller>${s7MaxAmqCaller}</maxAmqCaller>
+                        <maxAmqCallee>${s7MaxAmqCallee}</maxAmqCallee>
+                        <pduLength>${s7PduLength}</pduLength>
                       </s7:S7GeneralParameterSetupCommunication>
                     </parameter>
-                  </s7:parameters>
+                  </parameters>
                   <payloads>
                     <payload>
                       <s7:S7GeneralPayloadSetupCommunication/>
@@ -162,24 +200,38 @@
       <sc:transition event="failure" target="error"/>
     </sc:state>
 
+    <!--
+      If everything wen't fine, the remote should respond with a connection response.
+      As mentioned before we now have to update some of the values with the ones the
+      remote responded with.
+
+      If a "plcType" was provided, we are now connected.
+
+      If no "plcType" was provided, the type has to be discovered by sending another
+      request and processing that response first.
+    -->
     <sc:state id="receiveS7SetupCommunicationResponse">
       <sc:onentry>
-        <plc4x:receive socketParameterName="connection" timeout="5000">
-          <verification name="cotp-local-reference" xpath="s7:TpktMessage/userData/s7:CotpTpduConnectionResponse/destinationReference"/>
-          <extraction name="returnCode" xpath="s7:TpktMessage/userData/userData/s7:S7ResponseMessage/errorCode"/>
-          <extraction name="s7-max-amq-caller" xpath="s7:TpktMessage/userData/userData/s7:S7ResponseMessage/parameters/s7:S7GeneralParameterSetupCommunication/maxAmqCaller"/>
-          <extraction name="s7-max-amq-callee" xpath="s7:TpktMessage/userData/userData/s7:S7ResponseMessage/parameters/s7:S7GeneralParameterSetupCommunication/maxAmqCallee"/>
-          <extraction name="s7-pdu-length" xpath="s7:TpktMessage/userData/userData/s7:S7ResponseMessage/parameters/s7:S7GeneralParameterSetupCommunication/pduLength"/>
+        <plc4x:receive timeout="5000" packetLengthStartPosition="2" packetLengthSizeInBytes="2">
+          <extraction name="returnCode" xpath-expression="/s7:TpktMessage/userData/userData/s7:S7ResponseMessage/errorCode/text()[1]"/>
+          <extraction name="s7MaxAmqCaller" xpath-expression="/s7:TpktMessage/userData/userData/s7:S7ResponseMessage/parameters/parameter/s7:S7GeneralParameterSetupCommunication/maxAmqCaller/text()[1]"/>
+          <extraction name="s7MaxAmqCallee" xpath-expression="/s7:TpktMessage/userData/userData/s7:S7ResponseMessage/parameters/parameter/s7:S7GeneralParameterSetupCommunication/maxAmqCallee/text()[1]"/>
+          <extraction name="s7PduLength" xpath-expression="/s7:TpktMessage/userData/userData/s7:S7ResponseMessage/parameters/parameter/s7:S7GeneralParameterSetupCommunication/pduLength/text()[1]"/>
         </plc4x:receive>
       </sc:onentry>
-      <sc:transition event="" target="sendS7IdentificationRequest"/>
-      <sc:transition event="success" target="connected"/>
+      <sc:transition event="success" cond="plcType == null" target="sendS7IdentificationRequest"/>
+      <sc:transition event="success" cond="plcType != null" target="connected"/>
       <sc:transition event="failure" target="error"/>
     </sc:state>
 
+    <!--
+      If no "plcType" was provided, an S7 identification request will ask the
+      remote to send back so-called SSLs. These contain information on the type
+      and version of the remote PLC.
+    -->
     <sc:state id="sendS7IdentificationRequest">
       <sc:onentry>
-        <plc4x:send socketParameterName="connection">
+        <plc4x:send>
           <s7:TpktMessage>
             <magicByte>3</magicByte>
             <reserved>0</reserved>
@@ -199,7 +251,7 @@
                   <tpduReference>256</tpduReference>
                   <parametersLength>8</parametersLength>
                   <payloadsLength>8</payloadsLength>
-                  <s7:parameters>
+                  <parameters>
                     <parameter>
                       <type>0</type>
                       <s7:S7UserDataParameterCPUService>
@@ -212,7 +264,7 @@
                         <sequenceNumber>0</sequenceNumber>
                       </s7:S7UserDataParameterCPUService>
                     </parameter>
-                  </s7:parameters>
+                  </parameters>
                   <payloads>
                     <payload>
                       <s7:S7UserDataPayloadCpuServices>
@@ -234,130 +286,50 @@
       <sc:transition event="failure" target="error"/>
     </sc:state>
 
+    <!--
+      As we're currently only interested in the type of the PLC, we simply
+      take the article number returned and decode that in a S7 specific
+      custom action.
+    -->
     <sc:state id="receiveS7IdentificationRequest">
       <sc:onentry>
-        <plc4x:receive socketParameterName="connection">
-          <s7:TpktMessage>
-            <magicByte>3</magicByte>
-            <reserved>0</reserved>
-            <length><plc4x:ignore/></length>
-            <userData>
-              <headerLength>2</headerLength>
-              <type>240</type>
-              <s7:CotpTpduData>
-                <endOfTransmission>1</endOfTransmission>
-                <tpduRef>0</tpduRef>
-              </s7:CotpTpduData>
-              <userData>
-                <magicByte>50</magicByte>
-                <type>7</type>
-                <s7:S7UserDataMessage>
-                  <reserved>0</reserved>
-                  <tpduReference>256</tpduReference>
-                  <parametersLength><plc4x:ignore/></parametersLength>
-                  <payloadsLength><plc4x:ignore/></payloadsLength>
-                  <s7:parameters>
-                    <plc4x:unordered>
-                      <parameter>
-                        <type>0</type>
-                        <S7UserDataParameterCPUService>
-                          <header>274</header>
-                          <paramLength>8</paramLength>
-                          <typeCode>18</typeCode>
-                          <type>8</type>
-                          <functionGroup>4</functionGroup>
-                          <subFunctionGroup>1</subFunctionGroup>
-                          <sequenceNumber>2</sequenceNumber>
-                          <dataUnitReferenceNumber>0</dataUnitReferenceNumber>
-                          <lastDataUnit>0</lastDataUnit>
-                          <errorCode>0</errorCode>
-                        </S7UserDataParameterCPUService>
-                      </parameter>
-                      <plc4x:ignore/>
-                    </plc4x:unordered>
-                  </s7:parameters>
-                  <payloads>
-                    <plc4x:unordered>
-                      <payload>
-                        <S7UserDataPayloadCpuServices>
-                          <returnCode>255</returnCode>
-                          <transportSize>9</transportSize>
-                          <length>120</length>
-                          <sslId>17</sslId>
-                          <sslIndex>0</sslIndex>
-                          <partialList>
-                            <partialListLengthInBytes>28</partialListLengthInBytes>
-                            <partialListCount>4</partialListCount>
-                            <sslDataRecords>
-                              <plc4x:unordered>
-                                <sslDataRecord>
-                                  <S7ResponsePayloadCpuServicesSslDataRecordModuleIdentification>
-                                    <index>1</index>
-                                    <articleNumber><plc4x:extract type="s7:short" name="s7-ssl-1"/></articleNumber>
-                                    <bgType>192</bgType>
-                                    <moduleOrOsVersion>3</moduleOrOsVersion>
-                                    <pgDescriptionFileVersion>1</pgDescriptionFileVersion>
-                                  </S7ResponsePayloadCpuServicesSslDataRecordModuleIdentification>
-                                </sslDataRecord>
-                                <sslDataRecord>
-                                  <S7ResponsePayloadCpuServicesSslDataRecordModuleIdentification>
-                                    <index>6</index>
-                                    <articleNumber><plc4x:extract type="s7:short" name="s7-ssl-6"/></articleNumber>
-                                    <bgType>192</bgType>
-                                    <moduleOrOsVersion>3</moduleOrOsVersion>
-                                    <pgDescriptionFileVersion>1</pgDescriptionFileVersion>
-                                  </S7ResponsePayloadCpuServicesSslDataRecordModuleIdentification>
-                                </sslDataRecord>
-                                <sslDataRecord>
-                                  <S7ResponsePayloadCpuServicesSslDataRecordModuleIdentification>
-                                    <index>7</index>
-                                    <articleNumber><plc4x:extract type="s7:short" name="s7-ssl-7"/></articleNumber>
-                                    <bgType>192</bgType>
-                                    <moduleOrOsVersion>22019</moduleOrOsVersion>
-                                    <pgDescriptionFileVersion>519</pgDescriptionFileVersion>
-                                  </S7ResponsePayloadCpuServicesSslDataRecordModuleIdentification>
-                                </sslDataRecord>
-                                <sslDataRecord>
-                                  <S7ResponsePayloadCpuServicesSslDataRecordModuleIdentification>
-                                    <index>129</index>
-                                    <articleNumber><plc4x:extract type="s7:short" name="s7-ssl-129"/></articleNumber>
-                                    <bgType>0</bgType>
-                                    <moduleOrOsVersion>16672</moduleOrOsVersion>
-                                    <pgDescriptionFileVersion>2313</pgDescriptionFileVersion>
-                                  </S7ResponsePayloadCpuServicesSslDataRecordModuleIdentification>
-                                </sslDataRecord>
-                                <plc4x:ignore/>
-                              </plc4x:unordered>
-                            </sslDataRecords>
-                          </partialList>
-                        </S7UserDataPayloadCpuServices>
-                      </payload>
-                      <plc4x:ignore/>
-                    </plc4x:unordered>
-                  </payloads>
-                </s7:S7UserDataMessage>
-              </userData>
-            </userData>
-          </s7:TpktMessage>
+        <plc4x:receive timeout="5000" packetLengthStartPosition="2" packetLengthSizeInBytes="2">
+          <!-- Ensure everything is ok -->
+          <!--verification value="255" xpath-expression="/s7:TpktMessage/userData/userData/s7:S7UserDataMessage/payloads/payload/s7:S7UserDataPayloadCpuServices/returnCode/text()"/-->
+          <!-- Extract the article-number of the SslDataRecordModuleIdentification with the index 1 -->
+          <extraction name="s7ArticleNumber" xpath-expression="/s7:TpktMessage/userData/userData/s7:S7UserDataMessage/payloads/payload/s7:S7UserDataPayloadCpuServices/partialList/sslDataRecords/sslDataRecord//s7:S7ResponsePayloadCpuServicesSslDataRecordModuleIdentification[index='1']/articleNumber/text()"/>
         </plc4x:receive>
       </sc:onentry>
-      <sc:transition event="success" target="connected"/>
+      <sc:transition event="success" target="connected">
+        <plc4x:S7DecodeArticleNumber articleNumberParameterName="s7ArticleNumber" plcTypeParameterName="plcType"/>
+      </sc:transition>
       <sc:transition event="failure" target="error"/>
     </sc:state>
 
+    <!--
+      Default state after connecting to a PLC.
+    -->
     <sc:state id="connected">
       <sc:onentry>
-        <sc:log expr="'Connected'"/>
+        <sc:log expr="'Connected to PLC of type: ' + plcType"/>
       </sc:onentry>
     </sc:state>
 
+    <!--
+      Final state of this state-machine, after any of the parties disconnected.
+    -->
     <sc:final id="disconnected">
-
+      <sc:onentry>
+        <sc:log expr="'Disconnected'"/>
+      </sc:onentry>
     </sc:final>
 
+    <!--
+      Error state in case of any form of error during the processing of data.
+    -->
     <sc:final id="error">
       <sc:onentry>
-        <sc:log expr="'Error connecting'"/>
+        <sc:log expr="'Error'"/>
       </sc:onentry>
     </sc:final>
   </sc:state>
diff --git a/protocols/s7/src/test/resources/org/apache/plc4x/protocols/s7/protocol.tdml b/protocols/s7/src/test/resources/org/apache/plc4x/protocols/s7/protocol.tdml
index e9aff9f..c418628 100644
--- a/protocols/s7/src/test/resources/org/apache/plc4x/protocols/s7/protocol.tdml
+++ b/protocols/s7/src/test/resources/org/apache/plc4x/protocols/s7/protocol.tdml
@@ -49,7 +49,7 @@
                          description="Simple TKPT packet which contains a COTP Connection-Response as payload.">
         <!-- Define the input -->
         <tdml:document>
-            <tdml:documentPart type="byte">0300001211D00001000200C00109C1020100C2020102</tdml:documentPart>
+            <tdml:documentPart type="byte">0300001611D00001000200C00109C1020100C2020102</tdml:documentPart>
         </tdml:document>
 
         <!-- Define the expected output -->
@@ -59,7 +59,7 @@
                 <test:tpktMessage>
                     <magicByte>3</magicByte>
                     <reserved>0</reserved>
-                    <length>18</length>
+                    <length>22</length>
                     <userData>
                         <headerLength>17</headerLength>
                         <type>208</type>
diff --git a/sandbox/dynamic-driver-s7/pom.xml b/sandbox/dynamic-driver-s7/pom.xml
index 9e99f0a..f302333 100644
--- a/sandbox/dynamic-driver-s7/pom.xml
+++ b/sandbox/dynamic-driver-s7/pom.xml
@@ -58,6 +58,11 @@
       <artifactId>xml-apis</artifactId>
       <version>1.4.01</version>
     </dependency>
+    <dependency>
+      <groupId>jaxen</groupId>
+      <artifactId>jaxen</artifactId>
+      <version>1.1.6</version>
+    </dependency>
 
     <dependency>
       <groupId>org.slf4j</groupId>
@@ -74,6 +79,33 @@
   <build>
     <plugins>
       <plugin>
+        <groupId>net.alchim31.maven</groupId>
+        <artifactId>scala-maven-plugin</artifactId>
+        <version>3.4.6</version>
+        <executions>
+          <execution>
+            <id>add-scala-sources</id>
+            <phase>validate</phase>
+            <goals>
+              <goal>add-source</goal>
+            </goals>
+            <configuration>
+              <sourceDir>src/main/scala</sourceDir>
+            </configuration>
+          </execution>
+          <execution>
+            <id>compile-scala</id>
+            <phase>compile</phase>
+            <goals>
+              <goal>compile</goal>
+            </goals>
+            <configuration>
+              <outputDir>${project.build.outputDirectory}</outputDir>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-dependency-plugin</artifactId>
         <configuration>
diff --git a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/Poc.java b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/Poc.java
index 790f2ef..04faee4 100644
--- a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/Poc.java
+++ b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/Poc.java
@@ -30,15 +30,22 @@ import org.apache.plc4x.sandbox.java.s7.actions.ConnectAction;
 import org.apache.plc4x.sandbox.java.s7.actions.InitContextAction;
 import org.apache.plc4x.sandbox.java.s7.actions.ReceiveAction;
 import org.apache.plc4x.sandbox.java.s7.actions.SendAction;
+import org.apache.plc4x.sandbox.java.s7.actions.s7.S7DecodeArticleNumber;
 
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 public class Poc {
 
+    private String dataFormatURI;
+
     private SCXMLExecutor executor;
 
-    private Poc() throws Exception {
+    private Poc(String stateMachineURI, String dataFormatURI) throws Exception {
+        this.dataFormatURI = dataFormatURI;
+
         // Initialize our PLC4X specific actions.
         List<CustomAction> customActions = new LinkedList<>();
         customActions.add(
@@ -49,10 +56,12 @@ public class Poc {
             new CustomAction("https://plc4x.apache.org/scxml-extension", "send", SendAction.class));
         customActions.add(
             new CustomAction("https://plc4x.apache.org/scxml-extension", "receive", ReceiveAction.class));
+        customActions.add(
+            new CustomAction("https://plc4x.apache.org/scxml-extension", "S7DecodeArticleNumber", S7DecodeArticleNumber.class));
 
         // Initialize the state-machine with the definition from the protocol module.
         SCXML scxml = SCXMLReader.read(
-            Poc.class.getClassLoader().getResource("org/apache/plc4x/protocols/s7/protocol.scxml.xml"),
+            Poc.class.getClassLoader().getResource(stateMachineURI),
             new SCXMLReader.Configuration(null, null, customActions));
 
         // Create an executor for running the state-machine.
@@ -62,12 +71,27 @@ public class Poc {
     }
 
     private void run() throws Exception {
+        Map<String, Object> context = new HashMap<>();
+        context.put("protocolDaffodilSchema", dataFormatURI);
+
+        context.put("cotpLocalReference", "15");
+        context.put("cotpCalledTsap", "512");
+        context.put("cotpCallingTsap", "273");
+        context.put("cotpTpduSize", "10");
+        context.put("s7MaxAmqCaller", "10");
+        context.put("s7MaxAmqCallee", "10");
+        context.put("s7PduLength", "1024");
+
+        //context.put("plcType", "HURZ");
+
         // Run the state-machine.
-        executor.go();
+        executor.go(context);
     }
 
     public static void main(String[] args) throws Exception {
-        Poc poc = new Poc();
+        Poc poc = new Poc(
+            "org/apache/plc4x/protocols/s7/protocol.scxml.xml",
+            "org/apache/plc4x/protocols/s7/protocol.dfdl.xsd");
         poc.run();
     }
 
diff --git a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BaseConnectedAction.java b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BaseConnectedAction.java
new file mode 100644
index 0000000..cf37c81
--- /dev/null
+++ b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BaseConnectedAction.java
@@ -0,0 +1,34 @@
+/*
+ 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.sandbox.java.s7.actions;
+
+import org.apache.commons.scxml2.ActionExecutionContext;
+
+import java.net.Socket;
+
+public abstract class BaseConnectedAction extends BasePlc4xAction {
+
+    public static final String SOCKET_PARAMETER_NAME="connection";
+
+    protected Socket getSocket(ActionExecutionContext ctx) {
+        return (Socket) ctx.getGlobalContext().get(SOCKET_PARAMETER_NAME);
+    }
+
+}
diff --git a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BasePlc4xAction.java b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BaseDaffodilAction.java
similarity index 74%
copy from sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BasePlc4xAction.java
copy to sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BaseDaffodilAction.java
index e7bb675..4ed74ee 100644
--- a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BasePlc4xAction.java
+++ b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BaseDaffodilAction.java
@@ -20,32 +20,18 @@
 package org.apache.plc4x.sandbox.java.s7.actions;
 
 import org.apache.commons.scxml2.ActionExecutionContext;
-import org.apache.commons.scxml2.model.Action;
 import org.apache.commons.scxml2.model.ParsedValue;
 import org.apache.commons.scxml2.model.ParsedValueContainer;
 import org.apache.daffodil.japi.DataProcessor;
 import org.apache.daffodil.japi.Diagnostic;
 import org.apache.daffodil.japi.WithDiagnostics;
-import org.slf4j.Logger;
 
-import java.net.Socket;
 import java.util.List;
 
-public abstract class BasePlc4xAction extends Action implements ParsedValueContainer {
+public abstract class BaseDaffodilAction extends BaseConnectedAction implements ParsedValueContainer {
 
-
-
-    private String socketParameterName;
     private ParsedValue message;
 
-    public String getSocketParameterName() {
-        return socketParameterName;
-    }
-
-    public void setSocketParameterName(String socketParameterName) {
-        this.socketParameterName = socketParameterName;
-    }
-
     @Override
     public ParsedValue getParsedValue() {
         return message;
@@ -56,12 +42,6 @@ public abstract class BasePlc4xAction extends Action implements ParsedValueConta
         this.message = parsedValue;
     }
 
-    protected abstract Logger getLogger();
-
-    protected Socket getSocket(ActionExecutionContext ctx) {
-        return (Socket) ctx.getGlobalContext().get(getSocketParameterName());
-    }
-
     protected DataProcessor getDaffodilDataProcessor(ActionExecutionContext ctx) {
         return (DataProcessor) ctx.getGlobalContext().get("dfdl");
     }
diff --git a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BasePlc4xAction.java b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BasePlc4xAction.java
index e7bb675..6a763b6 100644
--- a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BasePlc4xAction.java
+++ b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BasePlc4xAction.java
@@ -20,57 +20,33 @@
 package org.apache.plc4x.sandbox.java.s7.actions;
 
 import org.apache.commons.scxml2.ActionExecutionContext;
+import org.apache.commons.scxml2.EventBuilder;
+import org.apache.commons.scxml2.TriggerEvent;
 import org.apache.commons.scxml2.model.Action;
-import org.apache.commons.scxml2.model.ParsedValue;
-import org.apache.commons.scxml2.model.ParsedValueContainer;
-import org.apache.daffodil.japi.DataProcessor;
-import org.apache.daffodil.japi.Diagnostic;
-import org.apache.daffodil.japi.WithDiagnostics;
 import org.slf4j.Logger;
 
-import java.net.Socket;
-import java.util.List;
-
-public abstract class BasePlc4xAction extends Action implements ParsedValueContainer {
-
-
-
-    private String socketParameterName;
-    private ParsedValue message;
-
-    public String getSocketParameterName() {
-        return socketParameterName;
-    }
-
-    public void setSocketParameterName(String socketParameterName) {
-        this.socketParameterName = socketParameterName;
-    }
-
-    @Override
-    public ParsedValue getParsedValue() {
-        return message;
-    }
-
-    @Override
-    public void setParsedValue(ParsedValue parsedValue) {
-        this.message = parsedValue;
-    }
+public abstract class BasePlc4xAction extends Action {
 
     protected abstract Logger getLogger();
 
-    protected Socket getSocket(ActionExecutionContext ctx) {
-        return (Socket) ctx.getGlobalContext().get(getSocketParameterName());
+    protected String getStateName() {
+        try {
+            return getParentEnterableState().getId();
+        } catch (Exception e) {
+            getLogger().error("Unable to get state.");
+        }
+        return "unknown";
     }
 
-    protected DataProcessor getDaffodilDataProcessor(ActionExecutionContext ctx) {
-        return (DataProcessor) ctx.getGlobalContext().get("dfdl");
+    protected void fireFailureEvent(ActionExecutionContext ctx, String message) {
+        TriggerEvent event = new EventBuilder("failure", TriggerEvent.SIGNAL_EVENT).
+            data(getStateName() + ": " + message).build();
+        ctx.getInternalIOProcessor().addEvent(event);
     }
 
-    protected void logDiagnosticInformation(WithDiagnostics withDiagnostics) {
-        List<Diagnostic> diags = withDiagnostics.getDiagnostics();
-        for (Diagnostic d : diags) {
-            getLogger().error(d.getSomeMessage());
-        }
+    protected void fireSuccessEvent(ActionExecutionContext ctx) {
+        TriggerEvent event = new EventBuilder("success", TriggerEvent.SIGNAL_EVENT).data(getStateName()).build();
+        ctx.getInternalIOProcessor().addEvent(event);
     }
 
 }
diff --git a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/ConnectAction.java b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/ConnectAction.java
index 5a7a371..bf374d1 100644
--- a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/ConnectAction.java
+++ b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/ConnectAction.java
@@ -20,14 +20,13 @@
 package org.apache.plc4x.sandbox.java.s7.actions;
 
 import org.apache.commons.scxml2.ActionExecutionContext;
-import org.apache.commons.scxml2.EventBuilder;
-import org.apache.commons.scxml2.TriggerEvent;
-import org.apache.commons.scxml2.model.Action;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.net.Socket;
 
-public class ConnectAction extends Action {
+public class ConnectAction extends BasePlc4xAction {
 
     private String type;
     private String host;
@@ -58,18 +57,24 @@ public class ConnectAction extends Action {
     }
 
     @Override
+    protected Logger getLogger() {
+        return LoggerFactory.getLogger(ConnectAction.class);
+    }
+
+    @Override
     public void execute(ActionExecutionContext ctx) {
-        ctx.getAppLog().info("Connecting...");
+        ctx.getAppLog().info(getStateName() + ": Connecting...");
         try {
             if ("TCP".equalsIgnoreCase(type)) {
                 Socket socket = new Socket(host, Integer.parseInt(port));
-                ctx.getGlobalContext().set("connection", socket);
-                TriggerEvent event = new EventBuilder("success", TriggerEvent.SIGNAL_EVENT).build();
-                ctx.getInternalIOProcessor().addEvent(event);
+                ctx.getGlobalContext().set(BaseConnectedAction.SOCKET_PARAMETER_NAME, socket);
+
                 ctx.getAppLog().info("Connected.");
+
+                fireSuccessEvent(ctx);
             }
         } catch (IOException e) {
-            e.printStackTrace();
+            getLogger().error("Error connecting to remote.", e);
         }
     }
 
diff --git a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/InitContextAction.java b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/InitContextAction.java
index 6b6e5f4..ed93053 100644
--- a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/InitContextAction.java
+++ b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/InitContextAction.java
@@ -22,62 +22,67 @@ package org.apache.plc4x.sandbox.java.s7.actions;
 import org.apache.commons.scxml2.ActionExecutionContext;
 import org.apache.commons.scxml2.EventBuilder;
 import org.apache.commons.scxml2.TriggerEvent;
-import org.apache.commons.scxml2.model.Action;
 import org.apache.daffodil.japi.Compiler;
 import org.apache.daffodil.japi.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
 import java.net.URI;
-import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.List;
 
-public class InitContextAction extends Action {
+public class InitContextAction extends BasePlc4xAction {
 
-    private static final Logger logger = LoggerFactory.getLogger(InitContextAction.class);
+    private String protocolDaffodilSchemaName;
+
+    public String getProtocolDaffodilSchemaName() {
+        return protocolDaffodilSchemaName;
+    }
+
+    public void setProtocolDaffodilSchemaName(String protocolDaffodilSchemaName) {
+        this.protocolDaffodilSchemaName = protocolDaffodilSchemaName;
+    }
+
+    @Override
+    protected Logger getLogger() {
+        return LoggerFactory.getLogger(InitContextAction.class);
+    }
 
     @Override
     public void execute(ActionExecutionContext ctx) {
-        ctx.getAppLog().info("Initializing Context.");
+        ctx.getAppLog().info(getStateName() + ": Initializing Context...");
 
         try {
             Compiler c = Daffodil.compiler();
             c.setValidateDFDLSchemas(true);
-            URL shemaUrl = SendAction.class.getClassLoader().getResource("org/apache/plc4x/protocols/s7/protocol.dfdl.xsd");
-            if (shemaUrl != null) {
-                URI schemaUri = shemaUrl.toURI();
+            String schemaUrlString = (String) ctx.getGlobalContext().get(protocolDaffodilSchemaName);
+            URL schemaUrl = SendAction.class.getClassLoader().getResource(schemaUrlString);
+            if (schemaUrl != null) {
+                URI schemaUri = schemaUrl.toURI();
                 ProcessorFactory pf = c.compileSource(schemaUri);
-                if (pf.isError()) {
-                    logDiagnosticInformation(pf);
-                    TriggerEvent event = new EventBuilder("failure", TriggerEvent.SIGNAL_EVENT).build();
-                    ctx.getInternalIOProcessor().addEvent(event);
-                    return;
-                }
+                logDiagnosticInformation(pf);
                 DataProcessor dp = pf.onPath("/");
-                if (dp.isError()) {
-                    logDiagnosticInformation(dp);
-                    TriggerEvent event = new EventBuilder("failure", TriggerEvent.SIGNAL_EVENT).build();
-                    ctx.getInternalIOProcessor().addEvent(event);
-                    return;
-                }
+                logDiagnosticInformation(dp);
                 ctx.getGlobalContext().set("dfdl", dp);
             }
-        } catch (IOException | URISyntaxException e) {
+        } catch (Exception e) {
+            fireFailureEvent(ctx, "Error initializing daffodil schema");
             TriggerEvent event = new EventBuilder("failure", TriggerEvent.SIGNAL_EVENT).data(e).build();
             ctx.getInternalIOProcessor().addEvent(event);
             return;
         }
 
-        TriggerEvent event = new EventBuilder("success", TriggerEvent.SIGNAL_EVENT).build();
-        ctx.getInternalIOProcessor().addEvent(event);
+        ctx.getAppLog().info("Context initialized.");
+        fireSuccessEvent(ctx);
     }
 
-    private void logDiagnosticInformation(WithDiagnostics withDiagnostics) {
-        List<Diagnostic> diags = withDiagnostics.getDiagnostics();
-        for (Diagnostic d : diags) {
-            logger.error(d.getSomeMessage());
+    private void logDiagnosticInformation(WithDiagnostics withDiagnostics) throws Exception {
+        if(withDiagnostics.isError()) {
+            List<Diagnostic> diags = withDiagnostics.getDiagnostics();
+            for (Diagnostic d : diags) {
+                getLogger().error(d.getSomeMessage());
+            }
+            throw new Exception();
         }
     }
 
diff --git a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/ReceiveAction.java b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/ReceiveAction.java
index d17d937..39ab99f 100644
--- a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/ReceiveAction.java
+++ b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/ReceiveAction.java
@@ -27,27 +27,36 @@ import org.apache.commons.scxml2.model.NodeValue;
 import org.apache.commons.scxml2.model.ParsedValue;
 import org.apache.daffodil.japi.DataProcessor;
 import org.apache.daffodil.japi.ParseResult;
-import org.apache.daffodil.japi.infoset.W3CDOMInfosetOutputter;
+import org.apache.daffodil.japi.infoset.JDOMInfosetOutputter;
+import org.apache.daffodil.japi.io.InputSourceDataInputStream;
+import org.jdom2.Document;
+import org.jdom2.Namespace;
+import org.jdom2.Text;
+import org.jdom2.filter.Filters;
+import org.jdom2.output.XMLOutputter;
+import org.jdom2.xpath.XPathExpression;
+import org.jdom2.xpath.XPathFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
-import java.io.BufferedReader;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.net.Socket;
-import java.nio.channels.Channels;
-import java.nio.channels.ReadableByteChannel;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
-public class ReceiveAction extends BasePlc4xAction {
+public class ReceiveAction extends BaseDaffodilAction {
 
-    private String timeout;
+    private long timeout = 5000;
+    private int packetLengthStartPosition;
+    private int packetLengthSizeInBytes;
+    private int packetLengthOffset = 0;
 
     private final Map<String, String> verificationRules;
     private final Map<String, String> extractionRules;
@@ -62,19 +71,42 @@ public class ReceiveAction extends BasePlc4xAction {
         return LoggerFactory.getLogger(ReceiveAction.class);
     }
 
+    public String getPacketLengthStartPosition() {
+        return Integer.toString(packetLengthStartPosition);
+    }
+
+    public void setPacketLengthStartPosition(String packetLengthStartPosition) {
+        this.packetLengthStartPosition = Integer.valueOf(packetLengthStartPosition);
+    }
+
+    public String getPacketLengthSizeInBytes() {
+        return Integer.toString(packetLengthSizeInBytes);
+    }
+
+    public void setPacketLengthSizeInBytes(String packetLengthSizeInBytes) {
+        this.packetLengthSizeInBytes = Integer.valueOf(packetLengthSizeInBytes);
+    }
+
+    public String getPacketLengthOffset() {
+        return Integer.toString(packetLengthOffset);
+    }
+
+    public void setPacketLengthOffset(String packetLengthOffset) {
+        this.packetLengthOffset = Integer.valueOf(packetLengthOffset);
+    }
+
     public String getTimeout() {
-        return timeout;
+        return Long.toString(timeout);
     }
 
     public void setTimeout(String timeout) {
-        this.timeout = timeout;
+        this.timeout = Long.valueOf(timeout);
     }
 
     @Override
     @SuppressWarnings("unchecked")
     public void setParsedValue(ParsedValue parsedValue) {
         super.setParsedValue(parsedValue);
-
         if(parsedValue != null) {
             if(parsedValue instanceof NodeListValue) {
                 List<Node> ruleList = (List<Node>) parsedValue.getValue();
@@ -91,11 +123,11 @@ public class ReceiveAction extends BasePlc4xAction {
 
     private void parseElement(Element ruleElement) {
         String name = ruleElement.getAttribute("name");
-        String xpath = ruleElement.getAttribute("xpath");
-        if("verification".equals(ruleElement.getTagName())) {
-            verificationRules.put(name, xpath);
-        } else if("extraction".equals(ruleElement.getTagName())) {
-            extractionRules.put(name, xpath);
+        String expression = ruleElement.getAttribute("xpath-expression");
+        if ("verification".equals(ruleElement.getTagName())) {
+            verificationRules.put(name, expression);
+        } else if ("extraction".equals(ruleElement.getTagName())) {
+            extractionRules.put(name, expression);
         } else {
             getLogger().error("unsupported rule type: " + ruleElement.getTagName());
         }
@@ -103,43 +135,132 @@ public class ReceiveAction extends BasePlc4xAction {
 
     @Override
     public void execute(ActionExecutionContext ctx) {
-        ctx.getAppLog().info("Receiving.");
+        ctx.getAppLog().info(getStateName() + ": Receiving...");
+
         try {
             DataProcessor dp = getDaffodilDataProcessor(ctx);
             if(dp == null) {
-                TriggerEvent event = new EventBuilder("failure", TriggerEvent.SIGNAL_EVENT).
-                    data("Couldn't initialize daffodil data processor.").build();
-                ctx.getInternalIOProcessor().addEvent(event);
+                fireFailureEvent(ctx, "Couldn't initialize daffodil data processor.");
                 return;
             }
 
-            try {
-                Thread.sleep(1000);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
             Socket connection = getSocket(ctx);
-            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
-            System.out.println(in.readLine());
+            DataInputStream inputStream = new DataInputStream(new BufferedInputStream(connection.getInputStream()));
+
+            // Remember when we started to receive.
+            long startTime = System.currentTimeMillis();
+
+            // Check if enough bytes are available to at least find out how big the full packet is.
+            while(inputStream.available() < packetLengthStartPosition + packetLengthSizeInBytes) {
+                waitWithTimeout(ctx, startTime, timeout);
+            }
+
+            // Read these length bytes and reset the input stream back to the start.
+            inputStream.mark(packetLengthStartPosition + packetLengthSizeInBytes);
+            // Jump to the start of the length data.
+            inputStream.skip(packetLengthStartPosition);
+
+            // Read the packet length.
+            int packetLength;
+            switch (packetLengthSizeInBytes) {
+                case 1:
+                    packetLength = inputStream.readUnsignedByte();
+                    break;
+                case 2:
+                    packetLength = inputStream.readUnsignedShort();
+                    break;
+                default:
+                    fireFailureEvent(ctx, "Unsupported size for packet length: " + packetLengthSizeInBytes);
+                    return;
+            }
+            packetLength += packetLengthOffset;
+
+            // Go back to the beginning of the packet.
+            inputStream.reset();
 
-            DataInputStream inputStream = new DataInputStream(connection.getInputStream());
-            ReadableByteChannel rbc = Channels.newChannel(inputStream);
-            W3CDOMInfosetOutputter outputter = new W3CDOMInfosetOutputter();
-            ParseResult byteMessage = dp.parse(rbc, outputter);
+            // Eventually wait till the entire packet is available.
+            while(inputStream.available() < packetLength) {
+                waitWithTimeout(ctx, startTime, timeout);
+            }
+
+            byte[] packet = new byte[packetLength];
+            if(inputStream.read(packet) != packetLength) {
+                TriggerEvent event = new EventBuilder("failure", TriggerEvent.SIGNAL_EVENT).
+                    data("Couldn't read entire packet.").build();
+                ctx.getInternalIOProcessor().addEvent(event);
+                return;
+            }
+
+            // After having enough bytes available, process the current package.
+            JDOMInfosetOutputter outputter = new JDOMInfosetOutputter();
+            ParseResult byteMessage = dp.parse(
+                new InputSourceDataInputStream(new ByteArrayInputStream(packet)), outputter);
             if (byteMessage.isError()) {
                 logDiagnosticInformation(byteMessage);
                 return;
             }
 
+            // Get the resulting XML document from the parser.
             Document message = outputter.getResult();
-            System.out.println(message);
-            ctx.getAppLog().info("Successfully sent message.");
+
+            // First verify all verification conditions.
+            for (Map.Entry<String, String> rule : verificationRules.entrySet()) {
+                Object reference = ctx.getGlobalContext().get(rule.getKey());
+                String current = getRuleText(message, rule.getValue());
+                if(current == null) {
+                    fireFailureEvent(ctx, "Error verifying. Expected: " + reference.toString() + " got null value");
+                    return;
+                }
+                if(!current.equals(reference)) {
+                    fireFailureEvent(ctx, "Error verifying. Expected: " + reference.toString() + " got: " + current);
+                    return;
+                }
+            }
+
+            // Then extract data from the document.
+            for (Map.Entry<String, String> rule : extractionRules.entrySet()) {
+                String current = getRuleText(message, rule.getValue());
+                if(current == null) {
+                    fireFailureEvent(ctx, "Error extracting. Got null value");
+                    return;
+                }
+                ctx.getGlobalContext().set(rule.getKey(), current);
+            }
         } catch (IOException e) {
             e.printStackTrace();
         }
 
-        TriggerEvent event = new EventBuilder("success", TriggerEvent.SIGNAL_EVENT).build();
-        ctx.getInternalIOProcessor().addEvent(event);
+        ctx.getAppLog().info("Received.");
+        fireSuccessEvent(ctx);
+    }
+
+    private String getRuleText(Document message, String xpathExpression) {
+        // Get the namespace definitions from the input document.
+        List<Namespace> namespaces = message.getRootElement().getNamespacesInScope();
+
+        XPathFactory xPathFactory = XPathFactory.instance();
+        XPathExpression<org.jdom2.Text> xpath = xPathFactory.compile(
+            xpathExpression, Filters.textOnly(), null, namespaces);
+        List<Text> result = xpath.evaluate(message);
+        if((result == null) || result.isEmpty()) {
+            getLogger().info("Couldn't find value for xpath expression: " + xpathExpression + " in document.");
+            if(getLogger().isInfoEnabled()) {
+                getLogger().info(new XMLOutputter().outputString(message));
+            }
+            return null;
+        }
+        return result.get(0).getTextNormalize();
+    }
+
+    private void waitWithTimeout(ActionExecutionContext ctx, long startTime, long timeout) {
+        try {
+            TimeUnit.MILLISECONDS.sleep(20);
+            if(System.currentTimeMillis() - startTime > timeout) {
+                fireFailureEvent(ctx, "Receive timed out.");
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
     }
 
 }
diff --git a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/SendAction.java b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/SendAction.java
index f761ac3..bd4a467 100644
--- a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/SendAction.java
+++ b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/SendAction.java
@@ -20,13 +20,11 @@
 package org.apache.plc4x.sandbox.java.s7.actions;
 
 import org.apache.commons.scxml2.ActionExecutionContext;
-import org.apache.commons.scxml2.EventBuilder;
-import org.apache.commons.scxml2.TriggerEvent;
 import org.apache.commons.scxml2.model.ParsedValue;
 import org.apache.daffodil.japi.DataProcessor;
 import org.apache.daffodil.japi.UnparseResult;
 import org.apache.daffodil.japi.infoset.InfosetInputter;
-import org.apache.daffodil.japi.infoset.W3CDOMInfosetInputter;
+import org.apache.plc4x.sandbox.java.s7.utils.W3CDOMTemplateInfosetInputter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
@@ -41,7 +39,7 @@ import java.net.Socket;
 import java.nio.channels.Channels;
 import java.nio.channels.WritableByteChannel;
 
-public class SendAction extends BasePlc4xAction {
+public class SendAction extends BaseDaffodilAction {
 
     @Override
     protected Logger getLogger() {
@@ -50,7 +48,8 @@ public class SendAction extends BasePlc4xAction {
 
     @Override
     public void execute(ActionExecutionContext ctx) {
-        ctx.getAppLog().info("Sending.");
+        ctx.getAppLog().info(getStateName() + ": Sending...");
+
         if(getParsedValue() != null) {
             if(getParsedValue().getType() == ParsedValue.ValueType.NODE) {
                 try {
@@ -63,12 +62,10 @@ public class SendAction extends BasePlc4xAction {
 
                     DataProcessor dp = getDaffodilDataProcessor(ctx);
                     if(dp == null) {
-                        TriggerEvent event = new EventBuilder("failure", TriggerEvent.SIGNAL_EVENT).
-                            data("Couldn't initialize daffodil data processor.").build();
-                        ctx.getInternalIOProcessor().addEvent(event);
+                        fireFailureEvent(ctx, "Couldn't initialize daffodil data processor.");
                         return;
                     }
-                    InfosetInputter inputter = new W3CDOMInfosetInputter(doc);
+                    InfosetInputter inputter = new W3CDOMTemplateInfosetInputter(doc, ctx.getGlobalContext());
 
                     Socket connection = getSocket(ctx);
                     DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
@@ -79,19 +76,17 @@ public class SendAction extends BasePlc4xAction {
                         return;
                     }
                     outputStream.flush();
-                    ctx.getAppLog().info("Successfully sent message.");
                 } catch(IOException | ParserConfigurationException e) {
                     e.printStackTrace();
                 }
             } else {
-                TriggerEvent event = new EventBuilder("failure", TriggerEvent.SIGNAL_EVENT).
-                    data("type '" + getParsedValue().getType() + "' not supported").build();
-                ctx.getInternalIOProcessor().addEvent(event);
+                fireFailureEvent(ctx, "type '" + getParsedValue().getType() + "' not supported");
                 return;
             }
         }
-        TriggerEvent event = new EventBuilder("success", TriggerEvent.SIGNAL_EVENT).build();
-        ctx.getInternalIOProcessor().addEvent(event);
+
+        ctx.getAppLog().info("Sent.");
+        fireSuccessEvent(ctx);
     }
 
 }
diff --git a/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/s7/S7DecodeArticleNumber.java b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/s7/S7DecodeArticleNumber.java
new file mode 100644
index 0000000..46e897b
--- /dev/null
+++ b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/s7/S7DecodeArticleNumber.java
@@ -0,0 +1,91 @@
+/*
+ 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.sandbox.java.s7.actions.s7;
+
+import org.apache.commons.scxml2.ActionExecutionContext;
+import org.apache.commons.scxml2.model.ActionExecutionError;
+import org.apache.plc4x.sandbox.java.s7.actions.BasePlc4xAction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class S7DecodeArticleNumber extends BasePlc4xAction {
+
+    private String articleNumberParameterName;
+    private String plcTypeParameterName;
+
+    public String getArticleNumberParameterName() {
+        return articleNumberParameterName;
+    }
+
+    public void setArticleNumberParameterName(String articleNumberParameterName) {
+        this.articleNumberParameterName = articleNumberParameterName;
+    }
+
+    public String getPlcTypeParameterName() {
+        return plcTypeParameterName;
+    }
+
+    public void setPlcTypeParameterName(String plcTypeParameterName) {
+        this.plcTypeParameterName = plcTypeParameterName;
+    }
+
+    @Override
+    protected Logger getLogger() {
+        return LoggerFactory.getLogger(S7DecodeArticleNumber.class);
+    }
+
+    @Override
+    public void execute(ActionExecutionContext ctx) throws ActionExecutionError {
+        String articleNumber = ctx.getGlobalContext().get(articleNumberParameterName).toString();
+        if(articleNumber == null) {
+            fireFailureEvent(ctx, "Couldn't find article number.");
+            return;
+        }
+
+        String plcType = lookupControllerType(articleNumber);
+        if(plcType == null) {
+            fireFailureEvent(ctx, "Unknown PLC type for article number: " + articleNumber);
+        }
+
+        ctx.getGlobalContext().set(plcTypeParameterName, plcType);
+        fireSuccessEvent(ctx);
+    }
+
+    private String lookupControllerType(String articleNumber) {
+        if(!articleNumber.startsWith("6ES7 ")) {
+            return null;
+        }
+
+        String model = articleNumber.substring(articleNumber.indexOf(' ') + 1, articleNumber.indexOf(' ') + 2);
+        switch (model) {
+            case "2":
+                return "S7-1200";
+            case "5":
+                return "S7-1500";
+            case "3":
+                return "S7-300";
+            case "4":
+                return "S7-400";
+            default:
+                return null;
+        }
+    }
+
+}
diff --git a/sandbox/dynamic-driver-s7/src/main/scala/org/apache/plc4x/sandbox/java/s7/utils/W3CDOMTemplateInfosetInputter.scala b/sandbox/dynamic-driver-s7/src/main/scala/org/apache/plc4x/sandbox/java/s7/utils/W3CDOMTemplateInfosetInputter.scala
new file mode 100644
index 0000000..04a83fd
--- /dev/null
+++ b/sandbox/dynamic-driver-s7/src/main/scala/org/apache/plc4x/sandbox/java/s7/utils/W3CDOMTemplateInfosetInputter.scala
@@ -0,0 +1,42 @@
+/*
+ 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.sandbox.java.s7.utils
+
+import org.apache.commons.scxml2.Context
+import org.apache.daffodil.dpath.NodeInfo
+import org.apache.daffodil.japi.infoset.{InfosetInputterProxy, W3CDOMInfosetInputter}
+
+class W3CDOMTemplateInfosetInputter(document: org.w3c.dom.Document, context: Context) extends InfosetInputterProxy {
+
+    override val infosetInputter = new W3CDOMInfosetInputter(document)
+
+    override def getSimpleText(primType: NodeInfo.Kind): String = {
+        val value = super.getSimpleText(primType)
+        if(value.startsWith("${") && value.endsWith("}")) {
+            val varName = value.substring(2, value.length - 1)
+            val varValue = context.get(varName)
+            if(varValue.isInstanceOf[String]) {
+                return varValue.toString
+            }
+        }
+        value
+    }
+
+}