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/14 11:19:53 UTC

[plc4x] branch feature/native_opua_client updated: Add support for wrtiing.

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


The following commit(s) were added to refs/heads/feature/native_opua_client by this push:
     new 2893f3d  Add support for wrtiing.
2893f3d is described below

commit 2893f3d91340e9861b0b4af9d81e8034ba21e49b
Author: hutcheb <be...@gmail.com>
AuthorDate: Thu Jan 14 06:18:40 2021 -0500

    Add support for wrtiing.
    
    Initial support for UsernameIdentityToken, stuck on encrypting the password :(
---
 .../apache/plc4x/java/opcua/OpcuaPlcDriver.java    |  19 +-
 .../java/opcua/config/OpcuaConfiguration.java      | 104 +++++-
 .../java/opcua/protocol/OpcuaProtocolLogic.java    | 382 ++++++++++++++++++---
 .../plc4x/java/opcua/OpcuaPlcDriverTest.java       |  82 ++++-
 .../apache/plc4x/java/spi/Plc4xNettyWrapper.java   |   2 -
 .../plc4x/java/spi/generation/ReadBuffer.java      |  16 -
 .../plc4x/java/spi/generation/WriteBuffer.java     |  12 -
 .../spi/internal/DefaultExpectRequestContext.java  |   1 -
 protocols/opcua/src/main/xslt/opc-types.xsl        |  16 +-
 9 files changed, 532 insertions(+), 102 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 a005cb3..b100a00 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
@@ -26,6 +26,7 @@ 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.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;
@@ -63,8 +64,8 @@ public class OpcuaPlcDriver extends GeneratedDriverBase<OpcuaAPU> {
 
     public static final Pattern URI_PATTERN = Pattern.compile("^(?<protocolCode>opcua)" +
                                                                     INET_ADDRESS_PATTERN +
-                                                                    "(?<transportEndpoint>[\\w/=]+)" +
-                                                                    "(?<paramString>[(\\?|\\&)\\w=]+\\=[\\w&]+)*"
+                                                                    "(?<transportEndpoint>[\\w/=]+)[\\?]?" +
+                                                                    "(?<paramString>[\\&\\w=]+\\=[\\w&]+)*"
                                                                 );
 
 
@@ -163,15 +164,15 @@ public class OpcuaPlcDriver extends GeneratedDriverBase<OpcuaAPU> {
         }
 
         // Create the configuration object.
-        OpcuaConfiguration configuration = new OpcuaConfiguration(  transportCode,
-                                                                    transportHost,
-                                                                    transportPort,
-                                                                    transportEndpoint,
-                                                                    paramString);
-
+        OpcuaConfiguration configuration = (OpcuaConfiguration) new ConfigurationFactory().createConfiguration(
+            getConfigurationType(), paramString);
         if(configuration == null) {
             throw new PlcConnectionException("Unsupported configuration");
         }
+        configuration.setTransportCode(transportCode);
+        configuration.setHost(transportHost);
+        configuration.setPort(transportPort);
+        configuration.setEndpoint("opc." + transportCode + "://" + transportHost + ":" + transportPort + "" + transportEndpoint);
 
         // Try to find a transport in order to create a communication channel.
         Transport transport = null;
@@ -226,9 +227,7 @@ public class OpcuaPlcDriver extends GeneratedDriverBase<OpcuaAPU> {
     public static class ByteLengthEstimator implements ToIntFunction<ByteBuf> {
         @Override
         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/config/OpcuaConfiguration.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/config/OpcuaConfiguration.java
index 99f43a3..94ae1d5 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/config/OpcuaConfiguration.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/config/OpcuaConfiguration.java
@@ -21,23 +21,83 @@ package org.apache.plc4x.java.opcua.config;
 
 import org.apache.plc4x.java.spi.configuration.Configuration;
 import org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter;
+import org.apache.plc4x.java.spi.configuration.annotations.defaults.BooleanDefaultValue;
 import org.apache.plc4x.java.spi.configuration.annotations.defaults.IntDefaultValue;
 import org.apache.plc4x.java.transport.tcp.TcpTransportConfiguration;
 
 public class OpcuaConfiguration implements Configuration, TcpTransportConfiguration {
 
-    private final String code;
-    private final String host;
-    private final String port;
-    private final String endpoint;
-    private final String params;
+    private String code;
+    private String host;
+    private String port;
+    private String endpoint;
+    private String params;
 
-    public OpcuaConfiguration(String transportCode, String transportHost, String transportPort, String transportEndpoint, String paramString) {
-        this.code = transportCode;
-        this.host = transportHost;
-        this.port = transportPort;
-        this.endpoint = "opc." + transportCode + "://" + transportHost + ":" + transportPort + "" + transportEndpoint;
-        this.params = paramString;
+    @ConfigurationParameter("discovery")
+    @BooleanDefaultValue(true)
+    private boolean discovery;
+
+    @ConfigurationParameter("username")
+    private String username;
+
+    @ConfigurationParameter("password")
+    private String password;
+
+    @ConfigurationParameter("certFile")
+    private String certFile;
+
+    @ConfigurationParameter("securityPolicy")
+    private String securityPolicy;
+
+    @ConfigurationParameter("keyStoreFile")
+    private String keyStoreFile;
+
+    public boolean isDiscovery() {
+        return discovery;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public String getCertFile() {
+        return certFile;
+    }
+
+    public String getSecurityPolicy() {
+        return securityPolicy;
+    }
+
+    public String getKeyStoreFile() {
+        return keyStoreFile;
+    }
+
+    public void setDiscovery(boolean discovery) {
+        this.discovery = discovery;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public void setCertFile(String certFile) {
+        this.certFile = certFile;
+    }
+
+    public void setSecurityPolicy(String securityPolicy) {
+        this.securityPolicy = securityPolicy;
+    }
+
+    public void setKeyStoreFile(String keyStoreFile) {
+        this.keyStoreFile = keyStoreFile;
     }
 
     public String getTransportCode() {
@@ -56,15 +116,24 @@ public class OpcuaConfiguration implements Configuration, TcpTransportConfigurat
         return endpoint;
     }
 
-    public String getParams() {
-        return params;
+    public void setTransportCode(String code) {
+        this.code = code;
     }
-        
-    @Override
-    public int getDefaultPort() {
-        return 12687;
+
+    public void setHost(String host) {
+        this.host = host;
     }
 
+    public void setPort(String port) {
+        this.port = port;
+    }
+
+    public void setEndpoint(String endpoint) {
+        this.endpoint = endpoint;
+    }
+
+
+
     @Override
     public String toString() {
         return "Configuration{" +
@@ -72,3 +141,4 @@ public class OpcuaConfiguration implements Configuration, TcpTransportConfigurat
     }
 
 }
+
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 06896f6..a8d0972 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
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.RandomUtils;
 import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.api.authentication.PlcAuthentication;
 import org.apache.plc4x.java.api.exceptions.*;
@@ -61,7 +62,11 @@ import org.apache.plc4x.java.spi.values.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
 import java.time.*;
 import java.math.BigInteger;
 import java.util.*;
@@ -75,6 +80,15 @@ import java.util.regex.Matcher;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 import java.nio.charset.StandardCharsets;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import java.security.*;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
 
 
 /**
@@ -95,9 +109,11 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     private static final int DEFAULT_RECEIVE_BUFFER_SIZE = 65535;
     private static final int DEFAULT_SEND_BUFFER_SIZE = 65535;
     private static final int VERSION = 0;
+    private static final String PASSWORD_ENCRYPTION_ALGORITHM = "http://www.w3.org/2001/04/xmlenc#rsa-oaep";
 
     private NodeId authenticationToken = new NodeIdTwoByte(NodeIdType.nodeIdTypeTwoByte, new TwoByteNodeId((short) 0));
     private static final PascalString NULL_STRING = new PascalString(-1,null);
+    private static final PascalByteString NULL_BYTE_STRING = new PascalByteString(-1, new byte[0]);
     private static ExpandedNodeId NULL_EXPANDED_NODEID = new ExpandedNodeIdTwoByte(false,
                                                                                     false,
                                                                                     null,
@@ -111,25 +127,42 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
 
     private static final String CHUNK = "F";
     private static final String nameSpaceSecurityPolicyNone = "http://opcfoundation.org/UA/SecurityPolicy#None";
+    private static final String nameSpaceSecurityPolicyUserName = "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256";
     private static final String applicationUri = "urn:apache:plc4x:client";
     private static final String productUri = "urn:apache:plc4x:client";
     private static final String applicationText = "OPCUA client for the Apache PLC4X:PLC4J project";
 
+
     private final String sessionName = "UaSession:" + applicationText + ":" + RandomStringUtils.random(20, true, true);
-    private final String clientNonce = RandomStringUtils.random(40, true, true);
+    private final byte[] clientNonce = RandomUtils.nextBytes(40);
     private RequestTransactionManager tm;
 
     private String endpoint;
+    private boolean discovery;
+    private String username;
+    private String password;
+    private String certFile;
+    private String securityPolicy;
+    private String keyStoreFile;
     private AtomicInteger transactionIdentifierGenerator = new AtomicInteger(1);
     private AtomicInteger requestHandleGenerator = new AtomicInteger(1);
     private AtomicInteger tokenId = new AtomicInteger(1);
     private AtomicInteger channelId = new AtomicInteger(1);
+    private byte[] senderCertificate = null;
+    private String certificateThumbprint = null;
 
     private AtomicBoolean securedConnection = new AtomicBoolean(false);
 
     @Override
     public void setConfiguration(OpcuaConfiguration configuration) {
         this.endpoint = configuration.getEndpoint();
+        this.discovery = configuration.isDiscovery();
+        this.username = configuration.getUsername();
+        this.password = configuration.getPassword();
+        this.certFile = configuration.getCertFile();
+        this.securityPolicy = configuration.getSecurityPolicy();
+        this.keyStoreFile = configuration.getKeyStoreFile();
+
         this.tm = new RequestTransactionManager(1);
     }
 
@@ -279,7 +312,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             VERSION,
             SecurityTokenRequestType.securityTokenRequestTypeIssue,
             MessageSecurityMode.messageSecurityModeNone,
-            NULL_STRING,
+            NULL_BYTE_STRING,
             DEFAULT_CONNECTION_LIFETIME);
 
         OpcuaOpenRequest openRequest = new OpcuaOpenRequest(CHUNK,
@@ -309,10 +342,13 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     }
 
     public void onConnectCreateSessionRequest(ConversationContext<OpcuaAPU> context, OpcuaOpenResponse opcuaOpenResponse) throws PlcConnectionException {
+
+        certificateThumbprint = opcuaOpenResponse.getReceiverCertificateThumbprint();
         OpenSecureChannelResponse openSecureChannelResponse = (OpenSecureChannelResponse) opcuaOpenResponse.getMessage();
         tokenId.set((int) openSecureChannelResponse.getSecurityToken().getTokenId());
         channelId.set((int) openSecureChannelResponse.getSecurityToken().getChannelId());
 
+
         int transactionId = getTransactionIdentifier(securedConnection.get());
 
         Integer nextSequenceNumber = opcuaOpenResponse.getSequenceNumber() + 1;
@@ -358,8 +394,8 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             NULL_STRING,
             new PascalString(endpoint.length(), endpoint),
             new PascalString(sessionName.length(), sessionName),
-            new PascalString(clientNonce.length(), clientNonce),
-            NULL_STRING,
+            new PascalByteString(clientNonce.length, clientNonce),
+            NULL_BYTE_STRING,
             120000L,
             0L);
 
@@ -387,7 +423,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     private void onConnectActivateSessionRequest(ConversationContext<OpcuaAPU> context, OpcuaMessageResponse opcuaMessageResponse) throws PlcConnectionException {
 
         CreateSessionResponse createSessionResponse = (CreateSessionResponse) opcuaMessageResponse.getMessage();
-
+        senderCertificate = createSessionResponse.getServerCertificate().getStringValue();
         authenticationToken = (NodeIdByteString) createSessionResponse.getAuthenticationToken();
         tokenId.set((int) opcuaMessageResponse.getSecureTokenId());
         channelId.set((int) opcuaMessageResponse.getSecureChannelId());
@@ -398,7 +434,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         Integer nextRequestId = opcuaMessageResponse.getRequestId() + 1;
 
         if (!(transactionId == nextSequenceNumber)) {
-            LOGGER.error("Sequence number isn't as expected, we might have missed a packet. - " +  transactionId + " != " + nextSequenceNumber);
+            LOGGER.error("Sequence number isn't as expected, we might have missed a packet. - {} != {}" , transactionId, nextSequenceNumber);
             throw new PlcConnectionException("Sequence number isn't as expected, we might have missed a packet. - " +  transactionId + " != " + nextSequenceNumber);
         }
 
@@ -412,29 +448,54 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             REQUEST_TIMEOUT_LONG,
             NULL_EXTENSION_OBJECT);
 
-        SignatureData clientSignature = new SignatureData(NULL_STRING, NULL_STRING);
+        SignatureData clientSignature = new SignatureData(NULL_STRING, NULL_BYTE_STRING);
 
         SignedSoftwareCertificate[] signedSoftwareCertificate = new SignedSoftwareCertificate[1];
 
-        signedSoftwareCertificate[0] = new SignedSoftwareCertificate(NULL_STRING, NULL_STRING);
-
-        //Manually serialize this object
-        PascalString anonymousIdentityToken = new PascalString("anonymous".length(), "anonymous");
-        WriteBuffer buffer = new WriteBuffer(anonymousIdentityToken.getLengthInBytes(), true);
-        try{
-            PascalStringIO.staticSerialize(buffer, anonymousIdentityToken);
-        } catch (ParseException e) {
-            LOGGER.error("Failed to serialize the user identity token - " + anonymousIdentityToken.getStringValue());
-            throw new PlcConnectionException("Failed to serialize the user identity token - " + anonymousIdentityToken.getStringValue());
-        }
+        signedSoftwareCertificate[0] = new SignedSoftwareCertificate(NULL_BYTE_STRING, NULL_BYTE_STRING);
 
-        ExpandedNodeId extExpandedNodeId4 = new ExpandedNodeIdFourByte(false,
-            false,
-            null,
-            null,
-            new FourByteNodeId((short) 0,  321));
 
-        ExtensionObject userIdentityToken = new ExtensionObject(extExpandedNodeId4, (short) 1, buffer.getData().length, buffer.getData());
+        ExpandedNodeId extExpandedNodeId = null;
+        ExtensionObject userIdentityToken = null;
+        if (this.username == null) {
+            //Manually serialize this object
+            PascalString anonymousIdentityToken = new PascalString("anonymous".length(), "anonymous");
+            WriteBuffer buffer = new WriteBuffer(anonymousIdentityToken.getLengthInBytes(), true);
+            try{
+                PascalStringIO.staticSerialize(buffer, anonymousIdentityToken);
+            } catch (ParseException e) {
+                LOGGER.error("Failed to serialize the user identity token - {}", anonymousIdentityToken.getStringValue());
+                throw new PlcConnectionException("Failed to serialize the user identity token - " + anonymousIdentityToken.getStringValue());
+            }
+            extExpandedNodeId = new ExpandedNodeIdFourByte(false,
+                false,
+                null,
+                null,
+                new FourByteNodeId((short) 0,  OpcuaNodeIdServices.AnonymousIdentityToken_Encoding_DefaultBinary.getValue()));
+            userIdentityToken = new ExtensionObject(extExpandedNodeId, (short) 1, buffer.getData().length, buffer.getData());
+        } else {
+            //Manually serialize this object
+            byte[] encryptedPassword = encrypt(this.password, senderCertificate);
+            UserNameIdentityToken userNameIdentityToken = new UserNameIdentityToken(
+                new PascalString("username".length(), "username"),
+                new PascalString(this.username.length(), this.username),
+                new PascalByteString(encryptedPassword.length, encryptedPassword),
+                new PascalString(PASSWORD_ENCRYPTION_ALGORITHM.length(), PASSWORD_ENCRYPTION_ALGORITHM)
+            );
+            WriteBuffer buffer = new WriteBuffer(userNameIdentityToken.getLengthInBytes(), true);
+            try{
+                UserNameIdentityTokenIO.staticSerialize(buffer, userNameIdentityToken);
+            } catch (ParseException e) {
+                LOGGER.error("Failed to serialize the user identity token - {}", userNameIdentityToken);
+                throw new PlcConnectionException("Failed to serialize the user identity token - " + userNameIdentityToken);
+            }
+            extExpandedNodeId = new ExpandedNodeIdFourByte(false,
+                false,
+                null,
+                null,
+                new FourByteNodeId((short) 0,  OpcuaNodeIdServices.UserNameIdentityToken_Encoding_DefaultBinary.getValue()));
+            userIdentityToken = new ExtensionObject(extExpandedNodeId, (short) 1, buffer.getData().length, buffer.getData());
+        }
 
         ActivateSessionRequest activateSessionRequest = new ActivateSessionRequest((byte) 1,
             (byte) 0,
@@ -465,7 +526,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
 
                 long returnedRequestHandle = activateMessageResponse.getResponseHeader().getRequestHandle();
                 if (!(requestHandle == returnedRequestHandle)) {
-                    LOGGER.error("Request handle isn't as expected, we might have missed a packet. - " +  requestHandle + " != " + returnedRequestHandle);
+                    LOGGER.error("Request handle isn't as expected, we might have missed a packet. {} != {}" ,requestHandle,  returnedRequestHandle);
                 }
 
                 // Send an event that connection setup is complete.
@@ -561,7 +622,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             PlcValue value = null;
             if (results[count].getStatusCode() == null) {
                 Variant variant = results[count].getValue();
-                LOGGER.info("Repsponse includes Variant of type " + variant.getClass().toString());
+                LOGGER.info("Response of type {}", variant.getClass().toString());
                 if (variant instanceof VariantBoolean) {
                     byte[] array = ((VariantBoolean) variant).getValue();
                     int length = array.length;
@@ -761,14 +822,215 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         return response;
     }
 
-    private Variant fromPlcValue(OpcuaField field, PlcWriteRequest request) {
-        return new VariantInt32(false,
-            null,
-            null,
-            request.getFieldNames().size(),
-            new int[] {request.getPlcValue("value-0").getInt()});
+    private Variant fromPlcValue(String fieldName, OpcuaField field, PlcWriteRequest request) {
+
+        PlcList valueObject;
+        if (request.getPlcValue(fieldName).getObject() instanceof ArrayList) {
+            valueObject = (PlcList) request.getPlcValue(fieldName);
+        } else {
+            ArrayList<PlcValue> list = new ArrayList<>();
+            list.add(request.getPlcValue(fieldName));
+            valueObject = new PlcList(list);
+        }
+
+        List<PlcValue> plcValueList = valueObject.getList();
+        String dataType = field.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";
+            }
+        }
+        int length = valueObject.getLength();
+        switch (dataType) {
+            case "IEC61131_BOOL":
+            case "IEC61131_BIT":
+                byte[] tmpBOOL = new byte[length];
+                for (int i = 0; i < length; i++) {
+                    tmpBOOL[i] = valueObject.getIndex(i).getByte();
+                }
+                return new VariantBoolean(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpBOOL);
+            case "IEC61131_BYTE":
+            case "IEC61131_BITARR8":
+            case "IEC61131_USINT":
+            case "IEC61131_UINT8":
+            case "IEC61131_BIT8":
+                short[] tmpBYTE = new short[length];
+                for (int i = 0; i < length; i++) {
+                    tmpBYTE[i] = valueObject.getIndex(i).getByte();
+                }
+                return new VariantByte(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpBYTE);
+            case "IEC61131_SINT":
+            case "IEC61131_INT8":
+                byte[] tmpSINT = new byte[length];
+                for (int i = 0; i < length; i++) {
+                    tmpSINT[i] = valueObject.getIndex(i).getByte();
+                }
+                return new VariantSByte(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpSINT);
+            case "IEC61131_INT":
+            case "IEC61131_INT16":
+                short[] tmpINT16 = new short[length];
+                for (int i = 0; i < length; i++) {
+                    tmpINT16[i] = valueObject.getIndex(i).getShort();
+                }
+                return new VariantInt16(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpINT16);
+            case "IEC61131_UINT":
+            case "IEC61131_UINT16":
+            case "IEC61131_WORD":
+            case "IEC61131_BITARR16":
+                int[] tmpUINT = new int[length];
+                for (int i = 0; i < length; i++) {
+                    tmpUINT[i] = valueObject.getIndex(i).getInt();
+                }
+                return new VariantUInt16(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpUINT);
+            case "IEC61131_DINT":
+            case "IEC61131_INT32":
+                int[] tmpDINT = new int[length];
+                for (int i = 0; i < length; i++) {
+                    tmpDINT[i] = valueObject.getIndex(i).getInt();
+                }
+                return new VariantInt32(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpDINT);
+            case "IEC61131_UDINT":
+            case "IEC61131_UINT32":
+            case "IEC61131_DWORD":
+            case "IEC61131_BITARR32":
+                long[] tmpUDINT = new long[length];
+                for (int i = 0; i < length; i++) {
+                    tmpUDINT[i] = valueObject.getIndex(i).getLong();
+                }
+                return new VariantUInt32(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpUDINT);
+            case "IEC61131_LINT":
+            case "IEC61131_INT64":
+                long[] tmpLINT = new long[length];
+                for (int i = 0; i < length; i++) {
+                    tmpLINT[i] = valueObject.getIndex(i).getLong();
+                }
+                return new VariantInt64(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpLINT);
+            case "IEC61131_ULINT":
+            case "IEC61131_UINT64":
+            case "IEC61131_LWORD":
+            case "IEC61131_BITARR64":
+                BigInteger[] tmpULINT = new BigInteger[length];
+                for (int i = 0; i < length; i++) {
+                    tmpULINT[i] = valueObject.getIndex(i).getBigInteger();
+                }
+                return new VariantUInt64(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpULINT);
+            case "IEC61131_REAL":
+            case "IEC61131_FLOAT":
+                float[] tmpREAL = new float[length];
+                for (int i = 0; i < length; i++) {
+                    tmpREAL[i] = valueObject.getIndex(i).getFloat();
+                }
+                return new VariantFloat(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpREAL);
+            case "IEC61131_LREAL":
+            case "IEC61131_DOUBLE":
+                double[] tmpLREAL = new double[length];
+                for (int i = 0; i < length; i++) {
+                    tmpLREAL[i] = valueObject.getIndex(i).getDouble();
+                }
+                return new VariantDouble(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpLREAL);
+            case "IEC61131_CHAR":
+            case "IEC61131_WCHAR":
+            case "IEC61131_STRING":
+            case "IEC61131_WSTRING":
+            case "IEC61131_STRING16":
+                PascalString[] tmpString = new PascalString[length];
+                for (int i = 0; i < length; i++) {
+                    String s = valueObject.getIndex(i).getString();
+                    tmpString[i] = new PascalString(s.length(), s);
+                }
+                return new VariantString(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpString);
+            case "IEC61131_DATE_AND_TIME":
+                long[] tmpDateTime = new long[length];
+                for (int i = 0; i < length; i++) {
+                    tmpDateTime[i] = valueObject.getIndex(i).getDateTime().toEpochSecond(ZoneOffset.UTC);
+                }
+                return new VariantDateTime(length == 1 ? false : true,
+                    false,
+                    null,
+                    null,
+                    length == 1 ? null : length,
+                    tmpDateTime);
+            default:
+                throw new PlcRuntimeException("Unsupported write field type " + dataType);
+        }
+
     }
 
+
     @Override
     public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
         LOGGER.info("Writing Value");
@@ -811,8 +1073,8 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
                     false,
                     false,
                     false,
-                    false,
-                    fromPlcValue(field, writeRequest),
+                    true,
+                    fromPlcValue(fieldName, field, writeRequest),
                     null,
                     null,
                     null,
@@ -843,12 +1105,8 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             .check(p -> p.getMessage() instanceof OpcuaMessageResponse)
             .unwrap(p -> (OpcuaMessageResponse) p.getMessage())
             .handle(opcuaResponse -> {
-
-                Map<String, PlcResponseCode> responseMap = new HashMap<>();
-                responseMap.put("value-0", PlcResponseCode.OK);
-
-                // Prepare the response.
-                PlcWriteResponse response = new DefaultPlcWriteResponse(request, responseMap);
+                WriteResponse responseMessage = (WriteResponse) opcuaResponse.getMessage();
+                PlcWriteResponse response = writeResponse(request, responseMessage);
 
                 // Pass the response back to the application.
                 future.complete(response);
@@ -860,6 +1118,29 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         return future;
     }
 
+    private PlcWriteResponse writeResponse(DefaultPlcWriteRequest request, WriteResponse writeResponse) {
+        Map<String, PlcResponseCode> responseMap = new HashMap<>();
+
+        StatusCode[] results = writeResponse.getResults();
+        Iterator<String> responseIterator = request.getFieldNames().iterator();
+        for (int i = 0; i < request.getFieldNames().size(); i++ ) {
+            String fieldName = responseIterator.next();
+            OpcuaStatusCodes statusCode = OpcuaStatusCodes.enumForValue(results[i].getStatusCode());
+            switch (statusCode) {
+                case Good:
+                    responseMap.put(fieldName, PlcResponseCode.OK);
+                    break;
+                case BadNodeIdUnknown:
+                    responseMap.put(fieldName, PlcResponseCode.NOT_FOUND);
+                    break;
+                default:
+                    responseMap.put(fieldName, PlcResponseCode.REMOTE_ERROR);
+            }
+        }
+
+        return new DefaultPlcWriteResponse(request, responseMap);
+    }
+
     /**
      * Returns the next transaction identifier, for a secured connection we should return a random number.
      * @param random - Select if we return a random number or the next value
@@ -901,4 +1182,25 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     private long getDateTime(long dateTime) {
         return (dateTime - epochOffset) / 10000;
     }
+
+    public byte[] encrypt(String data, byte[] publicKey) {
+        try {
+            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
+            cipher.init(Cipher.ENCRYPT_MODE, getCertificateX509().getPublicKey());
+            return cipher.doFinal(data.getBytes());
+        } catch (Exception e) {
+            LOGGER.error("Unable to encrypt Data");
+            return null;
+        }
+    }
+
+    public X509Certificate getCertificateX509() {
+        try {
+            CertificateFactory factory =  CertificateFactory.getInstance("X.509");
+            return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(this.senderCertificate));
+        } catch (Exception e) {
+            LOGGER.error("Unable to get certificate from String {}", this.senderCertificate);
+            return null;
+        }
+    }
 }
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
index e41954d..525cda7 100644
--- 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
@@ -24,6 +24,8 @@ 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.messages.PlcWriteRequest;
+import org.apache.plc4x.java.api.messages.PlcWriteResponse;
 import org.apache.plc4x.java.api.types.PlcResponseCode;
 import org.eclipse.milo.examples.server.ExampleServer;
 import org.junit.jupiter.api.*;
@@ -36,6 +38,8 @@ import static org.assertj.core.api.Assertions.fail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.math.BigInteger;
+
 /**
  */
 public class OpcuaPlcDriverTest {
@@ -269,6 +273,82 @@ public class OpcuaPlcDriverTest {
     }
 
     @Test
+    public void writeVariables() throws Exception {
+        PlcConnection opcuaConnection = new PlcDriverManager().getConnection(tcpConnectionAddress);
+        assert opcuaConnection.isConnected();
+
+        PlcWriteRequest.Builder builder = opcuaConnection.writeRequestBuilder();
+        builder.addItem("Bool", BOOL_IDENTIFIER_READ_WRITE, true);
+        builder.addItem("Byte", BYTE_IDENTIFIER_READ_WRITE + ":BYTE", 3);
+        builder.addItem("Double", DOUBLE_IDENTIFIER_READ_WRITE, 0.5d);
+        builder.addItem("Float", FLOAT_IDENTIFIER_READ_WRITE, 0.5f);
+        builder.addItem("Int16", INT16_IDENTIFIER_READ_WRITE + ":INT", 1);
+        builder.addItem("Int32", INT32_IDENTIFIER_READ_WRITE, 42);
+        builder.addItem("Int64", INT64_IDENTIFIER_READ_WRITE, 42L);
+        builder.addItem("Integer", INTEGER_IDENTIFIER_READ_WRITE, 42);
+        builder.addItem("SByte", SBYTE_IDENTIFIER_READ_WRITE + ":SINT", -127);
+        builder.addItem("String", STRING_IDENTIFIER_READ_WRITE, "Helllo Toddy!");
+        builder.addItem("UInt16", UINT16_IDENTIFIER_READ_WRITE + ":UINT", 65535);
+        builder.addItem("UInt32", UINT32_IDENTIFIER_READ_WRITE + ":UDINT", 101010101L);
+        builder.addItem("UInt64", UINT64_IDENTIFIER_READ_WRITE + ":ULINT", new BigInteger("1337"));
+        builder.addItem("UInteger", UINTEGER_IDENTIFIER_READ_WRITE + ":UDINT", 102020202L);
+
+
+        builder.addItem("BooleanArray", BOOL_ARRAY_IDENTIFIER, new Boolean[] {true, true, true, true, true});
+        builder.addItem("ByteArray", BYTE_ARRAY_IDENTIFIER + ":BYTE", new Short[] {1, 100, 100, 255, 123});
+        builder.addItem("DoubleArray", DOUBLE_ARRAY_IDENTIFIER, new Double[] {1.0,2.0,3.0,4.0,5.0});
+        builder.addItem("FloatArray", FLOAT_ARRAY_IDENTIFIER, new Float[] {1.0F,2.0F,3.0F,4.0F,5.0F});
+        builder.addItem("Int16Array", INT16_ARRAY_IDENTIFIER, new Short[] {1,2,3,4,5});
+        builder.addItem("Int32Array", INT32_ARRAY_IDENTIFIER, new Integer[] {1,2,3,4,5});
+        builder.addItem("Int64Array", INT64_ARRAY_IDENTIFIER, new Long[] {1L,2L,3L,4L,5L});
+        builder.addItem("IntegerArray", INT32_ARRAY_IDENTIFIER, new Integer[] {1,2,3,4,5});
+        builder.addItem("SByteArray", SBYTE_ARRAY_IDENTIFIER, new Byte[] {1,2,3,4,5});
+        builder.addItem("StringArray", STRING_ARRAY_IDENTIFIER, new String[] {"1","2","3","4","5"});
+        builder.addItem("UInt16Array", UINT16_ARRAY_IDENTIFIER + ":UINT", new Short[] {1,2,3,4,5});
+        builder.addItem("UInt32Array", UINT32_ARRAY_IDENTIFIER + ":UDINT", new Integer[] {1,2,3,4,5});
+        builder.addItem("UInt64Array", UINT64_ARRAY_IDENTIFIER + ":ULINT", new Long[] {1L,2L,3L,4L,5L});
+
+        builder.addItem("DoesNotExists", DOES_NOT_EXIST_IDENTIFIER_READ_WRITE, "11");
+
+        PlcWriteRequest request = builder.build();
+        PlcWriteResponse 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("BooleanArray").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("IntegerArray").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");
@@ -290,7 +370,7 @@ public class OpcuaPlcDriverTest {
         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://127.0.0.1?discovery=false");
         assertMatching(URI_PATTERN, "opcua:tcp://opcua.demo-this.com:51210/UA/SampleServer?discovery=false");
 
     }
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 9aba597..ea5c894 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,7 +98,6 @@ 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);
@@ -236,7 +235,6 @@ 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/generation/ReadBuffer.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java
index 4fcfe44..3e77df0 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java
@@ -89,7 +89,6 @@ public class ReadBuffer {
     public boolean readBit() throws ParseException {
         try {
             boolean ret = bi.readBoolean();
-            LOGGER.info("Reading Bit:- " + ret);
             return ret;
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -105,7 +104,6 @@ public class ReadBuffer {
         }
         try {
             byte ret = bi.readByte(true, bitLength);
-            LOGGER.info("Reading Unsigned Byte:- " + ret);
             return ret;
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -122,7 +120,6 @@ public class ReadBuffer {
         try {
             // No need to flip here as we're only reading one byte.
             short ret =  bi.readShort(true, bitLength);
-            LOGGER.info("Reading Unsigned Short:- " + ret);
             return ret;
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -140,11 +137,9 @@ public class ReadBuffer {
             if (littleEndian) {
                 int intValue = bi.readInt(true, bitLength);
                 int ret =  Integer.reverseBytes(intValue) >>> 16;
-                LOGGER.info("Reading Unsigned Int:- " + ret);
                 return ret;
             }
             int ret =  bi.readInt(true, bitLength);
-            LOGGER.info("Reading Unsigned Int:- " + ret);
             return ret;
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -162,11 +157,9 @@ public class ReadBuffer {
             if (littleEndian) {
                 final long longValue = bi.readLong(true, bitLength);
                 long ret = Long.reverseBytes(longValue) >>> 32;
-                LOGGER.info("Reading Unsigned Long:- " + ret);
                 return ret;
             }
             long ret =  bi.readLong(true, bitLength);
-            LOGGER.info("Reading Unsigned Long:- " + ret);
             return ret;
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -207,7 +200,6 @@ public class ReadBuffer {
         }
         try {
             byte ret = bi.readByte(false, bitLength);
-            LOGGER.info("Reading Byte:- " + ret);
             return ret;
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -224,11 +216,9 @@ public class ReadBuffer {
         try {
             if (littleEndian) {
                 short ret =  Short.reverseBytes(bi.readShort(false, bitLength));
-                LOGGER.info("Reading Short:- " + ret);
                 return ret;
             }
             short ret = bi.readShort(false, bitLength);
-            LOGGER.info("Reading Short:- " + ret);
             return ret;
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -245,11 +235,9 @@ public class ReadBuffer {
         try {
             if (littleEndian) {
                 int ret =  Integer.reverseBytes(bi.readInt(false, bitLength));
-                LOGGER.info("Reading Integer:- " + ret);
                 return ret;
             }
             int ret = bi.readInt(false, bitLength);
-            LOGGER.info("Reading Int:- " + ret);
             return ret;
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -266,11 +254,9 @@ public class ReadBuffer {
         try {
             if (littleEndian) {
                 long ret = Long.reverseBytes(bi.readLong(false, bitLength));
-                LOGGER.info("Reading Read Long:- " + ret);
                 return ret;
             }
             long ret = bi.readLong(false, bitLength);
-            LOGGER.info("Reading Read Long:- " + ret);
             return ret;
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -320,7 +306,6 @@ public class ReadBuffer {
         if(bitLength == 64) {
             long longValue = readLong(64);
             double ret = Double.longBitsToDouble(longValue);
-            LOGGER.info("Reading Double:- " + ret);
             return ret;
         } else {
             throw new UnsupportedOperationException("unsupported bit length (only 64 supported)");
@@ -342,7 +327,6 @@ public class ReadBuffer {
         }
         //replaceAll function removes and leading ' char or hypens.
         String ret =  new String(strBytes, Charset.forName(encoding.replaceAll("[^a-zA-Z0-9]","")));
-        LOGGER.info("Reading String:- " + ret);
         return ret;
     }
 
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBuffer.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBuffer.java
index d05eb8e..a0b6aeb 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBuffer.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBuffer.java
@@ -69,7 +69,6 @@ public class WriteBuffer {
 
     public void writeBit(boolean value) throws ParseException {
         try {
-            LOGGER.info("Writing Bit:- " + value);
             bo.writeBoolean(value);
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -84,7 +83,6 @@ public class WriteBuffer {
             throw new ParseException("unsigned byte can only contain max 8 bits");
         }
         try {
-            LOGGER.info("Writing Usigned Byte:- " + value + ", " + bitLength + ", " + getPos());
             bo.writeByte(true, bitLength, value);
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -99,7 +97,6 @@ public class WriteBuffer {
             throw new ParseException("unsigned short can only contain max 16 bits");
         }
         try {
-            LOGGER.info("Writing Unsigned Short:- " + value + ", " + bitLength + ", " + getPos());
             bo.writeShort(true, bitLength, value);
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -114,7 +111,6 @@ public class WriteBuffer {
             throw new ParseException("unsigned int can only contain max 32 bits");
         }
         try {
-            LOGGER.info("Writing Unisgned Int:- " + value + ", " + bitLength + ", " + getPos());
             if(littleEndian) {
                 value = Integer.reverseBytes(value) >> 16;
             }
@@ -132,7 +128,6 @@ public class WriteBuffer {
             throw new ParseException("unsigned long can only contain max 63 bits");
         }
         try {
-            LOGGER.info("Writing Unsigned Long:- " + value + ", " + bitLength + ", " + getPos());
             if(littleEndian) {
                 value = Long.reverseBytes(value) >> 32;
             }
@@ -178,7 +173,6 @@ public class WriteBuffer {
             throw new ParseException("byte can only contain max 8 bits");
         }
         try {
-            LOGGER.info("Writing Byte:- " + value + ", " + bitLength + ", " + getPos());
             bo.writeByte(false, bitLength, value);
         } catch (IOException e) {
             throw new ParseException("Error reading", e);
@@ -193,7 +187,6 @@ public class WriteBuffer {
             throw new ParseException("short can only contain max 16 bits");
         }
         try {
-            LOGGER.info("Writing Short:- " + value + ", " + bitLength + ", " + getPos());
             if(littleEndian) {
                 value = Short.reverseBytes(value);
             }
@@ -211,7 +204,6 @@ public class WriteBuffer {
             throw new ParseException("int can only contain max 32 bits");
         }
         try {
-            LOGGER.info("Writing int:- " + value + ", " + bitLength + ", " + getPos());
             if(littleEndian) {
                 value = Integer.reverseBytes(value);
             }
@@ -229,7 +221,6 @@ public class WriteBuffer {
             throw new ParseException("long can only contain max 64 bits");
         }
         try {
-            LOGGER.info("Writing long:- " + value + ", " + bitLength + ", " + getPos());
             if(littleEndian) {
                 value = Long.reverseBytes(value);
             }
@@ -254,7 +245,6 @@ public class WriteBuffer {
         if (bitsExponent != 8 || bitsMantissa != 23) {
             throw new UnsupportedOperationException("Exponent and/or Mantissa non standard size");
         }
-        LOGGER.info("Writing float:- " + value);
         writeInt(1 + bitsExponent + bitsMantissa, Float.floatToRawIntBits(value));
     }
 
@@ -262,7 +252,6 @@ public class WriteBuffer {
         if (bitsExponent != 11 || bitsMantissa != 52) {
             throw new UnsupportedOperationException("Exponent and/or Mantissa non standard size");
         }
-        LOGGER.info("Writing double:- " + value);
         writeLong(1 + bitsExponent + bitsMantissa, Double.doubleToRawLongBits(value));
     }
 
@@ -271,7 +260,6 @@ public class WriteBuffer {
     }
 
     public void writeString(int bitLength, String encoding, String value) throws ParseException {
-        LOGGER.info("Writing String:- " + value + ", " + bitLength + ", " + getPos());
         final byte[] bytes = value.getBytes(Charset.forName(encoding.replaceAll("[^a-zA-Z0-9]","")));
         try {
             int count = 0;
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 8b43f39..64c54f6 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,7 +51,6 @@ 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;
diff --git a/protocols/opcua/src/main/xslt/opc-types.xsl b/protocols/opcua/src/main/xslt/opc-types.xsl
index e3f2ac8..f34db8f 100644
--- a/protocols/opcua/src/main/xslt/opc-types.xsl
+++ b/protocols/opcua/src/main/xslt/opc-types.xsl
@@ -67,6 +67,7 @@
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='WriteResponse']"/>
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='BrowseRequest']"/>
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='BrowseResponse']"/>
+        <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='ServiceFault']"/>
 
     ['473' CloseSessionRequest
         [simple RequestHeader 'requestHeader']
@@ -132,7 +133,11 @@
 ]
 
 [enum int 32 'NodeClass'
-<xsl:apply-templates select="/opc:TypeDictionary/opc:EnumeratedType[@Name='NodeClass']"/>
+    <xsl:apply-templates select="/opc:TypeDictionary/opc:EnumeratedType[@Name='NodeClass']"/>
+]
+
+[type 'UserNameIdentityToken'
+    <xsl:apply-templates select="/opc:TypeDictionary/opc:StructuredType[@Name='UserNameIdentityToken']"/>
 ]
 
 [type 'DiagnosticInfo'
@@ -227,7 +232,7 @@
 ]
 
 [discriminatedType 'Variant'
-    [discriminator bit 'arrayLengthSpecified']
+    [simple bit 'arrayLengthSpecified']
     [simple bit 'arrayDimensionsSpecified']
     [discriminator uint 6 'VariantType']
     [typeSwitch 'VariantType','arrayLengthSpecified'
@@ -404,6 +409,11 @@
     [optional string 'stringLength == -1 ? 0 : stringLength * 8' 'UTF-8' 'stringValue' 'stringLength >= 0']
 ]
 
+[type 'PascalByteString'
+    [simple int 32 'stringLength']
+    [array int 8 'stringValue' count 'stringLength == -1 ? 0 : stringLength']
+]
+
 [type 'LocalizedText'
     [simple uint 6 'reserved1']
     [simple bit 'localeSpecified']
@@ -620,7 +630,7 @@
             <xsl:when test="$datatype = 'opc:Char'">string '1'</xsl:when>
             <xsl:when test="$datatype = 'opc:CharArray'">PascalString</xsl:when>
             <xsl:when test="$datatype = 'opc:Guid'">string '-1'</xsl:when>
-            <xsl:when test="$datatype = 'opc:ByteString'">PascalString</xsl:when>
+            <xsl:when test="$datatype = 'opc:ByteString'">PascalByteString</xsl:when>
             <xsl:when test="$datatype = 'opc:DateTime'">int 64</xsl:when>
             <xsl:when test="$datatype = 'opc:String'">PascalString</xsl:when>
             <xsl:otherwise><xsl:value-of select="substring-after($datatype,':')"/></xsl:otherwise>