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 2021/01/10 11:59:26 UTC

[plc4x] branch feature/native_opua_client updated (0361e7e -> 4e48563)

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

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


    from 0361e7e  Add future for close method. Still working on it.
     new 9ddb5d3  Added some close logic
     new 4e48563  Add back the opcua read tests.

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/opcua/pom.xml                        |  11 +-
 .../apache/plc4x/java/opcua/OpcuaPlcDriver.java    |  13 +-
 .../opcua/connection/OpcuaConnectionFactory.java   |  38 -
 .../opcua/connection/OpcuaTcpPlcConnection.java    | 916 ---------------------
 .../java/opcua/protocol/OpcuaProtocolLogic.java    |  79 +-
 .../opcua/protocol/OpcuaSubsriptionHandle.java     |  92 ---
 .../plc4x/java/opcua/OpcuaPlcDriverTest.java       | 277 +++++++
 .../org/apache/plc4x/java/opcua/UtilsTest.java}    |  26 +-
 .../connection/OpcuaTcpPlcConnectionTest.java      |  81 ++
 .../plc4x/java/opcua/protocol/OpcuaFieldTest.java  |  76 ++
 .../opcua/protocol/OpcuaPlcFieldHandlerTest.java}  |  25 +-
 .../protocol/OpcuaSubscriptionHandleTest.java}     |  26 +-
 .../apache/plc4x/java/spi/Plc4xNettyWrapper.java   |   2 +
 .../spi/connection/DefaultNettyPlcConnection.java  |  28 +-
 .../java/spi/events/CloseConnectionEvent.java      |   4 +
 .../spi/internal/DefaultExpectRequestContext.java  |   1 +
 .../src/main/resources/protocols/opcua/opcua.mspec |   7 +
 17 files changed, 576 insertions(+), 1126 deletions(-)
 delete mode 100644 plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java
 delete mode 100644 plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnection.java
 delete mode 100644 plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubsriptionHandle.java
 create mode 100644 plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java
 copy plc4j/{spi/src/main/java/org/apache/plc4x/java/spi/generation/io/MyDefaultBitInput.java => drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/UtilsTest.java} (59%)
 create mode 100644 plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnectionTest.java
 create mode 100644 plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaFieldTest.java
 copy plc4j/{integrations/apache-camel/src/test/java/org/apache/plc4x/camel/Plc4XConsumerTest.java => drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaPlcFieldHandlerTest.java} (72%)
 copy plc4j/{integrations/apache-camel/src/test/java/org/apache/plc4x/camel/Plc4XConsumerTest.java => drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java} (75%)


[plc4x] 01/02: Added some close logic

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

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

commit 9ddb5d3c192ce3b1fa894caa2f699f7d751e2b9a
Author: hutcheb <be...@gmail.com>
AuthorDate: Sat Jan 9 21:21:57 2021 -0500

    Added some close logic
---
 .../apache/plc4x/java/opcua/OpcuaPlcDriver.java    |  4 +++-
 .../java/opcua/protocol/OpcuaProtocolLogic.java    | 27 +++++++--------------
 .../apache/plc4x/java/spi/Plc4xNettyWrapper.java   |  2 ++
 .../spi/connection/DefaultNettyPlcConnection.java  | 28 ++++++++++++----------
 .../java/spi/events/CloseConnectionEvent.java      |  4 ++++
 .../spi/internal/DefaultExpectRequestContext.java  |  1 +
 6 files changed, 34 insertions(+), 32 deletions(-)

diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
index efb16b3..2875e8b 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
@@ -233,8 +233,10 @@ public class OpcuaPlcDriver extends GeneratedDriverBase<OpcuaAPU> {
     /** Estimate the Length of a Packet */
     public static class ByteLengthEstimator implements ToIntFunction<ByteBuf> {
         @Override
-        public int applyAsInt(ByteBuf byteBuf) {            
+        public int applyAsInt(ByteBuf byteBuf) {
+            System.out.println("Estimate:- ");
             if (byteBuf.readableBytes() >= 8) {
+                System.out.println("Estimate:- " + Integer.reverseBytes(byteBuf.getInt(byteBuf.readerIndex() + 4)));
                 return Integer.reverseBytes(byteBuf.getInt(byteBuf.readerIndex() + 4));
             }
             return -1;
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
index f05e906..b8220da 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
@@ -126,12 +126,12 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     }
 
     @Override
-    public void onDisconnect(ConversationContext<OpcuaAPU> context) {
-
+    public void close(ConversationContext<OpcuaAPU> context) {
+        //Nothing
     }
 
     @Override
-    public void close(ConversationContext<OpcuaAPU> context) {
+    public void onDisconnect(ConversationContext<OpcuaAPU> context) {
         int transactionId = transactionIdentifierGenerator.getAndIncrement();
         if(transactionIdentifierGenerator.get() == 0xFFFF) {
             transactionIdentifierGenerator.set(1);
@@ -168,22 +168,14 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             transactionId,
             closeSessionRequest);
 
-        CompletableFuture<Boolean> voidCompletableFuture = new CompletableFuture<>();
-
         context.sendRequest(new OpcuaAPU(messageRequest))
             .expectResponse(OpcuaAPU.class, REQUEST_TIMEOUT)
             .check(p -> p.getMessage() instanceof OpcuaMessageResponse)
             .unwrap(p -> (OpcuaMessageResponse) p.getMessage())
             .handle(opcuaMessageResponse -> {
-                LOGGER.info("Got Close Session Response Connection Response");
-                voidCompletableFuture.complete(true);
-            });
-
-        try {
-            voidCompletableFuture.get(REQUEST_TIMEOUT_LONG, TimeUnit.MILLISECONDS);
-        } catch (Exception e) {
-            LOGGER.debug("Timeout while waiting for session to close");
-        }
+                    LOGGER.info("Got Close Session Response Connection Response" + opcuaMessageResponse.toString());
+                    context.fireDisconnected();
+                });
     }
 
     @Override
@@ -261,6 +253,8 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             transactionId,
             openSecureChannelRequest);
 
+
+
         context.sendRequest(new OpcuaAPU(openRequest))
             .expectResponse(OpcuaAPU.class, REQUEST_TIMEOUT)
             .check(p -> p.getMessage() instanceof OpcuaOpenResponse)
@@ -661,11 +655,6 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         return future;
     }
 
-    @Override
-    protected void decode(ConversationContext<OpcuaAPU> context, OpcuaAPU msg) throws Exception {
-        LOGGER.info("Error while reading value from OPC UA server error code 11");
-        super.decode(context, msg);
-    }
 
 
     private long getCurrentDateTime() {
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/Plc4xNettyWrapper.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/Plc4xNettyWrapper.java
index ea5c894..9aba597 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/Plc4xNettyWrapper.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/Plc4xNettyWrapper.java
@@ -98,6 +98,7 @@ public class Plc4xNettyWrapper<T> extends MessageToMessageCodec<T, Object> {
 
             @Override
             public ExpectRequestContext<T> expectRequest(Class<T> clazz, Duration timeout) {
+                System.out.println("Testing BH ---");
                 return new DefaultExpectRequestContext<>(handler -> {
                     logger.trace("Adding Request Handler ...");
                     registeredHandlers.add(handler);
@@ -235,6 +236,7 @@ public class Plc4xNettyWrapper<T> extends MessageToMessageCodec<T, Object> {
 
         @Override
         public SendRequestContext<T1> sendRequest(T1 packet) {
+            System.out.println("Testing BH ---&&");
             return new DefaultSendRequestContext<>(handler -> {
                 logger.trace("Adding Response Handler ...");
                 registeredHandlers.add(handler);
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/DefaultNettyPlcConnection.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/DefaultNettyPlcConnection.java
index 3a718a6..1fc70ad 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/DefaultNettyPlcConnection.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/DefaultNettyPlcConnection.java
@@ -18,21 +18,14 @@
  */
 package org.apache.plc4x.java.spi.connection;
 
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.ChannelInitializer;
-import io.netty.channel.ChannelPipeline;
+import io.netty.channel.*;
 import io.netty.util.HashedWheelTimer;
 import io.netty.util.Timer;
 import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
 import org.apache.plc4x.java.api.exceptions.PlcIoException;
 import org.apache.plc4x.java.spi.configuration.Configuration;
 import org.apache.plc4x.java.spi.configuration.ConfigurationFactory;
-import org.apache.plc4x.java.spi.events.CloseConnectionEvent;
-import org.apache.plc4x.java.spi.events.ConnectEvent;
-import org.apache.plc4x.java.spi.events.ConnectedEvent;
+import org.apache.plc4x.java.spi.events.*;
 import org.apache.plc4x.java.spi.optimizer.BaseOptimizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,6 +33,7 @@ import org.apache.plc4x.java.api.value.PlcValueHandler;
 
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
 
 public class DefaultNettyPlcConnection extends AbstractPlcConnection implements ChannelExposingConnection {
 
@@ -54,6 +48,7 @@ public class DefaultNettyPlcConnection extends AbstractPlcConnection implements
     protected final ChannelFactory channelFactory;
     protected final boolean awaitSessionSetupComplete;
     protected final ProtocolStackConfigurer stackConfigurer;
+    private final CompletableFuture<Void> sessionDisconnectCompleteFuture = new CompletableFuture<>();
 
     protected Channel channel;
     protected boolean connected;
@@ -87,7 +82,7 @@ public class DefaultNettyPlcConnection extends AbstractPlcConnection implements
             ConfigurationFactory.configure(configuration, channelFactory);
 
             // Have the channel factory create a new channel instance.
-            channel = channelFactory.createChannel(getChannelHandler(sessionSetupCompleteFuture));
+            channel = channelFactory.createChannel(getChannelHandler(sessionSetupCompleteFuture, sessionDisconnectCompleteFuture));
             channel.closeFuture().addListener(future -> {
                 if (!sessionSetupCompleteFuture.isDone()) {
                     sessionSetupCompleteFuture.completeExceptionally(
@@ -115,8 +110,15 @@ public class DefaultNettyPlcConnection extends AbstractPlcConnection implements
     @Override
     public void close() throws PlcConnectionException {
         // TODO call protocols close method
+
+        channel.pipeline().fireUserEventTriggered(new DisconnectEvent());
+        try {
+            sessionDisconnectCompleteFuture.get(10000L, TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            //Do Nothing
+        }
         channel.pipeline().fireUserEventTriggered(new CloseConnectionEvent());
-        // Close channel
+
         channel.close().awaitUninterruptibly();
         channel = null;
         connected = false;
@@ -136,7 +138,7 @@ public class DefaultNettyPlcConnection extends AbstractPlcConnection implements
         return channel;
     }
 
-    public ChannelHandler getChannelHandler(CompletableFuture<Void> sessionSetupCompleteFuture) {
+    public ChannelHandler getChannelHandler(CompletableFuture<Void> sessionSetupCompleteFuture, CompletableFuture<Void> sessionDisconnectCompleteFuture) {
         if (stackConfigurer == null) {
             throw new IllegalStateException("No Protocol Stack Configurer is given!");
         }
@@ -153,6 +155,8 @@ public class DefaultNettyPlcConnection extends AbstractPlcConnection implements
                     public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                         if (evt instanceof ConnectedEvent) {
                             sessionSetupCompleteFuture.complete(null);
+                        } else if (evt instanceof DisconnectedEvent) {
+                            sessionDisconnectCompleteFuture.complete(null);
                         } else {
                             super.userEventTriggered(ctx, evt);
                         }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/events/CloseConnectionEvent.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/events/CloseConnectionEvent.java
index 6cb3404..912dcd1 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/events/CloseConnectionEvent.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/events/CloseConnectionEvent.java
@@ -18,6 +18,10 @@
  */
 package org.apache.plc4x.java.spi.events;
 
+import io.netty.util.concurrent.CompleteFuture;
+
+import java.util.concurrent.CompletableFuture;
+
 /** Signals the Protocol to Close the Connection */
 public class CloseConnectionEvent {
 }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/internal/DefaultExpectRequestContext.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/internal/DefaultExpectRequestContext.java
index 64c54f6..8b43f39 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/internal/DefaultExpectRequestContext.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/internal/DefaultExpectRequestContext.java
@@ -51,6 +51,7 @@ public class DefaultExpectRequestContext<T> implements ConversationContext.Expec
     private HandlerRegistration registration;
 
     public DefaultExpectRequestContext(Consumer<HandlerRegistration> finisher, Class<T> expectClazz, Duration timeout, ConversationContext context) {
+        System.out.println("Testing BH");
         this.finisher = finisher;
         this.expectClazz = expectClazz;
         this.timeout = timeout;


[plc4x] 02/02: Add back the opcua read tests.

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

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

commit 4e485638006899b03840eb480e62adb801259d06
Author: hutcheb <be...@gmail.com>
AuthorDate: Sun Jan 10 06:57:18 2021 -0500

    Add back the opcua read tests.
    
    Added back the opcua read tests.
    Update close logic.
    
    Outstanding
    *	add support for writing
    *	add support for certificate based security
    *	add support for user/pass authentication
---
 plc4j/drivers/opcua/pom.xml                        |  11 +-
 .../apache/plc4x/java/opcua/OpcuaPlcDriver.java    |   9 -
 .../opcua/connection/OpcuaTcpPlcConnection.java    | 916 ---------------------
 .../java/opcua/protocol/OpcuaProtocolLogic.java    |  58 +-
 .../opcua/protocol/OpcuaSubsriptionHandle.java     |  92 ---
 .../plc4x/java/opcua/OpcuaPlcDriverTest.java       | 277 +++++++
 .../org/apache/plc4x/java/opcua/UtilsTest.java}    |  32 +-
 .../connection/OpcuaTcpPlcConnectionTest.java      |  81 ++
 .../plc4x/java/opcua/protocol/OpcuaFieldTest.java  |  76 ++
 .../opcua/protocol/OpcuaPlcFieldHandlerTest.java}  |  26 +-
 .../protocol/OpcuaSubscriptionHandleTest.java}     |  25 +-
 .../src/main/resources/protocols/opcua/opcua.mspec |   7 +
 12 files changed, 545 insertions(+), 1065 deletions(-)

diff --git a/plc4j/drivers/opcua/pom.xml b/plc4j/drivers/opcua/pom.xml
index c9c6a60..aac1c0c 100644
--- a/plc4j/drivers/opcua/pom.xml
+++ b/plc4j/drivers/opcua/pom.xml
@@ -146,18 +146,25 @@
       <artifactId>plc4j-spi</artifactId>
       <version>0.8.0-SNAPSHOT</version>
     </dependency>
+    <dependency>
+      <groupId>io.vavr</groupId>
+      <artifactId>vavr</artifactId>
+    </dependency>
 
     <dependency>
       <groupId>org.eclipse.milo</groupId>
       <artifactId>sdk-client</artifactId>
+      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.milo</groupId>
       <artifactId>stack-core</artifactId>
+      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.milo</groupId>
       <artifactId>stack-client</artifactId>
+      <scope>test</scope>
     </dependency>
 
     <dependency>
@@ -207,17 +214,19 @@
         <groupId>org.eclipse.milo</groupId>
         <artifactId>sdk-client</artifactId>
         <version>${milo.version}</version>
+        <scope>test</scope>
       </dependency>
       <dependency>
         <groupId>org.eclipse.milo</groupId>
         <artifactId>stack-core</artifactId>
         <version>${milo.version}</version>
-        <scope>provided</scope>
+        <scope>scope</scope>
       </dependency>
       <dependency>
         <groupId>org.eclipse.milo</groupId>
         <artifactId>stack-client</artifactId>
         <version>${milo.version}</version>
+        <scope>test</scope>
       </dependency>
       <dependency>
         <groupId>org.eclipse.milo</groupId>
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
index 2875e8b..4e5cdfa 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
@@ -18,18 +18,13 @@
 */
 package org.apache.plc4x.java.opcua;
 
-import org.apache.commons.lang3.StringUtils;
 import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.api.authentication.PlcAuthentication;
 import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
-import org.apache.plc4x.java.opcua.connection.OpcuaConnectionFactory;
-import org.apache.plc4x.java.api.PlcDriver;
 import org.apache.plc4x.java.opcua.protocol.*;
 import org.apache.plc4x.java.opcua.config.*;
 import org.apache.plc4x.java.opcua.readwrite.*;
 import org.apache.plc4x.java.opcua.readwrite.io.*;
-import org.apache.plc4x.java.opcua.readwrite.types.*;
-import org.apache.plc4x.java.spi.configuration.ConfigurationFactory;
 import org.apache.plc4x.java.spi.connection.*;
 import org.apache.plc4x.java.spi.transport.Transport;
 import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
@@ -40,8 +35,6 @@ import org.apache.plc4x.java.spi.optimizer.BaseOptimizer;
 import org.apache.plc4x.java.spi.optimizer.SingleFieldOptimizer;
 import io.netty.buffer.ByteBuf;
 
-import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.ServiceLoader;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -75,8 +68,6 @@ public class OpcuaPlcDriver extends GeneratedDriverBase<OpcuaAPU> {
 
 
     private static final int requestTimeout = 10000;
-    private OpcuaConnectionFactory opcuaConnectionFactory = new OpcuaConnectionFactory();
-
 
     @Override
     public String getProtocolCode() {
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnection.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnection.java
deleted file mode 100644
index 9e06022..0000000
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnection.java
+++ /dev/null
@@ -1,916 +0,0 @@
-/*
- 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.opcua.connection;
-
-import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
-import org.apache.plc4x.java.api.messages.*;
-import org.apache.plc4x.java.api.model.PlcConsumerRegistration;
-import org.apache.plc4x.java.api.model.PlcField;
-import org.apache.plc4x.java.api.model.PlcSubscriptionField;
-import org.apache.plc4x.java.api.model.PlcSubscriptionHandle;
-import org.apache.plc4x.java.api.types.PlcResponseCode;
-import org.apache.plc4x.java.api.value.*;
-import org.apache.plc4x.java.opcua.protocol.OpcuaField;
-import org.apache.plc4x.java.opcua.protocol.OpcuaSubsriptionHandle;
-import org.apache.plc4x.java.spi.messages.*;
-import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
-import org.apache.plc4x.java.spi.model.DefaultPlcConsumerRegistration;
-import org.apache.plc4x.java.spi.values.*;
-import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
-import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfig;
-import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
-import org.eclipse.milo.opcua.sdk.client.api.identity.IdentityProvider;
-import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaMonitoredItem;
-import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
-import org.eclipse.milo.opcua.stack.client.DiscoveryClient;
-import org.eclipse.milo.opcua.stack.core.AttributeId;
-import org.eclipse.milo.opcua.stack.core.Identifiers;
-import org.eclipse.milo.opcua.stack.core.UaException;
-import org.eclipse.milo.opcua.stack.core.StatusCodes;
-import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
-import org.eclipse.milo.opcua.stack.core.types.builtin.*;
-import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.*;
-import org.eclipse.milo.opcua.stack.core.types.enumerated.MonitoringMode;
-import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
-import org.eclipse.milo.opcua.stack.core.types.structured.*;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.math.BigInteger;
-import java.math.BigDecimal;
-import java.util.stream.Collectors;
-import java.net.InetAddress;
-import java.time.Duration;
-import java.time.LocalDateTime;
-import java.util.*;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-
-import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
-import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ulong;
-
-/**
- * Corresponding implementaion for a TCP-based connection for an OPC UA server.
- * TODO: At the moment are just connections without any security mechanism possible
- * <p>
- */
-public class OpcuaTcpPlcConnection extends BaseOpcuaPlcConnection {
-
-    private static final int OPCUA_DEFAULT_TCP_PORT = 4840;
-
-    private static final Logger logger = LoggerFactory.getLogger(OpcuaTcpPlcConnection.class);
-    private final AtomicLong clientHandles = new AtomicLong(1L);
-    private InetAddress address;
-    private int requestTimeout = 5000;
-    private int port;
-    private String params;
-    private OpcUaClient client;
-    private boolean isConnected = false;
-
-    private OpcuaTcpPlcConnection(InetAddress address, String params, int requestTimeout) {
-        this(address, OPCUA_DEFAULT_TCP_PORT, params, requestTimeout);
-        logger.info("Configured OpcuaTcpPlcConnection with: host-name {}", address.getHostAddress());
-    }
-
-    private OpcuaTcpPlcConnection(InetAddress address, int port, String params, int requestTimeout) {
-        this(params);
-        logger.info("Configured OpcuaTcpPlcConnection with: host-name {}", address.getHostAddress());
-        this.address = address;
-        this.port = port;
-        this.params = params;
-        this.requestTimeout = requestTimeout;
-    }
-
-    private OpcuaTcpPlcConnection(String params) {
-        super(getOptionString(params));
-    }
-
-    public static OpcuaTcpPlcConnection of(InetAddress address, String params, int requestTimeout) {
-        return new OpcuaTcpPlcConnection(address, params, requestTimeout);
-    }
-
-    public static OpcuaTcpPlcConnection of(InetAddress address, int port, String params, int requestTimeout) {
-        return new OpcuaTcpPlcConnection(address, port, params, requestTimeout);
-    }
-
-    public static PlcValue encodePlcValue(DataValue value) {
-        ExpandedNodeId typeNode = value.getValue().getDataType().get();
-        Object objValue = value.getValue().getValue();
-
-        if (objValue.getClass().isArray()) {
-            Object[] objArray = (Object[]) objValue;
-            if (objArray[0] instanceof Boolean) {
-                Boolean[] obj = (Boolean[]) objValue;
-                List<PlcValue> plcValue;
-                {
-                    int itemCount = (int) obj.length;
-                    plcValue = new LinkedList<>();
-
-                    for(int curItem = 0; curItem < itemCount; curItem++) {
-                        plcValue.add(new PlcBOOL((Boolean) obj[curItem]));
-                    }
-                }
-                return new PlcList(plcValue);
-            } else if (objArray[0] instanceof Integer) {
-                Integer[] obj = (Integer[]) objValue;
-                List<PlcValue> plcValue;
-                {
-                    int itemCount = (int) obj.length;
-                    plcValue = new LinkedList<>();
-
-                    for(int curItem = 0; curItem < itemCount; curItem++) {
-                        plcValue.add(new PlcDINT((Integer) obj[curItem]));
-                    }
-                }
-                return new PlcList(plcValue);
-            } else if (objArray[0] instanceof Short) {
-                Short[] obj = (Short[]) objValue;
-                List<PlcValue> plcValue;
-                {
-                    int itemCount = (int) obj.length;
-                    plcValue = new LinkedList<>();
-
-                    for(int curItem = 0; curItem < itemCount; curItem++) {
-                        plcValue.add(new PlcINT((Short) obj[curItem]));
-                    }
-                }
-                return new PlcList(plcValue);
-            } else if (objArray[0] instanceof Byte) {
-                Byte[] obj = (Byte[]) objValue;
-                List<PlcValue> plcValue;
-                {
-                    int itemCount = (int) obj.length;
-                    plcValue = new LinkedList<>();
-
-                    for(int curItem = 0; curItem < itemCount; curItem++) {
-                        plcValue.add(new PlcSINT((Byte) obj[curItem]));
-                    }
-                }
-                return new PlcList(plcValue);
-            } else if (objArray[0] instanceof Long) {
-                Long[] obj = (Long[]) objValue;
-                List<PlcValue> plcValue;
-                {
-                    int itemCount = (int) obj.length;
-                    plcValue = new LinkedList<>();
-
-                    for(int curItem = 0; curItem < itemCount; curItem++) {
-                        plcValue.add(new PlcLINT((Long) obj[curItem]));
-                    }
-                }
-                return new PlcList(plcValue);
-            } else if (objArray[0] instanceof Float) {
-                Float[] obj = (Float[]) objValue;
-                List<PlcValue> plcValue;
-                {
-                    int itemCount = (int) obj.length;
-                    plcValue = new LinkedList<>();
-
-                    for(int curItem = 0; curItem < itemCount; curItem++) {
-                        plcValue.add(new PlcREAL((Float) obj[curItem]));
-                    }
-                }
-                return new PlcList(plcValue);
-            } else if (objArray[0] instanceof Double) {
-                Double[] obj = (Double[]) objValue;
-                List<PlcValue> plcValue;
-                {
-                    int itemCount = (int) obj.length;
-                    plcValue = new LinkedList<>();
-
-                    for(int curItem = 0; curItem < itemCount; curItem++) {
-                        plcValue.add(new PlcLREAL((Double) obj[curItem]));
-                    }
-                }
-                return new PlcList(plcValue);
-            } else if (objArray[0] instanceof String) {
-                String[] obj = (String[]) objValue;
-                List<PlcValue> plcValue;
-                {
-                    int itemCount = (int) obj.length;
-                    plcValue = new LinkedList<>();
-
-                    for(int curItem = 0; curItem < itemCount; curItem++) {
-                        plcValue.add(new PlcSTRING((String) obj[curItem]));
-                    }
-                }
-                return new PlcList(plcValue);
-            } else {
-                logger.warn("Node type for " + objArray[0].getClass() + " is not supported");
-                return null;
-            }
-
-        } else {
-            if (typeNode.equals(Identifiers.Boolean)) {
-                return new PlcBOOL((Boolean) objValue);
-            } else if (typeNode.equals(Identifiers.Integer)) {
-                return new PlcDINT((Integer) objValue);
-            } else if (typeNode.equals(Identifiers.Int16)) {
-                return new PlcINT((Short) objValue);
-            } else if (typeNode.equals(Identifiers.Int32)) {
-                return new PlcDINT((Integer) objValue);
-            } else if (typeNode.equals(Identifiers.Int64)) {
-                return new PlcLINT((Long) objValue);
-            } else if (typeNode.equals(Identifiers.UInteger)) {
-                return new PlcLINT((Long) objValue);
-            } else if (typeNode.equals(Identifiers.UInt16)) {
-                return new PlcUINT(((UShort) objValue).intValue());
-            } else if (typeNode.equals(Identifiers.UInt32)) {
-                return new PlcUDINT(((UInteger) objValue).longValue());
-            } else if (typeNode.equals(Identifiers.UInt64)) {
-                return new PlcULINT(new BigInteger(objValue.toString()));
-            } else if (typeNode.equals(Identifiers.Byte)) {
-                return new PlcINT(Short.valueOf(objValue.toString()));
-            } else if (typeNode.equals(Identifiers.Float)) {
-                return new PlcREAL((Float) objValue);
-            } else if (typeNode.equals(Identifiers.Double)) {
-                return new PlcLREAL((Double) objValue);
-            } else if (typeNode.equals(Identifiers.SByte)) {
-                return new PlcSINT((Byte) objValue);
-            } else {
-                return new PlcSTRING(objValue.toString());
-            }
-        }
-
-    }
-
-    public InetAddress getRemoteAddress() {
-        return address;
-    }
-
-    @Override
-    public void connect() throws PlcConnectionException {
-        List<EndpointDescription> endpoints = null;
-        EndpointDescription endpoint = null;
-
-        try {
-            endpoints = DiscoveryClient.getEndpoints(getEndpointUrl(address, port, getSubPathOfParams(params))).get();
-            //TODO Exception should be handeled better when the Discovery-API of Milo is stable
-        } catch (Exception ex) {
-            logger.info("Failed to discover Endpoint with enabled discovery. If the endpoint does not allow a correct discovery disable this option with the nDiscovery=true option. Failed Endpoint: {}", getEndpointUrl(address, port, params));
-
-            // try the explicit discovery endpoint as well
-            String discoveryUrl = getEndpointUrl(address, port, getSubPathOfParams(params));
-
-            if (!discoveryUrl.endsWith("/")) {
-                discoveryUrl += "/";
-            }
-            discoveryUrl += "discovery";
-
-            logger.info("Trying explicit discovery URL: {}", discoveryUrl);
-            try {
-                endpoints = DiscoveryClient.getEndpoints(discoveryUrl).get();
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-                throw new PlcConnectionException("Unable to discover URL:" + discoveryUrl);
-            } catch (ExecutionException e) {
-                throw new PlcConnectionException("Unable to discover URL:" + discoveryUrl);
-            }
-
-        }
-        endpoint = endpoints.stream()
-            .filter(e -> e.getSecurityPolicyUri().equals(getSecurityPolicy().getUri()))
-            .filter(endpointFilter())
-            .findFirst()
-            .orElseThrow(() -> new PlcConnectionException("No desired endpoints from"));
-
-        if (this.skipDiscovery) {
-            //ApplicationDescription applicationDescription = new ApplicationDescription();
-            //endpoint = new EndpointDescription(address.getHostAddress(),applicationDescription , null, MessageSecurityMode.None, SecurityPolicy.None.getUri(), null , TransportProfile.TCP_UASC_UABINARY.getUri(), UByte.valueOf(0));// TODO hier machen wenn fertig
-            ApplicationDescription currentAD = endpoint.getServer();
-            ApplicationDescription withoutDiscoveryAD = new ApplicationDescription(
-                currentAD.getApplicationUri(),
-                currentAD.getProductUri(),
-                currentAD.getApplicationName(),
-                currentAD.getApplicationType(),
-                currentAD.getGatewayServerUri(),
-                currentAD.getDiscoveryProfileUri(),
-                new String[0]);
-            //try to replace the overhanded address
-            //any error will result in the overhanded address of the client
-            String newEndpointUrl = endpoint.getEndpointUrl(), prefix = "", suffix = "";
-            String splitterPrefix = "://";
-            String splitterSuffix = ":";
-            String[] prefixSplit = newEndpointUrl.split(splitterPrefix);
-            if (prefixSplit.length > 1) {
-                String[] suffixSplit = prefixSplit[1].split(splitterSuffix);
-                //reconstruct the uri
-                newEndpointUrl = "";
-                newEndpointUrl += prefixSplit[0] + splitterPrefix + address.getHostAddress();
-                for (int suffixCounter = 1; suffixCounter < suffixSplit.length; suffixCounter++) {
-                    newEndpointUrl += splitterSuffix + suffixSplit[suffixCounter];
-                }
-                // attach surounding prefix match
-                for (int prefixCounter = 2; prefixCounter < prefixSplit.length; prefixCounter++) {
-                    newEndpointUrl += splitterPrefix + prefixSplit[prefixCounter];
-                }
-            }
-
-            EndpointDescription noDiscoverEndpoint = new EndpointDescription(
-                newEndpointUrl,
-                withoutDiscoveryAD,
-                endpoint.getServerCertificate(),
-                endpoint.getSecurityMode(),
-                endpoint.getSecurityPolicyUri(),
-                endpoint.getUserIdentityTokens(),
-                endpoint.getTransportProfileUri(),
-                endpoint.getSecurityLevel());
-            endpoint = noDiscoverEndpoint;
-        }
-
-
-        OpcUaClientConfig config = OpcUaClientConfig.builder()
-            .setApplicationName(LocalizedText.english("eclipse milo opc-ua client of the apache PLC4X:PLC4J project"))
-            .setApplicationUri("urn:eclipse:milo:plc4x:client")
-            .setEndpoint(endpoint)
-            .setIdentityProvider(getIdentityProvider())
-            .setRequestTimeout(UInteger.valueOf(requestTimeout))
-            .build();
-
-        try {
-            this.client = OpcUaClient.create(config);
-            this.client.connect().get();
-            isConnected = true;
-        } catch (UaException e) {
-            isConnected = false;
-            String message = (config == null) ? "NULL" : config.toString();
-            throw new PlcConnectionException("The given input values are a not valid OPC UA connection configuration [CONFIG]: " + message);
-        } catch (InterruptedException e) {
-            isConnected = false;
-            Thread.currentThread().interrupt();
-            throw new PlcConnectionException("Error while creation of the connection because of : " + e.getMessage());
-        } catch (ExecutionException e) {
-            isConnected = false;
-            throw new PlcConnectionException("Error while creation of the connection because of : " + e.getMessage());
-        }
-    }
-
-    @Override
-    public boolean isConnected() {
-        return client != null && isConnected;
-    }
-
-    @Override
-    public void close() throws Exception {
-        if (client != null) {
-            client.disconnect().get();
-            isConnected = false;
-        }
-    }
-
-    @Override
-    public CompletableFuture<PlcSubscriptionResponse> subscribe(PlcSubscriptionRequest subscriptionRequest) {
-        CompletableFuture<PlcSubscriptionResponse> future = CompletableFuture.supplyAsync(() -> {
-            Map<String, ResponseItem<PlcSubscriptionHandle>> responseItems = new HashMap<>();
-            for (String fieldName : subscriptionRequest.getFieldNames()) {
-                final PlcSubscriptionField subscriptionField = subscriptionRequest.getField(fieldName);
-                final OpcuaField field = (OpcuaField) Objects.requireNonNull(subscriptionField);
-                long cycleTime = subscriptionField.getDuration().orElse(Duration.ofSeconds(1)).toMillis();
-                NodeId idNode = generateNodeId(field);
-                ReadValueId readValueId = new ReadValueId(
-                    idNode,
-                    AttributeId.Value.uid(), null, QualifiedName.NULL_VALUE);
-                UInteger clientHandle = uint(clientHandles.getAndIncrement());
-
-                MonitoringParameters parameters = new MonitoringParameters(
-                    clientHandle,
-                    (double) cycleTime,     // sampling interval
-                    null,       // filter, null means use default
-                    uint(1),   // queue size
-                    true        // discard oldest
-                );
-                MonitoringMode monitoringMode;
-                switch (subscriptionField.getPlcSubscriptionType()) {
-                    case CYCLIC:
-                        monitoringMode = MonitoringMode.Sampling;
-                        break;
-                    case CHANGE_OF_STATE:
-                        monitoringMode = MonitoringMode.Reporting;
-                        break;
-                    case EVENT:
-                        monitoringMode = MonitoringMode.Reporting;
-                        break;
-                    default:
-                        monitoringMode = MonitoringMode.Reporting;
-                }
-
-                PlcSubscriptionHandle subHandle = null;
-                PlcResponseCode responseCode = PlcResponseCode.ACCESS_DENIED;
-                try {
-                    UaSubscription subscription = client.getSubscriptionManager().createSubscription(cycleTime).get();
-
-                    MonitoredItemCreateRequest request = new MonitoredItemCreateRequest(
-                        readValueId, monitoringMode, parameters);
-                    List<MonitoredItemCreateRequest> requestList = new LinkedList<>();
-                    requestList.add(request);
-                    OpcuaSubsriptionHandle subsriptionHandle = new OpcuaSubsriptionHandle(fieldName, clientHandle);
-                    BiConsumer<UaMonitoredItem, Integer> onItemCreated =
-                        (item, id) -> item.setValueConsumer(subsriptionHandle::onSubscriptionValue);
-
-                    List<UaMonitoredItem> items = subscription.createMonitoredItems(
-                        TimestampsToReturn.Both,
-                        requestList,
-                        onItemCreated
-                    ).get();
-
-                    subHandle = subsriptionHandle;
-                    responseCode = PlcResponseCode.OK;
-                } catch (InterruptedException e) {
-                    Thread.currentThread().interrupt();
-                    logger.warn("Unable to subscribe Elements because of: {}", e.getMessage());
-                } catch (ExecutionException e) {
-                    logger.warn("Unable to subscribe Elements because of: {}", e.getMessage());
-                }
-
-                responseItems.put(fieldName, new ResponseItem(responseCode, subHandle));
-            }
-            return new DefaultPlcSubscriptionResponse(subscriptionRequest, responseItems);
-        });
-
-        return future;
-    }
-
-    @Override
-    public CompletableFuture<PlcUnsubscriptionResponse> unsubscribe(PlcUnsubscriptionRequest unsubscriptionRequest) {
-        unsubscriptionRequest.getSubscriptionHandles().forEach(o -> {
-            OpcuaSubsriptionHandle opcSubHandle = (OpcuaSubsriptionHandle) o;
-            try {
-                client.getSubscriptionManager().deleteSubscription(opcSubHandle.getClientHandle()).get();
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-                logger.warn("Unable to unsubscribe Elements because of: {}", e.getMessage());
-            } catch (ExecutionException e) {
-                logger.warn("Unable to unsubscribe Elements because of: {}", e.getMessage());
-            }
-        });
-
-        return null;
-    }
-
-    @Override
-    public PlcConsumerRegistration register(Consumer<PlcSubscriptionEvent> consumer, Collection<PlcSubscriptionHandle> handles) {
-        List<PlcConsumerRegistration> registrations = new LinkedList<>();
-        // Register the current consumer for each of the given subscription handles
-        for (PlcSubscriptionHandle subscriptionHandle : handles) {
-            final PlcConsumerRegistration consumerRegistration = subscriptionHandle.register(consumer);
-            registrations.add(consumerRegistration);
-        }
-
-        return new DefaultPlcConsumerRegistration(this, consumer, handles.toArray(new PlcSubscriptionHandle[0]));
-    }
-
-    @Override
-    public void unregister(PlcConsumerRegistration registration) {
-        registration.unregister();
-    }
-
-    @Override
-    public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) {
-        CompletableFuture<PlcReadResponse> future = CompletableFuture.supplyAsync(() -> {
-            readRequest.getFields();
-            Map<String, ResponseItem<PlcValue>> fields = new HashMap<>();
-            List<NodeId> readValueIds = new LinkedList<>();
-            List<PlcField> readPLCValues = readRequest.getFields();
-            for (PlcField field : readPLCValues) {
-                NodeId idNode = generateNodeId((OpcuaField) field);
-                readValueIds.add(idNode);
-            }
-
-            CompletableFuture<List<DataValue>> dataValueCompletableFuture = client.readValues(0.0, TimestampsToReturn.Both, readValueIds);
-            List<DataValue> readValues = null;
-            try {
-                readValues = dataValueCompletableFuture.get();
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-                logger.warn("Unable to read Elements because of: {}", e.getMessage());
-            } catch (ExecutionException e) {
-                logger.warn("Unable to read Elements because of: {}", e.getMessage());
-            }
-            for (int counter = 0; counter < readValueIds.size(); counter++) {
-                PlcResponseCode resultCode = PlcResponseCode.OK;
-                PlcValue stringItem = null;
-                if (readValues == null || readValues.size() <= counter ||
-                    !readValues.get(counter).getStatusCode().equals(StatusCode.GOOD)) {
-                    resultCode = PlcResponseCode.NOT_FOUND;
-                } else {
-                    stringItem = encodePlcValue(readValues.get(counter));
-
-                }
-                ResponseItem<PlcValue> newPair = new ResponseItem<>(resultCode, stringItem);
-                fields.put((String) readRequest.getFieldNames().toArray()[counter], newPair);
-
-
-            }
-            return new DefaultPlcReadResponse(readRequest, fields);
-        });
-
-        return future;
-    }
-
-
-    @Override
-    public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
-        CompletableFuture<PlcWriteResponse> future;
-        future = CompletableFuture.supplyAsync(() -> {
-            List<PlcField> writePLCValues = writeRequest.getFields();
-            LinkedList<DataValue> values = new LinkedList<>();
-            LinkedList<NodeId> ids = new LinkedList<>();
-            LinkedList<String> names = new LinkedList<>();
-            Map<String, PlcResponseCode> fieldResponse = new HashMap<>();
-            for (String fieldName : writeRequest.getFieldNames()) {
-                OpcuaField uaField = (OpcuaField) writeRequest.getField(fieldName);
-                NodeId idNode = generateNodeId(uaField);
-                Object valueObject = writeRequest.getPlcValue(fieldName).getObject();
-                // Added small work around for handling BigIntegers as input type for UInt64
-                if (valueObject instanceof BigInteger) valueObject = ulong((BigInteger) valueObject);
-                Variant var = null;
-                if (valueObject instanceof ArrayList) {
-                    List<PlcValue> plcValueList = (List<PlcValue>) valueObject;
-                    String dataType = uaField.getPlcDataType();
-                    if (dataType.equals("IEC61131_NULL")) {
-                        if (plcValueList.get(0).getObject() instanceof Boolean) {
-                            dataType = "IEC61131_BOOL";
-                        } else if (plcValueList.get(0).getObject() instanceof Byte) {
-                            dataType = "IEC61131_SINT";
-                        } else if (plcValueList.get(0).getObject() instanceof Short) {
-                            dataType = "IEC61131_INT";
-                        } else if (plcValueList.get(0).getObject() instanceof Integer) {
-                            dataType = "IEC61131_DINT";
-                        } else if (plcValueList.get(0).getObject() instanceof Long) {
-                            dataType = "IEC61131_LINT";
-                        } else if (plcValueList.get(0).getObject() instanceof Float) {
-                            dataType = "IEC61131_REAL";
-                        } else if (plcValueList.get(0).getObject() instanceof Double) {
-                            dataType = "IEC61131_LREAL";
-                        } else if (plcValueList.get(0).getObject() instanceof String) {
-                            dataType = "IEC61131_STRING";
-                        }
-                    }
-                    switch (dataType) {
-                        case "IEC61131_BOOL":
-                        case "IEC61131_BIT":
-                            List<Boolean> booleanList = (plcValueList).stream().map(
-                                    x -> ((PlcBOOL) x).getBoolean()).collect(Collectors.toList());
-                            var = new Variant(booleanList.toArray(new Boolean[booleanList.size()]));
-                            break;
-                        case "IEC61131_BYTE":
-                        case "IEC61131_BITARR8":
-                            List<UByte> byteList = (plcValueList).stream().map(
-                                    x -> UByte.valueOf(((PlcBYTE) x).getShort())).collect(Collectors.toList());
-                            var = new Variant(byteList.toArray(new UByte[byteList.size()]));
-                            break;
-                        case "IEC61131_SINT":
-                        case "IEC61131_INT8":
-                            List<Byte> sintList = (plcValueList).stream().map(
-                                    x -> ((PlcSINT) x).getByte()).collect(Collectors.toList());
-                            var = new Variant(sintList.toArray(new Byte[sintList.size()]));
-                            break;
-                        case "IEC61131_USINT":
-                        case "IEC61131_UINT8":
-                        case "IEC61131_BIT8":
-                            List<UByte> usintList = (plcValueList).stream().map(
-                                    x -> UByte.valueOf(((PlcUSINT) x).getShort())).collect(Collectors.toList());
-                            var = new Variant(usintList.toArray(new UByte[usintList.size()]));
-                            break;
-                        case "IEC61131_INT":
-                        case "IEC61131_INT16":
-                            List<Short> intList = (plcValueList).stream().map(
-                                    x -> ((PlcINT) x).getShort()).collect(Collectors.toList());
-                            var = new Variant(intList.toArray(new Short[intList.size()]));
-                            break;
-                        case "IEC61131_UINT":
-                        case "IEC61131_UINT16":
-                            List<UShort> uintList = (plcValueList).stream().map(
-                                    x -> UShort.valueOf(((PlcUINT) x).getInteger())).collect(Collectors.toList());
-                            var = new Variant(uintList.toArray(new UShort[uintList.size()]));
-                            break;
-                        case "IEC61131_WORD":
-                        case "IEC61131_BITARR16":
-                            List<UShort> wordList = (plcValueList).stream().map(
-                                    x -> UShort.valueOf(((PlcWORD) x).getInteger())).collect(Collectors.toList());
-                            var = new Variant(wordList.toArray(new UShort[wordList.size()]));
-                            break;
-                        case "IEC61131_DINT":
-                        case "IEC61131_INT32":
-                            List<Integer> dintList = (plcValueList).stream().map(
-                                    x -> ((PlcDINT) x).getInteger()).collect(Collectors.toList());
-                            var = new Variant(dintList.toArray(new Integer[dintList.size()]));
-                            break;
-                        case "IEC61131_UDINT":
-                        case "IEC61131_UINT32":
-                            List<UInteger> udintList = (plcValueList).stream().map(
-                                    x -> UInteger.valueOf(((PlcUDINT) x).getLong())).collect(Collectors.toList());
-                            var = new Variant(udintList.toArray(new UInteger[udintList.size()]));
-                            break;
-                        case "IEC61131_DWORD":
-                        case "IEC61131_BITARR32":
-                            List<UInteger> dwordList = (plcValueList).stream().map(
-                                    x -> UInteger.valueOf(((PlcDWORD) x).getLong())).collect(Collectors.toList());
-                            var = new Variant(dwordList.toArray(new UInteger[dwordList.size()]));
-                            break;
-                        case "IEC61131_LINT":
-                        case "IEC61131_INT64":
-                            List<Long> lintList = (plcValueList).stream().map(
-                                    x -> ((PlcLINT) x).getLong()).collect(Collectors.toList());
-                            var = new Variant(lintList.toArray(new Long[lintList.size()]));
-                            break;
-                        case "IEC61131_ULINT":
-                        case "IEC61131_UINT64":
-                            List<ULong> ulintList = (plcValueList).stream().map(
-                                    x -> ULong.valueOf(((PlcULINT) x).getBigInteger())).collect(Collectors.toList());
-                            var = new Variant(ulintList.toArray(new ULong[ulintList.size()]));
-                            break;
-                        case "IEC61131_LWORD":
-                        case "IEC61131_BITARR64":
-                            List<ULong> lwordList = (plcValueList).stream().map(
-                                    x -> ULong.valueOf(((PlcLWORD) x).getBigInteger())).collect(Collectors.toList());
-                            var = new Variant(lwordList.toArray(new ULong[lwordList.size()]));
-                            break;
-                        case "IEC61131_REAL":
-                        case "IEC61131_FLOAT":
-                            List<Float> realList = (plcValueList).stream().map(
-                                    x -> ((PlcREAL) x).getFloat()).collect(Collectors.toList());
-                            var = new Variant(realList.toArray(new Float[realList.size()]));
-                            break;
-                        case "IEC61131_LREAL":
-                        case "IEC61131_DOUBLE":
-                            List<Double> lrealList = (plcValueList).stream().map(
-                                    x -> (Double) ((PlcLREAL) x).getDouble()).collect(Collectors.toList());
-                            var = new Variant(lrealList.toArray(new Double[lrealList.size()]));
-                            break;
-                        case "IEC61131_CHAR":
-                            List<String> charList = (plcValueList).stream().map(
-                                    x -> ((PlcCHAR) x).getString()).collect(Collectors.toList());
-                            var = new Variant(charList.toArray(new String[charList.size()]));
-                            break;
-                        case "IEC61131_WCHAR":
-                            List<String> wcharList = (plcValueList).stream().map(
-                                    x -> ((PlcWCHAR) x).getString()).collect(Collectors.toList());
-                            var = new Variant(wcharList.toArray(new String[wcharList.size()]));
-                            break;
-                        case "IEC61131_STRING":
-                            List<String> stringList = (plcValueList).stream().map(
-                                    x -> ((PlcSTRING) x).getString()).collect(Collectors.toList());
-                            var = new Variant(stringList.toArray(new String[stringList.size()]));
-                            break;
-                        case "IEC61131_WSTRING":
-                        case "IEC61131_STRING16":
-                            List<String> wstringList = (plcValueList).stream().map(
-                                    x -> (String) ((PlcSTRING) x).getString()).collect(Collectors.toList());
-                            var = new Variant(wstringList.toArray(new String[wstringList.size()]));
-                            break;
-                        case "IEC61131_DATE_AND_TIME":
-                            List<LocalDateTime> dateTimeList = (plcValueList).stream().map(
-                                    x -> ((PlcDATE_AND_TIME) x).getDateTime()).collect(Collectors.toList());
-                            var = new Variant(dateTimeList.toArray(new LocalDateTime[dateTimeList.size()]));
-                            break;
-                        default:
-                            logger.warn("Unsupported data type : {}, {}", plcValueList.get(0).getClass(), dataType);
-                    }
-                } else {
-                    String dataType = uaField.getPlcDataType();                    
-                    PlcValue plcValue = (PlcValue) writeRequest.getPlcValue(fieldName);
-
-                    if (dataType.equals("IEC61131_NULL")) {
-                        if (plcValue.getObject() instanceof Boolean) {
-                            dataType = "IEC61131_BOOL";
-                        } else if (plcValue.getObject() instanceof Byte) {
-                            dataType = "IEC61131_SINT";
-                        } else if (plcValue.getObject() instanceof Short) {
-                            dataType = "IEC61131_INT";
-                        } else if (plcValue.getObject() instanceof Integer) {
-                            dataType = "IEC61131_DINT";
-                        } else if (plcValue.getObject() instanceof Long) {
-                            dataType = "IEC61131_LINT";
-                        } else if (plcValue.getObject() instanceof Float) {
-                            dataType = "IEC61131_REAL";
-                        } else if (plcValue.getObject() instanceof Double) {
-                            dataType = "IEC61131_LREAL";
-                        } else if (plcValue.getObject() instanceof String) {
-                            dataType = "IEC61131_STRING";
-                        }
-                    }
-                    switch (dataType) {
-                        case "IEC61131_BOOL":
-                        case "IEC61131_BIT":
-                            var = new Variant(plcValue.getBoolean());
-                            break;
-                        case "IEC61131_BYTE":
-                        case "IEC61131_BITARR8":
-                            var = new Variant(UByte.valueOf(plcValue.getShort()));
-                            break;
-                        case "IEC61131_SINT":
-                        case "IEC61131_INT8":
-                            var = new Variant(plcValue.getByte());
-                            break;
-                        case "IEC61131_USINT":
-                        case "IEC61131_UINT8":
-                        case "IEC61131_BIT8":
-                            var = new Variant(UByte.valueOf(plcValue.getShort()));
-                            break;
-                        case "IEC61131_INT":
-                        case "IEC61131_INT16":
-                            var = new Variant(plcValue.getShort());
-                            break;
-                        case "IEC61131_UINT":
-                        case "IEC61131_UINT16":
-                            var = new Variant(UShort.valueOf(plcValue.getInteger()));
-                            break;
-                        case "IEC61131_WORD":
-                        case "IEC61131_BITARR16":
-                            var = new Variant(UShort.valueOf(plcValue.getInteger()));
-                            break;
-                        case "IEC61131_DINT":
-                        case "IEC61131_INT32":
-                            var = new Variant(plcValue.getInteger());
-                            break;
-                        case "IEC61131_UDINT":
-                        case "IEC61131_UINT32":
-                            var = new Variant(UInteger.valueOf(plcValue.getLong()));
-                            break;
-                        case "IEC61131_DWORD":
-                        case "IEC61131_BITARR32":
-                            var = new Variant(UInteger.valueOf(plcValue.getLong()));
-                            break;
-                        case "IEC61131_LINT":
-                        case "IEC61131_INT64":
-                            var = new Variant(plcValue.getLong());
-                            break;
-                        case "IEC61131_ULINT":
-                        case "IEC61131_UINT64":
-                            var = new Variant(ULong.valueOf(plcValue.getBigInteger()));
-                            break;
-                        case "IEC61131_LWORD":
-                        case "IEC61131_BITARR64":
-                            var = new Variant(ULong.valueOf(plcValue.getBigInteger()));
-                            break;
-                        case "IEC61131_REAL":
-                        case "IEC61131_FLOAT":
-                            var = new Variant(plcValue.getFloat());
-                            break;
-                        case "IEC61131_LREAL":
-                        case "IEC61131_DOUBLE":
-                            var = new Variant(plcValue.getDouble());
-                            break;
-                        case "IEC61131_CHAR":
-                            var = new Variant(plcValue.getString());
-                            break;
-                        case "IEC61131_WCHAR":
-                            var = new Variant(plcValue.getString());
-                            break;
-                        case "IEC61131_STRING":
-                            var = new Variant(plcValue.getString());
-                            break;
-                        case "IEC61131_WSTRING":
-                        case "IEC61131_STRING16":
-                            var = new Variant(plcValue.getString());
-                            break;
-                        case "IEC61131_DATE_AND_TIME":
-                            var = new Variant(plcValue.getDateTime());
-                            break;
-                        default:
-                            logger.warn("Unsupported data type : {}, {}", plcValue.getClass(), dataType);
-                    }
-                }
-                DataValue value = new DataValue(var);
-                ids.add(idNode);
-                names.add(fieldName);
-                values.add(value);
-            }
-            CompletableFuture<List<StatusCode>> opcRequest =
-                client.writeValues(ids, values);
-            List<StatusCode> statusCodes = null;
-            try {
-                statusCodes = opcRequest.get();
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-                statusCodes = new LinkedList<>();
-                for (int counter = 0; counter < ids.size(); counter++) {
-                    ((LinkedList<StatusCode>) statusCodes).push(StatusCode.BAD);
-                }
-            } catch (ExecutionException e) {
-                statusCodes = new LinkedList<>();
-                for (int counter = 0; counter < ids.size(); counter++) {
-                    ((LinkedList<StatusCode>) statusCodes).push(StatusCode.BAD);
-                }
-            }
-
-            for (int counter = 0; counter < names.size(); counter++) {
-                final PlcResponseCode resultCode;
-                if (statusCodes != null && statusCodes.size() > counter) {
-                    Optional<String[]> status = StatusCodes.lookup(statusCodes.get(counter).getValue());
-                    if (status.isPresent()) {
-                        if (status.get()[0].equals("Good")) {
-                            resultCode = PlcResponseCode.OK;
-                        } else if (status.get()[0].equals("Uncertain")) {
-                            resultCode = PlcResponseCode.NOT_FOUND;
-                        } else if (status.get()[0].equals("Bad")) {
-                            resultCode = PlcResponseCode.INVALID_DATATYPE;
-                        } else if (status.get()[0].equals("Bad_NodeIdUnknown")) {
-                            resultCode = PlcResponseCode.NOT_FOUND;
-                        } else {
-                            resultCode = PlcResponseCode.ACCESS_DENIED;
-                        }
-                    } else {
-                        resultCode = PlcResponseCode.ACCESS_DENIED;
-                    }
-                } else {
-                    resultCode = PlcResponseCode.ACCESS_DENIED;
-                }
-                fieldResponse.put(names.get(counter), resultCode);
-            }
-            PlcWriteResponse response = new DefaultPlcWriteResponse(writeRequest, fieldResponse);
-            return response;
-        });
-
-
-        return future;
-    }
-
-
-    private NodeId generateNodeId(OpcuaField uaField) {
-        NodeId idNode = null;
-        switch (uaField.getIdentifierType()) {
-            case STRING_IDENTIFIER:
-                idNode = new NodeId(uaField.getNamespace(), uaField.getIdentifier());
-                break;
-            case NUMBER_IDENTIFIER:
-                idNode = new NodeId(uaField.getNamespace(), UInteger.valueOf(uaField.getIdentifier()));
-                break;
-            case GUID_IDENTIFIER:
-                idNode = new NodeId(uaField.getNamespace(), UUID.fromString(uaField.getIdentifier()));
-                break;
-            case BINARY_IDENTIFIER:
-                idNode = new NodeId(uaField.getNamespace(), new ByteString(uaField.getIdentifier().getBytes()));
-                break;
-
-            default:
-                idNode = new NodeId(uaField.getNamespace(), uaField.getIdentifier());
-        }
-
-        return idNode;
-    }
-
-    private String getEndpointUrl(InetAddress address, Integer port, String params) {
-        return "opc.tcp://" + address.getHostAddress() + ":" + port + "/" + params;
-    }
-
-    private Predicate<EndpointDescription> endpointFilter() {
-        return e -> true;
-    }
-
-    private SecurityPolicy getSecurityPolicy() {
-        return SecurityPolicy.None;
-    }
-
-    private IdentityProvider getIdentityProvider() {
-        return new AnonymousProvider();
-    }
-
-    private static String getSubPathOfParams(String params){
-        if(params.contains("=")){
-            if(params.contains("?")){
-                return params.split("\\?")[0];
-            }else{
-                return "";
-            }
-
-        }else {
-            return params;
-        }
-    }
-
-    private static String getOptionString(String params){
-        if(params.contains("=")){
-            if(params.contains("?")){
-                return params.split("\\?")[1];
-            }else{
-                return params;
-            }
-
-        }else {
-            return "";
-        }
-    }
-}
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
index b8220da..229992a 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
@@ -58,13 +58,15 @@ import org.apache.plc4x.java.spi.messages.DefaultPlcWriteResponse;
 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.eclipse.milo.opcua.stack.core.types.builtin.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.UnsupportedEncodingException;
 import java.time.Duration;
 import java.math.BigInteger;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
@@ -174,10 +176,52 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             .unwrap(p -> (OpcuaMessageResponse) p.getMessage())
             .handle(opcuaMessageResponse -> {
                     LOGGER.info("Got Close Session Response Connection Response" + opcuaMessageResponse.toString());
-                    context.fireDisconnected();
+                    closeSecureChannel(context);
                 });
     }
 
+    private void closeSecureChannel(ConversationContext<OpcuaAPU> context) {
+
+        int transactionId = transactionIdentifierGenerator.getAndIncrement();
+        if(transactionIdentifierGenerator.get() == 0xFFFF) {
+            transactionIdentifierGenerator.set(1);
+        }
+
+        ExpandedNodeId expandedNodeId = new ExpandedNodeIdFourByte(false,           //Namespace Uri Specified
+            false,            //Server Index Specified
+            NULL_STRING,                      //Namespace Uri
+            1L,                     //Server Index
+            new FourByteNodeId((short) 0, 452));    //Identifier for OpenSecureChannel
+
+        RequestHeader requestHeader = new RequestHeader(authenticationToken,
+            getCurrentDateTime(),
+            0L,                                         //RequestHandle
+            0L,
+            NULL_STRING,
+            REQUEST_TIMEOUT_LONG,
+            NULL_EXTENSION_OBJECT);
+
+        CloseSecureChannelRequest closeSecureChannelRequest = new CloseSecureChannelRequest((byte) 1,
+            (byte) 0,
+            requestHeader);
+
+        OpcuaCloseRequest closeRequest = new OpcuaCloseRequest(CHUNK,
+            channelId.get(),
+            tokenId.get(),
+            transactionId,
+            transactionId,
+            closeSecureChannelRequest);
+
+        context.sendRequest(new OpcuaAPU(closeRequest))
+            .expectResponse(OpcuaAPU.class, REQUEST_TIMEOUT)
+            .check(p -> p.getMessage() instanceof OpcuaMessageResponse)
+            .unwrap(p -> (OpcuaMessageResponse) p.getMessage())
+            .handle(opcuaMessageResponse -> {
+                LOGGER.info("Got Close Secure Channel Response" + opcuaMessageResponse.toString());
+            });
+        context.fireDisconnected();
+    }
+
     @Override
     public void setDriverContext(DriverContext driverContext) {
         super.setDriverContext(driverContext);
@@ -611,9 +655,9 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
                             } else if (variant instanceof VariantDateTime) {
                                 long[] array = ((VariantDateTime) variant).getValue();
                                 int length = array.length;
-                                DateTime[] tmpValue = new DateTime[length];
+                                LocalDateTime[] tmpValue = new LocalDateTime[length];
                                 for (int i = 0; i < length; i++) {
-                                    tmpValue[i] = new DateTime(array[i]);
+                                    tmpValue[i] = LocalDateTime.ofInstant(Instant.ofEpochMilli(array[i]), ZoneId.systemDefault());
                                 }
                                 value = IEC61131ValueHandler.of(tmpValue);
                             } else if (variant instanceof VariantGuid) {
@@ -633,7 +677,11 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
                                 LOGGER.error("Data type - " +  variant.getClass() + " is not supported ");
                             }
                         } else {
-                            responseCode = PlcResponseCode.UNSUPPORTED;
+                            if (results[0].getStatusCode().getStatusCode() == OpcuaStatusCodes.BadNodeIdUnknown.getValue()) {
+                                responseCode = PlcResponseCode.NOT_FOUND;
+                            } else {
+                                responseCode = PlcResponseCode.UNSUPPORTED;
+                            }
                             LOGGER.error("Error while reading value from OPC UA server error code:- " + results[0].getStatusCode().toString());
                         }
 
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubsriptionHandle.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubsriptionHandle.java
deleted file mode 100644
index 756f0cd..0000000
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubsriptionHandle.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- 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.opcua.protocol;
-
-import org.apache.plc4x.java.api.messages.PlcSubscriptionEvent;
-import org.apache.plc4x.java.api.model.PlcConsumerRegistration;
-import org.apache.plc4x.java.api.model.PlcSubscriptionHandle;
-import org.apache.plc4x.java.api.types.PlcResponseCode;
-import org.apache.plc4x.java.api.value.PlcValue;
-import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionEvent;
-import org.apache.plc4x.java.opcua.connection.OpcuaTcpPlcConnection;
-import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
-import org.apache.plc4x.java.spi.model.DefaultPlcConsumerRegistration;
-import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaMonitoredItem;
-import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
-import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
-import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
-
-import java.time.Instant;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Consumer;
-
-/**
- */
-public class OpcuaSubsriptionHandle implements PlcSubscriptionHandle {
-
-    private Set<Consumer<PlcSubscriptionEvent>> consumers = new HashSet<>();
-    private String fieldName;
-    private UInteger clientHandle;
-
-    /**
-     * @param fieldName    corresponding map key in the PLC4X request/reply map
-     * @param clientHandle
-     */
-    public OpcuaSubsriptionHandle(String fieldName, UInteger clientHandle) {
-        this.fieldName = fieldName;
-        this.clientHandle = clientHandle;
-    }
-
-    public UInteger getClientHandle() {
-        return clientHandle;
-    }
-
-    /**
-     * @param item
-     * @param value
-     */
-    public void onSubscriptionValue(UaMonitoredItem item, DataValue value) {
-        consumers.forEach(plcSubscriptionEventConsumer -> {
-            PlcResponseCode resultCode = PlcResponseCode.OK;
-            PlcValue stringItem = null;
-            if (value.getStatusCode() != StatusCode.GOOD) {
-                resultCode = PlcResponseCode.NOT_FOUND;
-            } else {
-                stringItem = OpcuaTcpPlcConnection.encodePlcValue(value);
-
-            }
-            Map<String, ResponseItem<PlcValue>> fields = new HashMap<>();
-            ResponseItem<PlcValue> newPair = new ResponseItem<>(resultCode, stringItem);
-            fields.put(fieldName, newPair);
-            PlcSubscriptionEvent event = new DefaultPlcSubscriptionEvent(Instant.now(), fields);
-            plcSubscriptionEventConsumer.accept(event);
-        });
-    }
-
-    @Override
-    public PlcConsumerRegistration register(Consumer<PlcSubscriptionEvent> consumer) {
-        consumers.add(consumer);
-        return null;
-//        return () -> consumers.remove(consumer);
-    }
-
-}
diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java
new file mode 100644
index 0000000..3f676a6
--- /dev/null
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java
@@ -0,0 +1,277 @@
+/*
+ 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.opcua;
+
+import io.vavr.collection.List;
+import org.apache.plc4x.java.PlcDriverManager;
+import org.apache.plc4x.java.api.PlcConnection;
+import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
+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.eclipse.milo.examples.server.ExampleServer;
+import org.junit.jupiter.api.*;
+
+import static org.apache.plc4x.java.opcua.OpcuaPlcDriver.INET_ADDRESS_PATTERN;
+import static org.apache.plc4x.java.opcua.OpcuaPlcDriver.URI_PATTERN;
+import static org.apache.plc4x.java.opcua.UtilsTest.assertMatching;
+import static org.assertj.core.api.Assertions.fail;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ */
+public class OpcuaPlcDriverTest {
+
+    private static final Logger logger = LoggerFactory.getLogger(OpcuaPlcDriverTest.class);
+
+
+    // Read only variables of milo example server of version 3.6
+    private static final String BOOL_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Boolean";
+    private static final String BYTE_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Byte";
+    private static final String DOUBLE_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Double";
+    private static final String FLOAT_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Float";
+    private static final String INT16_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Int16";
+    private static final String INT32_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Int32";
+    private static final String INT64_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Int64";
+    private static final String INTEGER_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Integer";
+    private static final String SBYTE_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/SByte";
+    private static final String STRING_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/String";
+    private static final String UINT16_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/UInt16";
+    private static final String UINT32_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/UInt32";
+    private static final String UINT64_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/UInt64";
+    private static final String UINTEGER_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/UInteger";
+    private static final String DOES_NOT_EXIST_IDENTIFIER_READ_WRITE = "ns=2;i=12512623";
+
+    // At the moment not used in PLC4X or in the OPC UA driver
+    private static final String BYTE_STRING_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/ByteString";
+    private static final String DATE_TIME_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/DateTime";
+    private static final String DURATION_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Duration";
+    private static final String GUID_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Guid";
+    private static final String LOCALISED_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/LocalizedText";
+    private static final String NODE_ID_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/NodeId";
+    private static final String QUALIFIED_NAM_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/QualifiedName";
+    private static final String UTC_TIME_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/UtcTime";
+    private static final String VARIANT_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Variant";
+    private static final String XML_ELEMENT_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/XmlElement";
+
+    //Arrays
+    private static final String BOOL_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/BooleanArray";
+    private static final String BYTE_STRING_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/ByteStringArray";
+    private static final String BYTE_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/ByteArray";
+    private static final String DOUBLE_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/DoubleArray";
+    private static final String FLOAT_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/FloatArray";
+    private static final String INT16_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/Int16Array";
+    private static final String INT32_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/Int32Array";
+    private static final String INT64_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/Int64Array";
+    private static final String INTEGER_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/IntegerArray";
+    private static final String SBYTE_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/SByteArray";
+    private static final String STRING_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/StringArray";
+    private static final String UINT16_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/UInt16Array";
+    private static final String UINT32_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/UInt32Array";
+    private static final String UINT64_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/UInt64Array";
+
+
+    // Address of local milo server
+    private String miloLocalAddress = "127.0.0.1:12686/milo";
+    //Tcp pattern of OPC UA
+    private String opcPattern = "opcua:tcp://";
+
+    private String paramSectionDivider = "?";
+    private String paramDivider = "&";
+
+    private String tcpConnectionAddress = opcPattern + miloLocalAddress;
+
+    private List<String> connectionStringValidSet = List.of(tcpConnectionAddress);
+    private List<String> connectionStringCorruptedSet = List.of();
+
+    private String discoveryValidParamTrue = "discovery=true";
+    private String discoveryValidParamFalse = "discovery=false";
+    private String discoveryCorruptedParamWrongValueNum = "discovery=1";
+    private String discoveryCorruptedParamWronName = "diskovery=false";
+
+    List<String> discoveryParamValidSet = List.of(discoveryValidParamTrue, discoveryValidParamFalse);
+    List<String> discoveryParamCorruptedSet = List.of(discoveryCorruptedParamWrongValueNum, discoveryCorruptedParamWronName);
+
+    private static ExampleServer exampleServer;
+
+
+
+    @BeforeAll
+    public static void setup() {
+        try {
+            exampleServer = new ExampleServer();
+            exampleServer.startup().get();
+        } catch (Exception e) {
+
+        }
+    }
+
+    @AfterAll
+    public static void tearDown() {
+        try {
+            exampleServer.shutdown().get();
+        } catch (Exception e) {
+
+        }
+    }
+
+    @Test
+    public void connectionNoParams(){
+
+        connectionStringValidSet.forEach(connectionAddress -> {
+                String connectionString = connectionAddress;
+                try {
+                    PlcConnection opcuaConnection = new PlcDriverManager().getConnection(connectionString);
+                    assert opcuaConnection.isConnected();
+                    opcuaConnection.close();
+                    assert !opcuaConnection.isConnected();
+                } catch (PlcConnectionException e) {
+                    fail("Exception during connectionNoParams while connecting Test EXCEPTION: " + e.getMessage());
+                } catch (Exception e) {
+                    fail("Exception during connectionNoParams while closing Test EXCEPTION: " + e.getMessage());
+                }
+
+        });
+
+    }
+
+    @Test
+    public void connectionWithDiscoveryParam(){
+        connectionStringValidSet.forEach(connectionAddress -> {
+            discoveryParamValidSet.forEach(discoveryParam -> {
+                String connectionString = connectionAddress + paramSectionDivider + discoveryParam;
+                try {
+                    PlcConnection opcuaConnection = new PlcDriverManager().getConnection(connectionString);
+                    assert opcuaConnection.isConnected();
+                    opcuaConnection.close();
+                    assert !opcuaConnection.isConnected();
+                } catch (PlcConnectionException e) {
+                    fail("Exception during connectionWithDiscoveryParam while connecting Test EXCEPTION: " + e.getMessage());
+                } catch (Exception e) {
+                    fail("Exception during connectionWithDiscoveryParam while closing Test EXCEPTION: " + e.getMessage());
+                }
+            });
+        });
+
+
+    }
+
+    @Test
+    public void readVariables() throws Exception {
+        PlcConnection opcuaConnection = new PlcDriverManager().getConnection(tcpConnectionAddress);
+        assert opcuaConnection.isConnected();
+
+        PlcReadRequest.Builder builder = opcuaConnection.readRequestBuilder();
+        builder.addItem("Bool", BOOL_IDENTIFIER_READ_WRITE);
+        builder.addItem("Byte", BYTE_IDENTIFIER_READ_WRITE);
+        builder.addItem("Double", DOUBLE_IDENTIFIER_READ_WRITE);
+        builder.addItem("Float", FLOAT_IDENTIFIER_READ_WRITE);
+        builder.addItem("Int16", INT16_IDENTIFIER_READ_WRITE);
+        builder.addItem("Int32", INT32_IDENTIFIER_READ_WRITE);
+        builder.addItem("Int64", INT64_IDENTIFIER_READ_WRITE);
+        builder.addItem("Integer", INTEGER_IDENTIFIER_READ_WRITE);
+        builder.addItem("SByte", SBYTE_IDENTIFIER_READ_WRITE);
+        builder.addItem("String", STRING_IDENTIFIER_READ_WRITE);
+        builder.addItem("UInt16", UINT16_IDENTIFIER_READ_WRITE);
+        builder.addItem("UInt32", UINT32_IDENTIFIER_READ_WRITE);
+        builder.addItem("UInt64", UINT64_IDENTIFIER_READ_WRITE);
+        builder.addItem("UInteger", UINTEGER_IDENTIFIER_READ_WRITE);
+
+        builder.addItem("BoolArray", BOOL_ARRAY_IDENTIFIER);
+        builder.addItem("ByteStringArray", BYTE_STRING_ARRAY_IDENTIFIER);
+        builder.addItem("ByteArray", BYTE_ARRAY_IDENTIFIER);
+        builder.addItem("DoubleArray", DOUBLE_ARRAY_IDENTIFIER);
+        builder.addItem("FloatArray", FLOAT_ARRAY_IDENTIFIER);
+        builder.addItem("Int16Array", INT16_ARRAY_IDENTIFIER);
+        builder.addItem("Int32Array", INT32_ARRAY_IDENTIFIER);
+        builder.addItem("Int64Array", INT64_ARRAY_IDENTIFIER);
+        builder.addItem("SByteArray", SBYTE_ARRAY_IDENTIFIER);
+        builder.addItem("StringArray", STRING_ARRAY_IDENTIFIER);
+        builder.addItem("UInt16Array", UINT16_ARRAY_IDENTIFIER);
+        builder.addItem("UInt32Array", UINT32_ARRAY_IDENTIFIER);
+        builder.addItem("UInt64Array", UINT64_ARRAY_IDENTIFIER);
+
+        builder.addItem("DoesNotExists", DOES_NOT_EXIST_IDENTIFIER_READ_WRITE);
+
+        PlcReadRequest request = builder.build();
+        PlcReadResponse response = request.execute().get();
+        assert response.getResponseCode("Bool").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("Byte").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("Double").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("Float").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("Int16").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("Int32").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("Int64").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("Integer").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("SByte").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("String").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("UInt16").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("UInt32").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("UInt64").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("UInteger").equals(PlcResponseCode.OK);
+
+        assert response.getResponseCode("BoolArray").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("ByteArray").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("DoubleArray").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("FloatArray").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("Int16Array").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("Int32Array").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("Int64Array").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("SByteArray").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("StringArray").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("UInt16Array").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("UInt32Array").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("UInt64Array").equals(PlcResponseCode.OK);
+
+        assert response.getResponseCode("DoesNotExists").equals(PlcResponseCode.NOT_FOUND);
+
+        opcuaConnection.close();
+        assert !opcuaConnection.isConnected();
+    }
+
+    @Test
+    public void testOpcuaAddressPattern() {
+
+        assertMatching(INET_ADDRESS_PATTERN, ":tcp://localhost");
+        assertMatching(INET_ADDRESS_PATTERN, ":tcp://localhost:3131");
+        assertMatching(INET_ADDRESS_PATTERN, ":tcp://www.google.de");
+        assertMatching(INET_ADDRESS_PATTERN, ":tcp://www.google.de:443");
+        assertMatching(INET_ADDRESS_PATTERN, ":tcp://127.0.0.1");
+        assertMatching(INET_ADDRESS_PATTERN, ":tcp://127.0.0.1:251");
+        assertMatching(INET_ADDRESS_PATTERN, ":tcp://254.254.254.254:1337");
+        assertMatching(INET_ADDRESS_PATTERN, ":tcp://254.254.254.254");
+
+
+        assertMatching(URI_PATTERN, "opcua:tcp://localhost");
+        assertMatching(URI_PATTERN, "opcua:tcp://localhost:3131");
+        assertMatching(URI_PATTERN, "opcua:tcp://www.google.de");
+        assertMatching(URI_PATTERN, "opcua:tcp://www.google.de:443");
+        assertMatching(URI_PATTERN, "opcua:tcp://127.0.0.1");
+        assertMatching(URI_PATTERN, "opcua:tcp://127.0.0.1:251");
+        assertMatching(URI_PATTERN, "opcua:tcp://254.254.254.254:1337");
+        assertMatching(URI_PATTERN, "opcua:tcp://254.254.254.254");
+
+        assertMatching(URI_PATTERN, "opcua:tcp://127.0.0.1&discovery=false");
+        assertMatching(URI_PATTERN, "opcua:tcp://opcua.demo-this.com:51210/UA/SampleServer?discovery=false");
+
+    }
+
+}
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/UtilsTest.java
similarity index 56%
copy from plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java
copy to plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/UtilsTest.java
index d575bde..a84f308 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/UtilsTest.java
@@ -6,9 +6,9 @@
  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
- 
+
+     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
@@ -16,23 +16,25 @@
  specific language governing permissions and limitations
  under the License.
 */
-package org.apache.plc4x.java.opcua.connection;
+package org.apache.plc4x.java.opcua;
 
-import java.net.InetAddress;
-import java.util.Objects;
 
-/**
- */
-public class OpcuaConnectionFactory {
+import java.util.regex.Pattern;
 
-    public OpcuaTcpPlcConnection opcuaTcpPlcConnectionOf(InetAddress address, Integer port, String params, int requestTimeout) {
-        Objects.requireNonNull(address);
+import static org.junit.jupiter.api.Assertions.fail;
 
-        if (port == null) {
-            return OpcuaTcpPlcConnection.of(address, params, requestTimeout);
-        } else {
-            return OpcuaTcpPlcConnection.of(address, port, params, requestTimeout);
+/**
+ */
+public class UtilsTest {
+    public static void assertMatching(Pattern pattern, String match) {
+        if (!pattern.matcher(match).matches()) {
+            fail(pattern + "doesn't match " + match);
         }
     }
 
+    public static void assertNoMatching(Pattern pattern, String match) {
+        if (pattern.matcher(match).matches()) {
+            fail(pattern + "does match " + match + " but should not");
+        }
+    }
 }
diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnectionTest.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnectionTest.java
new file mode 100644
index 0000000..5edfd98
--- /dev/null
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnectionTest.java
@@ -0,0 +1,81 @@
+/*
+ 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.opcua.connection;
+
+import static org.apache.plc4x.java.opcua.OpcuaPlcDriver.URI_PATTERN;
+import static org.apache.plc4x.java.opcua.UtilsTest.assertMatching;
+import static org.apache.plc4x.java.opcua.UtilsTest.assertNoMatching;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ */
+public class OpcuaTcpPlcConnectionTest {
+
+    private final String[] validTCPOPC = {
+        "localhost",        
+        "127.0.0.1",
+        "254.254.254.254"
+    };
+    private final int[] validPorts = {
+        1337,
+        42,
+        1,
+        24152
+    };
+    private final String[] nDiscoveryParams = {
+        "discovery=false"
+    };
+
+    @BeforeEach
+    public void before() {
+    }
+
+    @AfterEach
+    public void after() {
+
+    }
+
+    @Test
+    public void testConectionStringPattern() {
+
+        for (String address : validTCPOPC) {
+            assertMatching(URI_PATTERN, "opcua:tcp://127.0.0.1:555?discovery=true");
+            assertMatching(URI_PATTERN, "opcua:tcp://127.0.0.1:555?discovery=True");
+            assertMatching(URI_PATTERN, "opcua:tcp://127.0.0.1:555?discovery=TRUE");
+            assertMatching(URI_PATTERN, "opcua:tcp://127.0.0.1:555?Discovery=True");
+            //No Port Specified
+            assertMatching(URI_PATTERN, "opcua:tcp://127.0.0.1?discovery=True");
+            //No Transport Specified
+            assertMatching(URI_PATTERN, "opcua://127.0.0.1:647?discovery=True");
+            //No Params Specified
+            assertMatching(URI_PATTERN, "opcua:tcp://127.0.0.1:111");
+            //No Transport and Params Specified
+            assertMatching(URI_PATTERN, "opcua://127.0.0.1:754");
+        }
+    }
+}
diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaFieldTest.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaFieldTest.java
new file mode 100644
index 0000000..7d2d631
--- /dev/null
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaFieldTest.java
@@ -0,0 +1,76 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+package org.apache.plc4x.java.opcua.protocol;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.plc4x.java.opcua.UtilsTest.assertMatching;
+import static org.apache.plc4x.java.opcua.UtilsTest.assertNoMatching;
+import static org.apache.plc4x.java.opcua.protocol.OpcuaField.ADDRESS_PATTERN;
+
+/**
+ */
+public class OpcuaFieldTest {
+
+    @BeforeEach
+    public void before() {
+    }
+
+    @AfterEach
+    public void after() {
+
+    }
+
+    @Test
+    public void testOpcuaAddressPattern() {
+
+        //standard integer based param
+        assertMatching(ADDRESS_PATTERN, "ns=2;i=10846");
+        //string based address values
+        assertMatching(ADDRESS_PATTERN, "ns=2;s=test.variable.name.inspect");
+        assertMatching(ADDRESS_PATTERN, "ns=2;s=key param with some spaces");
+        assertMatching(ADDRESS_PATTERN, "ns=2;s=\"aweired\".\"siemens\".\"param\".\"submodule\".\"param");
+        assertMatching(ADDRESS_PATTERN, "ns=2;s=Weee314Waannaaa\\somenice=ext=a234a*#+1455!§$%&/()tttraaaaSymbols-.,,");
+        // GUID address tests
+        assertMatching(ADDRESS_PATTERN, "ns=2;g=09087e75-8e5e-499b-954f-f2a8624db28a");
+        // binary encoded addresses
+        assertMatching(ADDRESS_PATTERN, "ns=2;b=asvaewavarahreb==");
+
+    }
+
+    @Test
+    public void testOpcuaAddressDataTypePattern() {
+
+        //standard integer based param
+        assertMatching(ADDRESS_PATTERN, "ns=2;i=10846:BOOL");
+        //string based address values
+        assertMatching(ADDRESS_PATTERN, "ns=2;s=test.variable.name.inspect:DINT");
+        assertMatching(ADDRESS_PATTERN, "ns=2;s=key param with some spaces:ULINT");
+        assertMatching(ADDRESS_PATTERN, "ns=2;s=\"aweired\".\"siemens\".\"param\".\"submodule\".\"param:LREAL");
+        //REGEX Valid, additional checks need to be done later
+        assertMatching(ADDRESS_PATTERN, "ns=2;s=Weee314Waannaaa\\somenice=ext=a234a*#+1455!§$%&/()tttraaaaSymbols-.,,:JIBBERISH");
+        // GUID address tests
+        assertNoMatching(ADDRESS_PATTERN, "ns=2;g=09087e75-8e5e-499b-954f-f2a8624db28a:*&#%^*$(*)");
+        // binary encoded addresses
+        assertNoMatching(ADDRESS_PATTERN, "ns=2;b=asvae:wavarahreb==");
+
+    }
+}
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaPlcFieldHandlerTest.java
similarity index 59%
copy from plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java
copy to plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaPlcFieldHandlerTest.java
index d575bde..8eb2c8c 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaPlcFieldHandlerTest.java
@@ -6,9 +6,9 @@
  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
@@ -16,23 +16,23 @@
  specific language governing permissions and limitations
  under the License.
 */
-package org.apache.plc4x.java.opcua.connection;
+package org.apache.plc4x.java.opcua.protocol;
 
-import java.net.InetAddress;
-import java.util.Objects;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 
 /**
  */
-public class OpcuaConnectionFactory {
+public class OpcuaPlcFieldHandlerTest {
 
-    public OpcuaTcpPlcConnection opcuaTcpPlcConnectionOf(InetAddress address, Integer port, String params, int requestTimeout) {
-        Objects.requireNonNull(address);
 
-        if (port == null) {
-            return OpcuaTcpPlcConnection.of(address, params, requestTimeout);
-        } else {
-            return OpcuaTcpPlcConnection.of(address, port, params, requestTimeout);
-        }
+    @BeforeEach
+    public void before() {
     }
 
+    @AfterEach
+    public void after() {
+
+    }
 }
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java
similarity index 59%
rename from plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java
rename to plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java
index d575bde..26e8f3e 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java
@@ -6,9 +6,9 @@
  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
@@ -16,23 +16,20 @@
  specific language governing permissions and limitations
  under the License.
 */
-package org.apache.plc4x.java.opcua.connection;
+package org.apache.plc4x.java.opcua.protocol;
 
-import java.net.InetAddress;
-import java.util.Objects;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 
 /**
  */
-public class OpcuaConnectionFactory {
+public class OpcuaSubscriptionHandleTest {
+    @BeforeEach
+    public void before() {
+    }
 
-    public OpcuaTcpPlcConnection opcuaTcpPlcConnectionOf(InetAddress address, Integer port, String params, int requestTimeout) {
-        Objects.requireNonNull(address);
+    @AfterEach
+    public void after() {
 
-        if (port == null) {
-            return OpcuaTcpPlcConnection.of(address, params, requestTimeout);
-        } else {
-            return OpcuaTcpPlcConnection.of(address, port, params, requestTimeout);
-        }
     }
-
 }
diff --git a/protocols/opcua/src/main/resources/protocols/opcua/opcua.mspec b/protocols/opcua/src/main/resources/protocols/opcua/opcua.mspec
index e2c8743..ffd7ca2 100644
--- a/protocols/opcua/src/main/resources/protocols/opcua/opcua.mspec
+++ b/protocols/opcua/src/main/resources/protocols/opcua/opcua.mspec
@@ -80,6 +80,13 @@
            [simple          OpcuaMessage       'message']
        ]
        ['CLO','false'     OpcuaCloseRequest
+           [simple          string '8'         'chunk']
+           [implicit        int 32             'messageSize' 'lengthInBytes']
+           [simple          int 32             'secureChannelId']
+           [simple          int 32             'secureTokenId']
+           [simple          int 32             'sequenceNumber']
+           [simple          int 32             'requestId']
+           [simple          OpcuaMessage       'message']
        ]
        ['CLO','true'     OpcuaCloseResponse
        ]