diff --git a/plc4j/drivers/fins/pom.xml b/plc4j/drivers/fins/pom.xml
new file mode 100644
index 0000000000..003379822c
--- /dev/null
+++ b/plc4j/drivers/fins/pom.xml
@@ -0,0 +1,257 @@
+
+
+
+ 4.0.0
+
+
+ org.apache.plc4x
+ plc4j-drivers
+ 0.13.0-SNAPSHOT
+
+
+ plc4j-driver-fins
+
+ PLC4J: Driver: fins
+ Implementation of a PLC4X driver for the classic Step7 fins protocol.
+
+
+ 2024-02-16T14:53:02Z
+
+
+
+
+
+ org.apache.karaf.tooling
+ karaf-maven-plugin
+
+
+ generate-feature-xml
+ compile
+
+
+ features-generate-descriptor
+
+ verify
+
+
+ true
+ true
+
+
+
+ build-kar
+ package
+
+
+ kar
+
+
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ true
+
+
+ ${project.groupId}.${project.artifactId}
+ org.apache.plc4x.java.osgi.DriverActivator
+ org.apache.plc4x.java.api.PlcDriver,org.apache.plc4x.java.fins.readwrite.FinsTcpDriver
+
+
+ *
+
+ *
+
+
+
+
+
+
+
+
+ org.apache.plc4x
+ plc4j-api
+ 0.13.0-SNAPSHOT
+
+
+ org.apache.plc4x
+ plc4j-spi
+ 0.13.0-SNAPSHOT
+
+
+
+ org.apache.plc4x
+ plc4j-transport-tcp
+ 0.13.0-SNAPSHOT
+
+
+
+ org.pcap4j
+ pcap4j-core
+
+ compile
+
+
+ org.pcap4j
+ pcap4j-packetfactory-static
+ runtime
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ commons-codec
+ commons-codec
+
+
+ io.netty
+ netty-buffer
+
+
+ io.netty
+ netty-transport
+
+
+ io.netty
+ netty-handler
+
+
+ io.netty
+ netty-codec
+
+
+ io.netty
+ netty-common
+
+
+ org.json
+ json
+
+
+
+ org.apache.plc4x
+ plc4j-utils-test-utils
+ 0.13.0-SNAPSHOT
+ test
+
+
+ org.skyscreamer
+ jsonassert
+ test
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ test
+
+
+
+ org.apache.plc4x
+ plc4x-protocols-s7
+ 0.13.0-SNAPSHOT
+ tests
+ test-jar
+ test
+
+
+ org.apache.plc4x
+ plc4j-utils-raw-sockets
+ 0.13.0-SNAPSHOT
+ compile
+
+
+
+
+
+ update-generated-code
+
+
+
+ org.apache.plc4x.plugins
+ plc4x-maven-plugin
+
+
+ generate-driver
+ generate-sources
+
+ generate-driver
+
+
+ finstcp
+ java
+ read-write
+ src/main/generated
+
+
+
+ generate-discovery
+ generate-sources
+
+ generate-driver
+
+
+ finstcp-discovery
+ java
+ read-write
+ src/main/generated
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ org.apache.plc4x:plc4x-code-generation-language-java
+ org.apache.plc4x:plc4x-protocols-fins
+
+
+
+
+
+
+
+
+ org.apache.plc4x
+ plc4x-code-generation-language-java
+ 0.13.0-SNAPSHOT
+
+ provided
+
+
+
+ org.apache.plc4x
+ plc4x-protocols-s7
+ 0.13.0-SNAPSHOT
+
+ provided
+
+
+
+
+
+
diff --git a/plc4j/drivers/fins/src/main/generated/org/apache/plc4x/java/fins/readwrite/FinsTcpMessage.java b/plc4j/drivers/fins/src/main/generated/org/apache/plc4x/java/fins/readwrite/FinsTcpMessage.java
new file mode 100644
index 0000000000..d6e7b78a0b
--- /dev/null
+++ b/plc4j/drivers/fins/src/main/generated/org/apache/plc4x/java/fins/readwrite/FinsTcpMessage.java
@@ -0,0 +1,148 @@
+/*
+ * 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
+ *
+ * https://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.fins.readwrite;
+
+import org.apache.plc4x.java.spi.codegen.ThreadLocalHelper;
+import org.apache.plc4x.java.spi.generation.EvaluationHelper;
+import org.apache.plc4x.java.spi.generation.Message;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.PositionAware;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.java.spi.generation.SerializationException;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
+import org.apache.plc4x.java.spi.generation.WriteBufferBoxBased;
+
+import java.util.Objects;
+
+import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
+
+// Code generated by code-generation. DO NOT EDIT.
+
+public abstract class FinsTcpMessage implements Message {
+
+ // Abstract accessors for discriminator values.
+ public abstract Short getMessageType();
+
+ // Constant values.
+ public static final Short PROTOCOLID = 0x32;
+
+
+ public FinsTcpMessage() {
+ super();
+ }
+
+
+ public short getProtocolId() {
+ return PROTOCOLID;
+ }
+
+ protected abstract void serializeS7MessageChild(WriteBuffer writeBuffer)
+ throws SerializationException;
+
+ public void serialize(WriteBuffer writeBuffer) throws SerializationException {
+ PositionAware positionAware = writeBuffer;
+ boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+ writeBuffer.pushContext("S7Message");
+
+ // Const Field (protocolId)
+ writeConstField("protocolId", PROTOCOLID, writeUnsignedShort(writeBuffer, 8));
+
+ // Discriminator Field (messageType) (Used as input to a switch field)
+ writeDiscriminatorField("messageType", getMessageType(), writeUnsignedShort(writeBuffer, 8));
+
+ // Reserved Field (reserved)
+ writeReservedField("reserved", (int) 0x0000, writeUnsignedInt(writeBuffer, 16));
+
+
+ // Switch field (Serialize the sub-type)
+ serializeS7MessageChild(writeBuffer);
+
+ writeBuffer.popContext("S7Message");
+ }
+
+ @Override
+ public int getLengthInBytes() {
+ return (int) Math.ceil((float) getLengthInBits() / 8.0);
+ }
+
+ @Override
+ public int getLengthInBits() {
+ int lengthInBits = 0;
+ FinsTcpMessage _value = this;
+ boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+ // Const Field (protocolId)
+ lengthInBits += 8;
+
+ // Discriminator Field (messageType)
+ lengthInBits += 8;
+
+ // Reserved Field (reserved)
+ lengthInBits += 16;
+
+ // Simple field (tpduReference)
+ lengthInBits += 16;
+
+ // Implicit Field (parameterLength)
+ lengthInBits += 16;
+
+ // Implicit Field (payloadLength)
+ lengthInBits += 16;
+
+ // Length of sub-type elements will be added by sub-type...
+
+
+ return lengthInBits;
+ }
+
+ public static FinsTcpMessage staticParse(ReadBuffer readBuffer) throws ParseException {
+ readBuffer.pullContext("S7Message");
+ PositionAware positionAware = readBuffer;
+ boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+ short protocolId =
+ readConstField("protocolId", readUnsignedShort(readBuffer, 8), FinsTcpMessage.PROTOCOLID);
+
+ short messageType = readDiscriminatorField("messageType", readUnsignedShort(readBuffer, 8));
+
+ Integer reservedField0 =
+ readReservedField("reserved", readUnsignedInt(readBuffer, 16), (int) 0x0000);
+
+ readBuffer.closeContext("S7Message");
+ // Create the instance
+ FinsTcpMessage _s7Message = null;
+ return _s7Message;
+ }
+
+
+
+ @Override
+ public String toString() {
+ WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, true);
+ try {
+ writeBufferBoxBased.writeSerializable(this);
+ } catch (SerializationException e) {
+ throw new RuntimeException(e);
+ }
+ return "\n" + writeBufferBoxBased.getBox().toString() + "\n";
+ }
+}
diff --git a/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/FinsTcpDriver.java b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/FinsTcpDriver.java
new file mode 100644
index 0000000000..cd5d765c55
--- /dev/null
+++ b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/FinsTcpDriver.java
@@ -0,0 +1,144 @@
+/*
+ * 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
+ *
+ * https://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.fins.readwrite;
+
+import io.netty.buffer.ByteBuf;
+import org.apache.plc4x.java.fins.readwrite.configuration.FinsTcpConfiguration;
+import org.apache.plc4x.java.fins.readwrite.context.FinsTcpDriverContext;
+import org.apache.plc4x.java.fins.readwrite.optimizer.FinsTcpOptimizer;
+import org.apache.plc4x.java.fins.readwrite.protocol.FinsTcpProtocolLogic;
+import org.apache.plc4x.java.fins.readwrite.protocol.FinsTcpProtocolStackConfigurer;
+import org.apache.plc4x.java.spi.configuration.PlcConnectionConfiguration;
+import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
+import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
+import org.apache.plc4x.java.spi.generation.Message;
+import org.apache.plc4x.java.spi.optimizer.BaseOptimizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.ToIntFunction;
+
+public class FinsTcpDriver extends GeneratedDriverBase {
+
+ public static final int ISO_ON_TCP_PORT = 9600;
+ private static final Logger log = LoggerFactory.getLogger(FinsTcpDriver.class);
+
+ @Override
+ public String getProtocolCode() {
+ return "finstcp";
+ }
+
+ @Override
+ public String getProtocolName() {
+ return "omron finsTcp (Basic)";
+ }
+
+ @Override
+ protected Class extends PlcConnectionConfiguration> getConfigurationClass() {
+ return FinsTcpConfiguration.class;
+ }
+
+ @Override
+ protected Optional getDefaultTransportCode() {
+ return Optional.of("tcp");
+ }
+
+ @Override
+ protected List getSupportedTransportCodes() {
+ return Collections.singletonList("tcp");
+ }
+
+ @Override
+ protected boolean canDiscover() {
+ return true;
+ }
+
+ @Override
+ protected boolean canRead() {
+ return true;
+ }
+
+ @Override
+ protected boolean canWrite() {
+ return true;
+ }
+
+ @Override
+ // TODO: Actually this is not quite true ... this is only true for some S7 devices
+ protected boolean canSubscribe() {
+ return true;
+ }
+
+ @Override
+ protected BaseOptimizer getOptimizer() {
+ return new FinsTcpOptimizer();
+ }
+
+ @Override
+ protected ProtocolStackConfigurer getStackConfigurer() {
+ return FinsTcpProtocolStackConfigurer.builder(FinsTcpMessage.class, FinsTcpMessage::staticParse)
+ .withProtocol(FinsTcpProtocolLogic.class)
+ .withDriverContext(FinsTcpDriverContext.class)
+ .withPacketSizeEstimator(ByteLengthEstimator.class)
+ .withCorruptPacketRemover(CorruptPackageCleaner.class)
+ .build();
+ }
+
+ /**
+ * 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;
+ }
+
+
+ /**
+ * Estimate the Length of a Packet
+ */
+ public static class ByteLengthEstimator implements ToIntFunction {
+ @Override
+ public int applyAsInt(ByteBuf byteBuf) {
+ if (byteBuf.readableBytes() >= 4) {
+ return byteBuf.getUnsignedShort(byteBuf.readerIndex() + 2);
+ }
+ return -1;
+ }
+ }
+
+ /**
+ * Consumes all Bytes till another Magic Byte is found
+ */
+ public static class CorruptPackageCleaner implements Consumer {
+ @Override
+ public void accept(ByteBuf byteBuf) {
+ while (byteBuf.getUnsignedByte(0) != FinsTcpMessage.PROTOCOLID) {
+ // Just consume the bytes till the next possible start position.
+ byteBuf.readByte();
+ }
+ }
+ }
+
+}
diff --git a/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/configuration/FinsTcpConfiguration.java b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/configuration/FinsTcpConfiguration.java
new file mode 100644
index 0000000000..e74d292108
--- /dev/null
+++ b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/configuration/FinsTcpConfiguration.java
@@ -0,0 +1,262 @@
+/*
+ * 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
+ *
+ * https://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.fins.readwrite.configuration;
+
+import org.apache.plc4x.java.spi.configuration.PlcConnectionConfiguration;
+import org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter;
+import org.apache.plc4x.java.spi.configuration.annotations.Description;
+import org.apache.plc4x.java.spi.configuration.annotations.Since;
+import org.apache.plc4x.java.spi.configuration.annotations.defaults.BooleanDefaultValue;
+import org.apache.plc4x.java.spi.configuration.annotations.defaults.IntDefaultValue;
+import org.apache.plc4x.java.spi.configuration.annotations.defaults.StringDefaultValue;
+
+public class FinsTcpConfiguration implements PlcConnectionConfiguration {
+
+ @ConfigurationParameter("local-rack")
+ @IntDefaultValue(1)
+ @Description("Rack value for the client (PLC4X device).")
+ public int localRack = 1;
+
+ @ConfigurationParameter("local-slot")
+ @IntDefaultValue(1)
+ @Description("Slot value for the client (PLC4X device).")
+ public int localSlot = 1;
+
+ @ConfigurationParameter("local-tsap")
+ @IntDefaultValue(0)
+ @Description("Local Transport Service Access Point. (Overrides settings made in local-rack, local-slot and local-device-group. Be sure to convert into integer representation)")
+ public int localTsap = 0;
+
+ @ConfigurationParameter("remote-rack")
+ @IntDefaultValue(0)
+ @Description("Rack value for the remote main CPU (PLC).")
+ public int remoteRack = 0;
+
+ @ConfigurationParameter("remote-slot")
+ @IntDefaultValue(0)
+ @Description("Slot value for the remote main CPU (PLC).")
+ public int remoteSlot = 0;
+
+ @ConfigurationParameter("remote-tsap")
+ @IntDefaultValue(0)
+ @Description("Remote Transport Service Access Point. (Overrides settings made in remote-rack, remote-slot and remote-device-group. Be sure to convert into integer representation)")
+ public int remoteTsap = 0;
+
+ @ConfigurationParameter("remote-rack2")
+ @IntDefaultValue(0)
+ @Description("Rack value for the remote secondary CPU (PLC).")
+ public int remoteRack2 = 0;
+
+ @ConfigurationParameter("remote-slot2")
+ @IntDefaultValue(0)
+ @Description("Slot value for the remote secondary CPU (PLC).")
+ public int remoteSlot2 = 0;
+
+ @ConfigurationParameter("pdu-size")
+ @IntDefaultValue(1024)
+ @Description("Maximum size of a data-packet sent to and received from the remote PLC. During the connection process both parties will negotiate a maximum size both parties can work with and is equal or smaller than the given value is used. The driver will automatically split up large requests to not exceed this value in a request or expected response.")
+ public int pduSize = 1024;
+
+ @ConfigurationParameter("max-amq-caller")
+ @IntDefaultValue(8)
+ @Description("Maximum number of unconfirmed requests the PLC will accept in parallel before discarding with errors. This parameter also will be negotiated during the connection process and the maximum both parties can work with and is equal or smaller than the given value is used. The driver will automatically take care not exceeding this value while processing requests. Too many requests can cause a growing queue.")
+ public int maxAmqCaller = 8;
+
+ @ConfigurationParameter("max-amq-callee")
+ @IntDefaultValue(8)
+ @Description("Maximum number of unconfirmed responses or requests PLC4X will accept in parallel before discarding with errors. This option is available for completeness and is correctly handled out during the connection process, however it is currently not enforced on PLC4X’s side. So if a PLC would send more messages than agreed upon, these would still be processed.")
+ public int maxAmqCallee = 8;
+
+ @ConfigurationParameter("controller-type")
+ @Description("As part of the connection process, usually the PLC4X S7 driver would try to identify the remote device. However some devices seem to have problems with this and hang up or cause other problems. In such a case, providing the controller-type will skip the identification process and hereby avoid this type of problem. Possible values are:/n- S7_300\n- S7_400\n- S7_1200\n- S7-1500\n- LOGO")
+ public String controllerType;
+
+ @ConfigurationParameter("read-timeout")
+ @IntDefaultValue(0)
+ @Description("This is the maximum waiting time for reading on the TCP channel. As there is no traffic, it must be assumed that the connection with the interlocutor was lost and it must be restarted. When the channel is closed, the \"fail over\" is carried out in case of having the secondary channel, or it is expected that it will be restored automatically, which is done every 4 seconds.")
+ public int readTimeout = 0;
+
+ @ConfigurationParameter("ping")
+ @BooleanDefaultValue(false)
+ @Description("If your application requires sampling times greater than the set \"read-timeout\" time, it is important that the PING option is activated, this will prevent the TCP channel from being closed unnecessarily.")
+ public boolean ping = false;
+
+ @ConfigurationParameter("ping-time")
+ @IntDefaultValue(0)
+ @Description("Time value in seconds at which the execution of the PING will be scheduled. Generally set by developer experience, but generally should be the same as (read-timeout / 2).")
+ public int pingTime = 0;
+
+ @ConfigurationParameter("retry-time")
+ @IntDefaultValue(0)
+ @Description("Time for supervision of TCP channels. If the channel is not active, a safe stop of the EventLoop must be performed, to ensure that no additional tasks are created.")
+ public int retryTime = 0;
+
+ public int getLocalRack() {
+ return localRack;
+ }
+
+ public void setLocalRack(int localRack) {
+ this.localRack = localRack;
+ }
+
+ public int getLocalSlot() {
+ return localSlot;
+ }
+
+ public void setLocalSlot(int localSlot) {
+ this.localSlot = localSlot;
+ }
+
+ public int getLocalTsap() {
+ return localTsap;
+ }
+
+ public void setLocalTsap(int localTsap) {
+ this.localTsap = localTsap;
+ }
+
+ public int getRemoteRack() {
+ return remoteRack;
+ }
+
+ public void setRemoteRack(int remoteRack) {
+ this.remoteRack = remoteRack;
+ }
+
+ public int getRemoteSlot() {
+ return remoteSlot;
+ }
+
+ public void setRemoteSlot(int remoteSlot) {
+ this.remoteSlot = remoteSlot;
+ }
+
+ public int getRemoteRack2() {
+ return remoteRack2;
+ }
+
+ public void setRemoteRack2(int remoteRack2) {
+ this.remoteRack2 = remoteRack2;
+ }
+
+ public int getRemoteSlot2() {
+ return remoteSlot2;
+ }
+
+ public void setRemoteSlot2(int remoteSlot2) {
+ this.remoteSlot2 = remoteSlot2;
+ }
+
+ public int getRemoteTsap() {
+ return remoteTsap;
+ }
+
+ public void setRemoteTsap(int remoteTsap) {
+ this.remoteTsap = remoteTsap;
+ }
+
+ public int getPduSize() {
+ return pduSize;
+ }
+
+ public void setPduSize(int pduSize) {
+ this.pduSize = pduSize;
+ }
+
+ public int getMaxAmqCaller() {
+ return maxAmqCaller;
+ }
+
+ public void setMaxAmqCaller(int maxAmqCaller) {
+ this.maxAmqCaller = maxAmqCaller;
+ }
+
+ public int getMaxAmqCallee() {
+ return maxAmqCallee;
+ }
+
+ public void setMaxAmqCallee(int maxAmqCallee) {
+ this.maxAmqCallee = maxAmqCallee;
+ }
+
+ public String getControllerType() {
+ return controllerType;
+ }
+
+ public void setControllerType(String controllerType) {
+ this.controllerType = controllerType;
+ }
+
+ public int getReadTimeout() {
+ return readTimeout;
+ }
+
+ public void setReadTimeout(int readTimeOut) {
+ this.readTimeout = readTimeOut;
+ }
+
+ public boolean getPing() {
+ return ping;
+ }
+
+ public void setPing(boolean ping) {
+ this.ping = ping;
+ }
+
+ public int getPingTime() {
+ return pingTime;
+ }
+
+ public void setPingTime(int pingTime) {
+ this.pingTime = pingTime;
+ }
+
+ public int getRetryTime() {
+ return retryTime;
+ }
+
+ public void setRetryTime(int retryTime) {
+ this.retryTime = retryTime;
+ }
+
+ @Override
+ public String toString() {
+ return "Configuration{" +
+ "local-rack=" + localRack +
+ ", local-slot=" + localSlot +
+ ", local-tsap=" + localTsap +
+ ", remote-rack=" + remoteRack +
+ ", remote-slot=" + remoteSlot +
+ ", remote-rack2=" + remoteRack2 +
+ ", remote-slot2=" + remoteSlot2 +
+ ", remote-tsap=" + remoteTsap +
+ ", pduSize=" + pduSize +
+ ", maxAmqCaller=" + maxAmqCaller +
+ ", maxAmqCallee=" + maxAmqCallee +
+ ", controllerType='" + controllerType +
+ ", readTimeOut='" + readTimeout +
+ ", ping='" + ping +
+ ", pingTime='" + pingTime +
+ ", retryTime='" + retryTime +
+ '\'' +
+ '}';
+ }
+
+}
+
diff --git a/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/context/FinsTcpDriverContext.java b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/context/FinsTcpDriverContext.java
new file mode 100644
index 0000000000..25405adde9
--- /dev/null
+++ b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/context/FinsTcpDriverContext.java
@@ -0,0 +1,153 @@
+/*
+ * 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
+ *
+ * https://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.fins.readwrite.context;
+
+import org.apache.plc4x.java.fins.readwrite.configuration.FinsTcpConfiguration;
+import org.apache.plc4x.java.spi.configuration.HasConfiguration;
+import org.apache.plc4x.java.spi.context.DriverContext;
+
+import java.time.Duration;
+
+public class FinsTcpDriverContext implements DriverContext, HasConfiguration {
+
+ private boolean passiveMode = false;
+ private int callingTsapId;
+ private int calledTsapId;
+ private int pduSize;
+ private int maxAmqCaller;
+ private int maxAmqCallee;
+
+ private int calledTsapId2;
+ private int readTimeout;
+ private boolean ping;
+ private int pingTime;
+ private int retryTime;
+
+ @Override
+ public void setConfiguration(FinsTcpConfiguration configuration) {
+
+ if (configuration.localTsap > 0) {
+ this.callingTsapId = configuration.localTsap;
+ }
+ if (configuration.remoteTsap > 0) {
+ this.calledTsapId = configuration.remoteTsap;
+ }
+
+ this.maxAmqCaller = configuration.maxAmqCaller;
+ this.maxAmqCallee = configuration.maxAmqCallee;
+
+ this.readTimeout = configuration.readTimeout;
+ this.ping = configuration.ping;
+ this.pingTime = (configuration.pingTime == 0) ? 10 : configuration.pingTime;
+ this.retryTime = configuration.retryTime;
+ }
+
+ public boolean isPassiveMode() {
+ return passiveMode;
+ }
+
+ public void setPassiveMode(boolean passiveMode) {
+ this.passiveMode = passiveMode;
+ }
+
+ public int getCallingTsapId() {
+ return callingTsapId;
+ }
+
+ public void setCallingTsapId(int callingTsapId) {
+ this.callingTsapId = callingTsapId;
+ }
+
+ public int getCalledTsapId() {
+ return calledTsapId;
+ }
+
+ public void setCalledTsapId(int calledTsapId) {
+ this.calledTsapId = calledTsapId;
+ }
+
+ public int getCalledTsapId2() {
+ return calledTsapId2;
+ }
+
+ public void setCalledTsapId2(int calledTsapId2) {
+ this.calledTsapId2 = calledTsapId2;
+ }
+
+ public int getPduSize() {
+ return pduSize;
+ }
+
+ public void setPduSize(int pduSize) {
+ this.pduSize = pduSize;
+ }
+
+ public int getMaxAmqCaller() {
+ return maxAmqCaller;
+ }
+
+ public void setMaxAmqCaller(int maxAmqCaller) {
+ this.maxAmqCaller = maxAmqCaller;
+ }
+
+ public int getMaxAmqCallee() {
+ return maxAmqCallee;
+ }
+
+ public void setMaxAmqCallee(int maxAmqCallee) {
+ this.maxAmqCallee = maxAmqCallee;
+ }
+
+ public int getReadTimeout() {
+ return readTimeout;
+ }
+
+ public void setReadTimeout(int readTimeout) {
+ this.readTimeout = readTimeout;
+ }
+
+ public Duration getReadTimeoutDuration() {
+ return Duration.ofMillis(readTimeout);
+ }
+
+ public boolean getPing() {
+ return ping;
+ }
+
+ public void setPing(boolean ping) {
+ this.ping = ping;
+ }
+
+ public int getPingTime() {
+ return pingTime;
+ }
+
+ public void setPingTime(int pingTime) {
+ this.pingTime = pingTime;
+ }
+
+ public int getRetryTime() {
+ return retryTime;
+ }
+
+ public void setRetryTime(int retryTime) {
+ this.retryTime = retryTime;
+ }
+
+}
diff --git a/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/optimizer/FinsTcpOptimizer.java b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/optimizer/FinsTcpOptimizer.java
new file mode 100644
index 0000000000..044d6a8465
--- /dev/null
+++ b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/optimizer/FinsTcpOptimizer.java
@@ -0,0 +1,108 @@
+/*
+ * 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
+ *
+ * https://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.fins.readwrite.optimizer;
+
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.types.PlcValueType;
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.spi.generation.SerializationException;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
+import org.apache.plc4x.java.spi.optimizer.BaseOptimizer;
+import org.apache.plc4x.java.spi.utils.Serializable;
+import org.apache.plc4x.java.spi.values.PlcValueAdapter;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class FinsTcpOptimizer extends BaseOptimizer {
+
+
+ @Override
+ protected int getNanosDelay() {
+ return 500000;
+ }
+
+
+
+ /**
+ * Little helper that helps avoid the problem, that the elements of a PlcList
+ * cannot be modified after being created.
+ */
+ private static class PlcListModifiable extends PlcValueAdapter {
+
+ private final List listItems;
+
+ public PlcListModifiable(List listItems) {
+ // This line is the only difference to the original one.
+ this.listItems = listItems;
+ }
+
+ @Override
+ public PlcValueType getPlcValueType() {
+ return PlcValueType.List;
+ }
+
+ public void add(PlcValue value) {
+ listItems.add(value);
+ }
+
+ @Override
+ public Object getObject() {
+ return getList();
+ }
+
+ @Override
+ public boolean isList() {
+ return true;
+ }
+
+ @Override
+ public int getLength() {
+ return listItems.size();
+ }
+
+ @Override
+ public PlcValue getIndex(int i) {
+ return listItems.get(i);
+ }
+
+ @Override
+ public List getList() {
+ return listItems;
+ }
+
+ @Override
+ public String toString() {
+ return "[" + listItems.stream().map(PlcValue::toString).collect(Collectors.joining(",")) + "]";
+ }
+
+ @Override
+ public void serialize(WriteBuffer writeBuffer) throws SerializationException {
+ writeBuffer.pushContext("PlcListModifiable");
+ for (PlcValue listItem : listItems) {
+ if (!(listItem instanceof Serializable)) {
+ throw new PlcRuntimeException("Error serializing. List item doesn't implement XmlSerializable");
+ }
+ ((Serializable) listItem).serialize(writeBuffer);
+ }
+ writeBuffer.popContext("PlcListModifiable");
+ }
+ }
+
+}
diff --git a/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/protocol/FinsTcpProtocolLogic.java b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/protocol/FinsTcpProtocolLogic.java
new file mode 100644
index 0000000000..8f3c40452c
--- /dev/null
+++ b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/protocol/FinsTcpProtocolLogic.java
@@ -0,0 +1,300 @@
+/*
+ * 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.fins.readwrite.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.util.AttributeKey;
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.apache.plc4x.java.api.exceptions.PlcProtocolException;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcReadResponse;
+import org.apache.plc4x.java.api.messages.PlcResponse;
+import org.apache.plc4x.java.api.messages.PlcSubscriptionRequest;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.fins.readwrite.FinsTcpMessage;
+import org.apache.plc4x.java.fins.readwrite.context.FinsTcpDriverContext;
+import org.apache.plc4x.java.fins.readwrite.tag.FinsTcpPlcTagHandler;
+import org.apache.plc4x.java.spi.ConversationContext;
+import org.apache.plc4x.java.spi.Plc4xProtocolBase;
+import org.apache.plc4x.java.spi.connection.PlcTagHandler;
+import org.apache.plc4x.java.spi.context.DriverContext;
+import org.apache.plc4x.java.spi.generation.Message;
+import org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest;
+import org.apache.plc4x.java.spi.messages.DefaultPlcReadResponse;
+import org.apache.plc4x.java.spi.messages.utils.DefaultPlcResponseItem;
+import org.apache.plc4x.java.spi.messages.utils.PlcResponseItem;
+import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
+import org.apache.plc4x.java.spi.values.PlcDATE_AND_LTIME;
+import org.apache.plc4x.java.spi.values.PlcList;
+import org.apache.plc4x.java.spi.values.PlcNull;
+import org.apache.plc4x.java.spi.values.PlcSINT;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * The S7 Protocol states that there can not be more then {min(maxAmqCaller, maxAmqCallee} "ongoing" requests.
+ * So we need to limit those.
+ * Thus, each request goes to a Work Queue and this Queue ensures, that only 3 are open at the same time.
+ */
+public class FinsTcpProtocolLogic extends Plc4xProtocolBase {
+
+ private static final Logger logger = LoggerFactory.getLogger(FinsTcpProtocolLogic.class);
+
+ public static final Duration REQUEST_TIMEOUT = Duration.ofMillis(10000);
+ private final AtomicInteger tpduGenerator = new AtomicInteger(10);
+
+ /*
+ * Task group for managing connection redundancy.
+ */
+ private final ExecutorService clientExecutorService = Executors.newFixedThreadPool(4, new BasicThreadFactory.Builder()
+ .namingPattern("plc4x-app-thread-%d")
+ .daemon(true)
+ .priority(Thread.MAX_PRIORITY)
+ .build());
+
+
+
+ /*
+ * This array stores the cyclic subscription requests between the driver
+ * and the PLC. The purpose is to document the tags associated with the
+ * request. Each subscription uses a 'JobID' that is managed by the PLC and
+ * obtained from the response to the request. In the following,
+ * the values sent PUSH from the PLC to the driver refer to this JobID.
+ */
+ private final Map cycRequests = new HashMap<>();
+
+ /*
+ * This data structure stores the last value associated with a cyclic
+ * subscription request. In each event received, the values of the internal
+ * PlcValue are compared and if any of them are different, the new value is
+ * transferred to the event stack and the value is updated in this HashMap.
+ */
+ private FinsTcpDriverContext finsTcpDriverContext;
+ private RequestTransactionManager tm;
+
+ @Override
+ public void setDriverContext(DriverContext driverContext) {
+ super.setDriverContext(driverContext);
+ this.finsTcpDriverContext = (FinsTcpDriverContext) driverContext;
+
+ // Initialize Transaction Manager.
+ // Until the number of concurrent requests is successfully negotiated we set it to a
+ // maximum of only one request being able to be sent at a time. During the login process
+ // No concurrent requests can be sent anyway. It will be updated when receiving the
+ // S7ParameterSetupCommunication response.
+ this.tm = new RequestTransactionManager(1);
+ }
+
+ @Override
+ public PlcTagHandler getTagHandler() {
+ return new FinsTcpPlcTagHandler();
+ }
+
+ @Override
+ public void close(ConversationContext context) {
+ // TODO: Find out how to close this prior to Java 19
+ //clientExecutorService.close();
+ tm.shutdown();
+ }
+
+ @Override
+ public void onConnect(ConversationContext context) {
+
+ //Set feature for all handlers in the pipeline from
+ //the driver configuration.
+ setChannelFeatures();
+
+ String order = "46494E530000000C0000000000000000000000";
+
+ String hexString = Integer.toHexString(10);
+ String handshakeMsg = order + String.format("%2s", hexString).replace(' ', '0');
+ logger.info("握手的消息:{}", handshakeMsg);
+ ByteBuf byteBuf = Unpooled.wrappedBuffer(hexToByteArray(handshakeMsg));
+
+ Channel channel = context.getChannel();
+
+ if(channel.isOpen() && channel.isActive() && channel.isWritable())
+ {
+ //必须有这句话
+ byteBuf.retain();
+ AttributeKey IS_CONNECTED = AttributeKey.valueOf("IS_CONNECTED");
+ ChannelFuture future = channel.writeAndFlush(byteBuf);
+ future.addListener((ChannelFutureListener) future1 -> {
+ if (future1.isSuccess()) {
+ System.out.println("Message sent successfully");
+ conversationContext.getChannel().attr(IS_CONNECTED).set(true);
+ } else {
+ System.err.println("Message send failed");
+ conversationContext.getChannel().attr(IS_CONNECTED).set(false);
+ future1.cause().printStackTrace();
+ }
+ });
+
+
+ context.fireConnected();
+ }
+
+ }
+
+
+ /*
+ * It performs the sequential and safe shutdown of the driver.
+ * Completion of pending requests, executors and associated tasks.
+ */
+ @Override
+ public void onDisconnect(ConversationContext context) {
+ // 1. Here we shut down the local task executor.
+ clientExecutorService.shutdown();
+ // 2. Performs the shutdown of the transaction executor.
+ tm.shutdown();
+ // 3. Finish the execution of the tasks for the handling of Events.
+ //eventLogic.stop();
+ // 4. Executes the closing of the main channel.
+ context.getChannel().close();
+ // 5. Here is the stop of any task or state machine that is added.
+ }
+
+
+ @Override
+ public CompletableFuture read(PlcReadRequest readRequest) {
+ // If we're not connected, just abort with an error.
+ if (!isConnected()) {
+ CompletableFuture future = new CompletableFuture<>();
+ future.completeExceptionally(new PlcRuntimeException("Disconnected"));
+ return future;
+ }
+
+ DefaultPlcReadRequest request = (DefaultPlcReadRequest) readRequest;
+ CompletableFuture responseFuture =null;
+
+
+
+ // Just send a single response and chain it as Response
+ return toPlcReadResponse(readRequest, responseFuture);
+ }
+
+
+ /**
+ * Maps the S7ReadResponse of a PlcReadRequest to a PlcReadResponse
+ */
+ private CompletableFuture toPlcReadResponse(PlcReadRequest readRequest, CompletableFuture responseFuture) {
+ CompletableFuture clientFuture = new CompletableFuture<>();
+ //Pointers
+ FinsTcpMessage[] responseMessage = new FinsTcpMessage[1];
+ PlcReadRequest[] plcReadRequest = new PlcReadRequest[1];
+
+ responseFuture.whenComplete((finsTcpMessage, throwable) -> {
+ if (throwable != null) {
+ clientFuture.completeExceptionally(new PlcProtocolException("Error reading", throwable));
+ } else {
+ try {
+ responseMessage[0] = finsTcpMessage;
+ plcReadRequest[0] = readRequest;
+ clientExecutorService.submit(() -> {
+ try {
+ PlcReadResponse response = (PlcReadResponse) decodeReadResponse(responseMessage[0], plcReadRequest[0]);
+ clientFuture.complete(response);
+ } catch (Exception ex){
+
+ }
+ });
+
+ } catch (Exception ex) {
+ logger.info(ex.toString());
+ }
+ }
+ });
+
+ return clientFuture;
+ }
+
+ private PlcResponse decodeReadResponse(FinsTcpMessage responseMessage, PlcReadRequest plcReadRequest) throws PlcProtocolException {
+ Map> values = new HashMap<>();
+ short errorClass;
+ short errorCode;
+
+ return new DefaultPlcReadResponse(plcReadRequest, values);
+ }
+
+
+ private void setChannelFeatures() {
+ conversationContext.getChannel().attr(AttributeKey.valueOf("READ_TIME_OUT")).set(finsTcpDriverContext.getReadTimeout());
+ conversationContext.getChannel().attr(AttributeKey.valueOf("IS_PIN_ACTIVE")).set(finsTcpDriverContext.getPing());
+ conversationContext.getChannel().attr(AttributeKey.valueOf("PING_TIME")).set(finsTcpDriverContext.getPingTime());
+ conversationContext.getChannel().attr(AttributeKey.valueOf("RETRY_TIME")).set(finsTcpDriverContext.getRetryTime());
+ }
+
+ private boolean isConnected() {
+ AttributeKey IS_CONNECTED = AttributeKey.valueOf("IS_CONNECTED");
+ return conversationContext.getChannel().attr(IS_CONNECTED).get();
+ //return true;
+ }
+
+
+ /**
+ * 16进制字符串转化为byte数组
+ *
+ * @param inHex 要转16进制字节流数组的16进制字符
+ * @return 16进制字节流
+ */
+ private byte[] hexToByteArray(String inHex) {
+ int hexlen = inHex.length();
+ byte[] result;
+ if (hexlen % 2 == 1) {
+ // 奇数
+ hexlen++;
+ result = new byte[(hexlen / 2)];
+ inHex = "0" + inHex;
+ } else {
+ // 偶数
+ result = new byte[(hexlen / 2)];
+ }
+ int j = 0;
+ for (int i = 0; i < hexlen; i += 2) {
+ result[j] = hexToByte(inHex.substring(i, i + 2));
+ j++;
+ }
+ return result;
+ }
+
+ private byte hexToByte(String inHex) {
+ return (byte) Integer.parseInt(inHex, 16);
+ }
+
+}
+
+
+
diff --git a/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/protocol/FinsTcpProtocolStackConfigurer.java b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/protocol/FinsTcpProtocolStackConfigurer.java
new file mode 100644
index 0000000000..73831878e7
--- /dev/null
+++ b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/protocol/FinsTcpProtocolStackConfigurer.java
@@ -0,0 +1,214 @@
+/*
+ * 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
+ *
+ * https://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.fins.readwrite.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.codec.MessageToMessageCodec;
+import org.apache.plc4x.java.api.authentication.PlcAuthentication;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.listener.EventListener;
+import org.apache.plc4x.java.spi.EventListenerMessageCodec;
+import org.apache.plc4x.java.spi.Plc4xNettyWrapper;
+import org.apache.plc4x.java.spi.Plc4xProtocolBase;
+import org.apache.plc4x.java.spi.configuration.PlcConnectionConfiguration;
+import org.apache.plc4x.java.spi.connection.GeneratedProtocolMessageCodec;
+import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
+import org.apache.plc4x.java.spi.connection.SingleProtocolStackConfigurer;
+import org.apache.plc4x.java.spi.context.DriverContext;
+import org.apache.plc4x.java.spi.generation.ByteOrder;
+import org.apache.plc4x.java.spi.generation.Message;
+import org.apache.plc4x.java.spi.generation.MessageInput;
+import org.apache.plc4x.java.spi.generation.MessageOutput;
+import org.apache.plc4x.java.spi.netty.NettyHashTimerTimeoutManager;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.ToIntFunction;
+
+import static org.apache.plc4x.java.spi.configuration.ConfigurationFactory.configure;
+
+/**
+ * Builds a Protocol Stack.
+ */
+public class FinsTcpProtocolStackConfigurer implements ProtocolStackConfigurer {
+
+ private final Class basePacketClass;
+ private final ByteOrder byteOrder;
+ private final Class extends Plc4xProtocolBase> protocolClass;
+ private final Class extends DriverContext> driverContextClass;
+ private final MessageInput messageInput;
+ private final MessageOutput messageOutput;
+ private final Class extends ToIntFunction> packetSizeEstimatorClass;
+ private final Class extends Consumer> corruptPacketRemoverClass;
+ private final MessageToMessageCodec encryptionHandler;
+
+ private Plc4xProtocolBase protocol = null;
+
+ public static FinsTcpSingleProtocolStackBuilder builder(Class basePacketClass, MessageInput messageInput) {
+ return new FinsTcpSingleProtocolStackBuilder<>(basePacketClass, messageInput, null);
+ }
+
+ public static FinsTcpSingleProtocolStackBuilder builder(Class basePacketClass, MessageInput messageInput, MessageOutput messageOutput) {
+ return new FinsTcpSingleProtocolStackBuilder<>(basePacketClass, messageInput, messageOutput);
+ }
+
+ /**
+ * Only accessible via Builder
+ */
+ FinsTcpProtocolStackConfigurer(Class basePacketClass,
+ ByteOrder byteOrder,
+ Class extends Plc4xProtocolBase> protocol,
+ Class extends DriverContext> driverContextClass,
+ MessageInput messageInput,
+ MessageOutput messageOutput,
+ Class extends ToIntFunction> packetSizeEstimatorClass,
+ Class extends Consumer> corruptPacketRemoverClass,
+ MessageToMessageCodec encryptionHandler) {
+ this.basePacketClass = basePacketClass;
+ this.byteOrder = byteOrder;
+ this.protocolClass = protocol;
+ this.driverContextClass = driverContextClass;
+ this.messageInput = messageInput;
+ this.messageOutput = messageOutput;
+ this.packetSizeEstimatorClass = packetSizeEstimatorClass;
+ this.corruptPacketRemoverClass = corruptPacketRemoverClass;
+ this.encryptionHandler = encryptionHandler;
+ }
+
+ private ChannelHandler getMessageCodec(PlcConnectionConfiguration configuration) {
+ return new GeneratedProtocolMessageCodec<>(basePacketClass, messageInput, messageOutput, byteOrder,
+ packetSizeEstimatorClass != null ? configure(configuration, createInstance(packetSizeEstimatorClass)) : null,
+ corruptPacketRemoverClass != null ? configure(configuration, createInstance(corruptPacketRemoverClass)) : null);
+ }
+
+ /**
+ * Applies the given Stack to the Pipeline
+ */
+ @Override
+ public Plc4xProtocolBase configurePipeline(PlcConnectionConfiguration configuration, ChannelPipeline pipeline,
+ PlcAuthentication authentication, boolean passive,
+ List listeners) {
+ if (null == protocol) {
+ if (this.encryptionHandler != null) {
+ pipeline.addLast("ENCRYPT", this.encryptionHandler);
+ }
+ pipeline.addLast("CODEC", getMessageCodec(configuration));
+ protocol = configure(configuration, createInstance(protocolClass));
+ if (driverContextClass != null) {
+ protocol.setDriverContext(configure(configuration, createInstance(driverContextClass)));
+ }
+ pipeline.addLast(new EventListenerMessageCodec(listeners));
+ Plc4xNettyWrapper context = new Plc4xNettyWrapper<>(new NettyHashTimerTimeoutManager(), pipeline, passive, protocol,
+ authentication, basePacketClass);
+ pipeline.addLast("WRAPPER", context);
+ }
+
+ return protocol;
+
+ }
+
+ private T createInstance(Class clazz, Object... args) {
+ try {
+ Class>[] parameterTypes = new Class>[args.length];
+ for (int i = 0; i < args.length; i++) {
+ parameterTypes[i] = args[i].getClass();
+ }
+ return clazz.getDeclaredConstructor(parameterTypes).newInstance(args);
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
+ NoSuchMethodException e) {
+ throw new PlcRuntimeException("Error creating instance of class " + clazz.getName());
+ }
+ }
+
+ /**
+ * Used to Build Instances of {@link SingleProtocolStackConfigurer}.
+ *
+ * @param Type of Created Message that is Exchanged.
+ */
+ public static final class FinsTcpSingleProtocolStackBuilder {
+
+ private final Class basePacketClass;
+ private final MessageInput messageInput;
+ private final MessageOutput messageOutput;
+ private Class extends DriverContext> driverContextClass;
+ private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
+ private Class extends Plc4xProtocolBase> protocol;
+ private Class extends ToIntFunction> packetSizeEstimator;
+ private Class extends Consumer> corruptPacketRemover;
+ private MessageToMessageCodec encryptionHandler;
+
+ public FinsTcpSingleProtocolStackBuilder(Class basePacketClass, MessageInput messageInput, MessageOutput messageOutput) {
+ this.basePacketClass = basePacketClass;
+ this.messageInput = messageInput;
+ this.messageOutput = messageOutput;
+ }
+
+ public FinsTcpSingleProtocolStackBuilder withDriverContext(Class extends DriverContext> driverContextClass) {
+ this.driverContextClass = driverContextClass;
+ return this;
+ }
+
+ public FinsTcpSingleProtocolStackBuilder byteOrder(ByteOrder byteOrder) {
+ this.byteOrder = byteOrder;
+ return this;
+ }
+
+ public FinsTcpSingleProtocolStackBuilder bigEndian() {
+ this.byteOrder = ByteOrder.BIG_ENDIAN;
+ return this;
+ }
+
+ public FinsTcpSingleProtocolStackBuilder littleEndian() {
+ this.byteOrder = ByteOrder.LITTLE_ENDIAN;
+ return this;
+ }
+
+ public FinsTcpSingleProtocolStackBuilder withProtocol(Class extends Plc4xProtocolBase> protocol) {
+ this.protocol = protocol;
+ return this;
+ }
+
+ public FinsTcpSingleProtocolStackBuilder withPacketSizeEstimator(Class extends ToIntFunction> packetSizeEstimator) {
+ this.packetSizeEstimator = packetSizeEstimator;
+ return this;
+ }
+
+ public FinsTcpSingleProtocolStackBuilder withCorruptPacketRemover(Class extends Consumer> corruptPacketRemover) {
+ this.corruptPacketRemover = corruptPacketRemover;
+ return this;
+ }
+
+ public FinsTcpSingleProtocolStackBuilder withEncryptionHandler(MessageToMessageCodec encryptionHandler) {
+ this.encryptionHandler = encryptionHandler;
+ return this;
+ }
+
+ public FinsTcpProtocolStackConfigurer build() {
+ assert this.protocol != null;
+ return new FinsTcpProtocolStackConfigurer<>(basePacketClass, byteOrder, protocol,
+ driverContextClass, messageInput, messageOutput, packetSizeEstimator, corruptPacketRemover,
+ encryptionHandler);
+ }
+
+ }
+
+}
diff --git a/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/tag/FinsTcpPlcTagHandler.java b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/tag/FinsTcpPlcTagHandler.java
new file mode 100644
index 0000000000..274a25940b
--- /dev/null
+++ b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/fins/readwrite/tag/FinsTcpPlcTagHandler.java
@@ -0,0 +1,45 @@
+/*
+ * 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
+ *
+ * https://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.fins.readwrite.tag;
+
+import org.apache.plc4x.java.api.exceptions.PlcInvalidTagException;
+import org.apache.plc4x.java.api.model.PlcQuery;
+import org.apache.plc4x.java.api.model.PlcTag;
+import org.apache.plc4x.java.spi.connection.PlcTagHandler;
+
+public class FinsTcpPlcTagHandler implements PlcTagHandler {
+
+ @Override
+ public PlcTag parseTag(String tagAddress) {
+
+ PlcTag tag =new PlcTag() {
+ @Override
+ public String getAddressString() {
+ return "D100";
+ }
+ };
+ return tag;
+ }
+
+ @Override
+ public PlcQuery parseQuery(String query) {
+ throw new UnsupportedOperationException("This driver doesn't support browsing");
+ }
+
+}
diff --git a/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/finsdiscovery/readwrite/utils/StaticHelper.java b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/finsdiscovery/readwrite/utils/StaticHelper.java
new file mode 100644
index 0000000000..b929302194
--- /dev/null
+++ b/plc4j/drivers/fins/src/main/java/org/apache/plc4x/java/finsdiscovery/readwrite/utils/StaticHelper.java
@@ -0,0 +1,28 @@
+/*
+ * 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.finsdiscovery.readwrite.utils;
+
+public class StaticHelper {
+
+ public static int arrayLength(byte[] arr) {
+ return arr.length;
+ }
+
+}
diff --git a/plc4j/drivers/fins/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver b/plc4j/drivers/fins/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
new file mode 100644
index 0000000000..9e361eea5d
--- /dev/null
+++ b/plc4j/drivers/fins/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
+#
+# https://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.fins.readwrite.FinsTcpDriver
\ No newline at end of file
diff --git a/plc4j/drivers/fins/src/test/java/BenchmarkGeneratedFins.java b/plc4j/drivers/fins/src/test/java/BenchmarkGeneratedFins.java
new file mode 100644
index 0000000000..62dc0c2cc7
--- /dev/null
+++ b/plc4j/drivers/fins/src/test/java/BenchmarkGeneratedFins.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.plc4x.java.DefaultPlcDriverManager;
+import org.apache.plc4x.java.api.PlcConnection;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcReadResponse;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.fins.readwrite.FinsTcpDriver;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
+import org.apache.plc4x.java.spi.generation.WriteBufferByteBased;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class BenchmarkGeneratedFins {
+
+ private static final Logger log = LoggerFactory.getLogger(BenchmarkGeneratedFins.class);
+
+ public static void main(String[] args) throws Exception {
+
+ Map resultMap = new HashMap<>();
+ PlcConnection mockConnection = new DefaultPlcDriverManager().getConnection("finstcp:tcp://127.0.0.1:9600");
+
+ PlcReadRequest.Builder requestBuilder = mockConnection.readRequestBuilder();
+ String s1 = "D100";
+ requestBuilder.addTagAddress( "test" , s1);
+
+ PlcReadRequest readRequest = requestBuilder.build();
+ PlcReadResponse response = readRequest.execute().get(Long.parseLong("2000"), TimeUnit.MILLISECONDS);
+ for (String tagName : response.getTagNames()) {
+ if (response.getResponseCode(tagName) == PlcResponseCode.OK) {
+ int numValues = response.getNumberOfValues(tagName);
+ // If it's just one element, output just one single line.
+ log.info("{}: {}", tagName, response.getPlcValue(tagName));
+ if (numValues == 1) {
+ resultMap.put(tagName, response.getPlcValue(tagName).toString());
+ }
+ // If it's more than one element, output each in a single row.
+ else {
+ for (int i = 0; i < numValues; i++) {
+ resultMap.put(tagName + "-" + i, response.getObject(tagName, i).toString());
+ }
+ }
+ } else {
+ log.error("Error[{}]: {}", tagName, response.getResponseCode(tagName).name());
+ }
+ }
+
+ }
+
+}
diff --git a/protocols/fins/pom.xml b/protocols/fins/pom.xml
new file mode 100644
index 0000000000..f43a76c8e4
--- /dev/null
+++ b/protocols/fins/pom.xml
@@ -0,0 +1,59 @@
+
+
+
+
+ 4.0.0
+
+
+ org.apache.plc4x
+ plc4x-protocols
+ 0.13.0-SNAPSHOT
+
+
+ plc4x-protocols-fins
+
+ Protocols: fins
+ Base protocol specifications for the omron fins protocol
+
+
+ 2024-02-16T14:53:02Z
+
+
+
+
+ org.apache.plc4x
+ plc4x-code-generation-protocol-base-mspec
+ 0.13.0-SNAPSHOT
+
+
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
+ org.apache.plc4x
+ plc4j-api
+ 0.13.0-SNAPSHOT
+ test
+
+
+
+
\ No newline at end of file
diff --git a/protocols/fins/src/main/java/org/apache/plc4x/protocol/fins/FinsTcpProtocol.java b/protocols/fins/src/main/java/org/apache/plc4x/protocol/fins/FinsTcpProtocol.java
new file mode 100644
index 0000000000..02b698a96f
--- /dev/null
+++ b/protocols/fins/src/main/java/org/apache/plc4x/protocol/fins/FinsTcpProtocol.java
@@ -0,0 +1,42 @@
+/*
+ * 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
+ *
+ * https://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.protocol.fins;
+
+import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
+import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
+import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
+import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
+
+public class FinsTcpProtocol implements Protocol, ProtocolHelpers {
+
+ @Override
+ public String getName() {
+ return "finstcp";
+ }
+
+ @Override
+ public TypeContext getTypeContext() throws GenerationException {
+ ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+ typeContext.validate();
+ return typeContext;
+ }
+
+}
diff --git a/protocols/fins/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol b/protocols/fins/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol
new file mode 100644
index 0000000000..3002cb18ca
--- /dev/null
+++ b/protocols/fins/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol
@@ -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
+#
+# https://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.protocol.fins.FinsTcpProtocol
diff --git a/protocols/fins/src/main/resources/protocols/finstcp/finstcp.mspec b/protocols/fins/src/main/resources/protocols/finstcp/finstcp.mspec
new file mode 100644
index 0000000000..7aebea21c2
--- /dev/null
+++ b/protocols/fins/src/main/resources/protocols/finstcp/finstcp.mspec
@@ -0,0 +1,30 @@
+/*
+ * 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
+ *
+ * https://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.
+ */
+
+////////////////////////////////////////////////////////////////
+// IsoOnTcp/FINSTCP
+////////////////////////////////////////////////////////////////
+
+[type FINSTCPPacket
+ [const uint 8 protocolId 0x03]
+ [reserved uint 8 '0x00']
+ [implicit uint 16 len 'payload.lengthInBytes + 4']
+ [simple uint 16 payload]
+]
+
diff --git a/protocols/fins/src/main/resources/protocols/finstcp/messages.xml b/protocols/fins/src/main/resources/protocols/finstcp/messages.xml
new file mode 100644
index 0000000000..ba11eb4dfc
--- /dev/null
+++ b/protocols/fins/src/main/resources/protocols/finstcp/messages.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+ 3
+ 0
+ 22
+
+
+ 17
+ 224
+
+ 0
+ 15
+
+ 0
+
+
+
+
+ 193
+ 2
+
+ ${ctx-read:callingTsapId}
+
+
+
+ 194
+ 2
+
+ ${ctx-read:calledTsapId}
+
+
+
+ 192
+ 1
+
+
+ ${ctx-read:cotpTpduSize}
+
+
+
+
+
+
+
+
+
+
+ 3
+ 0
+ 22
+
+
+
+ 15
+
+
+ 0
+
+
+
+
+
+
+ ${ctx-write:cotpTpduSize}
+
+
+
+
+
+ ${ctx-write::callingTsapId}
+
+
+
+
+ ${ctx-write::calledTsapId}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/protocols/fins/src/test/java/org/apache/plc4x/protocol/fins/FinsTcpProtocolTest.java b/protocols/fins/src/test/java/org/apache/plc4x/protocol/fins/FinsTcpProtocolTest.java
new file mode 100644
index 0000000000..64aa24b228
--- /dev/null
+++ b/protocols/fins/src/test/java/org/apache/plc4x/protocol/fins/FinsTcpProtocolTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ * https://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.protocol.fins;
+
+import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+class FinsTcpProtocolTest {
+
+ @Test
+ void getTypeContext() throws Exception {
+ TypeContext typeContext = new FinsTcpProtocol().getTypeContext();
+ assertNotNull(typeContext);
+ assertNotNull(typeContext.getUnresolvedTypeReferences());
+ assertSame(0, typeContext.getUnresolvedTypeReferences().size());
+ }
+
+}
diff --git a/protocols/fins/src/test/resources/protocols/DriverTestsuite.xml b/protocols/fins/src/test/resources/protocols/DriverTestsuite.xml
new file mode 100644
index 0000000000..a62da14586
--- /dev/null
+++ b/protocols/fins/src/test/resources/protocols/DriverTestsuite.xml
@@ -0,0 +1,746 @@
+
+
+
+
+ S7
+
+ s7
+ read-write
+
+ s7
+
+
+
+
+
+ 3
+ 0
+ 22
+
+
+ 17
+ 224
+
+ 0
+ 15
+
+ 0
+
+
+
+
+ 193
+ 2
+
+ 785
+
+
+
+ 194
+ 2
+
+ 256
+
+
+
+ 192
+ 1
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+ 3
+ 0
+ 22
+
+
+ 17
+ 208
+
+ 15
+ 11
+
+ 0
+
+
+
+
+ 192
+ 1
+
+
+ 10
+
+
+
+
+ 193
+ 2
+
+ 785
+
+
+
+ 194
+ 2
+
+ 256
+
+
+
+
+
+
+
+
+
+
+ 3
+ 0
+ 25
+
+
+ 2
+ 240
+
+ true
+ 1
+
+
+
+
+
+ 50
+ 1
+ 0
+ 0
+ 8
+ 0
+
+
+
+
+ 240
+
+ 0
+ 8
+ 8
+ 1008
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+ 0
+ 27
+
+
+ 2
+ 240
+
+ true
+ 0
+
+
+
+
+
+ 50
+ 3
+ 0
+ 0
+ 8
+ 0
+
+ 0
+ 0
+
+
+
+ 240
+
+ 0
+ 3
+ 3
+ 240
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+ 0
+ 33
+
+
+ 2
+ 240
+
+ true
+ 2
+
+
+
+
+
+ 50
+ 7
+ 0
+ 1
+ 8
+ 8
+
+
+
+
+ 0
+
+ 1
+
+
+ 18
+
+ 4
+ 17
+ 4
+ 4
+ 1
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+ 255
+
+
+
+ 9
+
+
+ 4
+
+
+
+
+ 0
+
+
+ 0
+
+ 17
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+ 0
+ 125
+
+
+ 2
+ 240
+
+ true
+ 0
+
+
+
+
+
+ 50
+ 7
+ 0
+ 1
+ 12
+ 96
+
+
+
+
+ 0
+
+ 1
+
+
+ 18
+
+ 8
+ 18
+ 8
+ 4
+ 1
+ 1
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+ 255
+
+
+
+ 9
+
+
+ 92
+
+
+
+
+ 0
+
+
+ 0
+
+ 17
+
+
+
+
+ 0
+ 28
+ 3
+
+
+ 1
+ 0x36455337203231322d31424433302d3058423020
+ 8224
+ 1
+ 8224
+
+
+ 6
+ 0x36455337203231322d31424433302d3058423020
+ 8224
+ 1
+ 8224
+
+
+ 7
+ 0x36455337203231322d31424433302d3058423020
+ 8224
+ 22018
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Single element read request
+
+
+
+
+
+ hurz
+ %Q0.0:BOOL
+
+
+
+
+
+
+ 3
+ 0
+ 31
+
+
+ 2
+ 240
+
+ true
+ 10
+
+
+
+
+
+ 50
+ 1
+ 0
+ 10
+ 14
+ 0
+
+
+
+
+ 4
+
+ 1
+
+
+ 18
+
+ 10
+
+
+ 16
+
+
+ 1
+
+
+ 1
+ 0
+
+ 130
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+ 0
+ 26
+
+
+ 2
+ 240
+
+ true
+ 0
+
+
+
+
+
+ 50
+ 3
+ 0
+ 10
+ 2
+ 6
+
+ 0
+ 0
+
+
+
+ 4
+
+ 1
+
+
+
+
+
+
+
+
+
+ 255
+
+
+
+ 3
+
+
+ 1
+ 0x01
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ OUTPUTS
+ 0
+ 0
+ 0
+ 1
+ BOOL
+
+
+
+
+
+
+
+
+
+ OK
+
+ true
+
+
+
+
+
+
+
+
+
+
+ Single element read request with disabled PUT/GET
+
+
+
+
+
+ hurz
+ %Q0.0:BOOL
+
+
+
+
+
+
+ 3
+ 0
+ 31
+
+
+ 2
+ 240
+
+ true
+ 10
+
+
+
+
+
+ 50
+ 1
+ 0
+ 10
+ 14
+ 0
+
+
+
+
+ 4
+
+ 1
+
+
+ 18
+
+ 10
+
+
+ 16
+
+
+ 1
+
+
+ 1
+ 0
+
+ 130
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+ 0
+ 19
+
+
+ 2
+ 240
+
+ true
+ 0
+
+
+
+
+
+ 50
+ 2
+ 0
+ 10
+ 0
+ 0
+
+ 129
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ OUTPUTS
+ 0
+ 0
+ 0
+ 1
+ BOOL
+
+
+
+
+
+
+
+
+
+ ACCESS_DENIED
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/protocols/fins/src/test/resources/protocols/ParserSerializerTestsuite.xml b/protocols/fins/src/test/resources/protocols/ParserSerializerTestsuite.xml
new file mode 100644
index 0000000000..4e0b3e5f93
--- /dev/null
+++ b/protocols/fins/src/test/resources/protocols/ParserSerializerTestsuite.xml
@@ -0,0 +1,1005 @@
+
+
+
+
+ S7
+
+ s7
+ read-write
+
+
+ COTP Connection Request
+ 0300001611e00000000f00c2020100c1020311c0010a
+ TPKTPacket
+
+
+ 3
+ 0
+ 22
+
+
+ 17
+ 224
+
+ 0
+ 15
+
+ 0
+
+
+
+
+ 194
+ 2
+
+ 256
+
+
+
+ 193
+ 2
+
+ 785
+
+
+
+ 192
+ 1
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+ COTP Connection Response
+ 0300001611d0000f000b00c0010ac1020311c2020100
+ TPKTPacket
+
+
+ 3
+ 0
+ 22
+
+
+ 17
+ 208
+
+ 15
+ 11
+
+ 0
+
+
+
+
+ 192
+ 1
+
+
+ 10
+
+
+
+
+ 193
+ 2
+
+ 785
+
+
+
+ 194
+ 2
+
+ 256
+
+
+
+
+
+
+
+
+
+
+ S7 Setup Communication Request
+ 0300001902f08132010000000000080000f0000008000803f0
+ TPKTPacket
+
+
+ 3
+ 0
+ 25
+
+
+ 2
+ 240
+
+ true
+ 1
+
+
+
+
+
+ 50
+ 1
+ 0
+ 0
+ 8
+ 0
+
+
+
+
+ 240
+
+ 0
+ 8
+ 8
+ 1008
+
+
+
+
+
+
+
+
+
+
+
+
+ S7 Setup Communication Response
+ 0300001b02f080320300000000000800000000f0000003000300f0
+ TPKTPacket
+
+
+ 3
+ 0
+ 27
+
+
+ 2
+ 240
+
+ true
+ 0
+
+
+
+
+
+ 50
+ 3
+ 0
+ 0
+ 8
+ 0
+
+ 0
+ 0
+
+
+
+ 240
+
+ 0
+ 3
+ 3
+ 240
+
+
+
+
+
+
+
+
+
+
+
+
+ S7 Read PLC Type Request
+ 0300002102f082320700000001000800080001120411440100ff09000400110000
+ TPKTPacket
+
+
+ 3
+ 0
+ 33
+
+
+ 2
+ 240
+
+ true
+ 2
+
+
+
+
+
+ 50
+ 7
+ 0
+ 1
+ 8
+ 8
+
+
+
+
+ 0
+
+ 1
+
+
+ 18
+
+ 4
+ 17
+ 4
+ 4
+ 1
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+ 255
+
+
+ 9
+
+ 4
+
+
+
+
+ 0
+
+ 0
+
+ 17
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ S7 Read PLC Type Response
+
+ 0300007d02f080320700000001000c0060000112081284010100000000ff09005c00110000001c0003000136455337203231322d31424433302d3058423020202000012020000636455337203231322d31424433302d3058423020202000012020000736455337203231322d31424433302d3058423020202056020002
+
+ TPKTPacket
+
+
+ 3
+ 0
+ 125
+
+
+ 2
+ 240
+
+ true
+ 0
+
+
+
+
+
+ 50
+ 7
+ 0
+ 1
+ 12
+ 96
+
+
+
+
+ 0
+
+ 1
+
+
+ 18
+
+ 8
+ 18
+ 8
+ 4
+ 1
+ 1
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+ 255
+
+
+ 9
+
+ 92
+
+
+
+
+ 0
+
+ 0
+
+ 17
+
+
+
+ 0
+ 28
+ 3
+
+
+ 1
+ 0x36455337203231322d31424433302d3058423020
+ 8224
+ 1
+ 8224
+
+
+ 6
+ 0x36455337203231322d31424433302d3058423020
+ 8224
+ 1
+ 8224
+
+
+ 7
+ 0x36455337203231322d31424433302d3058423020
+ 8224
+ 22018
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ S7 Read Request
+
+ 0300004302f08b32010000000b003200000404120a10010001000082000000120a10010001000082000000120a10010001000082000000120a10010001000082000000
+
+ TPKTPacket
+
+
+ 3
+ 0
+ 67
+
+
+ 2
+ 240
+
+ true
+ 11
+
+
+
+
+
+ 50
+ 1
+ 0
+ 11
+ 50
+ 0
+
+
+
+
+ 4
+
+ 4
+
+
+ 18
+
+ 10
+
+
+ 16
+
+
+ 1
+
+ 1
+ 0
+
+ 130
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 18
+
+ 10
+
+
+ 16
+
+
+ 1
+
+ 1
+ 0
+
+ 130
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 18
+
+ 10
+
+
+ 16
+
+
+ 1
+
+ 1
+ 0
+
+ 130
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 18
+
+ 10
+
+
+ 16
+
+
+ 1
+
+ 1
+ 0
+
+ 130
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ S7 Read Response
+ 0300002c02f08032030000000b0002001700000404ff0300010100ff0300010100ff0300010100ff03000101
+ TPKTPacket
+
+
+ 3
+ 0
+ 44
+
+
+ 2
+ 240
+
+ true
+ 0
+
+
+
+
+
+ 50
+ 3
+ 0
+ 11
+ 2
+ 23
+
+ 0
+ 0
+
+
+
+ 4
+
+ 4
+
+
+
+
+
+
+
+
+
+ 255
+
+
+ 3
+
+ 1
+ 0x01
+
+ 0
+
+
+
+
+ 255
+
+
+ 3
+
+ 1
+ 0x01
+
+ 0
+
+
+
+
+ 255
+
+
+ 3
+
+ 1
+ 0x01
+
+ 0
+
+
+
+
+ 255
+
+
+ 3
+
+ 1
+ 0x01
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ S7 Read Error Response
+ 0300001302f08032020000000a000000008500
+ TPKTPacket
+
+
+ 3
+ 0
+ 19
+
+
+ 2
+ 240
+
+ true
+ 0
+
+
+
+
+
+ 50
+ 2
+ 0
+ 10
+ 0
+ 0
+
+ 133
+ 0
+
+
+
+
+
+
+
+
+
+
+ S7 Write Request
+
+ 0300005a02f08e32010000000e003200170504120a10010001000082000000120a10010001000082000001120a10010001000082000002120a10010001000082000003ff0300010100ff0300010100ff0300010100ff03000101
+
+ TPKTPacket
+
+
+ 3
+ 0
+ 90
+
+
+ 2
+ 240
+
+ true
+ 14
+
+
+
+
+
+ 50
+ 1
+ 0
+ 14
+ 50
+ 23
+
+
+
+
+ 5
+
+ 4
+
+
+ 18
+
+ 10
+
+
+ 16
+
+
+ 1
+
+ 1
+ 0
+
+ 130
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 18
+
+ 10
+
+
+ 16
+
+
+ 1
+
+ 1
+ 0
+
+ 130
+
+ 0
+ 0
+ 1
+
+
+
+
+
+
+ 18
+
+ 10
+
+
+ 16
+
+
+ 1
+
+ 1
+ 0
+
+ 130
+
+ 0
+ 0
+ 2
+
+
+
+
+
+
+ 18
+
+ 10
+
+
+ 16
+
+
+ 1
+
+ 1
+ 0
+
+ 130
+
+ 0
+ 0
+ 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 255
+
+
+ 3
+
+ 1
+ 0x01
+
+ 0
+
+
+
+
+ 255
+
+
+ 3
+
+ 1
+ 0x01
+
+ 0
+
+
+
+
+ 255
+
+
+ 3
+
+ 1
+ 0x01
+
+ 0
+
+
+
+
+ 255
+
+
+ 3
+
+ 1
+ 0x01
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ S7 Write Response
+ 0300001902f08032030000000e0002000400000504ffffffff
+ TPKTPacket
+
+
+ 3
+ 0
+ 25
+
+
+ 2
+ 240
+
+ true
+ 0
+
+
+
+
+
+ 50
+ 3
+ 0
+ 14
+ 2
+ 4
+
+ 0
+ 0
+
+
+
+ 5
+
+ 4
+
+
+
+
+
+
+
+
+
+ 255
+
+
+
+
+ 255
+
+
+
+
+ 255
+
+
+
+
+ 255
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/protocols/pom.xml b/protocols/pom.xml
index a321240993..de035f47ca 100644
--- a/protocols/pom.xml
+++ b/protocols/pom.xml
@@ -60,6 +60,7 @@
simulated
socketcan
umas
+ fins
diff --git a/website/asciidoc/modules/developers/pages/code-gen/protocol/mspec.adoc b/website/asciidoc/modules/developers/pages/code-gen/protocol/mspec.adoc
index 303ea56cee..762d745cda 100644
--- a/website/asciidoc/modules/developers/pages/code-gen/protocol/mspec.adoc
+++ b/website/asciidoc/modules/developers/pages/code-gen/protocol/mspec.adoc
@@ -31,7 +31,7 @@ At the root level of these specs are a set of `type`, `discriminatedType`, `data
`type` elements are objects who`s content and structure is independent of the input.
-An example would be the `TPKTPacket` of the `S7` format:
+An example would be the `FinsTcpPacket` of the `S7` format:
....
[type TPKTPacket