diff --git a/NOTICE b/NOTICE
index 97048d6e923..6ff3737bfe3 100644
--- a/NOTICE
+++ b/NOTICE
@@ -8,3 +8,6 @@ The Apache Software Foundation (http://www.apache.org/).
This product includes software developed at
The Netty project (https://netty.io/).
+
+This product includes software developed at
+The Milo project (https://github.com/eclipse/milo).
diff --git a/plc4j/drivers/opcua/pom.xml b/plc4j/drivers/opcua/pom.xml
new file mode 100644
index 00000000000..f0f47df59b8
--- /dev/null
+++ b/plc4j/drivers/opcua/pom.xml
@@ -0,0 +1,86 @@
+
+
+
+
+ 4.0.0
+
+
+ org.apache.plc4x
+ plc4j-drivers
+ 0.4.0-SNAPSHOT
+
+
+ plc4j-driver-opcua
+ PLC4J: Driver: OPC UA
+ Implementation of a PLC4X driver able to speak with devices using the OPC UA protocol.
+
+
+
+ org.apache.plc4x
+ plc4j-api
+ 0.4.0-SNAPSHOT
+
+
+
+ org.apache.plc4x
+ plc4j-protocol-driver-base
+ 0.4.0-SNAPSHOT
+
+
+
+ org.eclipse.milo
+ sdk-client
+ 0.3.0-M1
+
+
+ org.eclipse.milo
+ stack-core
+ 0.3.0-M1
+
+
+ org.eclipse.milo
+ stack-client
+ 0.3.0-M1
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+
+
+
\ No newline at end of file
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
new file mode 100644
index 00000000000..7fefae7c45a
--- /dev/null
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
@@ -0,0 +1,94 @@
+/*
+ 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 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.spi.PlcDriver;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+/**
+ * Implementation of the OPC UA protocol, based on:
+ * - Eclipse Milo (https://github.com/eclipse/milo)
+ *
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public class OpcuaPlcDriver implements PlcDriver {
+
+
+
+ public static final Pattern INET_ADDRESS_PATTERN = Pattern.compile("tcp://(?[\\w.-]+)(:(?\\d*))?");
+ public static final Pattern OPCUA_URI_PATTERN = Pattern.compile("^opcua:(" + INET_ADDRESS_PATTERN + ")?" + "(?/[\\w/]+)?");
+ private static final int requestTimeout = 10000;
+ private OpcuaConnectionFactory opcuaConnectionFactory;
+
+ public OpcuaPlcDriver() {
+ this.opcuaConnectionFactory = new OpcuaConnectionFactory();
+ }
+
+ public OpcuaPlcDriver(OpcuaConnectionFactory opcuaConnectionFactory) {
+ this.opcuaConnectionFactory = opcuaConnectionFactory;
+ }
+
+ @Override
+ public String getProtocolCode() {
+ return "opcua";
+ }
+
+ @Override
+ public String getProtocolName() {
+ return "OPC UA (TCP)";
+ }
+
+ @Override
+ public PlcConnection connect(String url) throws PlcConnectionException {
+ Matcher matcher = OPCUA_URI_PATTERN.matcher(url);
+
+ if (!matcher.matches()) {
+ throw new PlcConnectionException(
+ "Connection url doesn't match the format 'opcua:{type}//{port|host}'");
+ }
+
+ String host = matcher.group("host");
+ String portString = matcher.group("port");
+ Integer port = StringUtils.isNotBlank(portString) ? Integer.parseInt(portString) : null;
+ String params = matcher.group("params") != null ? matcher.group("params").substring(1) : null;
+
+ try {
+ return opcuaConnectionFactory.opcuaTcpPlcConnectionOf(InetAddress.getByName(host), port, params, requestTimeout);
+ } catch (UnknownHostException e) {
+ throw new PlcConnectionException(e);
+ }
+ }
+
+ @Override
+ public PlcConnection connect(String url, PlcAuthentication authentication) throws PlcConnectionException {
+ throw new PlcConnectionException("opcua does not support Auth at this state");
+ }
+
+}
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/BaseOpcuaPlcConnection.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/BaseOpcuaPlcConnection.java
new file mode 100644
index 00000000000..50a5bd0633c
--- /dev/null
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/BaseOpcuaPlcConnection.java
@@ -0,0 +1,95 @@
+/*
+ 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.commons.lang3.StringUtils;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcSubscriptionRequest;
+import org.apache.plc4x.java.api.messages.PlcUnsubscriptionRequest;
+import org.apache.plc4x.java.api.messages.PlcWriteRequest;
+import org.apache.plc4x.java.base.connection.AbstractPlcConnection;
+import org.apache.plc4x.java.base.messages.*;
+import org.apache.plc4x.java.opcua.protocol.OpcuaPlcFieldHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public abstract class BaseOpcuaPlcConnection extends AbstractPlcConnection implements PlcReader, PlcWriter, PlcSubscriber {
+
+ private static final Logger logger = LoggerFactory.getLogger(BaseOpcuaPlcConnection.class);
+
+ BaseOpcuaPlcConnection(String params) {
+
+ if (!StringUtils.isEmpty(params)) {
+ for (String param : params.split("&")) {
+ String[] paramElements = param.split("=");
+ String paramName = paramElements[0];
+ if (paramElements.length == 2) {
+ String paramValue = paramElements[1];
+ switch (paramName) {
+ default:
+ logger.debug("Unknown parameter {} with value {}", paramName, paramValue);
+ }
+ } else {
+ logger.debug("Unknown no-value parameter {}", paramName);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean canRead() {
+ return true;
+ }
+
+ @Override
+ public boolean canWrite() {
+ return true;
+ }
+
+ @Override
+ public PlcReadRequest.Builder readRequestBuilder() {
+ return new DefaultPlcReadRequest.Builder(this, new OpcuaPlcFieldHandler());
+ }
+
+ @Override
+ public PlcWriteRequest.Builder writeRequestBuilder() {
+ return new DefaultPlcWriteRequest.Builder(this, new OpcuaPlcFieldHandler());
+ }
+
+ @Override
+ public boolean canSubscribe() {
+ return true;
+ }
+
+ @Override
+ public PlcSubscriptionRequest.Builder subscriptionRequestBuilder() {
+ return new DefaultPlcSubscriptionRequest.Builder(this, new OpcuaPlcFieldHandler());
+ }
+
+ @Override
+ public PlcUnsubscriptionRequest.Builder unsubscriptionRequestBuilder() {
+ return new DefaultPlcUnsubscriptionRequest.Builder(this);
+ }
+
+
+}
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java
new file mode 100644
index 00000000000..5c399f30843
--- /dev/null
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaConnectionFactory.java
@@ -0,0 +1,39 @@
+/*
+ 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 java.net.InetAddress;
+import java.util.Objects;
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public class OpcuaConnectionFactory {
+
+ 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);
+ }
+ }
+
+}
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
new file mode 100644
index 00000000000..c59c36a35ea
--- /dev/null
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnection.java
@@ -0,0 +1,464 @@
+/*
+ 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.
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+package org.apache.plc4x.java.opcua.connection;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+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.PlcSubscriptionHandle;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.base.messages.*;
+import org.apache.plc4x.java.base.messages.items.*;
+import org.apache.plc4x.java.base.model.SubscriptionPlcField;
+import org.apache.plc4x.java.opcua.protocol.OpcuaField;
+import org.apache.plc4x.java.opcua.protocol.OpcuaSubsriptionHandle;
+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.security.SecurityPolicy;
+import org.eclipse.milo.opcua.stack.core.types.builtin.*;
+import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
+import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;
+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.EndpointDescription;
+import org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemCreateRequest;
+import org.eclipse.milo.opcua.stack.core.types.structured.MonitoringParameters;
+import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.time.Duration;
+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 java.util.stream.Collectors;
+
+import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
+
+public class OpcuaTcpPlcConnection extends BaseOpcuaPlcConnection {
+
+ private static final int OPCUA_DEFAULT_TCP_PORT = 4840;
+
+ private static final Logger logger = LoggerFactory.getLogger(OpcuaTcpPlcConnection.class);
+ private InetAddress address;
+ private int requestTimeout = 5000;
+ private int port;
+ private String params;
+ private OpcUaClient client;
+ private boolean isConnected = false;
+ private final AtomicLong clientHandles = new AtomicLong(1L);
+
+ 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());
+ }
+
+ public 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;
+ }
+
+ public OpcuaTcpPlcConnection(String params) {
+ super(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 BaseDefaultFieldItem encodeFieldItem(DataValue value){
+ NodeId typeNode = value.getValue().getDataType().get();
+ Object objValue = value.getValue().getValue();
+
+ if(typeNode.equals(Identifiers.Boolean)){
+ return new DefaultBooleanFieldItem((Boolean)objValue);
+ }else if (typeNode.equals(Identifiers.ByteString)){
+ byte[] array = ((ByteString)objValue).bytes();
+ Byte[] byteArry = new Byte[array.length];
+ int counter = 0;
+ for (byte bytie: array
+ ) {
+ byteArry[counter] = bytie;
+ counter++;
+ }
+ return new DefaultByteArrayFieldItem(byteArry);
+ }else if (typeNode.equals(Identifiers.Integer)){
+ return new DefaultIntegerFieldItem((Integer)objValue);
+ }else if (typeNode.equals(Identifiers.Int16)){
+ return new DefaultShortFieldItem((Short)objValue);
+ }else if (typeNode.equals(Identifiers.Int32)){
+ return new DefaultIntegerFieldItem((Integer)objValue);
+ }else if (typeNode.equals(Identifiers.Int64)){
+ return new DefaultLongFieldItem((Long)objValue);
+ }else if (typeNode.equals(Identifiers.UInteger)){
+ return new DefaultLongFieldItem((Long)objValue);
+ }else if (typeNode.equals(Identifiers.UInt16)){
+ return new DefaultIntegerFieldItem(((UShort)objValue).intValue());
+ }else if (typeNode.equals(Identifiers.UInt32)){
+ return new DefaultLongFieldItem(((UInteger)objValue).longValue());
+ }else if (typeNode.equals(Identifiers.UInt64)){
+ return new DefaultBigIntegerFieldItem(new BigInteger(objValue.toString()));
+ }else if (typeNode.equals(Identifiers.Byte)){
+ return new DefaultShortFieldItem(Short.valueOf(objValue.toString()));
+ }else if (typeNode.equals(Identifiers.Float)){
+ return new DefaultFloatFieldItem((Float)objValue);
+ }else if (typeNode.equals(Identifiers.Double)){
+ return new DefaultDoubleFieldItem((Double)objValue);
+ }else if (typeNode.equals(Identifiers.SByte)){
+ return new DefaultByteFieldItem((Byte)objValue);
+ }else {
+ return new DefaultStringFieldItem(objValue.toString());
+ }
+
+ }
+
+ public InetAddress getRemoteAddress() {
+ return address;
+ }
+
+ @Override
+ public void connect() throws PlcConnectionException {
+ List endpoints = null;
+
+ try {
+ endpoints = DiscoveryClient.getEndpoints(getEndpointUrl(address, port, params)).get();
+ //TODO Exception should be handeled better when the Discovery-API of Milo is stable
+ } catch (Exception ex) {
+ // try the explicit discovery endpoint as well
+ String discoveryUrl = getEndpointUrl(address, port, params);
+
+ if (!discoveryUrl.endsWith("/")) {
+ discoveryUrl += "/";
+ }
+ discoveryUrl += "discovery";
+
+ logger.info("Trying explicit discovery URL: {}", discoveryUrl);
+ try {
+ endpoints = DiscoveryClient.getEndpoints(discoveryUrl).get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new PlcConnectionException("Unable to discover URL:" + discoveryUrl);
+ }
+ }
+
+ EndpointDescription endpoint = endpoints.stream()
+ .filter(e -> e.getSecurityPolicyUri().equals(getSecurityPolicy().getUri()))
+ .filter(endpointFilter())
+ .findFirst()
+ .orElseThrow(() -> new PlcConnectionException("No desired endpoints from"));
+
+ 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 | 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 subscribe(PlcSubscriptionRequest subscriptionRequest) {
+ InternalPlcSubscriptionRequest internalPlcSubscriptionRequest = checkInternal(subscriptionRequest, InternalPlcSubscriptionRequest.class);
+ CompletableFuture future = CompletableFuture.supplyAsync(() ->{
+ Map> responseItems = internalPlcSubscriptionRequest.getSubscriptionPlcFieldMap().entrySet().stream()
+ .map(subscriptionPlcFieldEntry -> {
+ final String plcFieldName = subscriptionPlcFieldEntry.getKey();
+ final SubscriptionPlcField subscriptionPlcField = subscriptionPlcFieldEntry.getValue();
+ final OpcuaField field = (OpcuaField)Objects.requireNonNull(subscriptionPlcField.getPlcField());
+ long cycleTime = subscriptionPlcField.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 (subscriptionPlcField.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(1000.0).get();
+
+ MonitoredItemCreateRequest request = new MonitoredItemCreateRequest(
+ readValueId, monitoringMode, parameters);
+ List requestList = new LinkedList<>();
+ requestList.add(request);
+ OpcuaSubsriptionHandle subsriptionHandle = new OpcuaSubsriptionHandle(plcFieldName, clientHandle);
+ BiConsumer onItemCreated =
+ (item, id) -> item.setValueConsumer(subsriptionHandle::onSubscriptionValue);
+
+ List items = subscription.createMonitoredItems(
+ TimestampsToReturn.Both,
+ requestList,
+ onItemCreated
+ ).get();
+
+ subHandle = subsriptionHandle;
+ responseCode = PlcResponseCode.OK;
+ } catch (InterruptedException | ExecutionException e) {
+ logger.warn("Unable to subscribe Elements because of: {}", e.getMessage());
+ }
+
+
+ return Pair.of(plcFieldName, Pair.of(responseCode, subHandle));
+ })
+ .collect(Collectors.toMap(Pair::getKey, Pair::getValue));
+ return (PlcSubscriptionResponse) new DefaultPlcSubscriptionResponse(internalPlcSubscriptionRequest, responseItems);
+ });
+
+ return future;
+ }
+
+ @Override
+ public CompletableFuture unsubscribe(PlcUnsubscriptionRequest unsubscriptionRequest) {
+ InternalPlcUnsubscriptionRequest internalPlcUnsubscriptionRequest = checkInternal(unsubscriptionRequest, InternalPlcUnsubscriptionRequest.class);
+ internalPlcUnsubscriptionRequest.getInternalPlcSubscriptionHandles().forEach(o -> {
+ OpcuaSubsriptionHandle opcSubHandle = (OpcuaSubsriptionHandle) o;
+ try {
+ client.getSubscriptionManager().deleteSubscription(opcSubHandle.getClientHandle()).get();
+ } catch (InterruptedException | ExecutionException e) {
+ logger.warn("Unable to unsubscribe Elements because of: {}", e.getMessage());
+ }
+ });
+
+ return null;
+ }
+
+ @Override
+ public PlcConsumerRegistration register(Consumer consumer, Collection handles) {
+ List unregisters = new LinkedList<>();
+ handles.forEach(plcSubscriptionHandle -> unregisters.add(plcSubscriptionHandle.register(consumer)));
+
+ return () -> unregisters.forEach(PlcConsumerRegistration::unregister);
+ }
+
+ @Override
+ public void unregister(PlcConsumerRegistration registration) {
+ registration.unregister();
+ }
+
+ @Override
+ public CompletableFuture read(PlcReadRequest readRequest) {
+ CompletableFuture future = CompletableFuture.supplyAsync(() -> {
+ readRequest.getFields();
+ Map> fields = new HashMap<>();
+ List readValueIds = new LinkedList<>();
+ List readPLCValues = readRequest.getFields();
+ for (PlcField field: readPLCValues) {
+ NodeId idNode = generateNodeId((OpcuaField) field);
+ readValueIds.add(idNode);
+ }
+
+ CompletableFuture> dataValueCompletableFuture = client.readValues(0.0, TimestampsToReturn.Both, readValueIds);
+ List readValues = null;
+ try {
+ readValues = dataValueCompletableFuture.get();
+ } catch (InterruptedException | ExecutionException e) {
+ logger.warn("Unable to read Elements because of: {}", e.getMessage());
+ }
+ for(int counter = 0; counter < readValueIds.size(); counter++){
+ PlcResponseCode resultCode = PlcResponseCode.OK;
+ BaseDefaultFieldItem stringItem = null;
+ if(readValues == null || readValues.size() <= counter || readValues.get(counter).getStatusCode() != StatusCode.GOOD){
+ resultCode = PlcResponseCode.NOT_FOUND;
+ }else{
+ stringItem = encodeFieldItem(readValues.get(counter));
+
+ }
+ Pair newPair = new ImmutablePair<>(resultCode, stringItem);
+ fields.put((String) readRequest.getFieldNames().toArray()[counter], newPair);
+
+
+ }
+ InternalPlcReadRequest internalPlcReadRequest = checkInternal(readRequest, InternalPlcReadRequest.class);
+ return (PlcReadResponse) new DefaultPlcReadResponse(internalPlcReadRequest, fields );
+ });
+
+
+ return future;
+ }
+
+
+ @Override
+ public CompletableFuture write(PlcWriteRequest writeRequest) {
+ CompletableFuture future;
+ future = CompletableFuture.supplyAsync(() -> {
+
+ InternalPlcWriteRequest internalPlcWriteRequest = (InternalPlcWriteRequest) writeRequest;
+
+ List writePLCValues = writeRequest.getFields();
+ LinkedList values = new LinkedList<>();
+ LinkedList ids = new LinkedList<>();
+ LinkedList names = new LinkedList<>();
+ Map fieldResponse = new HashMap<>();
+ for (String fieldName: writeRequest.getFieldNames()) {
+ OpcuaField uaField = (OpcuaField) writeRequest.getField(fieldName);
+ NodeId idNode = generateNodeId(uaField);
+ Variant var = new Variant(internalPlcWriteRequest.getFieldItem(fieldName).getObject(0));
+ DataValue value = new DataValue(var, null, null);
+ ids.add(idNode);
+ names.add(fieldName);
+ values.add(value);
+ }
+ CompletableFuture> opcRequest =
+ client.writeValues(ids, values);
+ List statusCodes = null;
+ try {
+ statusCodes = opcRequest.get();
+ } catch (InterruptedException | ExecutionException e) {
+ statusCodes = new LinkedList<>();
+ for(int counter = 0; counter < ids.size(); counter++){
+ ((LinkedList) statusCodes).push(StatusCode.BAD);
+ }
+ }
+
+ for(int counter = 0; counter < names.size(); counter++){
+ PlcResponseCode resultCode;
+ if(statusCodes != null && statusCodes.size() > counter){
+ if(statusCodes.get(counter).isGood()){
+ resultCode = PlcResponseCode.OK;
+ }else if(statusCodes.get(counter).isUncertain()){
+ resultCode = PlcResponseCode.NOT_FOUND;
+ }else {
+ resultCode = PlcResponseCode.ACCESS_DENIED;
+ }
+ }else{
+ resultCode = PlcResponseCode.ACCESS_DENIED;
+ }
+ fieldResponse.put(names.get(counter), resultCode);
+ }
+ InternalPlcWriteRequest internalPlcReadRequest = checkInternal(writeRequest, InternalPlcWriteRequest.class);
+ PlcWriteResponse response = new DefaultPlcWriteResponse(internalPlcReadRequest, 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 endpointFilter() {
+ return e -> true;
+ }
+
+ private SecurityPolicy getSecurityPolicy() {
+ return SecurityPolicy.None;
+ }
+
+ private IdentityProvider getIdentityProvider() {
+ return new AnonymousProvider();
+ }
+}
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaField.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaField.java
new file mode 100644
index 00000000000..91ece25e682
--- /dev/null
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaField.java
@@ -0,0 +1,118 @@
+/*
+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.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.opcua.protocol.model.OpcuaIdentifierType;
+
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public class OpcuaField implements PlcField {
+
+ public static final Pattern ADDRESS_PATTERN = Pattern.compile("^ns=(?\\d+);(?[isgb])=((?\\w+))?");
+
+ private final OpcuaIdentifierType identifierType;
+
+ private final int namespace;
+
+ private final String identifier;
+
+ protected OpcuaField(int namespace, OpcuaIdentifierType identifierType, String identifier) {
+ this.namespace = namespace;
+ this.identifier = identifier;
+ this.identifierType = identifierType;
+ if (this.identifier == null || this.namespace < 0) {
+ throw new IllegalArgumentException("Identifier can not be null or Namespace can not be lower then 0.");
+ }
+ }
+
+ private OpcuaField(Integer namespace, String identifier, OpcuaIdentifierType identifierType) {
+ this.identifier = Objects.requireNonNull(identifier);
+ this.identifierType = Objects.requireNonNull(identifierType);
+ this.namespace = namespace != null ? namespace : 0;
+ if (this.namespace < 0) {
+ throw new IllegalArgumentException("namespace must be greater then zero. Was " + this.namespace);
+ }
+ }
+
+ public static OpcuaField of(String address) {
+ Matcher matcher = ADDRESS_PATTERN.matcher(address);
+ if (!matcher.matches()) {
+ throw new PlcInvalidFieldException(address, ADDRESS_PATTERN, "{address}");
+ }
+ String identifier = matcher.group("identifier");
+
+ String identifierTypeString = matcher.group("identifierType");
+ OpcuaIdentifierType identifierType = OpcuaIdentifierType.fromString(identifierTypeString);
+
+ String namespaceString = matcher.group("namespace");
+ Integer namespace = namespaceString != null ? Integer.valueOf(namespaceString) : 0;
+
+ return new OpcuaField(namespace, identifier, identifierType);
+ }
+
+
+ public static boolean matches(String address) {
+ return ADDRESS_PATTERN.matcher(address).matches();
+ }
+
+ public int getNamespace() {
+ return namespace;
+ }
+
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ public OpcuaIdentifierType getIdentifierType() {
+ return identifierType;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof OpcuaField)) {
+ return false;
+ }
+ OpcuaField that = (OpcuaField) o;
+ return namespace == that.namespace && identifier.equals(that.identifier) && identifierType == that.identifierType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(namespace);
+ }
+
+ @Override
+ public String toString() {
+ return "OpcuaField{" +
+ "namespace=" + namespace +
+ "identifierType=" + identifierType.getText() +
+ "identifier=" + identifier +
+ '}';
+ }
+}
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaPlcFieldHandler.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaPlcFieldHandler.java
new file mode 100644
index 00000000000..bb44a8e9a75
--- /dev/null
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaPlcFieldHandler.java
@@ -0,0 +1,151 @@
+/*
+ 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.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.base.connection.DefaultPlcFieldHandler;
+import org.apache.plc4x.java.base.messages.items.*;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public class OpcuaPlcFieldHandler extends DefaultPlcFieldHandler {
+
+ @Override
+ public PlcField createField(String fieldQuery) throws PlcInvalidFieldException {
+ if (OpcuaField.matches(fieldQuery)) {
+ return OpcuaField.of(fieldQuery);
+ }
+ throw new PlcInvalidFieldException(fieldQuery);
+ }
+
+ @Override
+ public BaseDefaultFieldItem encodeString(PlcField field, Object[] values) {
+ OpcuaField adsField = (OpcuaField) field;
+ ArrayList resultSet = new ArrayList<>();
+ for(Object item : values){
+ resultSet.add(item.toString());
+ }
+ return new DefaultStringFieldItem(resultSet.toArray(new String[0]));
+ }
+
+ @Override
+ public BaseDefaultFieldItem encodeBoolean(PlcField field, Object[] values) {
+ OpcuaField adsField = (OpcuaField) field;
+ ArrayList resultSet = new ArrayList<>();
+ for(Object item : values){
+ resultSet.add((Boolean) item);
+ }
+ return new DefaultBooleanFieldItem(resultSet.toArray(new Boolean[0]));
+ }
+
+ @Override
+ public BaseDefaultFieldItem encodeByte(PlcField field, Object[] values) {
+ OpcuaField adsField = (OpcuaField) field;
+ ArrayList resultSet = new ArrayList<>();
+ for(Object item : values){
+ resultSet.add((Byte) item);
+ }
+ return new DefaultByteFieldItem(resultSet.toArray(new Byte[0]));
+ }
+
+ @Override
+ public BaseDefaultFieldItem encodeShort(PlcField field, Object[] values) {
+ OpcuaField adsField = (OpcuaField) field;
+ ArrayList resultSet = new ArrayList<>();
+ for(Object item : values){
+ resultSet.add((Short) item);
+ }
+ return new DefaultShortFieldItem(resultSet.toArray(new Short[0]));
+ }
+
+ @Override
+ public BaseDefaultFieldItem encodeInteger(PlcField field, Object[] values) {
+ OpcuaField adsField = (OpcuaField) field;
+ ArrayList resultSet = new ArrayList<>();
+ for(Object item : values){
+ resultSet.add((Integer) item);
+ }
+ return new DefaultIntegerFieldItem(resultSet.toArray(new Integer[0]));
+ }
+
+ @Override
+ public BaseDefaultFieldItem encodeBigInteger(PlcField field, Object[] values) {
+ OpcuaField adsField = (OpcuaField) field;
+ ArrayList resultSet = new ArrayList<>();
+ for(Object item : values){
+ resultSet.add((BigInteger) item);
+ }
+ return new DefaultBigIntegerFieldItem(resultSet.toArray(new BigInteger[0]));
+ }
+
+ @Override
+ public BaseDefaultFieldItem encodeLong(PlcField field, Object[] values) {
+ OpcuaField adsField = (OpcuaField) field;
+ ArrayList resultSet = new ArrayList<>();
+ for(Object item : values){
+ resultSet.add((Long) item);
+ }
+ return new DefaultLongFieldItem(resultSet.toArray(new Long[0]));
+ }
+
+ @Override
+ public BaseDefaultFieldItem encodeFloat(PlcField field, Object[] values) {
+ OpcuaField adsField = (OpcuaField) field;
+ ArrayList resultSet = new ArrayList<>();
+ for(Object item : values){
+ resultSet.add((Float) item);
+ }
+ return new DefaultFloatFieldItem(resultSet.toArray(new Float[0]));
+ }
+
+
+
+ @Override
+ public BaseDefaultFieldItem encodeDouble(PlcField field, Object[] values) {
+ OpcuaField adsField = (OpcuaField) field;
+ ArrayList resultSet = new ArrayList<>();
+ for(Object item : values){
+ resultSet.add((Double) item);
+ }
+ return new DefaultDoubleFieldItem(resultSet.toArray(new Double[0]));
+ }
+
+
+ @Override
+ public BaseDefaultFieldItem encodeByteArray(PlcField field, Object[] values) {
+ OpcuaField adsField = (OpcuaField) field;
+ Byte[][] byteArray = new Byte[values.length][];
+ int innerCounter = 0;
+ for(Object item : values){
+ byte[] itemArray = (byte[]) item;
+ byteArray[innerCounter] = new Byte[((byte[]) item).length];
+ for(int counter = 0; counter < itemArray.length; counter++){
+ byteArray[innerCounter][counter] = itemArray[counter];
+ }
+ innerCounter++;
+ }
+ return new DefaultByteArrayFieldItem(byteArray);
+ }
+}
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
new file mode 100644
index 00000000000..8080d2209c5
--- /dev/null
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubsriptionHandle.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.protocol;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+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.base.messages.DefaultPlcSubscriptionEvent;
+import org.apache.plc4x.java.base.messages.items.BaseDefaultFieldItem;
+import org.apache.plc4x.java.opcua.connection.OpcuaTcpPlcConnection;
+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;
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public class OpcuaSubsriptionHandle implements PlcSubscriptionHandle {
+ Set< Consumer> consumers = new HashSet<>();
+ String fieldName;
+ public UInteger getClientHandle() {
+ return clientHandle;
+ }
+
+ UInteger clientHandle;
+
+ public OpcuaSubsriptionHandle(String fieldName, UInteger clientHandle){
+ this.clientHandle = clientHandle;
+ }
+ @Override
+ public PlcConsumerRegistration register(Consumer consumer) {
+ consumers.add(consumer);
+ return () -> {consumers.remove(consumer);};
+ }
+
+ public void onSubscriptionValue(UaMonitoredItem item, DataValue value) {
+ consumers.forEach(plcSubscriptionEventConsumer -> {
+ PlcResponseCode resultCode = PlcResponseCode.OK;
+ BaseDefaultFieldItem stringItem = null;
+ if(value.getStatusCode() != StatusCode.GOOD){
+ resultCode = PlcResponseCode.NOT_FOUND;
+ }else{
+ stringItem = OpcuaTcpPlcConnection.encodeFieldItem(value);
+
+ }
+ Map> fields = new HashMap<>();
+ Pair newPair = new ImmutablePair<>(resultCode, stringItem);
+ fields.put(fieldName, newPair);
+ PlcSubscriptionEvent event = new DefaultPlcSubscriptionEvent(Instant.now(), fields);
+ plcSubscriptionEventConsumer.accept(event);
+ });
+ }
+
+}
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/model/OpcuaDataTypes.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/model/OpcuaDataTypes.java
new file mode 100644
index 00000000000..af951375b6e
--- /dev/null
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/model/OpcuaDataTypes.java
@@ -0,0 +1,46 @@
+/*
+ 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.model;
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public enum OpcuaDataTypes {
+ BOOL,
+ SByte,
+ Byte,
+ INT16,
+ UINT16,
+ INT32,
+ UINT32,
+ INT64,
+ UINT64,
+ FLOAT,
+ DOUBLE,
+ STATUS_CODE,
+ STRING,
+ DATE_TIME,
+ GUID,
+ BYTE_STRING,
+ XML_ELEMENT,
+ NODE_ID,
+ EXPANDABLE_NODE_ID,
+ QUALIFIED_NAME
+
+}
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/model/OpcuaIdentifierType.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/model/OpcuaIdentifierType.java
new file mode 100644
index 00000000000..3122deb11b7
--- /dev/null
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/model/OpcuaIdentifierType.java
@@ -0,0 +1,49 @@
+/*
+ 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.model;
+
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public enum OpcuaIdentifierType {
+ STRING_IDENTIFIER("s"),
+ NUMBER_IDENTIFIER("i"),
+ GUID_IDENTIFIER("g"),
+ BINARY_IDENTIFIER("BINARY_IDENTIFIER");
+
+ private String text;
+
+ OpcuaIdentifierType(String text) {
+ this.text = text;
+ }
+
+ public String getText() {
+ return this.text;
+ }
+
+ public static OpcuaIdentifierType fromString(String text) {
+ for (OpcuaIdentifierType type : OpcuaIdentifierType.values()) {
+ if (type.text.equalsIgnoreCase(text)) {
+ return type;
+ }
+ }
+ throw new IllegalArgumentException("No constant with text " + text + " found");
+ }
+}
diff --git a/plc4j/drivers/opcua/src/main/resources/META-INF/services/org.apache.plc4x.java.spi.PlcDriver b/plc4j/drivers/opcua/src/main/resources/META-INF/services/org.apache.plc4x.java.spi.PlcDriver
new file mode 100644
index 00000000000..fe96e3ab7cc
--- /dev/null
+++ b/plc4j/drivers/opcua/src/main/resources/META-INF/services/org.apache.plc4x.java.spi.PlcDriver
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+org.apache.plc4x.java.opcua.OpcuaPlcDriver
diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualPLC4XOpcua.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualPLC4XOpcua.java
new file mode 100644
index 00000000000..7708a89e5cd
--- /dev/null
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualPLC4XOpcua.java
@@ -0,0 +1,145 @@
+/*
+ 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 org.apache.plc4x.java.PlcDriverManager;
+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.types.PlcSubscriptionType;
+import org.apache.plc4x.java.base.messages.DefaultPlcSubscriptionRequest;
+import org.apache.plc4x.java.base.model.SubscriptionPlcField;
+import org.apache.plc4x.java.opcua.connection.OpcuaTcpPlcConnection;
+import org.apache.plc4x.java.opcua.protocol.OpcuaField;
+import org.apache.plc4x.java.opcua.protocol.OpcuaPlcFieldHandler;
+
+import java.math.BigInteger;
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.function.Consumer;
+/**
+ * This class serves only as a manual entry point for ad-hoc tests of the OPC UA PLC4J driver.
+ *
+ *
+ * The current version is tested against a public server, which is to be replaced later by a separate instance of the Milo framework.
+ * Afterwards the code represented here will be used as an example for the introduction page.
+ *
+ * TODO: replace current public server with local Milo instance
+ *
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public class ManualPLC4XOpcua {
+ private static final String BOOL_IDENTIFIER = "ns=2;i=10844";
+ private static final String BYTE_STRING_IDENTIFIER = "ns=2;i=10858";
+ private static final String BYTE_IDENTIFIER = "ns=2;i=10846";
+ private static final String DOUBLE_IDENTIFIER = "ns=2;i=10854";
+ private static final String FLOAT_IDENTIFIER = "ns=2;i=10853";
+ private static final String INT16_IDENTIFIER = "ns=2;i=10847";
+ private static final String INT32_IDENTIFIER = "ns=2;i=10849";
+ private static final String INT64_IDENTIFIER = "ns=2;i=10851";
+ private static final String INTEGER_IDENTIFIER = "ns=2;i=10869";
+ private static final String SBYTE_IDENTIFIER = "ns=2;i=10845";
+ private static final String STRING_IDENTIFIER = "ns=2;i=10855";
+ private static final String UINT16_IDENTIFIER = "ns=2;i=10848";
+ private static final String UINT32_IDENTIFIER = "ns=2;i=10850";
+ private static final String UINT64_IDENTIFIER = "ns=2;i=10852";
+ private static final String UINTEGER_IDENTIFIER = "ns=2;i=10870";
+ private static final String DOES_NOT_EXIST_IDENTIFIER = "ns=2;i=12512623";
+
+
+ public static void main(String args[]){
+
+
+ OpcuaTcpPlcConnection opcuaConnection = null;
+ OpcuaPlcFieldHandler fieldH = new OpcuaPlcFieldHandler();
+ PlcField field = fieldH.createField("ns=2;i=10855");
+ try {
+ opcuaConnection = (OpcuaTcpPlcConnection)
+ new PlcDriverManager().getConnection("opcua:tcp://opcua.demo-this.com:51210/UA/SampleServer");
+
+ } catch (PlcConnectionException e) {
+ e.printStackTrace();
+ }
+ try {
+ PlcReadRequest.Builder builder = opcuaConnection.readRequestBuilder();
+ builder.addItem("Bool", BOOL_IDENTIFIER);
+ builder.addItem("ByteString", BYTE_STRING_IDENTIFIER);
+ builder.addItem("Byte", BYTE_IDENTIFIER);
+ builder.addItem("Double", DOUBLE_IDENTIFIER);
+ builder.addItem("Float", FLOAT_IDENTIFIER);
+ builder.addItem("Int16", INT16_IDENTIFIER);
+ builder.addItem("Int32", INT32_IDENTIFIER);
+ builder.addItem("Int64", INT64_IDENTIFIER);
+ builder.addItem("Integer", INTEGER_IDENTIFIER);
+ builder.addItem("SByte", SBYTE_IDENTIFIER);
+ builder.addItem("String", STRING_IDENTIFIER);
+ builder.addItem("UInt16", UINT16_IDENTIFIER);
+ builder.addItem("UInt32", UINT32_IDENTIFIER);
+ builder.addItem("UInt64", UINT64_IDENTIFIER);
+ builder.addItem("UInteger", UINTEGER_IDENTIFIER);
+
+ builder.addItem("DoesNotExists", DOES_NOT_EXIST_IDENTIFIER);
+
+ PlcReadRequest request = builder.build();
+ PlcReadResponse response = opcuaConnection.read(request).get();
+ Collection coll = response.getAllStrings("String");
+
+ PlcWriteRequest.Builder wBuilder = opcuaConnection.writeRequestBuilder();
+ wBuilder.addItem("w-Bool", BOOL_IDENTIFIER, true);
+ wBuilder.addItem("w-ByteString", BYTE_STRING_IDENTIFIER, "TEST".getBytes());
+ wBuilder.addItem("w-Byte", BYTE_IDENTIFIER, (byte)1);
+ wBuilder.addItem("w-Double", DOUBLE_IDENTIFIER, (double)0.25);
+ wBuilder.addItem("w-Float", FLOAT_IDENTIFIER, (float)0.25);
+ wBuilder.addItem("w-INT16", INT16_IDENTIFIER, (short)12);
+ wBuilder.addItem("w-Int32", INT32_IDENTIFIER, (int)314);
+ wBuilder.addItem("w-Int64", INT64_IDENTIFIER, (long)123125);
+ wBuilder.addItem("w-Integer", INTEGER_IDENTIFIER, (int)314);
+ wBuilder.addItem("w-SByte", SBYTE_IDENTIFIER, (short)23);
+ wBuilder.addItem("w-String", STRING_IDENTIFIER, "TEST");
+ wBuilder.addItem("w-UInt16", UINT16_IDENTIFIER, (int)222);
+ wBuilder.addItem("w-UInt32", UINT32_IDENTIFIER, (long)21412);
+ wBuilder.addItem("w-UInt64", UINT64_IDENTIFIER, new BigInteger("1245152"));
+ wBuilder.addItem("w-UInteger", UINTEGER_IDENTIFIER, new BigInteger("1245152"));
+ PlcWriteRequest writeRequest = wBuilder.build();
+ PlcWriteResponse wResponse = opcuaConnection.write(writeRequest).get();
+
+ PlcSubscriptionResponse subResp = opcuaConnection.subscribe(new DefaultPlcSubscriptionRequest(
+ opcuaConnection,
+ new LinkedHashMap<>(
+ Collections.singletonMap("field1",
+ new SubscriptionPlcField(PlcSubscriptionType.CHANGE_OF_STATE, OpcuaField.of(STRING_IDENTIFIER), Duration.of(1, ChronoUnit.SECONDS)))
+ )
+ )).get();
+
+ Consumer consumer = plcSubscriptionEvent -> System.out.println(plcSubscriptionEvent.toString());
+ PlcConsumerRegistration registration = opcuaConnection.register(consumer, subResp.getSubscriptionHandles());
+ Thread.sleep(7000);
+ registration.unregister();
+ Thread.sleep(20000);
+ opcuaConnection.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
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 00000000000..8c502ed2fed
--- /dev/null
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java
@@ -0,0 +1,67 @@
+/*
+ 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 org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.plc4x.java.opcua.OpcuaPlcDriver.INET_ADDRESS_PATTERN;
+import static org.apache.plc4x.java.opcua.OpcuaPlcDriver.OPCUA_URI_PATTERN;
+import static org.apache.plc4x.java.opcua.UtilsTest.assertMatching;
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public class OpcuaPlcDriverTest {
+ @BeforeEach
+ public void before() {
+ }
+
+ @AfterEach
+ public void after() {
+
+ }
+
+ @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(OPCUA_URI_PATTERN, "opcua:tcp://localhost");
+ assertMatching(OPCUA_URI_PATTERN, "opcua:tcp://localhost:3131");
+ assertMatching(OPCUA_URI_PATTERN, "opcua:tcp://www.google.de");
+ assertMatching(OPCUA_URI_PATTERN, "opcua:tcp://www.google.de:443");
+ assertMatching(OPCUA_URI_PATTERN, "opcua:tcp://127.0.0.1");
+ assertMatching(OPCUA_URI_PATTERN, "opcua:tcp://127.0.0.1:251");
+ assertMatching(OPCUA_URI_PATTERN, "opcua:tcp://254.254.254.254:1337");
+ assertMatching(OPCUA_URI_PATTERN, "opcua:tcp://254.254.254.254");
+
+
+ }
+
+}
diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/UtilsTest.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/UtilsTest.java
new file mode 100644
index 00000000000..2e84a0b63a6
--- /dev/null
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/UtilsTest.java
@@ -0,0 +1,40 @@
+/*
+ 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 java.util.regex.Pattern;
+
+import static org.junit.Assert.fail;
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+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 00000000000..5a42c5e28ae
--- /dev/null
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnectionTest.java
@@ -0,0 +1,36 @@
+/*
+ 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.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public class OpcuaTcpPlcConnectionTest {
+ @BeforeEach
+ public void before() {
+ }
+
+ @AfterEach
+ public void after() {
+
+ }
+}
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 00000000000..828a0e6ac80
--- /dev/null
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaFieldTest.java
@@ -0,0 +1,51 @@
+/*
+ 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.protocol.OpcuaField.ADDRESS_PATTERN;
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public class OpcuaFieldTest {
+
+ @BeforeEach
+ public void before() {
+ }
+
+ @AfterEach
+ public void after() {
+
+ }
+
+ @Test
+ public void testOpcuaAddressPattern() {
+
+ assertMatching(ADDRESS_PATTERN, "ns=2;i=10846");
+ assertMatching(ADDRESS_PATTERN, "ns=2;s=test.variable.name.inspect");
+ assertMatching(ADDRESS_PATTERN, "ns=2;g=09087e75-8e5e-499b-954f-f2a8624db28a");
+ assertMatching(ADDRESS_PATTERN, "ns=2;b=asvaewavarahreb==");
+
+ }
+}
diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaPlcFieldHandlerTest.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaPlcFieldHandlerTest.java
new file mode 100644
index 00000000000..044de340e3f
--- /dev/null
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaPlcFieldHandlerTest.java
@@ -0,0 +1,36 @@
+/*
+ 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;
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public class OpcuaPlcFieldHandlerTest {
+ @BeforeEach
+ public void before() {
+ }
+
+ @AfterEach
+ public void after() {
+
+ }
+}
diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java
new file mode 100644
index 00000000000..153d4ca85dc
--- /dev/null
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java
@@ -0,0 +1,36 @@
+/*
+ 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;
+/**
+ * @author Matthias Milan Stlrljic
+ * Created by Matthias Milan Stlrljic on 10.05.2019
+ */
+public class OpcuaSubscriptionHandleTest {
+ @BeforeEach
+ public void before() {
+ }
+
+ @AfterEach
+ public void after() {
+
+ }
+}
diff --git a/plc4j/drivers/pom.xml b/plc4j/drivers/pom.xml
index 34f60a25c27..cf47c0761ca 100644
--- a/plc4j/drivers/pom.xml
+++ b/plc4j/drivers/pom.xml
@@ -39,6 +39,7 @@
modbus
s7
simulated
+ opcua
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/messages/DefaultPlcWriteRequest.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/messages/DefaultPlcWriteRequest.java
index 67dc29f5d01..641bc7eb5f0 100644
--- a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/messages/DefaultPlcWriteRequest.java
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/messages/DefaultPlcWriteRequest.java
@@ -209,12 +209,12 @@ public Builder addItem(String name, String fieldQuery, LocalDateTime... values)
@Override
public Builder addItem(String name, String fieldQuery, byte[]... values) {
- return addItem(name, fieldQuery, values, fieldHandler::encodeDateTime);
+ return addItem(name, fieldQuery, values, fieldHandler::encodeByteArray);
}
@Override
public Builder addItem(String name, String fieldQuery, Byte[]... values) {
- return addItem(name, fieldQuery, values, fieldHandler::encodeDateTime);
+ return addItem(name, fieldQuery, values, fieldHandler::encodeByteArray);
}
@Override