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

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


The following commit(s) were added to refs/heads/master by this push:
     new fa5dc16  - Added test-constructors to the Modbus drivers. - Added some 
documentation on creating drivers.
fa5dc16 is described below

commit fa5dc167e8a8207fe9474cd4ea52b965f098837a
Author: Christofer Dutz <christofer.d...@c-ware.de>
AuthorDate: Wed Mar 7 10:35:53 2018 +0100

    - Added test-constructors to the Modbus drivers.
    - Added some documentation on creating drivers.
---
 .../connection/ModbusSerialPlcConnection.java      |   8 +-
 .../modbus/connection/ModbusTcpPlcConnection.java  |  10 +-
 .../asciidoc/developers/implementing-drivers.adoc  | 253 +++++++++++++++++++++
 plc4j/protocols/src/site/site.xml                  |  33 +++
 4 files changed, 299 insertions(+), 5 deletions(-)

diff --git 
a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/ModbusSerialPlcConnection.java
 
b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/ModbusSerialPlcConnection.java
index 696181d..915a968 100644
--- 
a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/ModbusSerialPlcConnection.java
+++ 
b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/ModbusSerialPlcConnection.java
@@ -22,6 +22,7 @@ import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelPipeline;
+import org.apache.plc4x.java.base.connection.ChannelFactory;
 import org.apache.plc4x.java.base.connection.SerialChannelFactory;
 import org.apache.plc4x.java.modbus.netty.ModbusProtocol;
 import org.apache.plc4x.java.modbus.netty.ModbusSerialProtocol;
