You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by hu...@apache.org on 2022/05/25 09:41:55 UTC

[plc4x] branch logix_develop updated (94aa0010a4 -> 2c70689c5e)

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

hutcheb pushed a change to branch logix_develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git


    from 94aa0010a4 start of logix development
     new 66d5193800 mspec comiles
     new 2c70689c5e Outline of Logix Driver

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 plc4j/drivers/{eip => logix}/pom.xml               | 16 ++---
 .../plc4x/java/logix/readwrite/LogixDriver.java}   | 22 +++----
 .../configuration/LogixConfiguration.java}         |  8 +--
 .../java/logix/readwrite/field/LogixField.java}    | 24 +++----
 .../logix/readwrite/field/LogixFieldHandler.java}  |  4 +-
 .../readwrite/protocol/LogixProtocolLogic.java}    | 40 ++++++------
 .../services/org.apache.plc4x.java.api.PlcDriver   |  2 +-
 .../java/eip/readwrite/LogixDriverTestsuite.java}  |  6 +-
 .../eip/readwrite/LogixParserSerializerTest.java}  |  6 +-
 .../logix}/src/test/resources/logback.xml          |  0
 plc4j/drivers/pom.xml                              |  1 +
 .../src/main/resources/protocols/logix/logix.mspec | 75 +++++++++++-----------
 protocols/pom.xml                                  |  1 +
 13 files changed, 105 insertions(+), 100 deletions(-)
 copy plc4j/drivers/{eip => logix}/pom.xml (92%)
 copy plc4j/drivers/{eip/src/main/java/org/apache/plc4x/java/eip/readwrite/EIPDriver.java => logix/src/main/java/org/apache/plc4x/java/logix/readwrite/LogixDriver.java} (85%)
 copy plc4j/drivers/{eip/src/main/java/org/apache/plc4x/java/eip/readwrite/configuration/EIPConfiguration.java => logix/src/main/java/org/apache/plc4x/java/logix/readwrite/configuration/LogixConfiguration.java} (84%)
 copy plc4j/drivers/{eip/src/main/java/org/apache/plc4x/java/eip/readwrite/field/EipField.java => logix/src/main/java/org/apache/plc4x/java/logix/readwrite/field/LogixField.java} (87%)
 copy plc4j/drivers/{eip/src/main/java/org/apache/plc4x/java/eip/readwrite/field/EipFieldHandler.java => logix/src/main/java/org/apache/plc4x/java/logix/readwrite/field/LogixFieldHandler.java} (91%)
 copy plc4j/drivers/{eip/src/main/java/org/apache/plc4x/java/eip/readwrite/protocol/EipProtocolLogic.java => logix/src/main/java/org/apache/plc4x/java/logix/readwrite/protocol/LogixProtocolLogic.java} (95%)
 copy plc4j/{api/src/test => drivers/logix/src/main}/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver (94%)
 copy plc4j/drivers/{eip/src/test/java/org/apache/plc4x/java/eip/readwrite/EIPDriverTestsuite.java => logix/src/test/java/org/apache/plc4x/java/eip/readwrite/LogixDriverTestsuite.java} (85%)
 copy plc4j/drivers/{eip/src/test/java/org/apache/plc4x/java/eip/readwrite/EIPParserSerializerTest.java => logix/src/test/java/org/apache/plc4x/java/eip/readwrite/LogixParserSerializerTest.java} (83%)
 copy plc4j/{api => drivers/logix}/src/test/resources/logback.xml (100%)


[plc4x] 02/02: Outline of Logix Driver

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hutcheb pushed a commit to branch logix_develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 2c70689c5e6e96a0b55b486801ed7adfbfbc9099
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Wed May 25 19:41:25 2022 +1000

    Outline of Logix Driver
---
 plc4j/drivers/logix/pom.xml                        | 170 ++++++
 .../plc4x/java/logix/readwrite/LogixDriver.java    | 126 +++++
 .../configuration/LogixConfiguration.java          |  53 ++
 .../java/logix/readwrite/field/LogixField.java     | 158 ++++++
 .../logix/readwrite/field/LogixFieldHandler.java   |  35 ++
 .../readwrite/protocol/LogixProtocolLogic.java     | 590 +++++++++++++++++++++
 .../services/org.apache.plc4x.java.api.PlcDriver   |  19 +
 .../java/eip/readwrite/LogixDriverTestsuite.java   |  29 +
 .../eip/readwrite/LogixParserSerializerTest.java   |  29 +
 plc4j/drivers/logix/src/test/resources/logback.xml |  36 ++
 plc4j/drivers/pom.xml                              |   1 +
 .../src/main/resources/protocols/logix/logix.mspec |   6 +-
 12 files changed, 1249 insertions(+), 3 deletions(-)

