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/10/10 09:22:27 UTC

[plc4x] 03/03: - Initiated a plc simulator module - Started working on a s7 server module for the simulator

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/plc4x.git

commit f9c8818b701a9d63711ffd8a6c68a32673247a0e
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Thu Oct 10 11:22:11 2019 +0200

    - Initiated a plc simulator module
    - Started working on a s7 server module for the simulator
---
 sandbox/plc-simulator/pom.xml                      | 129 +++++++++++++
 .../org/apache/plc4x/simulator/PlcSimulator.java   |  88 +++++++++
 .../plc4x/simulator/server/ServerModule.java       |  32 ++++
 .../apache/plc4x/simulator/server/s7/S7Server.java |  61 ++++++
 .../plc4x/simulator/server/s7/S7ServerModule.java  |  40 ++++
 .../server/s7/protocol/S7Step7Protocol.java        |  76 ++++++++
 .../server/s7/protocol/S7Step7ServerProtocol.java  | 207 +++++++++++++++++++++
 .../simulator/simulation/SimulationModule.java     |  43 +++++
 .../watertank/WaterTankSimulationModule.java       |  60 ++++++
 .../org.apache.plc4x.simulator.server.ServerModule |  20 ++
 ...che.plc4x.simulator.simulation.SimulationModule |  20 ++
 .../plc-simulator/src/main/resources/logback.xml   |  36 ++++
 12 files changed, 812 insertions(+)