@@ -37,16 +38,19 @@ public class ModbusSerialPlcConnection extends 
BaseModbusPlcConnection {
 
     public ModbusSerialPlcConnection(String port, String params) {
         super(new SerialChannelFactory(port), params);
-
         logger.info("Configured ModbusSerialPlcConnection with: serial-port 
{}", port);
     }
 
+    ModbusSerialPlcConnection(ChannelFactory channelFactory, String params) {
+        super(channelFactory, params);
+    }
+
     @Override
     protected ChannelHandler getChannelHandler(CompletableFuture<Void> 
sessionSetupCompleteFuture) {
         return new ChannelInitializer() {
             @Override
             protected void initChannel(Channel channel) {
-                // Build the protocol stack for communicating with the s7 
protocol.
+                // Build the protocol stack for communicating with the Modbus 
protocol.
                 ChannelPipeline pipeline = channel.pipeline();
                 pipeline.addLast(new ModbusSerialProtocol());
                 pipeline.addLast(new ModbusProtocol());
diff --git 
a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/ModbusTcpPlcConnection.java
 
b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/ModbusTcpPlcConnection.java
index fc2a9b2..373e96f 100644
--- 
a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/ModbusTcpPlcConnection.java
+++ 
b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/connection/ModbusTcpPlcConnection.java
@@ -19,6 +19,7 @@ under the License.
 package org.apache.plc4x.java.modbus.connection;
 
 import io.netty.channel.*;
+import org.apache.plc4x.java.base.connection.ChannelFactory;
 import org.apache.plc4x.java.base.connection.TcpSocketChannelFactory;
 import org.apache.plc4x.java.modbus.netty.ModbusProtocol;
 import org.apache.plc4x.java.modbus.netty.ModbusTcpProtocol;
@@ -36,17 +37,20 @@ public class ModbusTcpPlcConnection extends 
BaseModbusPlcConnection {
     private static final Logger logger = 
LoggerFactory.getLogger(ModbusTcpPlcConnection.class);
 
     public ModbusTcpPlcConnection(InetAddress address, String params) {
-        super(new TcpSocketChannelFactory(address, MODBUS_TCP_PORT), params);
-
+        this(new TcpSocketChannelFactory(address, MODBUS_TCP_PORT), params);
         logger.info("Configured ModbusTcpPlcConnection with: host-name {}", 
address.getHostAddress());
     }
 
+    ModbusTcpPlcConnection(ChannelFactory channelFactory, String params) {
+        super(channelFactory, params);
+    }
+
     @Override
     protected ChannelHandler getChannelHandler(CompletableFuture<Void> 
sessionSetupCompleteFuture) {
         return new ChannelInitializer() {
             @Override
             protected void initChannel(Channel channel) {
-                // Build the protocol stack for communicating with the s7 
protocol.
+                // Build the protocol stack for communicating with the Modbus 
protocol.
                 ChannelPipeline pipeline = channel.pipeline();
                 pipeline.addLast(new ModbusTcpProtocol());
                 pipeline.addLast(new ModbusProtocol());
diff --git 
a/plc4j/protocols/src/site/asciidoc/developers/implementing-drivers.adoc 
b/plc4j/protocols/src/site/asciidoc/developers/implementing-drivers.adoc
new file mode 100644
index 0000000..87830fc
--- /dev/null
+++ b/plc4j/protocols/src/site/asciidoc/developers/implementing-drivers.adoc
@@ -0,0 +1,253 @@
+//
+//  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.
+//
+:imagesdir: ./../img/
+
+== Implementing Drivers
+
+All Java drivers are implemented using https://netty.io/[Netty] which sort of 
evolved from https://mina.apache.org/[Apache Mina].
+
+Usually the protocols used in industrial controllers are layered protocols in 
which one protocol is embedded in another.
+Some times several layers can be involved. Also some times, depending on the 
mode a protocol is used in, one protocol can be embedded in different 
containers.
+
+We decided not to implement each driver in one monolithic block of code, but 
modeled each protocol layer separately.
+This makes the implementation and testing a lot simpler and makes the entire 
architecture much more flexible.
+
+Two examples would be `Modbus` and the `S7` protocol:
+The TCP variant of the `Modbus` Protocol is nothing more than adding a TCP 
header to a normal serial Modbus message.
+So by layering the implementations we can serve both the serial as the TCP 
variant with the same driver logic for the `Modbus` protocol.
+
+The `S7` Protocol is built to be transported using a protocol called `ISO TP`.
+When using the normal `TCP` communication, this is again embedded in a `ISO 
over TCP` protocol frame, which is then included in a `TCP` packet.
+However it is also possible to use it on native `Ethernet` Frames.
+This high-performance and reduced latency implementation would directly 
transport `ISO TP` packets in native `Ethernet` frames.
+
+Keeping the drivers layered, gives us the flexibility to support this.
+
+While the layers control `what` is communicated with the outside world, we 
have also defined a set of connectors that implement `how` our driver 
communicates with it.
+
+All is then put together in a so-called `pipeline`.
+
+A PLC4X driver is responsible for creating a connection.
+This connection is a synonym for a new instance of a Netty pipeline which is 
terminated with a corresponding connector.
+
+[ditaa,netty-pipeline]
+....
+
+
+  +------------------------------------------+
+  |c0BA                                      |
+  |               Application                |  Application
+  |                                          |
+  +---------+--------------------------------+
+            |                      ^
+ - - - - - -|- - - - - - - - - - - | - - - - - - - - - - -
+            v                      |
+  +--------------------------------+---------+
+  |c05A                                      |
+  |                  PLC4X                   |
+  |                                          |
+  +---------+--------------------------------+
+  |         |                      ^         |
+  |         v                      |         |
+  |  +------------------------------------+  |
+  |  |cAAA                                |  |
+  |  |      PLC4X Driver Connection       |  |
+  |  |                                    |  |
+  |  +------+-----------------------------+  |
+  |  |      |                      ^      |  |
+  |  |      v                      |      |  |
+  |  |  +--------------------------+---+  |  |
+  |  |  |cAAA                          |  |  |
+  |  |  |     PLC4X Protocol Layer     |  |  |
+  |  |  |                              |  |  |
+  |  |  +---+--------------------------+  |  |
+  |  |      |                      ^      |  |
+  |  |      v                      |      |  |
+  |  |  +--------------------------+---+  |  |
+  |  |  :           Optional           |  |  |  PLC4X
+  |  |  |      Protocol Layer(s)       |  |  |  Netty
+  |  |  |                              |  |  |  Pipeline
+  |  |  +---+--------------------------+  |  |
+  |  |      |                      ^      |  |
+  |  |      v                      |      |  |
+  |  |  +--------------------------+---+  |  |
+  |  |  |cAAA                          |  |  |
+  |  |  |        Protocol Layer        |  |  |
+  |  |  |                              |  |  |
+  |  |  +---+--------------------------+  |  |
+  |  |      |                      ^      |  |
+  |  |      v                      |      |  |
+  |  +-----------------------------+------+  |
+  |  |cAAA                                |  |
+  |  |             Connector              |  |
+  |  |                                    |  |
+  +--+------+-----------------------------+--+
+            |                      ^
+ - - - - - -|- - - - - - - - - - - | - - - - - - - - - - -
+            v                      |
+  +--------------------------------+---------+
+  |cF6F                                      |
+  |                   PLC                    |  Device
+  |                                          |
+  +------------------------------------------+
+....
+
+== Default Layers
+
+Each driver should consist of at least 2 layers:
+
+- The PLC4X Layer
+- The Protocol Layer
+
+=== The PLC4X Layer
+
+The main objective of this layer is to translate PLC4X requests into messages 
the target protocol understands.
+It also acts as the bridge to link the requests and corresponding responses.
+This is also the layer on which emulation of functionality should be 
implemented.
+
+For example S7 controllers allow reading of multiple addresses in one request.
+However they do not support writing of multiple values at once.
+The `Plc4XS7Protocol` takes care of sending multiple single-value requests to 
the PLC to simulate writing of multiple values.
+This simulation however should be transparent from the underlying protocol 
implementations.
+
+=== The Protocol Layer(s)
+
+The protocol layer(s) are responsible for encoding and decoding messages of 
the protocol they belong to.
+So usually we require at least one of these layers for every driver.
+
+== Connectors
+
+We currently have defined 4 different types of connectors:
+
+- Tcp Socket
+- Raw Socket
+- Serial
+- Test
+
+But this list can easily be extended as needed.
+
+=== Tcp Sockets
+
+This is the default type of connector used when implementing protocols using 
the normal TCP protocol.
+It is what comes with Netty out of the box and should be used if possible.
+
+=== Raw Sockets
+
+This is a special form of connector that allows implementing protocols below 
the TCP level.
+This is also where things start getting a little more complicated.
+As Java doesn't support communication below TCP and UDP, this option makes use 
of the `Java Native Interface (JNI)` to access native libs that then implement 
the functionality on OS level.
+The library used for this is called `libpcap` (for Linux and Mac) or `winpcap` 
for Windows.
+Also as creating of raw sockets requires elevated user permissions the 
application has to be run as `root` or (preferred option) the library has to be 
setup to run with root privileges (`setuid`).
+
+When setup correctly the raw socket connector allows implementing protocols 
right down to manually constructing `Ehternet` frames.
+
+This is currently treated as a temporary solution as we have to collect 
experience with this approach. Eventually native transports implemented as part 
of the PLC4X project might be the more performant solution.
+
+=== Serial
+
+This connector doesn't open any form of network interface, but uses the 
operating systems serial ports for communication.
+It is used by some of the protocols that don't support Networking, such as the 
serial variant of the Modbus protocol.
+
+=== Test
+
+This connector is used for testing purposes.
+Instead of opening a connection to a device using a normal communication 
channel, this connector is made to be used inside unit- and integration-tests.
+It allows to manually send and receive (binary) data to and from a pipeline 
for testing.
+
+== Implementing a custom driver
+
+PLC4X's `DriverManager` finds it's drivers, by using the default `Java 
ServiceLoader`.
+This requires a file called `org.apache.plc4x.java.api.PlcDriver` in the 
`META-INF/services` directory of the drivers JAR file.
+For each type of driver provided inside this JAR, one line has to be added to 
this file containing the fully qualified class name of the driver 
implementation.
+
+For the S7 driver for example all it contains is this line:
+
+    org.apache.plc4x.java.s7.S7PlcDriver
+
+As the Modbus driver contains two variants, it contains two lines - one for 
each variant:
+
+    org.apache.plc4x.java.modbus.ModbusTcpPlcDriver
+    org.apache.plc4x.java.modbus.ModbusSerialPlcDriver
+
+A driver implementation must implement the 
`org.apache.plc4x.java.api.PlcDriver` interface.
+This defines the necessary methods for the `DriverManager` to find the correct 
implementation and create a new connection instance.
+
+The important methods here are:
+
+- getProtocolCode
+- connect(String url)
+- connect(String url, PlcAuthentication authentication)
+
+`getProtocolCode` is used to find a driver suitable for providing a connection 
mathing the prefix of the PLC4X connection string.
+
+So if for example the connection string is:
+
+   s7://192.168.0.1/1/
+
+The DriverManager will look if he can find a PlcDriver implementation for 
which `getProtocolCode` method returns the string "s7".
+If no form of `PlcAuthentication` is provided, the normal `connect` method is 
then used to create a new connection instance.
+If however authentication information is provided, the second connect method 
is used.
+However we still have to find and implement a protocol that actually supports 
authentication.
+
+The probably simplest way to implement a custom connection is to extend 
`org.apache.plc4x.java.base.connection.AbstractPlcConnection`.
+This allows passing in a `ChannelFactory` instance, which allows overriding 
the default communication channel used by the driver.
+
+An `AbstractPlcConnection` is required to implement a method called 
`getChannelHandler`.
+This is responsible for constructing the communication pipeline.
+
+Here is an example of the connection for the TCP variant of the `Modbus` 
protocol:
+
+....
+    public class ModbusTcpPlcConnection extends BaseModbusPlcConnection {
+
+        private static final int MODBUS_TCP_PORT = 502;
+
+        public ModbusTcpPlcConnection(InetAddress address, String params) {
+            this(new TcpSocketChannelFactory(address, MODBUS_TCP_PORT), 
params);
+            logger.info("Configured ModbusTcpPlcConnection with: host-name 
{}", address.getHostAddress());
+        }
+
+        ModbusTcpPlcConnection(ChannelFactory channelFactory, String params) {
+            super(channelFactory, params);
+        }
+
+        @Override
+        protected ChannelHandler getChannelHandler(CompletableFuture<Void> 
sessionSetupCompleteFuture) {
+            return new ChannelInitializer() {
+                @Override
+                protected void initChannel(Channel channel) {
+                    // Build the protocol stack for communicating with the 
modbus protocol.
+                    ChannelPipeline pipeline = channel.pipeline();
+                    pipeline.addLast(new ModbusTcpProtocol());
+                    pipeline.addLast(new ModbusProtocol());
+                    pipeline.addLast(new Plc4XModbusProtocol());
+                }
+            };
+        }
+
+    }
+....
+
+As you can see in above example there are two constructors.
+The first one is the default, which establishes a connection using the default 
connector.
+As the TCP variant of the `Modbus` protocol uses normal TCP, a 
`TcpSocketChannelFactory` instance is used.
+However in order to test the driver, a unit- or integration-test can use the 
second constructor to inject a different `ChannelFactory`.
+Notice that this constructor can be package-private if the test-case is in the 
same package.
+Here the `TestConnectionFactory` will allow creating tests without having to 
worry about the physical connection and all problems that come with it.
+
+The pipeline itself is created in the `getChannelHandler` method.
+Here you have to keep in mind that the layer that is closest to the connection 
has to be added first, the `PLC4X Layer` last.
diff --git a/plc4j/protocols/src/site/site.xml 
b/plc4j/protocols/src/site/site.xml
new file mode 100644
index 0000000..696d543
--- /dev/null
+++ b/plc4j/protocols/src/site/site.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ 
+ 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.
+ 
+ -->
+<project>
+
+  <body>
+    <menu name="Users">
+    </menu>
+    <menu name="Developers">
+      <item name="Implementing Drivers" 
href="developers/implementing-drivers.html"/>
+    </menu>
+    <menu name="Reports" ref="reports" inherit="top"/>
+    <menu ref="modules" inherit="top"/>
+    <menu ref="parent" inherit="top"/>
+  </body>
+
+</project>
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
cd...@apache.org.

Reply via email to