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/08/27 11:14:59 UTC

[plc4x] branch develop updated: - First Implementation of a driver for AB-ETH without any testing and without decoding of the response.

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


The following commit(s) were added to refs/heads/develop by this push:
     new d744d4d  - First Implementation of a driver for AB-ETH without any testing and without decoding of the response.
d744d4d is described below

commit d744d4d03765a458320b60e576eb149572ca28f1
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Tue Aug 27 13:14:53 2019 +0200

    - First Implementation of a driver for AB-ETH without any testing and without decoding of the response.
---
 sandbox/test-java-ab-eth-driver/pom.xml            |  16 +++
 .../org/apache/plc4x/java/abeth/AbEthDriver.java   |  72 ++++++++++
 .../java/abeth/connection/AbEthPlcConnection.java  | 123 ++++++++++++++++
 .../apache/plc4x/java/abeth/model/AbEthField.java  |  86 ++++++++++++
 .../plc4x/java/abeth/model/types/FileType.java     |  61 ++++++++
 .../plc4x/java/abeth/protocol/AbEthProtocol.java   |  69 +++++++++
 .../java/abeth/protocol/Plc4xAbEthProtocol.java    | 155 +++++++++++++++++++++
 .../services/org.apache.plc4x.java.spi.PlcDriver   |  19 +++
 8 files changed, 601 insertions(+)

diff --git a/sandbox/test-java-ab-eth-driver/pom.xml b/sandbox/test-java-ab-eth-driver/pom.xml
index 136aeae..4750ea2 100644
--- a/sandbox/test-java-ab-eth-driver/pom.xml
+++ b/sandbox/test-java-ab-eth-driver/pom.xml
@@ -55,12 +55,28 @@
   </build>
 
   <dependencies>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-api</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
     <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>org.apache.plc4x</groupId>
+      <artifactId>plc4j-protocol-driver-base-tcp</artifactId>
+      <version>0.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-annotations</artifactId>
     </dependency>