diff --git a/sandbox/plc-simulator/pom.xml b/sandbox/plc-simulator/pom.xml
new file mode 100644
index 0000000..e853987
--- /dev/null
+++ b/sandbox/plc-simulator/pom.xml
@@ -0,0 +1,129 @@
+<?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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.plc4x.sandbox</groupId>
+    <artifactId>plc4x-sandbox</artifactId>
+    <version>0.5.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>plc-simulator</artifactId>
+
+  <name>Sandbox: PLC-Simulator</name>
+
+  <build>
+    <plugins>
+      <!-- Generate the code for parsing and serializing S7 packets -->
+      <plugin>
+        <groupId>org.apache.plc4x.plugins</groupId>
+        <artifactId>plc4x-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>test</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>generate-driver</goal>
+            </goals>
+            <configuration>
+              <protocolName>s7</protocolName>
+              <languageName>java</languageName>
+              <outputFlavor>read-write</outputFlavor>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <!-- Build a fat jar containing all dependencies -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <version>3.2.1</version>
+        <executions>
+          <execution>
+            <id>generate-uber-jar</id>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+            <configuration>
+              <transformers combine.children="append">
+                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                  <mainClass>org.apache.plc4x.simulator.PlcSimulator</mainClass>
+                </transformer>
+              </transformers>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-utils-driver-base-java</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-protocol-driver-base</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-all</artifactId>
+      <version>${netty.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-annotations</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-core</artifactId>
+      <scope>compile</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-build-utils-language-java</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+      <!-- Scope is 'provided' as this way it's not shipped with the driver -->
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-protocols-s7</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+      <!-- Scope is 'provided' as this way it's not shipped with the driver -->
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/PlcSimulator.java b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/PlcSimulator.java
new file mode 100644
index 0000000..9553c02
--- /dev/null
+++ b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/PlcSimulator.java
@@ -0,0 +1,88 @@
+/*
+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.simulator;
+
+import org.apache.plc4x.simulator.server.ServerModule;
+import org.apache.plc4x.simulator.simulation.SimulationModule;
+
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+
+public class PlcSimulator {
+
+    private boolean running;
+    private static Map<String, ServerModule> serverModules;
+    private static Map<String, SimulationModule> simulationModules;
+
+    public PlcSimulator() {
+        this(Thread.currentThread().getContextClassLoader());
+    }
+
+    public PlcSimulator(ClassLoader classLoader) {
+        // Initialize all the server modules.
+        serverModules = new TreeMap<>();
+        ServiceLoader<ServerModule> serverModuleLoader = ServiceLoader.load(ServerModule.class, classLoader);
+        for (ServerModule serverModule : serverModuleLoader) {
+            serverModules.put(serverModule.getName(), serverModule);
+        }
+
+        // Initialize all the simulation modules.
+        simulationModules = new TreeMap<>();
+        ServiceLoader<SimulationModule> simulationModuleLoader = ServiceLoader.load(SimulationModule.class, classLoader);
+        for (SimulationModule simulationModule : simulationModuleLoader) {
+            simulationModules.put(simulationModule.getName(), simulationModule);
+        }
+
+        running = true;
+    }
+
+    public void stop() {
+        running = false;
+    }
+
+    public void run() throws Exception {
+        // Start all server modules.
+        for (ServerModule serverModule : serverModules.values()) {
+            serverModule.start();
+        }
+
+        try {
+            while (running) {
+                // Give all the simulation modules the chance to do something.
+                for (SimulationModule simulationModule : simulationModules.values()) {
+                    simulationModule.loop();
+                }
+                // Sleep 100 ms to not run the simulation too eagerly.
+                TimeUnit.MILLISECONDS.sleep(100);
+            }
+        } finally {
+            // Start all server modules.
+            for (ServerModule serverModule : serverModules.values()) {
+                serverModule.stop();
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        new PlcSimulator().run();
+    }
+
+}
diff --git a/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/ServerModule.java b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/ServerModule.java
new file mode 100644
index 0000000..41aecac
--- /dev/null
+++ b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/ServerModule.java
@@ -0,0 +1,32 @@
+/*
+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.simulator.server;
+
+public interface ServerModule {
+
+    /**
+     * @return the name of the server module
+     */
+    String getName();
+
+    void start();
+
+    void stop();
+
+}
diff --git a/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/S7Server.java b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/S7Server.java
new file mode 100644
index 0000000..16cb8a8
--- /dev/null
+++ b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/S7Server.java
@@ -0,0 +1,61 @@
+/*
+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.simulator.server.s7;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import org.apache.plc4x.simulator.server.s7.protocol.S7Step7Protocol;
+import org.apache.plc4x.simulator.server.s7.protocol.S7Step7ServerProtocol;
+
+public class S7Server {
+
+    private static final int ISO_ON_TCP_PORT = 102;
+
+    public void run() throws Exception {
+        EventLoopGroup loopGroup = new NioEventLoopGroup();
+        EventLoopGroup workerGroup = new NioEventLoopGroup();
+        try {
+            ServerBootstrap bootstrap = new ServerBootstrap();
+            bootstrap.group(loopGroup, workerGroup)
+                .channel(NioServerSocketChannel.class)
+                .childHandler(new ChannelInitializer<SocketChannel>() {
+                    @Override
+                    public void initChannel(SocketChannel channel) {
+                        ChannelPipeline pipeline = channel.pipeline();
+                        pipeline.addLast(new S7Step7Protocol());
+                        pipeline.addLast(new S7Step7ServerProtocol());
+                    }
+                }).option(ChannelOption.SO_BACKLOG, 128)
+                .childOption(ChannelOption.SO_KEEPALIVE, true);
+
+            ChannelFuture future = bootstrap.bind(ISO_ON_TCP_PORT).sync();
+
+            // Wait till it ends ...
+            // TODO: Remove this ...
+            future.channel().closeFuture().sync();
+        } finally {
+            workerGroup.shutdownGracefully();
+            loopGroup.shutdownGracefully();
+        }
+    }
+
+}
diff --git a/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/S7ServerModule.java b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/S7ServerModule.java
new file mode 100644
index 0000000..e9ca944
--- /dev/null
+++ b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/S7ServerModule.java
@@ -0,0 +1,40 @@
+/*
+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.simulator.server.s7;
+
+import org.apache.plc4x.simulator.server.ServerModule;
+
+public class S7ServerModule implements ServerModule {
+
+    @Override
+    public String getName() {
+        return "S7-STEP7";
+    }
+
+    @Override
+    public void start() {
+
+    }
+
+    @Override
+    public void stop() {
+
+    }
+
+}
diff --git a/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/protocol/S7Step7Protocol.java b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/protocol/S7Step7Protocol.java
new file mode 100644
index 0000000..f79b3bd
--- /dev/null
+++ b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/protocol/S7Step7Protocol.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.simulator.server.s7.protocol;
+
+import io.netty.buffer.ByteBuf;
+import org.apache.plc4x.java.base.GeneratedDriverByteToMessageCodec;
+import org.apache.plc4x.java.s7.readwrite.TPKTPacket;
+import org.apache.plc4x.java.s7.readwrite.io.TPKTPacketIO;
+import org.apache.plc4x.java.utils.MessageIO;
+import org.apache.plc4x.java.utils.ParseException;
+import org.apache.plc4x.java.utils.ReadBuffer;
+import org.apache.plc4x.java.utils.WriteBuffer;
+
+public class S7Step7Protocol extends GeneratedDriverByteToMessageCodec<TPKTPacket> {
+
+    public S7Step7Protocol() {
+        super(new MessageIO<TPKTPacket, TPKTPacket>() {
+            @Override
+            public TPKTPacket parse(ReadBuffer io) throws ParseException {
+                try {
+                    return TPKTPacketIO.parse(io);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    throw new ParseException("Error parsing message", e);
+                }
+            }
+
+            @Override
+            public void serialize(WriteBuffer io, TPKTPacket value) throws ParseException {
+                try {
+                    TPKTPacketIO.serialize(io, value);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    throw new ParseException("Error serializing message", e);
+                }
+            }
+        });
+    }
+
+    @Override
+    protected int getPacketSize(ByteBuf byteBuf) {
+        if(byteBuf.readableBytes() >= 4) {
+            if (byteBuf.getByte(0) != TPKTPacket.PROTOCOLID) {
+                return -1;
+            }
+            // Byte 1 is a reserved byte set to 0x00
+            return byteBuf.getShort(2);
+        }
+        return -1;
+    }
+
+    @Override
+    protected void removeRestOfCorruptPackage(ByteBuf byteBuf) {
+        while (byteBuf.getUnsignedByte(0) != TPKTPacket.PROTOCOLID) {
+            // Just consume the bytes till the next possible start position.
+            byteBuf.readByte();
+        }
+    }
+
+}
diff --git a/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/protocol/S7Step7ServerProtocol.java b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/protocol/S7Step7ServerProtocol.java
new file mode 100644
index 0000000..d5dd6f8
--- /dev/null
+++ b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/server/s7/protocol/S7Step7ServerProtocol.java
@@ -0,0 +1,207 @@
+/*
+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.simulator.server.s7.protocol;
+
+import io.netty.channel.*;
+import org.apache.plc4x.java.s7.readwrite.*;
+import org.apache.plc4x.java.s7.readwrite.types.COTPProtocolClass;
+import org.apache.plc4x.java.s7.readwrite.types.COTPTpduSize;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class S7Step7ServerProtocol extends ChannelInboundHandlerAdapter {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(S7Step7ServerProtocol.class);
+
+    private State state = State.INITIAL;
+
+    // COTP parameters
+    private static final int localReference = 42;
+    private int remoteReference = -1;
+    private COTPProtocolClass protocolClass;
+    private static final int localTsapId = 1;
+    private int remoteTsapId = -1;
+    private static final COTPTpduSize maxTpduSize = COTPTpduSize.SIZE_256;
+    private COTPTpduSize tpduSize;
+    // S7 parameters
+    // Set this to 1 as we don't want to handle stuff in parallel
+    private static final int maxAmqCaller = 1;
+    private int amqCaller;
+    // Set this to 1 as we don't want to handle stuff in parallel
+    private static final int maxAmqCallee = 1;
+    private int amqCallee;
+    private static final int maxPduLength = 240;
+    private int pduLength;
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+        if(msg instanceof TPKTPacket) {
+            TPKTPacket packet = (TPKTPacket) msg;
+            switch (state) {
+                case INITIAL: {
+                    final COTPPacket cotpPacket = packet.getPayload();
+                    if (!(cotpPacket instanceof COTPPacketConnectionRequest)) {
+                        LOGGER.error("Expecting COTP Connection-Request");
+                        return;
+                    }
+
+                    COTPTpduSize proposedTpduSize = null;
+                    COTPPacketConnectionRequest cotpConnectionRequest = (COTPPacketConnectionRequest) cotpPacket;
+                    for (COTPParameter parameter : cotpConnectionRequest.getParameters()) {
+                        if (parameter instanceof COTPParameterCalledTsap) {
+                            COTPParameterCalledTsap calledTsapParameter = (COTPParameterCalledTsap) parameter;
+                        } else if (parameter instanceof COTPParameterCallingTsap) {
+                            COTPParameterCallingTsap callingTsapParameter = (COTPParameterCallingTsap) parameter;
+                            remoteTsapId = callingTsapParameter.getTsapId();
+                        } else if (parameter instanceof COTPParameterTpduSize) {
+                            COTPParameterTpduSize tpduSizeParameter = (COTPParameterTpduSize) parameter;
+                            proposedTpduSize = tpduSizeParameter.getTpduSize();
+                        } else {
+                            LOGGER.error(
+                                "Unexpected COTP Connection-Request Parameter " + parameter.getClass().getName());
+                            return;
+                        }
+                    }
+
+                    remoteReference = cotpConnectionRequest.getSourceReference();
+                    protocolClass = cotpConnectionRequest.getProtocolClass();
+                    tpduSize = (proposedTpduSize.getSizeInBytes() > maxTpduSize.getSizeInBytes()) ? maxTpduSize : proposedTpduSize;
+
+                    // Prepare a response and send it back to the remote.
+                    COTPParameter[] parameters = new COTPParameter[3];
+                    parameters[0] = new COTPParameterCalledTsap(remoteTsapId);
+                    parameters[1] = new COTPParameterCallingTsap(localTsapId);
+                    parameters[2] = new COTPParameterTpduSize(tpduSize);
+                    COTPPacketConnectionResponse response = new COTPPacketConnectionResponse(
+                        parameters, null, remoteReference, localReference, protocolClass);
+                    ctx.writeAndFlush(new TPKTPacket(response));
+
+                    state = State.COTP_CONNECTED;
+                    break;
+                }
+                case COTP_CONNECTED: {
+                    final COTPPacket cotpPacket = packet.getPayload();
+                    if (!(cotpPacket instanceof COTPPacketData)) {
+                        LOGGER.error("Expecting COTP Data packet");
+                        return;
+                    }
+
+                    COTPPacketData packetData = (COTPPacketData) cotpPacket;
+                    final short cotpTpduRef = packetData.getTpduRef();
+                    final S7Message payload = packetData.getPayload();
+                    if(!(payload instanceof S7MessageRequest)) {
+                        LOGGER.error("Expecting S7 Message Request");
+                        return;
+                    }
+                    S7MessageRequest s7MessageRequest = (S7MessageRequest) payload;
+                    final int s7TpduReference = s7MessageRequest.getTpduReference();
+                    final S7Parameter s7Parameter = s7MessageRequest.getParameter();
+                    if(!(s7Parameter instanceof S7ParameterSetupCommunication)) {
+                        LOGGER.error("Expecting S7 Message Request containing a S7 Setup Communication Parameter");
+                        return;
+                    }
+                    S7ParameterSetupCommunication s7ParameterSetupCommunication =
+                        (S7ParameterSetupCommunication) s7Parameter;
+
+                    amqCaller = Math.min(s7ParameterSetupCommunication.getMaxAmqCaller(), maxAmqCaller);
+                    amqCallee = Math.min(s7ParameterSetupCommunication.getMaxAmqCallee(), maxAmqCallee);
+                    pduLength = Math.min(s7ParameterSetupCommunication.getPduLength(), maxPduLength);
+
+                    S7ParameterSetupCommunication s7ParameterSetupCommunicationResponse =
+                        new S7ParameterSetupCommunication(amqCaller, amqCallee, pduLength);
+                    S7MessageResponse s7MessageResponse = new S7MessageResponse(
+                        s7TpduReference, s7ParameterSetupCommunicationResponse, new S7PayloadSetupCommunication(),
+                        (short) 0, (short) 0);
+                    ctx.writeAndFlush(new TPKTPacket(new COTPPacketData(null, s7MessageResponse, true, cotpTpduRef)));
+
+                    state = State.S7_CONNECTED;
+                    break;
+                }
+                case S7_CONNECTED: {
+                    final COTPPacket cotpPacket = packet.getPayload();
+                    if (!(cotpPacket instanceof COTPPacketData)) {
+                        LOGGER.error("Expecting COTP Data packet");
+                        return;
+                    }
+
+                    COTPPacketData packetData = (COTPPacketData) cotpPacket;
+                    final short cotpTpduRef = packetData.getTpduRef();
+                    final S7Message payload = packetData.getPayload();
+                    if(payload instanceof S7MessageUserData) {
+                        S7MessageUserData s7MessageUserData = (S7MessageUserData) payload;
+                        final int s7TpduReference = s7MessageUserData.getTpduReference();
+                        final S7Parameter s7Parameter = s7MessageUserData.getParameter();
+                        if(s7Parameter instanceof S7ParameterUserData) {
+                            S7ParameterUserData userData = (S7ParameterUserData) s7Parameter;
+                            for (S7ParameterUserDataItem item : userData.getItems()) {
+                                if(item instanceof S7ParameterUserDataItemCPUFunctions) {
+                                    S7ParameterUserDataItemCPUFunctions function = (S7ParameterUserDataItemCPUFunctions) item;
+                                    /*s7MessageUserData.getParameter()
+                                    final S7ParameterUserDataItemCpuFunctionsItem cpuFunction = function.getCpuFunction();
+                                    if(cpuFunction instanceof S7ParameterUserDataItemCpuFunctionReadSzlRequest) {
+                                        S7ParameterUserDataItemCpuFunctionReadSzlRequest readSzlRequest =
+                                            (S7ParameterUserDataItemCpuFunctionReadSzlRequest) cpuFunction;
+                                        final SzlId szlId = readSzlRequest.getSzlId();
+                                        // This is a request to list the type of device
+                                        if((szlId.getTypeClass() == SzlModuleTypeClass.CPU) &&
+                                            (szlId.getSublistList() == SzlSublist.MODULE_IDENTIFICATION)) {
+
+                                            SzlDataTreeItem[] items = new SzlDataTreeItem[1];
+                                            items[0] = new SzlDataTreeItem((short) 0x0001, new byte[20],
+                                                0x2020, 0x0001, 0x2020);
+                                            S7ParameterUserDataItemCpuFunctionReadSzlResponse readSzlResponse =
+                                                new S7ParameterUserDataItemCpuFunctionReadSzlResponse(
+                                                    readSzlRequest.getCpuFunctionType(), readSzlRequest.getCpuFunctionGroup(),
+                                                    readSzlRequest.getCpuSubfunction(), (short) 1, (short) 0, (short) 0,
+                                                    (short) 0, (short) 0xFF, (short) 0x09, readSzlRequest.getSzlId(),
+                                                    readSzlRequest.getSzlIndex(), items);
+                                            S7ParameterUserDataItem[] responseItems = new S7ParameterUserDataItem[1];
+                                            responseItems[0] = new S7ParameterUserDataItemCPUFunctions((short) 0x12, readSzlResponse);
+                                            S7ParameterUserData responseUserData = new S7ParameterUserData(responseItems);
+                                            S7Message s7ResponseMessage = new S7MessageResponse(s7TpduReference, responseUserData, new S7PayloadUserData());
+                                            ctx.writeAndFlush(new TPKTPacket(new COTPPacketData(null, responseUserData, true, cotpTpduRef)));
+                                        } else {
+                                            LOGGER.error("Not able to respond to the given request Read SZL with SZL type class " +
+                                                szlId.getTypeClass().name() + " and SZL sublise " + szlId.getSublistList().name());
+                                        }
+                                    }*/
+                                }
+                            }
+                        } else {
+                            LOGGER.error("Unsupported type of S7MessageUserData parameter " +
+                                s7Parameter.getClass().getName());
+                            return;
+                        }
+                    } else {
+                        LOGGER.error("Unsupported type of message " + payload.getClass().getName());
+                        return;
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
+    private enum State {
+        INITIAL,
+        COTP_CONNECTED,
+        S7_CONNECTED
+    }
+
+}
diff --git a/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/simulation/SimulationModule.java b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/simulation/SimulationModule.java
new file mode 100644
index 0000000..f08b082
--- /dev/null
+++ b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/simulation/SimulationModule.java
@@ -0,0 +1,43 @@
+/*
+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.simulator.simulation;
+
+import java.util.Map;
+
+public interface SimulationModule {
+
+    /**
+     * @return the name of the simulation module
+     */
+    String getName();
+
+    /**
+     * Gives access to the internal simulations context.
+     * This is an immutable map of named properties that should contain only simple data-types.
+     * @return reference to the simulations context
+     */
+    Map<String, Object> getContext();
+
+    /**
+     * Method for doing the actual processing inside the simulation.
+     * In this method the simulation can do calculations and update it's context variables.
+     */
+    void loop();
+
+}
diff --git a/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/simulation/watertank/WaterTankSimulationModule.java b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/simulation/watertank/WaterTankSimulationModule.java
new file mode 100644
index 0000000..996076d
--- /dev/null
+++ b/sandbox/plc-simulator/src/main/java/org/apache/plc4x/simulator/simulation/watertank/WaterTankSimulationModule.java
@@ -0,0 +1,60 @@
+/*
+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.simulator.simulation.watertank;
+
+import org.apache.plc4x.simulator.simulation.SimulationModule;
+
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+
+public class WaterTankSimulationModule implements SimulationModule {
+
+    private static final String PROP_WATER_LEVEL = "waterLevel";
+
+    private final Map<String, Object> context;
+
+    public WaterTankSimulationModule() {
+        context = new TreeMap<>();
+        context.put(PROP_WATER_LEVEL, 0);
+    }
+
+    @Override
+    public String getName() {
+        return "Water Tank";
+    }
+
+    @Override
+    public Map<String, Object> getContext() {
+        return context;
+    }
+
+    @Override
+    public void loop() {
+        // TODO: Do something sensible ;-)
+        try {
+            // Just increase the level by 1 (Whatever this means ...
+            context.put(PROP_WATER_LEVEL, ((Integer) context.get(context)) + 1);
+            TimeUnit.MILLISECONDS.sleep(100);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+}
diff --git a/sandbox/plc-simulator/src/main/resources/META-INF/services/org.apache.plc4x.simulator.server.ServerModule b/sandbox/plc-simulator/src/main/resources/META-INF/services/org.apache.plc4x.simulator.server.ServerModule
new file mode 100644
index 0000000..9392a75
--- /dev/null
+++ b/sandbox/plc-simulator/src/main/resources/META-INF/services/org.apache.plc4x.simulator.server.ServerModule
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+org.apache.plc4x.simulator.server.s7.S7ServerModule
\ No newline at end of file
diff --git a/sandbox/plc-simulator/src/main/resources/META-INF/services/org.apache.plc4x.simulator.simulation.SimulationModule b/sandbox/plc-simulator/src/main/resources/META-INF/services/org.apache.plc4x.simulator.simulation.SimulationModule
new file mode 100644
index 0000000..bb5db94
--- /dev/null
+++ b/sandbox/plc-simulator/src/main/resources/META-INF/services/org.apache.plc4x.simulator.simulation.SimulationModule
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+org.apache.plc4x.simulator.simulation.watertank.WaterTankSimulationModule
\ No newline at end of file
diff --git a/sandbox/plc-simulator/src/main/resources/logback.xml b/sandbox/plc-simulator/src/main/resources/logback.xml
new file mode 100644
index 0000000..27d40c0
--- /dev/null
+++ b/sandbox/plc-simulator/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="info">
+    <appender-ref ref="STDOUT" />
+  </root>
+
+</configuration>
\ No newline at end of file