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
The following commit(s) were added to refs/heads/develop by this push: new f0e7d31 - Fixed a bug in the SCXML template making the S7 not respond correctly - Started implementing the processing of responses in SCXML f0e7d31 is described below commit f0e7d31d8ed0ef14cebe557485337078795fb132 Author: Christofer Dutz <christofer.d...@c-ware.de> AuthorDate: Wed Feb 6 11:20:25 2019 +0100 - Fixed a bug in the SCXML template making the S7 not respond correctly - Started implementing the processing of responses in SCXML --- pom.xml | 5 + .../apache/plc4x/protocols/s7/protocol.scxml.xml | 206 ++++++--------------- sandbox/dynamic-driver-s7/pom.xml | 4 + .../java/org/apache/plc4x/sandbox/java/s7/Poc.java | 10 +- .../sandbox/java/s7/actions/BasePlc4xAction.java | 76 ++++++++ .../sandbox/java/s7/actions/ConnectAction.java | 7 +- .../sandbox/java/s7/actions/InitContextAction.java | 48 +++++ .../sandbox/java/s7/actions/ReceiveAction.java | 103 ++++++++++- .../plc4x/sandbox/java/s7/actions/SendAction.java | 98 ++++------ 9 files changed, 339 insertions(+), 218 deletions(-) diff --git a/pom.xml b/pom.xml index ac58c96..e83282c 100644 --- a/pom.xml +++ b/pom.xml @@ -582,6 +582,11 @@ <version>${slf4j.version}</version> </dependency> <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <version>${slf4j.version}</version> + </dependency> + <dependency> <groupId>org.spockframework</groupId> <artifactId>spock-core</artifactId> <version>${spock.version}</version> 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 7f07b76..e272f77 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 @@ -27,7 +27,6 @@ <sc:datamodel> <sc:data id="args"/> - <sc:data id="connection"/> </sc:datamodel> <sc:state id="init"> @@ -50,15 +49,13 @@ <!-- Initialize the network connection to the remote host using the tcp adapter with a given host and port --> <plc4x:connect type="tcp" host="10.10.64.20" port="102"/> </sc:onentry> - <sc:transition event="success" target="sendCotpConnectionRequest"> - <sc:assign location="connection" expr="_event.data"/> - </sc:transition> + <sc:transition event="success" target="sendCotpConnectionRequest"/> <sc:transition event="failure" target="error"/> </sc:state> <sc:state id="sendCotpConnectionRequest"> <sc:onentry> - <plc4x:send socket="connection"> + <plc4x:send socketParameterName="connection"> <s7:TpktMessage> <magicByte>3</magicByte> <reserved>0</reserved> @@ -69,24 +66,24 @@ <s7:CotpTpduConnectionRequest> <destinationReference>0</destinationReference> <!-- Insert the value for "cotp-local-reference" as short here --> - <sourceReference>16</sourceReference><!--plc4x:insert type="s7:short" name="cotp-local-reference"/--> + <sourceReference>15</sourceReference><!--plc4x:insert type="s7:short" name="cotp-local-reference"/--> <protocolClass>0</protocolClass> <s7:parameters> <parameter> - <type>193</type> - <parameterLength>2</parameterLength> - <s7:CotpParameterCallingTsap> - <tsapId>256</tsapId><!--plc4x:insert type="s7:short" name="cotp-calling-tsap"/--> - </s7:CotpParameterCallingTsap> - </parameter> - <parameter> <type>194</type> <parameterLength>2</parameterLength> <s7:CotpParameterCalledTsap> - <tsapId>258</tsapId><!--plc4x:insert type="s7:short" name="cotp-called-tsap"/--> + <tsapId>512</tsapId><!--plc4x:insert type="s7:short" name="cotp-called-tsap"/--> </s7:CotpParameterCalledTsap> </parameter> <parameter> + <type>193</type> + <parameterLength>2</parameterLength> + <s7:CotpParameterCallingTsap> + <tsapId>273</tsapId><!--plc4x:insert type="s7:short" name="cotp-calling-tsap"/--> + </s7:CotpParameterCallingTsap> + </parameter> + <parameter> <type>192</type> <parameterLength>1</parameterLength> <s7:CotpParameterTpduSize> @@ -105,56 +102,12 @@ <sc:state id="receiveCotpConnectionResponse"> <sc:onentry> - <plc4x:receive timeout="5000"> - <s7:tpktMessage> - <magicByte>3</magicByte> - <reserved>0</reserved> - <!-- Just ignore the content of this field, we don't care about it as it's only required for parsing. --> - <length><plc4x:ignore/></length> - <userData> - <!-- Just ignore the content of this field, we don't care about it as it's only required for parsing. --> - <headerLength><plc4x:ignore/></headerLength> - <type>208</type> - <CotpTpduConnectionResponse> - <!-- Make sure the reply uses the same reference as we used in the request. --> - <destinationReference><plc4x:verify type="s7:short" name="cotp-local-reference"/></destinationReference> - <!-- Extract the reference the remote would like us to use in this session. --> - <sourceReference><plc4x:extract type="s7:short" name="cotp-remote-reference"/></sourceReference> - <protocolClass>0</protocolClass> - <parameters> - <!-- - These elements might be transferred in alternate order, we just care about all of them being - transferred. - --> - <plc4x:unordered> - <parameter> - <type>192</type> - <parameterLength>1</parameterLength> - <CotpParameterTpduSize> - <tpduSize><plc4x:extract type="s7:byte" name="cotp-tpdu-size"/></tpduSize> - </CotpParameterTpduSize> - </parameter> - <parameter> - <type>193</type> - <parameterLength>2</parameterLength> - <CotpParameterCallingTsap> - <tsapId><plc4x:extract type="s7:short" name="cotp-calling-tsap"/></tsapId> - </CotpParameterCallingTsap> - </parameter> - <parameter> - <type>194</type> - <parameterLength>2</parameterLength> - <CotpParameterCalledTsap> - <tsapId><plc4x:extract type="s7:short" name="cotp-called-tsap"/></tsapId> - </CotpParameterCalledTsap> - </parameter> - <!-- The remote might be passing other parameters, we'll just ignore them for now --> - <plc4x:ignore/> - </plc4x:unordered> - </parameters> - </CotpTpduConnectionResponse> - </userData> - </s7:tpktMessage> + <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> </sc:onentry> <sc:transition event="success" target="sendS7SetupCommunicationRequest"/> @@ -163,46 +116,46 @@ <sc:state id="sendS7SetupCommunicationRequest"> <sc:onentry> - <plc4x:send> - <s7:tpktMessage> + <plc4x:send socketParameterName="connection"> + <s7:TpktMessage> <magicByte>3</magicByte> <reserved>0</reserved> <length>25</length> <userData> <headerLength>2</headerLength> <type>240</type> - <CotpTpduData> + <s7:CotpTpduData> <endOfTransmission>1</endOfTransmission> <tpduRef>0</tpduRef> - </CotpTpduData> + </s7:CotpTpduData> <userData> <magicByte>50</magicByte> <type>1</type> - <S7RequestMessage> + <s7:S7RequestMessage> <reserved>0</reserved> <tpduReference>0</tpduReference> <parametersLength>8</parametersLength> <payloadsLength>0</payloadsLength> - <parameters> + <s7:parameters> <parameter> <type>240</type> - <S7GeneralParameterSetupCommunication> + <s7:S7GeneralParameterSetupCommunication> <reserved>0</reserved> - <maxAmqCaller><plc4x:insert type="s7:short" name="s7-max-amq-caller"/></maxAmqCaller> - <maxAmqCallee><plc4x:insert type="s7:short" name="s7-max-amq-callee"/></maxAmqCallee> - <pduLength><plc4x:insert type="s7:short" name="s7-pdu-length"/></pduLength> - </S7GeneralParameterSetupCommunication> + <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"/--> + </s7:S7GeneralParameterSetupCommunication> </parameter> - </parameters> + </s7:parameters> <payloads> <payload> - <S7GeneralPayloadSetupCommunication/> + <s7:S7GeneralPayloadSetupCommunication/> </payload> </payloads> - </S7RequestMessage> + </s7:S7RequestMessage> </userData> </userData> - </s7:tpktMessage> + </s7:TpktMessage> </plc4x:send> </sc:onentry> <sc:transition event="success" target="receiveS7SetupCommunicationResponse"/> @@ -211,51 +164,12 @@ <sc:state id="receiveS7SetupCommunicationResponse"> <sc:onentry> - <plc4x:receive timeout="5000"> - <s7:tpktMessage> - <magicByte>3</magicByte> - <reserved>0</reserved> - <length><plc4x:ignore/></length> - <userData> - <headerLength>2</headerLength> - <type>240</type> - <CotpTpduData> - <endOfTransmission>1</endOfTransmission> - <tpduRef>0</tpduRef> - </CotpTpduData> - <userData> - <magicByte>50</magicByte> - <type>3</type> - <S7ResponseMessage> - <reserved>0</reserved> - <tpduReference>0</tpduReference> - <parametersLength><plc4x:ignore/></parametersLength> - <payloadsLength>0</payloadsLength> - <errorClass><plc4x:ignore/></errorClass> - <errorCode><plc4x:extract/></errorCode> - <parameters> - <plc4x:unordered> - <parameter> - <type>240</type> - <S7GeneralParameterSetupCommunication> - <reserved>0</reserved> - <maxAmqCaller><plc4x:extract type="s7:short" name="s7-max-amq-caller"/></maxAmqCaller> - <maxAmqCallee><plc4x:extract type="s7:short" name="s7-max-amq-callee"/></maxAmqCallee> - <pduLength><plc4x:extract type="s7:short" name="s7-pdu-length"/></pduLength> - </S7GeneralParameterSetupCommunication> - </parameter> - <plc4x:ignore/> - </plc4x:unordered> - </parameters> - <payloads> - <payload> - <S7GeneralPayloadSetupCommunication/> - </payload> - </payloads> - </S7ResponseMessage> - </userData> - </userData> - </s7:tpktMessage> + <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> </sc:onentry> <sc:transition event="" target="sendS7IdentificationRequest"/> @@ -265,30 +179,30 @@ <sc:state id="sendS7IdentificationRequest"> <sc:onentry> - <plc4x:send> - <s7:tpktMessage> + <plc4x:send socketParameterName="connection"> + <s7:TpktMessage> <magicByte>3</magicByte> <reserved>0</reserved> <length>33</length> <userData> <headerLength>2</headerLength> <type>240</type> - <CotpTpduData> + <s7:CotpTpduData> <endOfTransmission>1</endOfTransmission> <tpduRef>0</tpduRef> - </CotpTpduData> + </s7:CotpTpduData> <userData> <magicByte>50</magicByte> <type>7</type> - <S7UserDataMessage> + <s7:S7UserDataMessage> <reserved>0</reserved> <tpduReference>256</tpduReference> <parametersLength>8</parametersLength> <payloadsLength>8</payloadsLength> - <parameters> + <s7:parameters> <parameter> <type>0</type> - <S7UserDataParameterCPUService> + <s7:S7UserDataParameterCPUService> <header>274</header> <paramLength>4</paramLength> <typeCode>17</typeCode> @@ -296,24 +210,24 @@ <functionGroup>4</functionGroup> <subFunctionGroup>1</subFunctionGroup> <sequenceNumber>0</sequenceNumber> - </S7UserDataParameterCPUService> + </s7:S7UserDataParameterCPUService> </parameter> - </parameters> + </s7:parameters> <payloads> <payload> - <S7UserDataPayloadCpuServices> + <s7:S7UserDataPayloadCpuServices> <returnCode>255</returnCode> <transportSize>9</transportSize> <length>4</length> <sslId>17</sslId> <sslIndex>0</sslIndex> - </S7UserDataPayloadCpuServices> + </s7:S7UserDataPayloadCpuServices> </payload> </payloads> - </S7UserDataMessage> + </s7:S7UserDataMessage> </userData> </userData> - </s7:tpktMessage> + </s7:TpktMessage> </plc4x:send> </sc:onentry> <sc:transition event="success" target="receiveS7IdentificationRequest"/> @@ -322,27 +236,27 @@ <sc:state id="receiveS7IdentificationRequest"> <sc:onentry> - <plc4x:receive> - <s7:tpktMessage> + <plc4x:receive socketParameterName="connection"> + <s7:TpktMessage> <magicByte>3</magicByte> <reserved>0</reserved> <length><plc4x:ignore/></length> <userData> <headerLength>2</headerLength> <type>240</type> - <CotpTpduData> + <s7:CotpTpduData> <endOfTransmission>1</endOfTransmission> <tpduRef>0</tpduRef> - </CotpTpduData> + </s7:CotpTpduData> <userData> <magicByte>50</magicByte> <type>7</type> - <S7UserDataMessage> + <s7:S7UserDataMessage> <reserved>0</reserved> <tpduReference>256</tpduReference> <parametersLength><plc4x:ignore/></parametersLength> <payloadsLength><plc4x:ignore/></payloadsLength> - <parameters> + <s7:parameters> <plc4x:unordered> <parameter> <type>0</type> @@ -361,7 +275,7 @@ </parameter> <plc4x:ignore/> </plc4x:unordered> - </parameters> + </s7:parameters> <payloads> <plc4x:unordered> <payload> @@ -421,10 +335,10 @@ <plc4x:ignore/> </plc4x:unordered> </payloads> - </S7UserDataMessage> + </s7:S7UserDataMessage> </userData> </userData> - </s7:tpktMessage> + </s7:TpktMessage> </plc4x:receive> </sc:onentry> <sc:transition event="success" target="connected"/> diff --git a/sandbox/dynamic-driver-s7/pom.xml b/sandbox/dynamic-driver-s7/pom.xml index 80e9432..5dd20dd 100644 --- a/sandbox/dynamic-driver-s7/pom.xml +++ b/sandbox/dynamic-driver-s7/pom.xml @@ -60,6 +60,10 @@ </dependency> <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + </dependency> + <dependency> <groupId>org.apache.plc4x</groupId> <artifactId>plc4x-protocols-s7</artifactId> <version>0.4.0-SNAPSHOT</version> 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 08e6e8a..790f2ef 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 @@ -38,8 +38,8 @@ public class Poc { private SCXMLExecutor executor; - public Poc() throws Exception { - + private Poc() throws Exception { + // Initialize our PLC4X specific actions. List<CustomAction> customActions = new LinkedList<>(); customActions.add( new CustomAction("https://plc4x.apache.org/scxml-extension", "initContext", InitContextAction.class)); @@ -50,15 +50,19 @@ public class Poc { customActions.add( new CustomAction("https://plc4x.apache.org/scxml-extension", "receive", ReceiveAction.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"), new SCXMLReader.Configuration(null, null, customActions)); + + // Create an executor for running the state-machine. executor = new SCXMLExecutor(null, new SimpleDispatcher(), new SimpleErrorReporter()); executor.setStateMachine(scxml); executor.registerInvokerClass("scxml", SimpleSCXMLInvoker.class); } - protected void run() throws Exception { + private void run() throws Exception { + // Run the state-machine. executor.go(); } 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 new file mode 100644 index 0000000..e7bb675 --- /dev/null +++ b/sandbox/dynamic-driver-s7/src/main/java/org/apache/plc4x/sandbox/java/s7/actions/BasePlc4xAction.java @@ -0,0 +1,76 @@ +/* + 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 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; + } + + 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"); + } + + protected void logDiagnosticInformation(WithDiagnostics withDiagnostics) { + List<Diagnostic> diags = withDiagnostics.getDiagnostics(); + for (Diagnostic d : diags) { + getLogger().error(d.getSomeMessage()); + } + } + +} 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 490ad05..5a7a371 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 @@ -59,17 +59,18 @@ public class ConnectAction extends Action { @Override public void execute(ActionExecutionContext ctx) { - ctx.getAppLog().info("Connecting."); + ctx.getAppLog().info("Connecting..."); try { if ("TCP".equalsIgnoreCase(type)) { Socket socket = new Socket(host, Integer.parseInt(port)); - TriggerEvent event = new EventBuilder("success", TriggerEvent.SIGNAL_EVENT).data(socket).build(); + ctx.getGlobalContext().set("connection", socket); + TriggerEvent event = new EventBuilder("success", TriggerEvent.SIGNAL_EVENT).build(); ctx.getInternalIOProcessor().addEvent(event); + ctx.getAppLog().info("Connected."); } } catch (IOException e) { e.printStackTrace(); } - return; } } 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 9285c64..6b6e5f4 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 @@ -23,14 +23,62 @@ 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 { + private static final Logger logger = LoggerFactory.getLogger(InitContextAction.class); + @Override public void execute(ActionExecutionContext ctx) { ctx.getAppLog().info("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(); + ProcessorFactory pf = c.compileSource(schemaUri); + if (pf.isError()) { + logDiagnosticInformation(pf); + TriggerEvent event = new EventBuilder("failure", TriggerEvent.SIGNAL_EVENT).build(); + ctx.getInternalIOProcessor().addEvent(event); + return; + } + DataProcessor dp = pf.onPath("/"); + if (dp.isError()) { + logDiagnosticInformation(dp); + TriggerEvent event = new EventBuilder("failure", TriggerEvent.SIGNAL_EVENT).build(); + ctx.getInternalIOProcessor().addEvent(event); + return; + } + ctx.getGlobalContext().set("dfdl", dp); + } + } catch (IOException | URISyntaxException e) { + 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); } + private void logDiagnosticInformation(WithDiagnostics withDiagnostics) { + List<Diagnostic> diags = withDiagnostics.getDiagnostics(); + for (Diagnostic d : diags) { + logger.error(d.getSomeMessage()); + } + } + } 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 f6f62c0..d17d937 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 @@ -22,12 +22,46 @@ 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.NodeListValue; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; -public class ReceiveAction extends Action { +import java.io.BufferedReader; +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; + +public class ReceiveAction extends BasePlc4xAction { private String timeout; + private final Map<String, String> verificationRules; + private final Map<String, String> extractionRules; + + public ReceiveAction() { + verificationRules = new HashMap<>(); + extractionRules = new HashMap<>(); + } + + @Override + protected Logger getLogger() { + return LoggerFactory.getLogger(ReceiveAction.class); + } + public String getTimeout() { return timeout; } @@ -37,8 +71,73 @@ public class ReceiveAction extends Action { } @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(); + for (Node node : ruleList) { + if(node instanceof Element) { + parseElement((Element) node); + } + } + } else if(parsedValue instanceof NodeValue) { + parseElement((Element) parsedValue.getValue()); + } + } + } + + 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); + } else { + getLogger().error("unsupported rule type: " + ruleElement.getTagName()); + } + } + + @Override public void execute(ActionExecutionContext ctx) { ctx.getAppLog().info("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); + 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(connection.getInputStream()); + ReadableByteChannel rbc = Channels.newChannel(inputStream); + W3CDOMInfosetOutputter outputter = new W3CDOMInfosetOutputter(); + ParseResult byteMessage = dp.parse(rbc, outputter); + if (byteMessage.isError()) { + logDiagnosticInformation(byteMessage); + return; + } + + Document message = outputter.getResult(); + System.out.println(message); + ctx.getAppLog().info("Successfully sent message."); + } catch (IOException e) { + e.printStackTrace(); + } + TriggerEvent event = new EventBuilder("success", TriggerEvent.SIGNAL_EVENT).build(); ctx.getInternalIOProcessor().addEvent(event); } 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 392d0c5..f761ac3 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 @@ -22,13 +22,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.apache.commons.scxml2.model.ParsedValue; -import org.apache.commons.scxml2.model.ParsedValueContainer; -import org.apache.daffodil.japi.Compiler; -import org.apache.daffodil.japi.*; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -38,88 +38,58 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; -import java.util.List; -public class SendAction extends Action implements ParsedValueContainer { - - private ParsedValue message; - - @Override - public ParsedValue getParsedValue() { - return message; - } +public class SendAction extends BasePlc4xAction { @Override - public void setParsedValue(ParsedValue parsedValue) { - message = parsedValue; + protected Logger getLogger() { + return LoggerFactory.getLogger(SendAction.class); } @Override public void execute(ActionExecutionContext ctx) { - if(message != null) { - if(message.getType() == ParsedValue.ValueType.NODE) { + ctx.getAppLog().info("Sending."); + if(getParsedValue() != null) { + if(getParsedValue().getType() == ParsedValue.ValueType.NODE) { try { - Node messageTemplate = (Node) message.getValue(); + Node messageTemplate = (Node) getParsedValue().getValue(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dbf.newDocumentBuilder(); Document doc = builder.newDocument(); Node messageTemplateClone = doc.importNode(messageTemplate, true); doc.appendChild(messageTemplateClone); - 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(); - ProcessorFactory pf = c.compileSource(schemaUri); - if(pf.isError()) { - List<Diagnostic> diags = pf.getDiagnostics(); - for (Diagnostic d : diags) { - System.err.println(d.getSomeMessage()); - } - return; - } - DataProcessor dp = pf.onPath("/"); - if(dp.isError()) { - List<Diagnostic> diags = dp.getDiagnostics(); - for (Diagnostic d : diags) { - System.err.println(d.getSomeMessage()); - } - return; - } - InfosetInputter inputter = new W3CDOMInfosetInputter(doc); - - Socket connection = (Socket) ctx.getGlobalContext().get("connection"); - DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); - WritableByteChannel wbc = Channels.newChannel(outputStream); - UnparseResult byteMessage = dp.unparse(inputter, wbc); - if(byteMessage.isError()) { - List<Diagnostic> diags = byteMessage.getDiagnostics(); - for (Diagnostic d : diags) { - System.err.println(d.getSomeMessage()); - } - return; - } + 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); + return; + } + InfosetInputter inputter = new W3CDOMInfosetInputter(doc); - outputStream.flush(); + Socket connection = getSocket(ctx); + DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); + WritableByteChannel wbc = Channels.newChannel(outputStream); + UnparseResult byteMessage = dp.unparse(inputter, wbc); + if(byteMessage.isError()) { + logDiagnosticInformation(byteMessage); + return; } - } catch(URISyntaxException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } catch (ParserConfigurationException e) { + outputStream.flush(); + ctx.getAppLog().info("Successfully sent message."); + } catch(IOException | ParserConfigurationException e) { e.printStackTrace(); } - } else if(message.getType() == ParsedValue.ValueType.JSON) { - + } else { + TriggerEvent event = new EventBuilder("failure", TriggerEvent.SIGNAL_EVENT). + data("type '" + getParsedValue().getType() + "' not supported").build(); + ctx.getInternalIOProcessor().addEvent(event); + return; } } - ctx.getAppLog().info("Sending."); TriggerEvent event = new EventBuilder("success", TriggerEvent.SIGNAL_EVENT).build(); ctx.getInternalIOProcessor().addEvent(event); }