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 2018/02/09 14:51:12 UTC

[incubator-plc4x] branch feature/junit4 updated: PLC4X-3 - Implement a testing framework for protocols

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

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


The following commit(s) were added to refs/heads/feature/junit4 by this push:
     new d1c1e63  PLC4X-3 - Implement a testing framework for protocols
d1c1e63 is described below

commit d1c1e63da3eebdae580388ffc6442c629bb5f64f
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Fri Feb 9 15:51:07 2018 +0100

    PLC4X-3 - Implement a testing framework for protocols
    
    - Implemented a first integration-test that tests a s7 connection using a Netty EmbeddedChannel for testing.
---
 .../dummydriver/connection/DummyConnection.java    |  11 ++
 .../apache/plc4x/edgent/mock/MockConnection.java   |  11 +-
 .../plc4x/java/api/connection/PlcConnection.java   |   7 ++
 .../plc4x/java/s7/connection/S7PlcConnection.java  |  82 +++++++------
 .../org/apache/plc4x/java/s7/netty/S7Protocol.java |   2 +-
 .../java/s7/connection/S7PlcConnectionIT.java      |  74 ++++++++++++
 .../java/s7/connection/S7PlcConnectionTests.java   |  47 +++++---
 .../java/s7/connection/S7PlcTestConnection.java    | 129 +++++++++++++++++++++
 .../test-utils/src/main/resources/logback.xml      |  36 ++++++
 9 files changed, 344 insertions(+), 55 deletions(-)

diff --git a/examples/dummy-driver/src/main/java/org/apache/plc4x/java/examples/dummydriver/connection/DummyConnection.java b/examples/dummy-driver/src/main/java/org/apache/plc4x/java/examples/dummydriver/connection/DummyConnection.java
index 4edf6c0..9619fe4 100644
--- a/examples/dummy-driver/src/main/java/org/apache/plc4x/java/examples/dummydriver/connection/DummyConnection.java
+++ b/examples/dummy-driver/src/main/java/org/apache/plc4x/java/examples/dummydriver/connection/DummyConnection.java
@@ -44,9 +44,11 @@ public class DummyConnection extends AbstractPlcConnection implements PlcReader,
 
     private EventLoopGroup workerGroup;
     private Channel channel;
+    private boolean connected;
 
     public DummyConnection(String hostName) {
         this.hostName = hostName;
+        this.connected = false;
     }
 
     public String getHostName() {
@@ -83,6 +85,8 @@ public class DummyConnection extends AbstractPlcConnection implements PlcReader,
             channel = f.channel();
 
             sessionSetupCompleteFuture.get();
+
+            connected = true;
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
             throw new PlcConnectionException(e);
@@ -101,6 +105,13 @@ public class DummyConnection extends AbstractPlcConnection implements PlcReader,
         if (workerGroup != null) {
             workerGroup.shutdownGracefully();
         }
+
+        connected = false;
+    }
+
+    @Override
+    public boolean isConnected() {
+        return true;
     }
 
     @Override
diff --git a/integrations/apache-edgent/src/test/java/org/apache/plc4x/edgent/mock/MockConnection.java b/integrations/apache-edgent/src/test/java/org/apache/plc4x/edgent/mock/MockConnection.java
index 81121ac..1b52ff9 100644
--- a/integrations/apache-edgent/src/test/java/org/apache/plc4x/edgent/mock/MockConnection.java
+++ b/integrations/apache-edgent/src/test/java/org/apache/plc4x/edgent/mock/MockConnection.java
@@ -54,6 +54,7 @@ public class MockConnection extends AbstractPlcConnection implements PlcReader,
     private long curWriteCnt;
     private int writeExceptionTriggerCount;
     private String writeExceptionMsg;