diff --git a/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/AbEthDriver.java b/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/AbEthDriver.java
new file mode 100644
index 0000000..9b36cfc
--- /dev/null
+++ b/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/AbEthDriver.java
@@ -0,0 +1,72 @@
+/*
+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.abeth;
+
+import org.apache.plc4x.java.abeth.connection.AbEthPlcConnection;
+import org.apache.plc4x.java.api.PlcConnection;
+import org.apache.plc4x.java.api.authentication.PlcAuthentication;
+import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
+import org.apache.plc4x.java.spi.PlcDriver;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class AbEthDriver implements PlcDriver {
+
+    private static final Pattern ABETH_URI_PATTERN = Pattern.compile("^ab-eth://(?<host>.*)(?<params>\\?.*)?");
+
+    @Override
+    public String getProtocolCode() {
+        return "ab-eth";
+    }
+
+    @Override
+    public String getProtocolName() {
+        return "Allen Bradley ETH";
+    }
+
+    @Override
+    public PlcConnection connect(String url) throws PlcConnectionException {
+        Matcher matcher = ABETH_URI_PATTERN.matcher(url);
+        if (!matcher.matches()) {
+            throw new PlcConnectionException(
+                "Connection url doesn't match the format 'ab-eth://{host|ip}'");
+        }
+        String host = matcher.group("host");
+
+        String params = matcher.group("params") != null ? matcher.group("params").substring(1) : null;
+
+        try {
+            InetAddress serverInetAddress = InetAddress.getByName(host);
+            return new AbEthPlcConnection(serverInetAddress, params);
+        } catch (UnknownHostException e) {
+            throw new PlcConnectionException("Error parsing address", e);
+        } catch (Exception e) {
+            throw new PlcConnectionException("Error connecting to host", e);
+        }
+    }
+
+    @Override
+    public PlcConnection connect(String url, PlcAuthentication authentication) throws PlcConnectionException {
+        throw new PlcConnectionException("AB-ETH connections don't support authentication.");
+    }
+
+}
diff --git a/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/connection/AbEthPlcConnection.java b/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/connection/AbEthPlcConnection.java
new file mode 100644
index 0000000..7c0228c
--- /dev/null
+++ b/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/connection/AbEthPlcConnection.java
@@ -0,0 +1,123 @@
+/*
+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.abeth.connection;
+
+import io.netty.channel.*;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.plc4x.java.abeth.model.AbEthField;
+import org.apache.plc4x.java.abeth.protocol.AbEthProtocol;
+import org.apache.plc4x.java.abeth.protocol.Plc4xAbEthProtocol;
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcReadResponse;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.base.connection.ChannelFactory;
+import org.apache.plc4x.java.base.connection.NettyPlcConnection;
+import org.apache.plc4x.java.base.connection.TcpSocketChannelFactory;
+import org.apache.plc4x.java.base.events.ConnectedEvent;
+import org.apache.plc4x.java.base.messages.InternalPlcReadRequest;
+import org.apache.plc4x.java.base.messages.InternalPlcReadResponse;
+import org.apache.plc4x.java.base.messages.PlcReader;
+import org.apache.plc4x.java.base.messages.PlcRequestContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetAddress;
+import java.util.concurrent.CompletableFuture;
+
+public class AbEthPlcConnection extends NettyPlcConnection implements PlcReader {
+
+    private static final int AB_ETH_PORT = 2222;
+    private static final Logger logger = LoggerFactory.getLogger(AbEthPlcConnection.class);
+
+    public AbEthPlcConnection(InetAddress address, String params) {
+        this(new TcpSocketChannelFactory(address, AB_ETH_PORT), params);
+
+        logger.info("Setting up AB-ETH Connection with: host-name {}", address.getHostAddress());
+    }
+
+    public AbEthPlcConnection(ChannelFactory channelFactory, String params) {
+        super(channelFactory, true);
+
+        if (!StringUtils.isEmpty(params)) {
+            for (String param : params.split("&")) {
+                String[] paramElements = param.split("=");
+                String paramName = paramElements[0];
+                if (paramElements.length == 2) {
+                    String paramValue = paramElements[1];
+                    switch (paramName) {
+                        default:
+                            logger.debug("Unknown parameter {} with value {}", paramName, paramValue);
+                    }
+                } else {
+                    logger.debug("Unknown no-value parameter {}", paramName);
+                }
+            }
+        }
+    }
+
+    @Override
+    public PlcField prepareField(String fieldQuery) throws PlcInvalidFieldException {
+        return AbEthField.of(fieldQuery);
+    }
+
+    @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.
+                ChannelPipeline pipeline = channel.pipeline();
+                pipeline.addLast(new ChannelInboundHandlerAdapter() {
+                    @Override
+                    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+                        if (evt instanceof ConnectedEvent) {
+                            sessionSetupCompleteFuture.complete(null);
+                        } else {
+                            super.userEventTriggered(ctx, evt);
+                        }
+                    }
+                });
+                pipeline.addLast(new AbEthProtocol());
+                pipeline.addLast(new Plc4xAbEthProtocol());
+            }
+        };
+    }
+
+    @Override
+    public boolean canRead() {
+        return true;
+    }
+
+    @Override
+    public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) {
+        InternalPlcReadRequest internalReadRequest = checkInternal(readRequest, InternalPlcReadRequest.class);
+        CompletableFuture<InternalPlcReadResponse> future = new CompletableFuture<>();
+        PlcRequestContainer<InternalPlcReadRequest, InternalPlcReadResponse> container =
+            new PlcRequestContainer<>(internalReadRequest, future);
+        channel.writeAndFlush(container).addListener(f -> {
+            if (!f.isSuccess()) {
+                future.completeExceptionally(f.cause());
+            }
+        });
+        return future
+            .thenApply(PlcReadResponse.class::cast);
+    }
+
+}
diff --git a/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/model/AbEthField.java b/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/model/AbEthField.java
new file mode 100644
index 0000000..49143d3
--- /dev/null
+++ b/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/model/AbEthField.java
@@ -0,0 +1,86 @@
+/*
+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.abeth.model;
+
+import org.apache.plc4x.java.abeth.model.types.FileType;
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.api.model.PlcField;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class AbEthField implements PlcField {
+
+    private static final Pattern ADDRESS_PATTERN =
+        Pattern.compile("^N(?<fileNumber>\\d{1,7})\\:(?<elementNumber>\\d{1,7})/(?<subElementNumber>\\d{1,7}):(?<dataType>[a-zA-Z_]+)(\\[(?<size>\\d+)])?");
+
+    private static final String FILE_NUMBER = "fileNumber";
+    private static final String ELEMENT_NUMBER = "elementNumber";
+    private static final String SUB_ELEMENT_NUMBER = "subElementNumber";
+    private static final String DATA_TYPE = "dataType";
+    private static final String SIZE = "size";
+
+    private final short byteSize;
+    private final short fileNumber;
+    private final FileType fileType;
+    private final short elementNumber;
+    private final short subElementNumber;
+
+    public AbEthField(short byteSize, short fileNumber, FileType fileType, short elementNumber, short subElementNumber) {
+        this.byteSize = byteSize;
+        this.fileNumber = fileNumber;
+        this.fileType = fileType;
+        this.elementNumber = elementNumber;
+        this.subElementNumber = subElementNumber;
+    }
+
+    public short getByteSize() {
+        return byteSize;
+    }
+
+    public short getFileNumber() {
+        return fileNumber;
+    }
+
+    public FileType getFileType() {
+        return fileType;
+    }
+
+    public short getElementNumber() {
+        return elementNumber;
+    }
+
+    public short getSubElementNumber() {
+        return subElementNumber;
+    }
+
+    public static AbEthField of(String fieldString) {
+        Matcher matcher = ADDRESS_PATTERN.matcher(fieldString);
+        if(matcher.matches()) {
+            short fileNumber = Short.parseShort(matcher.group(FILE_NUMBER));
+            short elementNumber = Short.parseShort(matcher.group(ELEMENT_NUMBER));
+            short subElementNumber = Short.parseShort(matcher.group(SUB_ELEMENT_NUMBER));
+            FileType fileType = FileType.valueOf(Short.parseShort(matcher.group(DATA_TYPE)));
+            short byteSize = Short.parseShort(matcher.group(SIZE));
+            return new AbEthField(byteSize, fileNumber, fileType,elementNumber, subElementNumber);
+        }
+        throw new PlcInvalidFieldException("Unable to parse address: " + fieldString);
+    }
+
+}
diff --git a/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/model/types/FileType.java b/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/model/types/FileType.java
new file mode 100644
index 0000000..3c3df85
--- /dev/null
+++ b/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/model/types/FileType.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.java.abeth.model.types;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum FileType {
+
+    STATUS((short) 0x84),
+    BIT((short) 0x85),
+    TIMER((short) 0x86),
+    COUNTER((short) 0x87),
+    CONTROL((short) 0x88),
+    INTEGER((short) 0x89),
+    FLOAT((short) 0x8A),
+    OUTPUT((short) 0x8B),
+    INPUT((short) 0x8C),
+    STRING((short) 0x8D),
+    ASCII((short) 0x8E),
+    BCD((short) 0x8F);
+
+    private final short typeCode;
+
+    FileType(short typeCode) {
+        this.typeCode = typeCode;
+    }
+
+    public short getTypeCode() {
+        return typeCode;
+    }
+
+    private static final Map<Short, FileType> map;
+    static {
+        map = new HashMap<>();
+        for (FileType dataType : FileType.values()) {
+            map.put(dataType.typeCode, dataType);
+        }
+    }
+
+    public static FileType valueOf(short code) {
+        return map.get(code);
+    }
+
+}
diff --git a/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/protocol/AbEthProtocol.java b/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/protocol/AbEthProtocol.java
new file mode 100644
index 0000000..c45d9e6
--- /dev/null
+++ b/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/protocol/AbEthProtocol.java
@@ -0,0 +1,69 @@
+/*
+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.abeth.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import org.apache.plc4x.java.abeth.CIPEncapsulationConnectionRequest;
+import org.apache.plc4x.java.abeth.CIPEncapsulationConnectionResponse;
+import org.apache.plc4x.java.abeth.CIPEncapsulationPacket;
+import org.apache.plc4x.java.abeth.io.CIPEncapsulationPacketIO;
+import org.apache.plc4x.java.base.PlcByteToMessageCodec;
+import org.apache.plc4x.java.base.events.ConnectEvent;
+import org.apache.plc4x.java.base.events.ConnectedEvent;
+import org.apache.plc4x.java.utils.ReadBuffer;
+import org.apache.plc4x.java.utils.WriteBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class AbEthProtocol extends PlcByteToMessageCodec<CIPEncapsulationPacket> {
+
+    private static final Logger logger = LoggerFactory.getLogger(AbEthProtocol.class);
+
+    private CIPEncapsulationPacketIO io;
+
+    public AbEthProtocol() {
+        io = new CIPEncapsulationPacketIO();
+    }
+
+    @Override
+    protected void encode(ChannelHandlerContext ctx, CIPEncapsulationPacket cipEncapsulationPacket, ByteBuf byteBuf) throws Exception {
+        WriteBuffer buffer = new WriteBuffer(cipEncapsulationPacket.getLengthInBytes());
+        io.serialize(buffer, cipEncapsulationPacket);
+        byteBuf.writeBytes(buffer.getData());
+    }
+
+    @Override
+    protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
+        byte[] bytes = new byte[byteBuf.readableBytes()];
+        byteBuf.readBytes(bytes);
+        ReadBuffer readBuffer = new ReadBuffer(bytes);
+        while (readBuffer.getPos() < bytes.length) {
+            try {
+                CIPEncapsulationPacket packet = io.parse(readBuffer);
+                out.add(packet);
+            } catch (Exception e) {
+                logger.warn("Error decoding package: " + e.getMessage());
+            }
+        }
+    }
+
+}
diff --git a/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/protocol/Plc4xAbEthProtocol.java b/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/protocol/Plc4xAbEthProtocol.java
new file mode 100644
index 0000000..7e90dfd
--- /dev/null
+++ b/sandbox/test-java-ab-eth-driver/src/main/java/org/apache/plc4x/java/abeth/protocol/Plc4xAbEthProtocol.java
@@ -0,0 +1,155 @@
+/*
+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.abeth.protocol;
+
+import io.netty.channel.ChannelHandlerContext;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.plc4x.java.abeth.*;
+import org.apache.plc4x.java.abeth.model.AbEthField;
+import org.apache.plc4x.java.api.exceptions.PlcProtocolException;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcRequest;
+import org.apache.plc4x.java.api.messages.PlcResponse;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.base.PlcMessageToMessageCodec;
+import org.apache.plc4x.java.base.events.ConnectEvent;
+import org.apache.plc4x.java.base.events.ConnectedEvent;
+import org.apache.plc4x.java.base.messages.DefaultPlcReadResponse;
+import org.apache.plc4x.java.base.messages.InternalPlcReadRequest;
+import org.apache.plc4x.java.base.messages.PlcRequestContainer;
+import org.apache.plc4x.java.base.messages.items.BaseDefaultFieldItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class Plc4xAbEthProtocol extends PlcMessageToMessageCodec<CIPEncapsulationPacket, PlcRequestContainer> {
+
+    private static final Logger logger = LoggerFactory.getLogger(Plc4xAbEthProtocol.class);
+
+    private static final AtomicInteger transactionCounterGenerator = new AtomicInteger(10);
+    private static final short[] emptySenderContext = new short[] {(short) 0x00 ,(short) 0x00 ,(short) 0x00,
+        (short) 0x00,(short) 0x00,(short) 0x00, (short) 0x00,(short) 0x00};
+
+    private long sessionHandle;
+    private Map<Integer, PlcRequestContainer> requests;
+
+    public Plc4xAbEthProtocol() {
+        this.requests = new HashMap<>();
+    }
+
+    @Override
+    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+        // If the connection has just been established, start setting up the connection
+        // by sending a connection request to the plc.
+        if (evt instanceof ConnectEvent) {
+            logger.debug("AB-ETH Sending Connection Request");
+            // Open the session on ISO Transport Protocol first.
+            CIPEncapsulationConnectionRequest connectionRequest = new CIPEncapsulationConnectionRequest(0L, 0L,
+                emptySenderContext, 0L);
+            ctx.channel().writeAndFlush(connectionRequest);
+        } else {
+            super.userEventTriggered(ctx, evt);
+        }
+    }
+
+    @Override
+    protected void encode(ChannelHandlerContext ctx, PlcRequestContainer msg, List<Object> out) throws Exception {
+        PlcRequest request = msg.getRequest();
+        if (request instanceof PlcReadRequest) {
+            PlcReadRequest readRequest = (PlcReadRequest) msg.getRequest();
+
+            for (String fieldName : readRequest.getFieldNames()) {
+                PlcField field = readRequest.getField(fieldName);
+                if (!(field instanceof AbEthField)) {
+                    throw new PlcProtocolException("The field should have been of type AbEthField");
+                }
+                AbEthField abEthField = (AbEthField) field;
+
+                DF1RequestProtectedTypedLogicalRead logicalRead = new DF1RequestProtectedTypedLogicalRead(
+                    abEthField.getByteSize(), abEthField.getFileNumber(), abEthField.getFileType().getTypeCode(),
+                    abEthField.getElementNumber(), abEthField.getSubElementNumber());
+                DF1RequestMessage requestMessage = new DF1CommandRequestMessage(
+                    (short) 8, (short) 5, (short) 0, transactionCounterGenerator.incrementAndGet(), logicalRead);
+                CIPEncapsulationReadRequest read = new CIPEncapsulationReadRequest(
+                    sessionHandle, 0, emptySenderContext, 0, requestMessage);
+
+                requests.put(requestMessage.getTransactionCounter(), msg);
+
+                out.add(read);
+            }
+        } else {
+            ctx.fireExceptionCaught(
+                new PlcProtocolException("Unsupported request type " + request.getClass().getName()));
+        }
+    }
+
+    @Override
+    protected void decode(ChannelHandlerContext ctx, CIPEncapsulationPacket packet, List<Object> out) throws Exception {
+        if(packet instanceof CIPEncapsulationConnectionResponse) {
+            CIPEncapsulationConnectionResponse connectionResponse = (CIPEncapsulationConnectionResponse) packet;
+            // Save the session handle
+            sessionHandle = connectionResponse.getSessionHandle();
+
+            // Tell Netty we're finished connecting
+            ctx.channel().pipeline().fireUserEventTriggered(new ConnectedEvent());
+        } else {
+            // We're currently just expecting responses.
+            if (!(packet instanceof CIPEncapsulationReadResponse)) {
+                return;
+            }
+            CIPEncapsulationReadResponse cipResponse = (CIPEncapsulationReadResponse) packet;
+            int transactionCounter = cipResponse.getResponse().getTransactionCounter();
+            if(!requests.containsKey(transactionCounter)) {
+                ctx.fireExceptionCaught(
+                    new PlcProtocolException(
+                        "Couldn't find request for response with transaction counter " + transactionCounter));
+                return;
+            }
+
+            PlcRequestContainer requestContainer = requests.remove(transactionCounter);
+            PlcRequest request = requestContainer.getRequest();
+            PlcResponse response = null;
+            if (request instanceof PlcReadRequest) {
+                response = decodeReadResponse(cipResponse, requestContainer);
+            } else {
+                ctx.fireExceptionCaught(
+                    new PlcProtocolException("Unsupported request type " + request.getClass().getName()));
+            }
+
+            // Confirm the response being handled.
+            if (response != null) {
+                requestContainer.getResponseFuture().complete(response);
+            }
+        }
+    }
+
+    private PlcResponse decodeReadResponse(
+        CIPEncapsulationReadResponse cipResponse, PlcRequestContainer requestContainer) {
+
+        InternalPlcReadRequest readRequest = (InternalPlcReadRequest) requestContainer.getRequest();
+
+        Map<String, Pair<PlcResponseCode, BaseDefaultFieldItem>> fields = new HashMap<>();
+        return new DefaultPlcReadResponse(readRequest, fields);
+    }
+}
diff --git a/sandbox/test-java-ab-eth-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.spi.PlcDriver b/sandbox/test-java-ab-eth-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.spi.PlcDriver
new file mode 100644
index 0000000..a8f485e
--- /dev/null
+++ b/sandbox/test-java-ab-eth-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.spi.PlcDriver
@@ -0,0 +1,19 @@
+#
+# 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.java.abeth.AbEthDriver