Skip to content

Commit

Permalink
First draft for DataTypeDefinition-based dynamic datatypes and codecs
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinherron committed Sep 8, 2022
1 parent 9525f0c commit 35231e9
Show file tree
Hide file tree
Showing 14 changed files with 1,279 additions and 184 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 the Eclipse Milo Authors
* Copyright (c) 2022 the Eclipse Milo Authors
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -109,22 +109,24 @@ public void run() {
OpcUaClient client = createClient();

// For the sake of the examples we will create mutual trust between the client and
// server so we can run them with security enabled by default.
// server, so we can run them with security enabled by default.
// If the client example is pointed at another server then the rejected certificate
// will need to be moved from the security "pki/rejected" directory to the
// "pki/trusted/certs" directory.

// Make the example server trust the example client certificate by default.
client.getConfig().getCertificate().ifPresent(
certificate ->
exampleServer.getServer().getConfig().getTrustListManager().addTrustedCertificate(certificate)
);

// Make the example client trust the example server certificate by default.
exampleServer.getServer().getConfig().getCertificateManager().getCertificates().forEach(
certificate ->
trustListManager.addTrustedCertificate(certificate)
);
if (serverRequired && exampleServer != null) {
// Make the example server trust the example client certificate by default.
client.getConfig().getCertificate().ifPresent(
certificate ->
exampleServer.getServer().getConfig().getTrustListManager().addTrustedCertificate(certificate)
);

// Make the example client trust the example server certificate by default.
exampleServer.getServer().getConfig().getCertificateManager().getCertificates().forEach(
certificate ->
trustListManager.addTrustedCertificate(certificate)
);
}

future.whenCompleteAsync((c, ex) -> {
if (ex != null) {
Expand Down
Expand Up @@ -17,7 +17,8 @@
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.methods.UaMethod;
import org.eclipse.milo.opcua.sdk.client.nodes.UaObjectNode;
import org.eclipse.milo.opcua.sdk.core.DataTypeTree;
import org.eclipse.milo.opcua.sdk.core.types.DataType;
import org.eclipse.milo.opcua.sdk.core.types.DataTypeTree;
import org.eclipse.milo.opcua.stack.core.UaException;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
Expand Down Expand Up @@ -72,7 +73,7 @@ private void logArguments(Argument[] arguments, DataTypeTree dataTypeTree, boole
logger.info("{} arguments:", input ? "Input" : "Output");
for (Argument argument : arguments) {
NodeId dataTypeId = argument.getDataType();
DataTypeTree.DataType dataType = dataTypeTree.getDataType(dataTypeId);
DataType dataType = dataTypeTree.getDataType(dataTypeId);
assert dataType != null;
String dataTypeName = dataType.getBrowseName().getName();

Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 the Eclipse Milo Authors
* Copyright (c) 2022 the Eclipse Milo Authors
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand All @@ -15,6 +15,7 @@
import org.eclipse.milo.opcua.binaryschema.GenericBsdParser;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.dtd.DataTypeDictionarySessionInitializer;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
Expand Down Expand Up @@ -70,4 +71,9 @@ public String getEndpointUrl() {
return "opc.tcp://localhost:48010";
}

@Override
public SecurityPolicy getSecurityPolicy() {
return SecurityPolicy.None;
}

}
@@ -0,0 +1,127 @@
/*
* Copyright (c) 2022 the Eclipse Milo Authors
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/

package org.eclipse.milo.examples.client;

import java.util.Random;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

import org.eclipse.milo.opcua.sdk.client.DataTypeTreeSessionInitializer;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.core.types.DynamicEnum;
import org.eclipse.milo.opcua.sdk.core.types.DynamicStruct;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An example that shows reading the value of a node whose DataType is a custom structure type.
* <p>
* Requires the Unified Automation CPP Demo server be running and the endpoint URL be pointing to it.
*/
public class UnifiedAutomationReadCustomDataTypeExample2 implements ClientExample {

public static void main(String[] args) throws Exception {
UnifiedAutomationReadCustomDataTypeExample2 example =
new UnifiedAutomationReadCustomDataTypeExample2();

new ClientExampleRunner(example, false).run();
}

private final Logger logger = LoggerFactory.getLogger(getClass());

@Override
public void run(OpcUaClient client, CompletableFuture<OpcUaClient> future) throws Exception {
// DataTypeTree reads DataTypeDefinition attributes while traversing the type hierarchy
// and, by default, registers codecs with the client's dynamic DataTypeManager.
client.addSessionInitializer(new DataTypeTreeSessionInitializer());

client.connect().get();

readWriteReadPerson(client);
readWriteReadWorkOrder(client);

future.complete(client);
}

private void readWriteReadPerson(OpcUaClient client) throws Exception {
NodeId nodeId = NodeId.parse("ns=2;s=Person1");

DynamicStruct value = readValue(client, nodeId);
logger.info("Person1: {}", value);

Random r = new Random();
DynamicEnum gender = (DynamicEnum) value.getMembers().get("Gender");
value.getMembers().put("Name", "Fat Boy" + r.nextInt(100));
value.getMembers().put("Gender", new DynamicEnum(gender.getDataType(), r.nextInt(2)));

StatusCode status = writeValue(client, nodeId, value);
System.out.println("write status: " + status);

value = readValue(client, nodeId);
logger.info("Person1': {}", value);
}

private void readWriteReadWorkOrder(OpcUaClient client) throws Exception {
NodeId nodeId = NodeId.parse("ns=2;s=Demo.Static.Scalar.WorkOrder");

DynamicStruct value = readValue(client, nodeId);
logger.info("WorkOrder: {}", value);

value.getMembers().put("ID", UUID.randomUUID());

StatusCode status = writeValue(client, nodeId, value);
System.out.println("write status: " + status);

value = readValue(client, nodeId);
logger.info("WorkOrder': {}", value);
}

private static DynamicStruct readValue(OpcUaClient client, NodeId nodeId) throws Exception {
DataValue dataValue = client.readValue(
0.0,
TimestampsToReturn.Neither,
nodeId
).get();

ExtensionObject xo = (ExtensionObject) dataValue.getValue().getValue();

return (DynamicStruct) xo.decode(client.getDynamicSerializationContext());
}

private static StatusCode writeValue(OpcUaClient client, NodeId nodeId, DynamicStruct value) throws Exception {
ExtensionObject xo = ExtensionObject.encodeDefaultBinary(
client.getDynamicSerializationContext(),
value,
value.getDataType().getBinaryEncodingId()
);

return client.writeValue(nodeId, DataValue.valueOnly(new Variant(xo))).get();
}

@Override
public String getEndpointUrl() {
// Change this if UaCPPServer is running somewhere other than localhost.
return "opc.tcp://localhost:48010";
}

@Override
public SecurityPolicy getSecurityPolicy() {
return SecurityPolicy.None;
}

}
Expand Up @@ -13,6 +13,8 @@
import java.util.Objects;

import org.eclipse.milo.opcua.sdk.client.DataTypeTreeBuilder;
import org.eclipse.milo.opcua.sdk.core.types.DataType;
import org.eclipse.milo.opcua.sdk.core.types.DataTypeTree;
import org.eclipse.milo.opcua.sdk.test.AbstractClientServerTest;
import org.eclipse.milo.opcua.stack.core.BuiltinDataType;
import org.eclipse.milo.opcua.stack.core.NodeIds;
Expand Down Expand Up @@ -86,7 +88,7 @@ public void testGetBackingClass() {
}

private void checkSubtypes(NodeId dataTypeId, Class<?> expectedBackingClass) {
Tree<DataTypeTree.DataType> nodeIdNode = dataTypeTree.getTreeNode(dataTypeId);
Tree<DataType> nodeIdNode = dataTypeTree.getTreeNode(dataTypeId);
assertNotNull(nodeIdNode);
nodeIdNode.traverse(dataType -> {
Class<?> backingClass = dataTypeTree.getBackingClass(dataType.getNodeId());
Expand Down Expand Up @@ -162,7 +164,7 @@ public void testIsAssignable() {

@Test
public void testGetEncodingIds() {
Tree<DataTypeTree.DataType> treeNode = dataTypeTree.getTreeNode(NodeIds.Structure);
Tree<DataType> treeNode = dataTypeTree.getTreeNode(NodeIds.Structure);
assertNotNull(treeNode);

treeNode.traverse(dataType -> {
Expand All @@ -179,7 +181,7 @@ public void testGetEncodingIds() {

@Test
public void enumerationsHaveDataTypeDefinitions() {
Tree<DataTypeTree.DataType> treeNode = dataTypeTree.getTreeNode(NodeIds.Enumeration);
Tree<DataType> treeNode = dataTypeTree.getTreeNode(NodeIds.Enumeration);
assertNotNull(treeNode);

treeNode.traverse(dataType -> {
Expand All @@ -192,7 +194,7 @@ public void enumerationsHaveDataTypeDefinitions() {

@Test
public void structuresHaveDataTypeDefinitions() {
Tree<DataTypeTree.DataType> treeNode = dataTypeTree.getTreeNode(NodeIds.Structure);
Tree<DataType> treeNode = dataTypeTree.getTreeNode(NodeIds.Structure);
assertNotNull(treeNode);

treeNode.traverse(dataType -> {
Expand Down
Expand Up @@ -16,7 +16,8 @@
import java.util.concurrent.ExecutionException;
import java.util.stream.Stream;

import org.eclipse.milo.opcua.sdk.core.DataTypeTree;
import org.eclipse.milo.opcua.sdk.core.types.DataType;
import org.eclipse.milo.opcua.sdk.core.types.DataTypeTree;
import org.eclipse.milo.opcua.stack.client.UaStackClient;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.NamespaceTable;
Expand Down Expand Up @@ -118,9 +119,9 @@ public static CompletableFuture<DataTypeTree> buildAsync(OpcUaClient client) {
* @return a {@link DataTypeTree}.
*/
public static CompletableFuture<DataTypeTree> buildAsync(UaStackClient client, OpcUaSession session) {
Tree<DataTypeTree.DataType> root = new Tree<>(
Tree<DataType> root = new Tree<>(
null,
new DataTypeTree.DataType(
new DataType(
QualifiedName.parse("0:BaseDataType"),
NodeIds.BaseDataType,
null,
Expand Down Expand Up @@ -167,7 +168,7 @@ private static CompletableFuture<NamespaceTable> readNamespaceTable(UaStackClien
}

private static CompletableFuture<Unit> addChildren(
Tree<DataTypeTree.DataType> tree,
Tree<DataType> tree,
UaStackClient client,
OpcUaSession session,
NamespaceTable namespaceTable
Expand All @@ -186,8 +187,8 @@ private static CompletableFuture<Unit> addChildren(
)
);

CompletableFuture<List<DataTypeTree.DataType>> dataTypesFuture = subtypes.thenCompose(references -> {
Stream<CompletableFuture<DataTypeTree.DataType>> dataTypeFutures =
CompletableFuture<List<DataType>> dataTypesFuture = subtypes.thenCompose(references -> {
Stream<CompletableFuture<DataType>> dataTypeFutures =
references.stream().map(dataTypeReference -> {
NodeId dataTypeId = dataTypeReference.getNodeId()
.toNodeId(namespaceTable)
Expand Down Expand Up @@ -227,7 +228,7 @@ private static CompletableFuture<Unit> addChildren(
}
}

return new DataTypeTree.DataType(
return new DataType(
dataTypeReference.getBrowseName(),
dataTypeId,
binaryEncodingId,
Expand Down

0 comments on commit 35231e9

Please sign in to comment.