+    private boolean connected;
 
     public MockConnection(String url) {
         this(url, null);
@@ -62,6 +63,7 @@ public class MockConnection extends AbstractPlcConnection implements PlcReader,
     public MockConnection(String url, PlcAuthentication authentication) {
         this.url = url;
         this.authentication = authentication;
+        this.connected = false;
     }
 
     public PlcAuthentication getAuthentication() {
@@ -73,13 +75,18 @@ public class MockConnection extends AbstractPlcConnection implements PlcReader,
     }
 
     @Override
-    public void connect() throws PlcConnectionException {
+    public boolean isConnected() {
+        return connected;
+    }
 
+    @Override
+    public void connect() throws PlcConnectionException {
+        connected = true;
     }
 
     @Override
     public void close() throws Exception {
-
+        connected = false;
     }
 
     @Override
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/connection/PlcConnection.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/connection/PlcConnection.java
index f0680c8..47d3ff4 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/connection/PlcConnection.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/connection/PlcConnection.java
@@ -41,6 +41,13 @@ public interface PlcConnection extends AutoCloseable {
     void connect() throws PlcConnectionException;
 
     /**
+     * Returns true if the PlcConnection is connected to a remote PLC.
+     *
+     * @return true, if connected, false, if not.
+     */
+    boolean isConnected();
+
+    /**
      * Closes the connection to the remote PLC.
      *
      * @throws Exception an exception if shutting down the connection failed.
diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/connection/S7PlcConnection.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/connection/S7PlcConnection.java
index 2913585..2dce3ba 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/connection/S7PlcConnection.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/connection/S7PlcConnection.java
@@ -69,17 +69,19 @@ public class S7PlcConnection extends AbstractPlcConnection implements PlcReader,
     private final int rack;
     private final int slot;
 
-    private final int paramPduSize;
+    private final TpduSize paramPduSize;
     private final short paramMaxAmqCaller;
     private final short paramMaxAmqCallee;
 
     private EventLoopGroup workerGroup;
-    private Channel channel;
+    protected Channel channel;
+    protected boolean connected;
 
     public S7PlcConnection(String hostName, int rack, int slot, String params) {
         this.hostName = hostName;
         this.rack = rack;
         this.slot = slot;
+        this.connected = false;
 
         int paramPduSize = 1024;
         short paramMaxAmqCaller = 8;
@@ -109,7 +111,10 @@ public class S7PlcConnection extends AbstractPlcConnection implements PlcReader,
                 }
             }
         }
-        this.paramPduSize = paramPduSize;
+
+        // IsoTP uses pre defined sizes. Find the smallest box,
+        // that would be able to contain the requested pdu size.
+        this.paramPduSize = TpduSize.valueForGivenSize(paramPduSize);
         this.paramMaxAmqCaller = paramMaxAmqCaller;
         this.paramMaxAmqCallee = paramMaxAmqCallee;
 
@@ -129,7 +134,7 @@ public class S7PlcConnection extends AbstractPlcConnection implements PlcReader,
         return slot;
     }
 
-    public int getParamPduSize() {
+    public TpduSize getParamPduSize() {
         return paramPduSize;
     }
 
@@ -142,6 +147,11 @@ public class S7PlcConnection extends AbstractPlcConnection implements PlcReader,
     }
 
     @Override
+    public boolean isConnected() {
+        return connected;
+    }
+
+    @Override
     public void connect() throws PlcConnectionException {
         workerGroup = new NioEventLoopGroup();
 
@@ -153,42 +163,12 @@ public class S7PlcConnection extends AbstractPlcConnection implements PlcReader,
 
             InetAddress serverInetAddress = InetAddress.getByName(hostName);
 
-            // IsoTP uses pre defined sizes. Find the smallest box,
-            // that would be able to contain the requested pdu size.
-            TpduSize isoTpTpduSize = TpduSize.valueForGivenSize(paramPduSize);
-
-            // If the pdu size was very big, it might have exceeded the
-            // maximum of 8MB defined in the IsoTP protocol, so we have
-            // to generally downgrade the requested size.
-            int pduSize = (isoTpTpduSize.getValue() < paramPduSize) ? isoTpTpduSize.getValue() : paramPduSize;
-
             Bootstrap bootstrap = new Bootstrap();
             bootstrap.group(workerGroup);
             bootstrap.channel(NioSocketChannel.class);
             bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
             bootstrap.option(ChannelOption.TCP_NODELAY, true);
-            bootstrap.handler(new ChannelInitializer() {
-                @Override
-                protected void initChannel(Channel channel) {
-                    // Build the protocol stack for communicating with the s7 protocol.
-                    ChannelPipeline pipeline = channel.pipeline();
-                    pipeline.addLast(new ChannelInboundHandlerAdapter() {
-                        @Override
-                        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
-                            if(evt instanceof S7ConnectionEvent &&
-                                ((S7ConnectionEvent) evt).getState() == S7ConnectionState.SETUP_COMPLETE) {
-                                sessionSetupCompleteFuture.complete(null);
-                            } else {
-                                super.userEventTriggered(ctx, evt);
-                            }
-                        }
-                    });
-                    pipeline.addLast(new IsoOnTcpProtocol());
-                    pipeline.addLast(new IsoTPProtocol((byte) rack, (byte) slot, isoTpTpduSize));
-                    pipeline.addLast(new S7Protocol(paramMaxAmqCaller, paramMaxAmqCallee, (short) pduSize));
-                    pipeline.addLast(new Plc4XS7Protocol());
-                }
-            });
+            bootstrap.handler(getChannelHandler(sessionSetupCompleteFuture));
             // Start the client.
             ChannelFuture f = bootstrap.connect(serverInetAddress, ISO_ON_TCP_PORT).sync();
             f.awaitUninterruptibly();
@@ -199,13 +179,14 @@ public class S7PlcConnection extends AbstractPlcConnection implements PlcReader,
             channel.pipeline().fireUserEventTriggered(new S7ConnectionEvent());
 
             sessionSetupCompleteFuture.get();
+
+            connected = true;
         } catch (UnknownHostException e) {
             throw new PlcConnectionException("Unknown Host " + hostName, e);
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
             throw new PlcConnectionException(e);
-        }
-        catch (ExecutionException e) {
+        } catch (ExecutionException e) {
             throw new PlcConnectionException(e);
         }
     }
@@ -224,6 +205,8 @@ public class S7PlcConnection extends AbstractPlcConnection implements PlcReader,
                 workerGroup.shutdownGracefully();
             });
             sendDisconnectRequestFuture.awaitUninterruptibly();
+
+            connected = false;
         } else if (workerGroup != null) {
             workerGroup.shutdownGracefully();
         }
@@ -269,4 +252,29 @@ public class S7PlcConnection extends AbstractPlcConnection implements PlcReader,
         return writeFuture;
     }
 
+    ChannelHandler getChannelHandler(CompletableFuture<Void> sessionSetupCompleteFuture) {
+        return new ChannelInitializer() {
+            @Override
+            protected void initChannel(Channel channel) {
+                // Build the protocol stack for communicating with the s7 protocol.
+                ChannelPipeline pipeline = channel.pipeline();
+                pipeline.addLast(new ChannelInboundHandlerAdapter() {
+                    @Override
+                    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+                        if(evt instanceof S7ConnectionEvent &&
+                            ((S7ConnectionEvent) evt).getState() == S7ConnectionState.SETUP_COMPLETE) {
+                            sessionSetupCompleteFuture.complete(null);
+                        } else {
+                            super.userEventTriggered(ctx, evt);
+                        }
+                    }
+                });
+                pipeline.addLast(new IsoOnTcpProtocol());
+                pipeline.addLast(new IsoTPProtocol((byte) rack, (byte) slot, paramPduSize));
+                pipeline.addLast(new S7Protocol(paramMaxAmqCaller, paramMaxAmqCallee, (short) paramPduSize.getValue()));
+                pipeline.addLast(new Plc4XS7Protocol());
+            }
+        };
+    }
+
 }
diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/S7Protocol.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/S7Protocol.java
index e42013b..2aa4a75 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/S7Protocol.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/S7Protocol.java
@@ -49,7 +49,7 @@ import java.util.List;
 
 public class S7Protocol extends MessageToMessageCodec<IsoTPMessage, S7Message> {
 
-    private static final byte S7_PROTOCOL_MAGIC_NUMBER = 0x32;
+    public static final byte S7_PROTOCOL_MAGIC_NUMBER = 0x32;
 
     private static final Logger logger = LoggerFactory.getLogger(S7Protocol.class);
 
diff --git a/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcConnectionIT.java b/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcConnectionIT.java
new file mode 100644
index 0000000..fe6476e
--- /dev/null
+++ b/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcConnectionIT.java
@@ -0,0 +1,74 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+package org.apache.plc4x.java.s7.connection;
+
+import io.netty.channel.Channel;
+import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
+import org.assertj.core.api.Assertions;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class S7PlcConnectionIT {
+
+    private static final Logger logger = LoggerFactory.getLogger(S7PlcConnectionIT.class);
+
+    @Rule
+    public Timeout globalTimeout = Timeout.seconds(2); // 2 seconds max per method tested
+
+    private S7PlcTestConnection  s7PlcConnection;
+    private Channel channel;
+
+    @Before
+    public void setUp() {
+        try {
+            s7PlcConnection = new S7PlcTestConnection("localhost", 1, 2, "");
+            s7PlcConnection.connect();
+            channel = s7PlcConnection.getChannel();
+        } catch (PlcConnectionException e) {
+            logger.error("Error initializing connection", e);
+            Assertions.fail("Error initializing connection");
+        }
+    }
+
+    @After
+    public void tearDown() {
+        if(s7PlcConnection.isConnected()) {
+            s7PlcConnection.close();
+        }
+        s7PlcConnection = null;
+        channel = null;
+    }
+
+    @Test
+    public void connect() {
+        assertThat(s7PlcConnection).isNotNull();
+        assertThat(s7PlcConnection.isConnected()).isTrue()
+            .withFailMessage("The connection should be 'connected'");
+    }
+
+    // TODO more tests for connect, close, read and write
+
+}
diff --git a/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcConnectionTests.java b/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcConnectionTests.java
index c6cc76d..069f174 100644
--- a/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcConnectionTests.java
+++ b/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcConnectionTests.java
@@ -23,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.fail;
 
 import org.apache.plc4x.java.api.exceptions.PlcException;
+import org.apache.plc4x.java.isotp.netty.model.types.TpduSize;
 import org.apache.plc4x.java.s7.model.S7Address;
 import org.apache.plc4x.java.s7.model.S7BitAddress;
 import org.apache.plc4x.java.s7.model.S7DataBlockAddress;
@@ -47,12 +48,18 @@ public class S7PlcConnectionTests {
 
     @Test
     public void initialState() {
-        assertThat(s7PlcConnection.getHostName()).isEqualToIgnoringCase("localhost").withFailMessage("Hostname is incorrect");
-        assertThat(s7PlcConnection.getRack()).isEqualTo(1).withFailMessage("Rack is incorrect");
-        assertThat(s7PlcConnection.getSlot()).isEqualTo(2).withFailMessage("Slot is incorrect");
-        assertThat(s7PlcConnection.getParamPduSize()).isEqualTo(1024).withFailMessage("Pdu size is incorrect");
-        assertThat(s7PlcConnection.getParamMaxAmqCaller()).isEqualTo(8).withFailMessage("Max AMQ Caller size is incorrect");
-        assertThat(s7PlcConnection.getParamMaxAmqCallee()).isEqualTo(8).withFailMessage("Max AMQ Callee size is incorrect");
+        assertThat(s7PlcConnection.getHostName()).isEqualToIgnoringCase("localhost")
+            .withFailMessage("Hostname is incorrect");
+        assertThat(s7PlcConnection.getRack()).isEqualTo(1)
+            .withFailMessage("Rack is incorrect");
+        assertThat(s7PlcConnection.getSlot()).isEqualTo(2)
+            .withFailMessage("Slot is incorrect");
+        assertThat(s7PlcConnection.getParamPduSize()).isEqualTo(TpduSize.SIZE_1024)
+            .withFailMessage("Pdu size is incorrect");
+        assertThat(s7PlcConnection.getParamMaxAmqCaller()).isEqualTo(8)
+            .withFailMessage("Max AMQ Caller size is incorrect");
+        assertThat(s7PlcConnection.getParamMaxAmqCallee()).isEqualTo(8)
+            .withFailMessage("Max AMQ Callee size is incorrect");
     }
 
     @Test
@@ -68,9 +75,13 @@ public class S7PlcConnectionTests {
     @Test
     public void parseDatablockAddress() {
         try {
-            S7DataBlockAddress address = (S7DataBlockAddress) s7PlcConnection.parseAddress("DATA_BLOCKS/20/100");
-            assertThat(address.getDataBlockNumber()).isEqualTo((short) 20).withFailMessage("unexpected data block");
-            assertThat(address.getByteOffset()).isEqualTo((short) 100).withFailMessage("unexpected byte offset");
+            S7DataBlockAddress address = (S7DataBlockAddress)
+                s7PlcConnection.parseAddress("DATA_BLOCKS/20/100");
+
+            assertThat(address.getDataBlockNumber()).isEqualTo((short) 20)
+                .withFailMessage("unexpected data block");
+            assertThat(address.getByteOffset()).isEqualTo((short) 100)
+                .withFailMessage("unexpected byte offset");
         }
         catch (PlcException exception) {
             fail("valid data block address");
@@ -81,8 +92,11 @@ public class S7PlcConnectionTests {
     public void parseAddressAddress() {
         try {
             S7Address address = (S7Address) s7PlcConnection.parseAddress("TIMERS/10");
-            assertThat(address.getMemoryArea()).isEqualTo(MemoryArea.TIMERS).withFailMessage("unexpected memory area");
-            assertThat(address.getByteOffset()).isEqualTo((short) 10).withFailMessage("unexpected byte offset");
+
+            assertThat(address.getMemoryArea()).isEqualTo(MemoryArea.TIMERS)
+                .withFailMessage("unexpected memory area");
+            assertThat(address.getByteOffset()).isEqualTo((short) 10)
+                .withFailMessage("unexpected byte offset");
         }
         catch (PlcException exception) {
             fail("valid timer block address");
@@ -93,14 +107,17 @@ public class S7PlcConnectionTests {
     public void parseAddressBitAddress() {
         try {
             S7BitAddress address = (S7BitAddress) s7PlcConnection.parseAddress("TIMERS/10/4");
-            assertThat(address.getMemoryArea()).isEqualTo(MemoryArea.TIMERS).withFailMessage("unexpected memory area");
-            assertThat(address.getByteOffset()).isEqualTo((short) 10).withFailMessage("unexpected byte offset");
-            assertThat(address.getBitOffset()).isEqualTo((byte) 4).withFailMessage("unexpected but offset");
+
+            assertThat(address.getMemoryArea()).isEqualTo(MemoryArea.TIMERS)
+                .withFailMessage("unexpected memory area");
+            assertThat(address.getByteOffset()).isEqualTo((short) 10)
+                .withFailMessage("unexpected byte offset");
+            assertThat(address.getBitOffset()).isEqualTo((byte) 4)
+                .withFailMessage("unexpected but offset");
         }
         catch (PlcException exception) {
             fail("valid timer block bit address");
         }
     }
 
-    // TODO more tests for connect, close, read and write
 }
\ No newline at end of file
diff --git a/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcTestConnection.java b/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcTestConnection.java
new file mode 100644
index 0000000..2f57265
--- /dev/null
+++ b/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcTestConnection.java
@@ -0,0 +1,129 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+package org.apache.plc4x.java.s7.connection;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.embedded.EmbeddedChannel;
+import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
+import org.apache.plc4x.java.isotp.netty.model.types.*;
+import org.apache.plc4x.java.netty.events.S7ConnectionEvent;
+import org.apache.plc4x.java.s7.netty.S7Protocol;
+import org.apache.plc4x.java.s7.netty.model.types.MessageType;
+import org.apache.plc4x.java.s7.netty.model.types.ParameterType;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+public class S7PlcTestConnection extends S7PlcConnection {
+
+    public S7PlcTestConnection(String hostName, int rack, int slot, String params) {
+        super(hostName, rack, slot, params);
+    }
+
+    @Override
+    public void connect() throws PlcConnectionException {
+        CompletableFuture<Void> sessionSetupCompleteFuture = new CompletableFuture<>();
+
+        // Create an embedded channel instance for testing.
+        EmbeddedChannel channel = new EmbeddedChannel(getChannelHandler(sessionSetupCompleteFuture));
+
+        // Send an event to the pipeline telling the Protocol filters what's going on.
+        channel.pipeline().fireUserEventTriggered(new S7ConnectionEvent());
+
+        ByteBuf writtenData = channel.readOutbound();
+        byte[] connectionRequest = new byte[writtenData.readableBytes()];
+        writtenData.readBytes(connectionRequest);
+        // TODO: Check the content of the Iso TP connection request.
+
+        // Send an Iso TP connection response back to the pipeline.
+        byte[] connectionConfirm = toByteArray(
+            new int[] {
+                // ISO on TCP packet
+                0x03, 0x00, 0x00, 0x16,
+                // ISO TP packet
+                0x11,
+                TpduCode.CONNECTION_CONFIRM.getCode(),
+                0x00, 0x01, 0x00, 0x02,
+                ProtocolClass.CLASS_0.getCode(),
+
+                ParameterCode.CALLED_TSAP.getCode(), 0x02, DeviceGroup.PG_OR_PC.getCode(), 0x01,
+                ParameterCode.CALLING_TSAP.getCode(), 0x02, DeviceGroup.OTHERS.getCode(), 0x12,
+                ParameterCode.TPDU_SIZE.getCode(), 0x01, TpduSize.SIZE_512.getCode()
+            });
+        channel.writeInbound(Unpooled.wrappedBuffer(connectionConfirm));
+
+        // Read a S7 Setup Communication request.
+        writtenData = channel.readOutbound();
+        byte[] setupCommunicationRequest = new byte[writtenData.readableBytes()];
+        writtenData.readBytes(setupCommunicationRequest);
+        // TODO: Check the content of the S7 Setup Communication connection request.
+
+        // Send an S7 Setup Communication response back to the pipeline.
+        byte[] setupCommunicationResponse = toByteArray(
+            new int[] {
+                // ISO on TCP packet
+                0x03, 0x00,
+                0x00, 0x1B,
+                // ISO TP packet
+                0x02,
+                TpduCode.DATA.getCode(),
+                0x80,
+                S7Protocol.S7_PROTOCOL_MAGIC_NUMBER,
+                MessageType.ACK_DATA.getCode(), 0x00, 0x00,
+                0x00, 0x01,
+                // Parameter Length
+                0x00, 0x08,
+                // Data Length
+                0x00, 0x00,
+                // Error codes
+                0x00, 0x00,
+                // Parameters:
+                ParameterType.SETUP_COMMUNICATION.getCode(), 0x00, 0x00, 0x08, 0x00, 0x08, 0x01, 0x00
+            });
+        channel.writeInbound(Unpooled.wrappedBuffer(setupCommunicationResponse));
+
+        // Wait till the connection is established.
+        try {
+            sessionSetupCompleteFuture.get();
+
+            connected = true;
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new PlcConnectionException(e);
+        } catch (ExecutionException e) {
+            throw new PlcConnectionException(e);
+        }
+
+        this.channel = channel;
+    }
+
+    public EmbeddedChannel getChannel() {
+        return (EmbeddedChannel) channel;
+    }
+
+    public static byte[] toByteArray(int[] in) {
+        byte[] out = new byte[in.length];
+        for(int i = 0; i < in.length; i++) {
+            out[i] = (byte) in[i];
+        }
+        return out;
+    }
+
+}
diff --git a/plc4j/utils/test-utils/src/main/resources/logback.xml b/plc4j/utils/test-utils/src/main/resources/logback.xml
new file mode 100644
index 0000000..a8ddebb
--- /dev/null
+++ b/plc4j/utils/test-utils/src/main/resources/logback.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+  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.
+
+-->
+<configuration xmlns="http://ch.qos.logback/xml/ns/logback"
+               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+               xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd">
+
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <!-- encoders are assigned the type
+         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+    <encoder>
+      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="trace">
+    <appender-ref ref="STDOUT" />
+  </root>
+
+</configuration>
\ No newline at end of file

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