diff --git a/plc4j/drivers/logix/pom.xml b/plc4j/drivers/logix/pom.xml
new file mode 100644
index 0000000000..08d85200a1
--- /dev/null
+++ b/plc4j/drivers/logix/pom.xml
@@ -0,0 +1,170 @@
+<?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</groupId>
+    <artifactId>plc4j-drivers</artifactId>
+    <version>0.10.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>plc4j-driver-logix</artifactId>
+  <name>PLC4J: Driver: Allen Bradley Logix</name>
+  <description>Implementation of a PLC4X driver able to speak using a custom Ethernet/IP Protocol in line with Allen Bradley Logix controllers.
+  </description>
+
+  <build>
+    <plugins>
+      <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>logix</protocolName>
+              <languageName>java</languageName>
+              <outputFlavor>read-write</outputFlavor>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.karaf.tooling</groupId>
+        <artifactId>karaf-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>generate-feature-xml</id>
+            <phase>compile</phase>
+            <goals>
+              <!-- Generate the feature.xml -->
+              <goal>features-generate-descriptor</goal>
+              <!-- Check the feature.xml -->
+              <goal>verify</goal>
+            </goals>
+            <configuration>
+              <enableGeneration>true</enableGeneration>
+              <aggregateFeatures>true</aggregateFeatures>
+            </configuration>
+          </execution>
+          <execution>
+            <id>build-kar</id>
+            <phase>package</phase>
+            <goals>
+              <!--
+                Build a kar archive (Jar containing the feature.xml
+                as well as the module content and it's dependencies.
+              -->
+              <goal>kar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+            <Bundle-Activator>org.apache.plc4x.java.osgi.DriverActivator</Bundle-Activator>
+            <Export-Service>org.apache.plc4x.java.api.PlcDriver,org.apache.plc4x.java.logix.readwrite.LogixDriver
+            </Export-Service>
+            <Import-Package>
+              com.fasterxml.jackson.annotation;resolution:=optional,
+              *
+            </Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <configuration>
+          <usedDependencies combine.children="append">
+            <usedDependency>org.apache.plc4x:plc4x-code-generation-language-java</usedDependency>
+            <usedDependency>org.apache.plc4x:plc4x-protocols-logix</usedDependency>
+          </usedDependencies>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-api</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-spi</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-transport-tcp</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+    </dependency>
+
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-buffer</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-utils-test-utils</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-code-generation-language-java</artifactId>
+      <version>0.10.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-logix</artifactId>
+      <version>0.10.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-logix</artifactId>
+      <version>0.10.0-SNAPSHOT</version>
+      <classifier>tests</classifier>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/LogixDriver.java b/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/LogixDriver.java
new file mode 100644
index 0000000000..47c28f45e0
--- /dev/null
+++ b/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/LogixDriver.java
@@ -0,0 +1,126 @@
+/*
+ * 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.logix.readwrite;
+
+import io.netty.buffer.ByteBuf;
+import org.apache.plc4x.java.logix.readwrite.configuration.LogixConfiguration;
+import org.apache.plc4x.java.logix.readwrite.field.LogixField;
+import org.apache.plc4x.java.logix.readwrite.field.LogixFieldHandler;
+import org.apache.plc4x.java.logix.readwrite.protocol.LogixProtocolLogic;
+import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
+import org.apache.plc4x.java.api.value.PlcValueHandler;
+import org.apache.plc4x.java.spi.configuration.Configuration;
+import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
+import org.apache.plc4x.java.spi.connection.PlcFieldHandler;
+import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
+import org.apache.plc4x.java.spi.connection.SingleProtocolStackConfigurer;
+
+import java.util.function.Consumer;
+import java.util.function.ToIntFunction;
+
+public class LogixDriver extends GeneratedDriverBase<EipPacket> {
+    public static final int PORT = 44818;
+    @Override
+    public String getProtocolCode() {
+        return "eip";
+    }
+
+    @Override
+    public String getProtocolName() {
+        return "EthernetIP";
+    }
+
+    @Override
+    protected Class<? extends Configuration> getConfigurationType() {
+        return LogixConfiguration.class;
+    }
+
+    @Override
+    protected PlcFieldHandler getFieldHandler() {
+        return new LogixFieldHandler();
+    }
+
+    @Override
+    protected PlcValueHandler getValueHandler() {
+        return new IEC61131ValueHandler();
+    }
+
+    /**
+     * This protocol doesn't have a disconnect procedure, so there is no need to wait for a login to finish.
+     * @return false
+     */
+    @Override
+    protected boolean awaitDisconnectComplete() {
+        return false;
+    }
+
+    @Override
+    protected String getDefaultTransport() {
+        return "tcp";
+    }
+
+    @Override
+    protected boolean canRead() {
+        return true;
+    }
+
+    @Override
+    protected boolean canWrite() {
+        return true;
+    }
+
+    @Override
+    protected ProtocolStackConfigurer<EipPacket> getStackConfigurer() {
+        return SingleProtocolStackConfigurer.builder(EipPacket.class, EipPacket::staticParse)
+            .withProtocol(LogixProtocolLogix.class)
+            .withPacketSizeEstimator(ByteLengthEstimator.class)
+            .littleEndian()
+            .build();
+    }
+
+    /** Estimate the Length of a Packet */
+    public static class ByteLengthEstimator implements ToIntFunction<ByteBuf> {
+        @Override
+        public int applyAsInt(ByteBuf byteBuf) {
+            if (byteBuf.readableBytes() >= 4) {
+                //Second byte for the size and then add the header size 24
+                int size = byteBuf.getUnsignedShort(byteBuf.readerIndex()+1)+24;
+                return size;
+            }
+            return -1;
+        }
+    }
+
+     /**Consumes all Bytes till another Magic Byte is found */
+    public static class CorruptPackageCleaner implements Consumer<ByteBuf> {
+        @Override
+        public void accept(ByteBuf byteBuf) {
+            while (byteBuf.getUnsignedByte(0) != 0x00) {
+                // Just consume the bytes till the next possible start position.
+                byteBuf.readByte();
+            }
+        }
+    }
+
+    @Override
+    public LogixField prepareField(String query){
+        return LogixField.of(query);
+    }
+
+}
diff --git a/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/configuration/LogixConfiguration.java b/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/configuration/LogixConfiguration.java
new file mode 100644
index 0000000000..9402357d61
--- /dev/null
+++ b/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/configuration/LogixConfiguration.java
@@ -0,0 +1,53 @@
+/*
+ * 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.logix.readwrite.configuration;
+
+import org.apache.plc4x.java.logix.readwrite.LogixDriver;
+import org.apache.plc4x.java.spi.configuration.Configuration;
+import org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter;
+import org.apache.plc4x.java.transport.tcp.TcpTransportConfiguration;
+
+public class LogixConfiguration implements Configuration, TcpTransportConfiguration {
+
+    @ConfigurationParameter
+    private int backplane;
+
+    @ConfigurationParameter
+    private int slot;
+
+    public int getBackplane() {
+        return backplane;
+    }
+
+    public void setBackplane(int backpane) {
+        this.backplane = backpane;
+    }
+
+    public int getSlot() {
+        return slot;
+    }
+
+    public void setSlot(int slot) {
+        this.slot = slot;
+    }
+
+    @Override
+    public int getDefaultPort(){return LogixDriver.PORT;}
+
+}
diff --git a/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/field/LogixField.java b/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/field/LogixField.java
new file mode 100644
index 0000000000..39d9d194b5
--- /dev/null
+++ b/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/field/LogixField.java
@@ -0,0 +1,158 @@
+/*
+ * 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.logix.readwrite.field;
+
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.logix.readwrite.CIPDataTypeCode;
+import org.apache.plc4x.java.spi.generation.SerializationException;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
+import org.apache.plc4x.java.spi.utils.Serializable;
+
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class LogixField implements PlcField, Serializable {
+
+    private static final Pattern ADDRESS_PATTERN =
+        Pattern.compile("^%(?<tag>[a-zA-Z_.0-9]+\\[?[0-9]*\\]?):?(?<dataType>[A-Z]*):?(?<elementNb>[0-9]*)");
+
+    private static final String TAG = "tag";
+    private static final String ELEMENTS = "elementNb";
+    private static final String TYPE = "dataType";
+
+
+    private final String tag;
+    private CIPDataTypeCode type;
+    private int elementNb;
+
+    public CIPDataTypeCode getType() {
+        return type;
+    }
+
+    public void setType(CIPDataTypeCode type) {
+        this.type = type;
+    }
+
+    public int getElementNb() {
+        return elementNb;
+    }
+
+    public void setElementNb(int elementNb) {
+        this.elementNb = elementNb;
+    }
+
+    public String getTag() {
+        return tag;
+    }
+
+    public LogixField(String tag) {
+        this.tag = tag;
+    }
+
+    public LogixField(String tag, int elementNb) {
+        this.tag = tag;
+        this.elementNb = elementNb;
+    }
+
+    public LogixField(String tag, CIPDataTypeCode type, int elementNb) {
+        this.tag = tag;
+        this.type = type;
+        this.elementNb = elementNb;
+    }
+
+    public LogixField(String tag, CIPDataTypeCode type) {
+        this.tag = tag;
+        this.type = type;
+    }
+
+    public static boolean matches(String fieldQuery) {
+        return ADDRESS_PATTERN.matcher(fieldQuery).matches();
+    }
+
+    public static LogixField of(String fieldString) {
+        Matcher matcher = ADDRESS_PATTERN.matcher(fieldString);
+        if (matcher.matches()) {
+            String tag = matcher.group(TAG);
+            int nb = 0;
+            CIPDataTypeCode type = null;
+            if (!matcher.group(ELEMENTS).isEmpty()) {
+                nb = Integer.parseInt(matcher.group(ELEMENTS));
+            }
+            if (!matcher.group(TYPE).isEmpty()) {
+                type = CIPDataTypeCode.valueOf(matcher.group(TYPE));
+            }
+            if (nb != 0) {
+                if (type != null) {
+                    return new LogixField(tag, type, nb);
+                }
+                return new LogixField(tag, nb);
+            } else {
+                if (type != null) {
+                    return new LogixField(tag, type);
+                }
+                return new LogixField(tag);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String getPlcDataType() {
+        return type.toString();
+    }
+
+    @Override
+    public Class<?> getDefaultJavaType() {
+        switch (type) {
+            //ToDo differenciate Short, Integer and Long
+            case INT:
+            case DINT:
+            case SINT:
+            case LINT:
+                return java.lang.Integer.class;
+            case STRING:
+            case STRING36:
+                return java.lang.String.class;
+            case REAL:
+                return java.lang.Double.class;
+            case BOOL:
+                return java.lang.Boolean.class;
+            default:
+                return Object.class;
+        }
+    }
+
+    @Override
+    public void serialize(WriteBuffer writeBuffer) throws SerializationException {
+        writeBuffer.pushContext(getClass().getSimpleName());
+
+        writeBuffer.writeString("node", tag.getBytes(StandardCharsets.UTF_8).length * 8, StandardCharsets.UTF_8.name(), tag);
+        if (type != null) {
+            writeBuffer.writeString("type", type.name().getBytes(StandardCharsets.UTF_8).length * 8, StandardCharsets.UTF_8.name(), type.name());
+        }
+        writeBuffer.writeUnsignedInt("elementNb", 16, elementNb);
+        // TODO: remove this (not language agnostic)
+        String defaultJavaType = (type == null ? Object.class : getDefaultJavaType()).getName();
+        writeBuffer.writeString("defaultJavaType", defaultJavaType.getBytes(StandardCharsets.UTF_8).length * 8, StandardCharsets.UTF_8.name(), defaultJavaType);
+
+        writeBuffer.popContext(getClass().getSimpleName());
+    }
+
+}
diff --git a/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/field/LogixFieldHandler.java b/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/field/LogixFieldHandler.java
new file mode 100644
index 0000000000..bfd97dc53b
--- /dev/null
+++ b/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/field/LogixFieldHandler.java
@@ -0,0 +1,35 @@
+/*
+ * 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.logix.readwrite.field;
+
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.spi.connection.PlcFieldHandler;
+
+public class LogixFieldHandler implements PlcFieldHandler {
+
+    @Override
+    public PlcField createField(String fieldQuery) throws PlcInvalidFieldException {
+       if(EipField.matches(fieldQuery)){
+           return EipField.of(fieldQuery);
+       }
+       else throw new PlcInvalidFieldException("Invalid field "+fieldQuery);
+    }
+
+}
diff --git a/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/protocol/LogixProtocolLogic.java b/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/protocol/LogixProtocolLogic.java
new file mode 100644
index 0000000000..84a9dfcf8a
--- /dev/null
+++ b/plc4j/drivers/logix/src/main/java/org/apache/plc4x/java/logix/readwrite/protocol/LogixProtocolLogic.java
@@ -0,0 +1,590 @@
+/*
+ * 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.logix.readwrite.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.messages.*;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.logix.readwrite.*;
+import org.apache.plc4x.java.logix.readwrite.configuration.LogixConfiguration;
+import org.apache.plc4x.java.logix.readwrite.field.LogixField;
+import org.apache.plc4x.java.spi.ConversationContext;
+import org.apache.plc4x.java.spi.Plc4xProtocolBase;
+import org.apache.plc4x.java.spi.configuration.HasConfiguration;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
+import org.apache.plc4x.java.spi.messages.*;
+import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
+import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
+import org.apache.plc4x.java.spi.values.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class LogixProtocolLogic extends Plc4xProtocolBase<EipPacket> implements HasConfiguration<LogixConfiguration> {
+
+    private static final Logger logger = LoggerFactory.getLogger(LogixProtocolLogic.class);
+    public static final Duration REQUEST_TIMEOUT = Duration.ofMillis(10000);
+
+    private static final List<Short> emptySenderContext = Arrays.asList((short) 0x00, (short) 0x00, (short) 0x00,
+        (short) 0x00, (short) 0x00, (short) 0x00, (short) 0x00, (short) 0x00);
+    private List<Short> senderContext;
+    private LogixConfiguration configuration;
+
+    private final AtomicInteger transactionCounterGenerator = new AtomicInteger(10);
+    private RequestTransactionManager tm;
+    private long sessionHandle;
+
+    @Override
+    public void setConfiguration(LogixConfiguration configuration) {
+        this.configuration = configuration;
+        // Set the transaction manager to allow only one message at a time.
+        this.tm = new RequestTransactionManager(1);
+    }
+
+    @Override
+    public void onConnect(ConversationContext<EipPacket> context) {
+        logger.debug("Sending RegisterSession EIP Package");
+        EipConnectionRequest connectionRequest =
+            new EipConnectionRequest(0L, 0L, emptySenderContext, 0L);
+        context.sendRequest(connectionRequest)
+            .expectResponse(EipPacket.class, REQUEST_TIMEOUT).unwrap(p -> p)
+            .check(p -> p instanceof EipConnectionRequest)
+            .handle(p -> {
+                if (p.getStatus() == 0L) {
+                    sessionHandle = p.getSessionHandle();
+                    senderContext = p.getSenderContext();
+                    logger.debug("Got assigned with Session {}", sessionHandle);
+                    // Send an event that connection setup is complete.
+                    context.fireConnected();
+                } else {
+                    logger.warn("Got status code [{}]", p.getStatus());
+                }
+
+            });
+    }
+
+    @Override
+    public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) {
+        DefaultPlcReadRequest request = (DefaultPlcReadRequest) readRequest;
+        List<CipReadRequest> requests = new ArrayList<>(request.getNumberOfFields());
+        for (PlcField field : request.getFields()) {
+            LogixField plcField = (LogixField) field;
+            String tag = plcField.getTag();
+            int elements = 1;
+            if (plcField.getElementNb() > 1) {
+                elements = plcField.getElementNb();
+            }
+            CipReadRequest req = new CipReadRequest(getRequestSize(tag), toAnsi(tag), elements, -1);
+            requests.add(req);
+        }
+        return toPlcReadResponse(readRequest, readInternal(requests));
+    }
+
+    private byte getRequestSize(String tag) {
+        //We need the size of the request in words (0x91, tagLength, ... tag + possible pad)
+        // Taking half to get word size
+        boolean isArray = false;
+        boolean isStruct = false;
+        String tagIsolated = tag;
+        if (tag.contains("[")) {
+            isArray = true;
+            tagIsolated = tag.substring(0, tag.indexOf("["));
+        }
+
+        if (tag.contains(".")) {
+            isStruct = true;
+            tagIsolated = tagIsolated.replace(".", "");
+        }
+        int dataLength = (tagIsolated.length() + 2)
+            + (tagIsolated.length() % 2)
+            + (isArray ? 2 : 0)
+            + (isStruct ? 2 : 0);
+        byte requestPathSize = (byte) (dataLength / 2);
+        return requestPathSize;
+    }
+
+    private byte[] toAnsi(String tag) {
+        int arrayIndex = 0;
+        boolean isArray = false;
+        boolean isStruct = false;
+        String tagFinal = tag;
+        if (tag.contains("[")) {
+            isArray = true;
+            String index = tag.substring(tag.indexOf("[") + 1, tag.indexOf("]"));
+            arrayIndex = Integer.parseInt(index);
+            tagFinal = tag.substring(0, tag.indexOf("["));
+        }
+        if (tag.contains(".")) {
+            tagFinal = tag.substring(0, tag.indexOf("."));
+            isStruct = true;
+        }
+        boolean isPadded = tagFinal.length() % 2 != 0;
+        int dataSegLength = 2 + tagFinal.length()
+            + (isPadded ? 1 : 0)
+            + (isArray ? 2 : 0);
+
+        if (isStruct) {
+            for (String subStr : tag.substring(tag.indexOf(".") + 1).split("\\.", -1)) {
+                dataSegLength += 2 + subStr.length() + subStr.length() % 2;
+            }
+        }
+
+        ByteBuffer buffer = ByteBuffer.allocate(dataSegLength).order(ByteOrder.LITTLE_ENDIAN);
+
+        buffer.put((byte) 0x91);
+        buffer.put((byte) tagFinal.length());
+        byte[] tagBytes = null;
+        tagBytes = tagFinal.getBytes(StandardCharsets.US_ASCII);
+
+        buffer.put(tagBytes);
+        buffer.position(2 + tagBytes.length);
+
+
+        if (isPadded) {
+            buffer.put((byte) 0x00);
+        }
+
+        if (isArray) {
+            buffer.put((byte) 0x28);
+            buffer.put((byte) arrayIndex);
+        }
+        if (isStruct) {
+            buffer.put(toAnsi(tag.substring(tag.indexOf(".") + 1, tag.length())));
+        }
+        return buffer.array();
+    }
+
+    private CompletableFuture<PlcReadResponse> toPlcReadResponse(PlcReadRequest readRequest, CompletableFuture<CipService> response) {
+        return response
+            .thenApply(p -> {
+                return ((PlcReadResponse) decodeReadResponse(p, readRequest));
+            });
+    }
+
+    private CompletableFuture<CipService> readInternal(List<CipReadRequest> request) {
+        CompletableFuture<CipService> future = new CompletableFuture<>();
+        RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+        if (request.size() > 1) {
+
+            short nb = (short) request.size();
+            List<Integer> offsets = new ArrayList<>(nb);
+            int offset = 2 + nb * 2;
+            for (int i = 0; i < nb; i++) {
+                offsets.add(offset);
+                offset += request.get(i).getLengthInBytes();
+            }
+
+            List<CipService> serviceArr = new ArrayList<>(nb);
+            for (int i = 0; i < nb; i++) {
+                serviceArr.add(request.get(i));
+            }
+            Services data = new Services(nb, offsets, serviceArr, -1);
+            //Encapsulate the data
+
+            AnsiExtendedSymbolSegment pathSegment0 = new AnsiExtendedSymbolSegment();
+
+            CipRRData pkt = new CipRRData(sessionHandle, 0L, emptySenderContext, 0L, 0, 2,
+                new CipExchange(
+                    new CipUnconnectedRequest(
+                        pathSegment0.length(),
+                        pathSegment0
+                    ),
+                    -1
+                ),
+                -1
+            );
+
+
+            transaction.submit(() -> context.sendRequest(pkt)
+                .expectResponse(EipPacket.class, REQUEST_TIMEOUT)
+                .onTimeout(future::completeExceptionally)
+                .onError((p, e) -> future.completeExceptionally(e))
+                .check(p -> p instanceof CipRRData)
+                .check(p -> p.getSessionHandle() == sessionHandle)
+                //.check(p -> p.getSenderContext() == senderContext)
+                .unwrap(p -> (CipRRData) p)
+                .unwrap(p -> p.getExchange().getService()).check(p -> p instanceof MultipleServiceResponse)
+                .unwrap(p -> (MultipleServiceResponse) p)
+                .check(p -> p.getServiceNb() == nb)
+                .handle(p -> {
+                    future.complete(p);
+                    // Finish the request-transaction.
+                    transaction.endRequest();
+                }));
+        } else if (request.size() == 1) {
+            CipExchange exchange = new CipExchange(
+                new CipUnconnectedRequest(
+                    request.get(0), (byte) configuration.getBackplane(), (byte) configuration.getSlot(), -1
+                ),
+                -1
+            );
+            CipRRData pkt = new CipRRData(sessionHandle, 0L, emptySenderContext, 0L, exchange, -1);
+            transaction.submit(() -> context.sendRequest(pkt)
+                .expectResponse(EipPacket.class, REQUEST_TIMEOUT)
+                .onTimeout(future::completeExceptionally)
+                .onError((p, e) -> future.completeExceptionally(e))
+                .check(p -> p instanceof CipRRData)
+                .check(p -> p.getSessionHandle() == sessionHandle)
+                //.check(p -> p.getSenderContext() == senderContext)
+                .unwrap(p -> (CipRRData) p)
+                .unwrap(p -> p.getExchange().getService()).check(p -> p instanceof CipReadResponse)
+                .unwrap(p -> (CipReadResponse) p)
+                .handle(p -> {
+                    future.complete(p);
+                    // Finish the request-transaction.
+                    transaction.endRequest();
+                }));
+        }
+        return future;
+    }
+
+    private PlcResponse decodeReadResponse(CipService p, PlcReadRequest readRequest) {
+        Map<String, ResponseItem<PlcValue>> values = new HashMap<>();
+        // only 1 field
+        if (p instanceof CipReadResponse) {
+            CipReadResponse resp = (CipReadResponse) p;
+            String fieldName = readRequest.getFieldNames().iterator().next();
+            LogixField field = (LogixField) readRequest.getField(fieldName);
+            PlcResponseCode code = decodeResponseCode(resp.getStatus());
+            PlcValue plcValue = null;
+            CIPDataTypeCode type = resp.getDataType();
+            ByteBuf data = Unpooled.wrappedBuffer(resp.getData());
+            if (code == PlcResponseCode.OK) {
+                plcValue = parsePlcValue(field, data, type);
+            }
+            ResponseItem<PlcValue> result = new ResponseItem<>(code, plcValue);
+            values.put(fieldName, result);
+        }
+        //Multiple response
+        else if (p instanceof MultipleServiceResponse) {
+            MultipleServiceResponse responses = (MultipleServiceResponse) p;
+            int nb = responses.getServiceNb();
+            List<CipService> arr = new ArrayList<>(nb);
+            ReadBufferByteBased read = new ReadBufferByteBased(responses.getServicesData(), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN);
+            int total = (int) read.getTotalBytes();
+            for (int i = 0; i < nb; i++) {
+                int length = 0;
+                int offset = responses.getOffsets().get(i) - responses.getOffsets().get(0); //Substract first offset as we only have the service in the buffer (not servicesNb and offsets)
+                if (i == nb - 1) {
+                    length = total - offset; //Get the rest if last
+                } else {
+                    length = responses.getOffsets().get(i + 1) - offset - responses.getOffsets().get(0); //Calculate length with offsets (substracting first offset)
+                }
+                ReadBuffer serviceBuf = new ReadBufferByteBased(read.getBytes(offset, offset + length), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN);
+                CipService service = null;
+                try {
+                    service = CipService.staticParse(read, length);
+                    arr.add(service);
+                } catch (ParseException e) {
+                    throw new PlcRuntimeException(e);
+                }
+            }
+            Services services = new Services(nb, responses.getOffsets(), arr, -1);
+            Iterator<String> it = readRequest.getFieldNames().iterator();
+            for (int i = 0; i < nb && it.hasNext(); i++) {
+                String fieldName = it.next();
+                LogixField field = (LogixField) readRequest.getField(fieldName);
+                PlcValue plcValue = null;
+                if (services.getServices().get(i) instanceof CipReadResponse) {
+                    CipReadResponse readResponse = (CipReadResponse) services.getServices().get(i);
+                    PlcResponseCode code;
+                    if (readResponse.getStatus() == 0) {
+                        code = PlcResponseCode.OK;
+                    } else {
+                        code = PlcResponseCode.INTERNAL_ERROR;
+                    }
+                    CIPDataTypeCode type = readResponse.getDataType();
+                    ByteBuf data = Unpooled.wrappedBuffer(readResponse.getData());
+                    if (code == PlcResponseCode.OK) {
+                        plcValue = parsePlcValue(field, data, type);
+                    }
+                    ResponseItem<PlcValue> result = new ResponseItem<>(code, plcValue);
+                    values.put(fieldName, result);
+                }
+            }
+        }
+        return new DefaultPlcReadResponse(readRequest, values);
+    }
+
+    private PlcValue parsePlcValue(LogixField field, ByteBuf data, CIPDataTypeCode type) {
+        int nb = field.getElementNb();
+        if (nb > 1) {
+            int index = 0;
+            List<PlcValue> list = new ArrayList<>();
+            for (int i = 0; i < nb; i++) {
+                switch (type) {
+                    case DINT:
+                        list.add(new PlcDINT(Integer.reverseBytes(data.getInt(index))));
+                        index += type.getSize();
+                        break;
+                    case INT:
+                        list.add(new PlcINT(Integer.reverseBytes(data.getInt(index))));
+                        index += type.getSize();
+                        break;
+                    case SINT:
+                        list.add(new PlcSINT(Integer.reverseBytes(data.getInt(index))));
+                        index += type.getSize();
+                        break;
+                    case REAL:
+                        list.add(new PlcLREAL(swap(data.getFloat(index))));
+                        index += type.getSize();
+                        break;
+                    case BOOL:
+                        list.add(new PlcBOOL(data.getBoolean(index)));
+                        index += type.getSize();
+                    default:
+                        return null;
+                }
+            }
+            return new PlcList(list);
+        } else {
+            switch (type) {
+                case SINT:
+                    return new PlcSINT(data.getByte(0));
+                case INT:
+                    return new PlcINT(Short.reverseBytes(data.getShort(0)));
+                case DINT:
+                    return new PlcDINT(Integer.reverseBytes(data.getInt(0)));
+                case REAL:
+                    return new PlcREAL(swap(data.getFloat(0)));
+                case BOOL:
+                    return new PlcBOOL(data.getBoolean(0));
+                default:
+                    return null;
+            }
+        }
+    }
+
+    public float swap(float value) {
+        int bytes = Float.floatToIntBits(value);
+        int b1 = (bytes >> 0) & 0xff;
+        int b2 = (bytes >> 8) & 0xff;
+        int b3 = (bytes >> 16) & 0xff;
+        int b4 = (bytes >> 24) & 0xff;
+        return Float.intBitsToFloat(b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0);
+    }
+
+    @Override
+    public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
+        CompletableFuture<PlcWriteResponse> future = new CompletableFuture<>();
+        DefaultPlcWriteRequest request = (DefaultPlcWriteRequest) writeRequest;
+        List<CipWriteRequest> items = new ArrayList<>(writeRequest.getNumberOfFields());
+        for (String fieldName : request.getFieldNames()) {
+            final LogixField field = (LogixField) request.getField(fieldName);
+            final PlcValue value = request.getPlcValue(fieldName);
+            String tag = field.getTag();
+            int elements = 1;
+            if (field.getElementNb() > 1) {
+                elements = field.getElementNb();
+            }
+
+            //We need the size of the request in words (0x91, tagLength, ... tag + possible pad)
+            // Taking half to get word size
+            boolean isArray = false;
+            String tagIsolated = tag;
+            if (tag.contains("[")) {
+                isArray = true;
+                tagIsolated = tag.substring(0, tag.indexOf("["));
+            }
+            int dataLength = (tagIsolated.length() + 2 + (tagIsolated.length() % 2) + (isArray ? 2 : 0));
+            byte requestPathSize = (byte) (dataLength / 2);
+            byte[] data = encodeValue(value, field.getType(), (short) elements);
+            CipWriteRequest writeReq = new CipWriteRequest(requestPathSize, toAnsi(tag), field.getType(), elements, data, -1);
+            items.add(writeReq);
+        }
+
+        RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+        if (items.size() == 1) {
+            tm.startRequest();
+            CipRRData rrdata = new CipRRData(sessionHandle, 0L, senderContext, 0L,
+                new CipExchange(
+                    new CipUnconnectedRequest(
+                        items.get(0), (byte) configuration.getBackplane(), (byte) configuration.getSlot(), -1
+                    ),
+                    -1
+                ),
+                -1
+            );
+            transaction.submit(() -> context.sendRequest(rrdata)
+                .expectResponse(EipPacket.class, REQUEST_TIMEOUT)
+                .onTimeout(future::completeExceptionally)
+                .onError((p, e) -> future.completeExceptionally(e))
+                .check(p -> p instanceof CipRRData).unwrap(p -> (CipRRData) p)
+                .check(p -> p.getSessionHandle() == sessionHandle)
+                //.check(p -> p.getSenderContext() == senderContext)
+                .check(p -> p.getExchange().getService() instanceof CipWriteResponse)
+                .unwrap(p -> (CipWriteResponse) p.getExchange().getService())
+                .handle(p -> {
+                    future.complete((PlcWriteResponse) decodeWriteResponse(p, writeRequest));
+                    transaction.endRequest();
+                })
+            );
+        } else {
+            tm.startRequest();
+            short nb = (short) items.size();
+            List<Integer> offsets = new ArrayList<>(nb);
+            int offset = 2 + nb * 2;
+            for (int i = 0; i < nb; i++) {
+                offsets.add(offset);
+                offset += items.get(i).getLengthInBytes();
+            }
+
+            List<CipService> serviceArr = new ArrayList<>(nb);
+            for (int i = 0; i < nb; i++) {
+                serviceArr.add(items.get(i));
+            }
+            Services data = new Services(nb, offsets, serviceArr, -1);
+            //Encapsulate the data
+
+            CipRRData pkt = new CipRRData(sessionHandle, 0L, emptySenderContext, 0L,
+                new CipExchange(
+                    new CipUnconnectedRequest(
+                        new MultipleServiceRequest(data, -1),
+                        (byte) configuration.getBackplane(),
+                        (byte) configuration.getSlot(),
+                        -1
+                    ),
+                    -1
+                ),
+                -1
+            );
+
+
+            transaction.submit(() -> context.sendRequest(pkt)
+                .expectResponse(EipPacket.class, REQUEST_TIMEOUT)
+                .onTimeout(future::completeExceptionally)
+                .onError((p, e) -> future.completeExceptionally(e))
+                .check(p -> p instanceof CipRRData)
+                .check(p -> p.getSessionHandle() == sessionHandle)
+                //.check(p -> p.getSenderContext() == senderContext)
+                .unwrap(p -> (CipRRData) p)
+                .unwrap(p -> p.getExchange().getService()).check(p -> p instanceof MultipleServiceResponse)
+                .unwrap(p -> (MultipleServiceResponse) p)
+                .check(p -> p.getServiceNb() == nb)
+                .handle(p -> {
+                    future.complete((PlcWriteResponse) decodeWriteResponse(p, writeRequest));
+                    // Finish the request-transaction.
+                    transaction.endRequest();
+                }));
+        }
+        return future;
+    }
+
+    private PlcResponse decodeWriteResponse(CipService p, PlcWriteRequest writeRequest) {
+        Map<String, PlcResponseCode> responses = new HashMap<>();
+
+        if (p instanceof CipWriteResponse) {
+            CipWriteResponse resp = (CipWriteResponse) p;
+            String fieldName = writeRequest.getFieldNames().iterator().next();
+            LogixField field = (LogixField) writeRequest.getField(fieldName);
+            responses.put(fieldName, decodeResponseCode(resp.getStatus()));
+            return new DefaultPlcWriteResponse(writeRequest, responses);
+        } else if (p instanceof MultipleServiceResponse) {
+            MultipleServiceResponse resp = (MultipleServiceResponse) p;
+            int nb = resp.getServiceNb();
+            List<CipService> arr = new ArrayList<>(nb);
+            ReadBufferByteBased read = new ReadBufferByteBased(resp.getServicesData());
+            int total = (int) read.getTotalBytes();
+            for (int i = 0; i < nb; i++) {
+                int length = 0;
+                int offset = resp.getOffsets().get(i);
+                if (offset == nb - 1) {
+                    length = total - offset; //Get the rest if last
+                } else {
+                    length = resp.getOffsets().get(i + 1) - offset; //Calculate length with offsets
+                }
+                ReadBuffer serviceBuf = new ReadBufferByteBased(read.getBytes(offset, length), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN);
+                CipService service = null;
+                try {
+                    service = CipService.staticParse(read, length);
+                    arr.add(service);
+                } catch (ParseException e) {
+                    throw new PlcRuntimeException(e);
+                }
+            }
+            Services services = new Services(nb, resp.getOffsets(), arr, -1);
+            Iterator<String> it = writeRequest.getFieldNames().iterator();
+            for (int i = 0; i < nb && it.hasNext(); i++) {
+                String fieldName = it.next();
+                LogixField field = (LogixField) writeRequest.getField(fieldName);
+                PlcValue plcValue = null;
+                if (services.getServices().get(i) instanceof CipWriteResponse) {
+                    CipWriteResponse writeResponse = (CipWriteResponse) services.getServices().get(i);
+                    PlcResponseCode code = decodeResponseCode(writeResponse.getStatus());
+                    responses.put(fieldName, code);
+                }
+            }
+            return new DefaultPlcWriteResponse(writeRequest, responses);
+        }
+        return null;
+    }
+
+    private byte[] encodeValue(PlcValue value, CIPDataTypeCode type, short elements) {
+        //ByteBuffer buffer = ByteBuffer.allocate(4+type.getSize()).order(ByteOrder.LITTLE_ENDIAN);
+        ByteBuffer buffer = ByteBuffer.allocate(type.getSize()).order(ByteOrder.LITTLE_ENDIAN);
+        switch (type) {
+            case SINT:
+                buffer.put(value.getByte());
+                break;
+            case INT:
+                buffer.putShort(value.getShort());
+                break;
+            case DINT:
+                buffer.putInt(value.getInteger());
+                break;
+            case REAL:
+                buffer.putDouble(value.getDouble());
+                break;
+            default:
+                break;
+        }
+        return buffer.array();
+
+    }
+
+    private PlcResponseCode decodeResponseCode(int status) {
+        //TODO other status
+        switch (status) {
+            case 0:
+                return PlcResponseCode.OK;
+            default:
+                return PlcResponseCode.INTERNAL_ERROR;
+        }
+    }
+
+    @Override
+    public void close(ConversationContext<EipPacket> context) {
+        logger.debug("Sending UnregisterSession EIP Pakcet");
+        context.sendRequest(new EipDisconnectRequest(sessionHandle, 0L, emptySenderContext, 0L)); //Unregister gets no response
+        logger.debug("Unregistred Session {}", sessionHandle);
+    }
+}
diff --git a/plc4j/drivers/logix/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver b/plc4j/drivers/logix/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
new file mode 100644
index 0000000000..c62562f371
--- /dev/null
+++ b/plc4j/drivers/logix/src/main/resources/META-INF/services/org.apache.plc4x.java.api.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.logix.readwrite.LogixDriver
\ No newline at end of file
diff --git a/plc4j/drivers/logix/src/test/java/org/apache/plc4x/java/eip/readwrite/LogixDriverTestsuite.java b/plc4j/drivers/logix/src/test/java/org/apache/plc4x/java/eip/readwrite/LogixDriverTestsuite.java
new file mode 100644
index 0000000000..e8d9f9bd9f
--- /dev/null
+++ b/plc4j/drivers/logix/src/test/java/org/apache/plc4x/java/eip/readwrite/LogixDriverTestsuite.java
@@ -0,0 +1,29 @@
+/*
+ * 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.eip.readwrite;
+
+import org.apache.plc4x.test.driver.DriverTestsuiteRunner;
+
+public class LogixDriverTestsuite extends DriverTestsuiteRunner {
+
+    public LogixDriverTestsuite() {
+        super("/protocols/logix/DriverTestsuite.xml");
+    }
+
+}
diff --git a/plc4j/drivers/logix/src/test/java/org/apache/plc4x/java/eip/readwrite/LogixParserSerializerTest.java b/plc4j/drivers/logix/src/test/java/org/apache/plc4x/java/eip/readwrite/LogixParserSerializerTest.java
new file mode 100644
index 0000000000..8e22238f1e
--- /dev/null
+++ b/plc4j/drivers/logix/src/test/java/org/apache/plc4x/java/eip/readwrite/LogixParserSerializerTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.eip.readwrite;
+
+import org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner;
+
+public class LogixParserSerializerTest extends ParserSerializerTestsuiteRunner {
+
+    public LogixParserSerializerTest() {
+        super("/protocols/logix/ParserSerializerTestsuite.xml");
+    }
+
+}
diff --git a/plc4j/drivers/logix/src/test/resources/logback.xml b/plc4j/drivers/logix/src/test/resources/logback.xml
new file mode 100644
index 0000000000..4f7ce5ad5a
--- /dev/null
+++ b/plc4j/drivers/logix/src/test/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">
+    <encoder>
+      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="error">
+    <appender-ref ref="STDOUT" />
+  </root>
+
+</configuration>
\ No newline at end of file
diff --git a/plc4j/drivers/pom.xml b/plc4j/drivers/pom.xml
index 21048b6dc4..036d506dad 100644
--- a/plc4j/drivers/pom.xml
+++ b/plc4j/drivers/pom.xml
@@ -43,6 +43,7 @@
     <module>eip</module>
     <module>firmata</module>
     <module>knxnetip</module>
+    <module>logix</module>
     <module>mock</module>
     <module>modbus</module>
     <module>opcua</module>
diff --git a/protocols/logix/src/main/resources/protocols/logix/logix.mspec b/protocols/logix/src/main/resources/protocols/logix/logix.mspec
index 02506377b3..efb664ea21 100644
--- a/protocols/logix/src/main/resources/protocols/logix/logix.mspec
+++ b/protocols/logix/src/main/resources/protocols/logix/logix.mspec
@@ -161,7 +161,7 @@
     [simple     uint    3   pathSegmentType]
     [simple     uint    3   logicalSegmentType]
     [simple     uint    2   logicalSegmentFormat]
-    [simple     uint    8   class]
+    [simple     uint    8   classSegment]
 ]
 
 [type   PortSegment
@@ -180,9 +180,9 @@
 ]
 
 [type   TransportType
-   [simple      bit        direction]
+   [simple      bit         direction]
    [simple      uint    3   trigger]
-   [simple      uint    4   class]
+   [simple      uint    4   classTransport]
 ]
 
 [type   Services  (uint   16   servicesLen)


[plc4x] 01/02: mspec comiles

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hutcheb pushed a commit to branch logix_develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 66d5193800134ac4955f493605c64835328516a5
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Wed May 25 19:00:08 2022 +1000

    mspec comiles
---
 .../src/main/resources/protocols/logix/logix.mspec | 75 +++++++++++-----------
 protocols/pom.xml                                  |  1 +
 2 files changed, 40 insertions(+), 36 deletions(-)

diff --git a/protocols/logix/src/main/resources/protocols/logix/logix.mspec b/protocols/logix/src/main/resources/protocols/logix/logix.mspec
index 65860e0021..02506377b3 100644
--- a/protocols/logix/src/main/resources/protocols/logix/logix.mspec
+++ b/protocols/logix/src/main/resources/protocols/logix/logix.mspec
@@ -93,27 +93,19 @@
                [array      byte   servicesData count 'serviceLen - 6 - (2 * serviceNb)']
         ]
         ['0x52','false'   CipUnconnectedRequest
-               [implicit   uint    8    requestPathSize ]
-               [optional   PathSegment  pathSegment0 'requestPathSize - pathSegment0.dataSize']
-               [optional   PathSegment  pathSegment1 'requestPathSize - pathSegment0.dataSize - pathSegment1.dataSize']
-
-
-               [reserved   uint    8   '0x20']   // setRequestPathLogicalClassSegment
-               [reserved   uint    8   '0x06']   // set request class path
-               [reserved   uint    8   '0x24']   // setRequestPathLogicalInstanceSegment
-               [reserved   uint    8   '0x01']   // setRequestPathInstance
-               [reserved   uint    16  '0x9D05']   //Timeout 5s
-               [implicit   uint    16  messageSize   'lengthInBytes - 10 - 4']   //subtract above and routing
-               [simple     CipService('messageSize')  unconnectedService ]
-               [const      uint    16  route 0x0001]
-               [simple     int     8   backPlane]
-               [simple     int     8   slot]
+               [simple     uint    8    requestPathSize ]
+               [simple     PathSegment  pathSegment0]
+               [optional   PathSegment  pathSegment1 '(requestPathSize - pathSegment0.dataSize) > 0']
+               [optional   PathSegment  pathSegment2 '(requestPathSize - pathSegment0.dataSize - pathSegment1.dataSize) > 0']
+               [optional   PathSegment  pathSegment3 '(requestPathSize - pathSegment0.dataSize - pathSegment1.dataSize - pathSegment2.dataSize) > 0']
+               [optional   PathSegment  pathSegment4 '(requestPathSize - pathSegment0.dataSize - pathSegment1.dataSize - pathSegment2.dataSize - pathSegment3.dataSize) > 0']
+               [optional   PathSegment  pathSegment5 '(requestPathSize - pathSegment0.dataSize - pathSegment1.dataSize - pathSegment2.dataSize - pathSegment3.dataSize - pathSegment4.dataSize) > 0']
         ]
         ['0x5B','false'     CipConnectionManagerRequest
                [simple      int     8           requestPathSize]
                [simple      ClassSegment        classSegment]
                [simple      InstanceSegment     instanceSegment]
-               [simple      unit    4           priority]
+               [simple      uint    4           priority]
                [simple      uint    4           tickTime]
                [simple      uint    8           timeoutTicks]
                [simple      uint    16          actualTimeout]
@@ -147,39 +139,50 @@
     ]
 ]
 
+[discriminatedType PathSegment
+    [discriminator  uint    3   pathSegment]
+    [discriminator  uint    5   dataSegment]
+    [typeSwitch pathSegment,dataSegment
+        ['0x04','0x11'      AnsiExtendedSymbolSegment
+            [implicit   uint    8   dataSize    'symbol.length']
+            [simple     vstring 'dataSize'  symbol]
+        ]
+    ]
+]
+
 [type   InstanceSegment
-                [simple     uint    3   pathSegmentType]
-                [simple     uint    3   logicalSegmentType]
-                [simple     uint    2   logicalSegmentFormat]
-                [simple     uint    8   instance]
+    [simple     uint    3   pathSegmentType]
+    [simple     uint    3   logicalSegmentType]
+    [simple     uint    2   logicalSegmentFormat]
+    [simple     uint    8   instance]
 ]
 
 [type   ClassSegment
-                [simple     uint    3   pathSegmentType]
-                [simple     uint    3   logicalSegmentType]
-                [simple     uint    2   logicalSegmentFormat]
-                [simple     uint    8   class]
+    [simple     uint    3   pathSegmentType]
+    [simple     uint    3   logicalSegmentType]
+    [simple     uint    2   logicalSegmentFormat]
+    [simple     uint    8   class]
 ]
 
 [type   PortSegment
-                [simple     uint    3   portSegmentType]
-                [simple     bool    1   extendedLinkAddress]
-                [simple     uint    4   port]
-                [simple     uint    8   linkAddress]
+    [simple     uint    3   portSegmentType]
+    [simple     bit         extendedLinkAddress]
+    [simple     uint    4   port]
+    [simple     uint    8   linkAddress]
 ]
 
 [type   NetworkConnectionParameters
-               [simple      bool    1   owner]
-               [simple      uint    2   connectionType]
-               [simple      uint    2   priority]
-               [simple      bool    1   connectionSizeType]
-               [simple      uint    16  connectionSize]
+   [simple      bit         owner]
+   [simple      uint    2   connectionType]
+   [simple      uint    2   priority]
+   [simple      bit         connectionSizeType]
+   [simple      uint    16  connectionSize]
 ]
 
 [type   TransportType
-               [simple      bool    1   direction]
-               [simple      uint    3   trigger]
-               [simple      uint    4   class]
+   [simple      bit        direction]
+   [simple      uint    3   trigger]
+   [simple      uint    4   class]
 ]
 
 [type   Services  (uint   16   servicesLen)
diff --git a/protocols/pom.xml b/protocols/pom.xml
index 62e70031b2..102ef60381 100644
--- a/protocols/pom.xml
+++ b/protocols/pom.xml
@@ -44,6 +44,7 @@
     <module>firmata</module>
     <module>genericcan</module>
     <module>knxnetip</module>
+    <module>logix</module>
     <module>modbus</module>
     <module>opcua</module>
     <module>plc4x</module>