123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850 |
- package at.acdp.opcur.opc;
- /*
- * Copyright (c) 2016 Kevin Herron
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
- *
- * The Eclipse Public License is available at
- * http://www.eclipse.org/legal/epl-v10.html
- * and the Eclipse Distribution License is available at
- * http://www.eclipse.org/org/documents/edl-v10.html.
- */
- import java.lang.reflect.Array;
- import java.util.List;
- import java.util.Optional;
- import java.util.Random;
- import java.util.UUID;
- import java.util.concurrent.CompletableFuture;
- import com.google.common.collect.Lists;
- import org.eclipse.milo.opcua.sdk.core.AccessLevel;
- import org.eclipse.milo.opcua.sdk.core.Reference;
- import org.eclipse.milo.opcua.sdk.core.ValueRank;
- import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
- import org.eclipse.milo.opcua.sdk.server.api.AccessContext;
- import org.eclipse.milo.opcua.sdk.server.api.DataItem;
- import org.eclipse.milo.opcua.sdk.server.api.MethodInvocationHandler;
- import org.eclipse.milo.opcua.sdk.server.api.MonitoredItem;
- import org.eclipse.milo.opcua.sdk.server.api.Namespace;
- import org.eclipse.milo.opcua.sdk.server.api.nodes.VariableNode;
- import org.eclipse.milo.opcua.sdk.server.model.nodes.variables.AnalogItemNode;
- import org.eclipse.milo.opcua.sdk.server.nodes.AttributeContext;
- import org.eclipse.milo.opcua.sdk.server.nodes.NodeFactory;
- import org.eclipse.milo.opcua.sdk.server.nodes.ServerNode;
- import org.eclipse.milo.opcua.sdk.server.nodes.UaDataTypeNode;
- import org.eclipse.milo.opcua.sdk.server.nodes.UaFolderNode;
- import org.eclipse.milo.opcua.sdk.server.nodes.UaMethodNode;
- import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectNode;
- import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectTypeNode;
- import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode;
- import org.eclipse.milo.opcua.sdk.server.nodes.delegates.AttributeDelegate;
- import org.eclipse.milo.opcua.sdk.server.nodes.delegates.AttributeDelegateChain;
- import org.eclipse.milo.opcua.sdk.server.util.AnnotationBasedInvocationHandler;
- import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel;
- import org.eclipse.milo.opcua.stack.core.AttributeId;
- import org.eclipse.milo.opcua.stack.core.Identifiers;
- import org.eclipse.milo.opcua.stack.core.StatusCodes;
- import org.eclipse.milo.opcua.stack.core.UaException;
- import org.eclipse.milo.opcua.stack.core.types.OpcUaBinaryDataTypeDictionary;
- import org.eclipse.milo.opcua.stack.core.types.OpcUaDataTypeManager;
- import org.eclipse.milo.opcua.stack.core.types.builtin.ByteString;
- import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
- import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime;
- import org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject;
- import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
- import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
- import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName;
- 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.builtin.XmlElement;
- 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.NodeClass;
- import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
- import org.eclipse.milo.opcua.stack.core.types.structured.Range;
- import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
- import org.eclipse.milo.opcua.stack.core.types.structured.WriteValue;
- import org.eclipse.milo.opcua.stack.core.util.FutureUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ubyte;
- import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
- import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ulong;
- import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ushort;
- public class MyNamespace implements Namespace {
- public static final String NAMESPACE_URI = "urn:eclipse:milo:hello-world";
- private static final Object[][] STATIC_SCALAR_NODES = new Object[][]{
- {"Boolean", Identifiers.Boolean, new Variant(false)},
- {"Byte", Identifiers.Byte, new Variant(ubyte(0x00))},
- {"SByte", Identifiers.SByte, new Variant((byte) 0x00)},
- {"Integer", Identifiers.Integer, new Variant(32)},
- {"Int16", Identifiers.Int16, new Variant((short) 16)},
- {"Int32", Identifiers.Int32, new Variant(32)},
- {"Int64", Identifiers.Int64, new Variant(64L)},
- {"UInteger", Identifiers.UInteger, new Variant(uint(32))},
- {"UInt16", Identifiers.UInt16, new Variant(ushort(16))},
- {"UInt32", Identifiers.UInt32, new Variant(uint(32))},
- {"UInt64", Identifiers.UInt64, new Variant(ulong(64L))},
- {"Float", Identifiers.Float, new Variant(3.14f)},
- {"Double", Identifiers.Double, new Variant(3.14d)},
- {"String", Identifiers.String, new Variant("string value")},
- {"DateTime", Identifiers.DateTime, new Variant(DateTime.now())},
- {"Guid", Identifiers.Guid, new Variant(UUID.randomUUID())},
- {"ByteString", Identifiers.ByteString, new Variant(new ByteString(new byte[]{0x01, 0x02, 0x03, 0x04}))},
- {"XmlElement", Identifiers.XmlElement, new Variant(new XmlElement("<a>hello</a>"))},
- {"LocalizedText", Identifiers.LocalizedText, new Variant(LocalizedText.english("localized text"))},
- {"QualifiedName", Identifiers.QualifiedName, new Variant(new QualifiedName(1234, "defg"))},
- {"NodeId", Identifiers.NodeId, new Variant(new NodeId(1234, "abcd"))},
- {"Duration", Identifiers.Duration, new Variant(1.0)},
- {"UtcTime", Identifiers.UtcTime, new Variant(DateTime.now())},
- };
- private static final Object[][] STATIC_ARRAY_NODES = new Object[][]{
- {"BooleanArray", Identifiers.Boolean, false},
- {"ByteArray", Identifiers.Byte, ubyte(0)},
- {"SByteArray", Identifiers.SByte, (byte) 0x00},
- {"Int16Array", Identifiers.Int16, (short) 16},
- {"Int32Array", Identifiers.Int32, 32},
- {"Int64Array", Identifiers.Int64, 64L},
- {"UInt16Array", Identifiers.UInt16, ushort(16)},
- {"UInt32Array", Identifiers.UInt32, uint(32)},
- {"UInt64Array", Identifiers.UInt64, ulong(64L)},
- {"FloatArray", Identifiers.Float, 3.14f},
- {"DoubleArray", Identifiers.Double, 3.14d},
- {"StringArray", Identifiers.String, "string value"},
- {"DateTimeArray", Identifiers.DateTime, DateTime.now()},
- {"GuidArray", Identifiers.Guid, UUID.randomUUID()},
- {"ByteStringArray", Identifiers.ByteString, new ByteString(new byte[]{0x01, 0x02, 0x03, 0x04})},
- {"XmlElementArray", Identifiers.XmlElement, new XmlElement("<a>hello</a>")},
- {"LocalizedTextArray", Identifiers.LocalizedText, LocalizedText.english("localized text")},
- {"QualifiedNameArray", Identifiers.QualifiedName, new QualifiedName(1234, "defg")},
- {"NodeIdArray", Identifiers.NodeId, new NodeId(1234, "abcd")}
- };
- private final Logger logger = LoggerFactory.getLogger(getClass());
- private final Random random = new Random();
- private final SubscriptionModel subscriptionModel;
- private final NodeFactory nodeFactory;
- private final OpcUaServer server;
- private final UShort namespaceIndex;
- public MyNamespace(OpcUaServer server, UShort namespaceIndex) {
- this.server = server;
- this.namespaceIndex = namespaceIndex;
- subscriptionModel = new SubscriptionModel(server, this);
- nodeFactory = new NodeFactory(
- server.getNodeMap(),
- server.getObjectTypeManager(),
- server.getVariableTypeManager()
- );
- try {
- // Create a "HelloWorld" folder and add it to the node manager
- NodeId folderNodeId = new NodeId(namespaceIndex, "HelloWorld");
- UaFolderNode folderNode = new UaFolderNode(
- server.getNodeMap(),
- folderNodeId,
- new QualifiedName(namespaceIndex, "HelloWorld"),
- LocalizedText.english("HelloWorld")
- );
- server.getNodeMap().addNode(folderNode);
- // Make sure our new folder shows up under the server's Objects folder
- server.getUaNamespace().addReference(
- Identifiers.ObjectsFolder,
- Identifiers.Organizes,
- true,
- folderNodeId.expanded(),
- NodeClass.Object
- );
- // Add the rest of the nodes
- addVariableNodes(folderNode);
- addMethodNode(folderNode);
- addCustomDataTypeVariable(folderNode);
- addCustomObjectTypeAndInstance(folderNode);
- } catch (UaException e) {
- logger.error("Error adding nodes: {}", e.getMessage(), e);
- }
- }
- @Override
- public UShort getNamespaceIndex() {
- return namespaceIndex;
- }
- @Override
- public String getNamespaceUri() {
- return NAMESPACE_URI;
- }
- private void addVariableNodes(UaFolderNode rootNode) {
- addArrayNodes(rootNode);
- addScalarNodes(rootNode);
- addAdminReadableNodes(rootNode);
- addAdminWritableNodes(rootNode);
- addDynamicNodes(rootNode);
- addDataAccessNodes(rootNode);
- addWriteOnlyNodes(rootNode);
- }
- private void addArrayNodes(UaFolderNode rootNode) {
- UaFolderNode arrayTypesFolder = new UaFolderNode(
- server.getNodeMap(),
- new NodeId(namespaceIndex, "HelloWorld/ArrayTypes"),
- new QualifiedName(namespaceIndex, "ArrayTypes"),
- LocalizedText.english("ArrayTypes")
- );
- server.getNodeMap().addNode(arrayTypesFolder);
- rootNode.addOrganizes(arrayTypesFolder);
- for (Object[] os : STATIC_ARRAY_NODES) {
- String name = (String) os[0];
- NodeId typeId = (NodeId) os[1];
- Object value = os[2];
- Object array = Array.newInstance(value.getClass(), 5);
- for (int i = 0; i < 5; i++) {
- Array.set(array, i, value);
- }
- Variant variant = new Variant(array);
- UaVariableNode node = new UaVariableNode.UaVariableNodeBuilder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "HelloWorld/ArrayTypes/" + name))
- .setAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setUserAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setBrowseName(new QualifiedName(namespaceIndex, name))
- .setDisplayName(LocalizedText.english(name))
- .setDataType(typeId)
- .setTypeDefinition(Identifiers.BaseDataVariableType)
- .setValueRank(ValueRank.OneDimension.getValue())
- .setArrayDimensions(new UInteger[]{uint(0)})
- .build();
- node.setValue(new DataValue(variant));
- node.setAttributeDelegate(new ValueLoggingDelegate());
- server.getNodeMap().addNode(node);
- arrayTypesFolder.addOrganizes(node);
- }
- }
- private void addScalarNodes(UaFolderNode rootNode) {
- UaFolderNode scalarTypesFolder = new UaFolderNode(
- server.getNodeMap(),
- new NodeId(namespaceIndex, "HelloWorld/ScalarTypes"),
- new QualifiedName(namespaceIndex, "ScalarTypes"),
- LocalizedText.english("ScalarTypes")
- );
- server.getNodeMap().addNode(scalarTypesFolder);
- rootNode.addOrganizes(scalarTypesFolder);
- for (Object[] os : STATIC_SCALAR_NODES) {
- String name = (String) os[0];
- NodeId typeId = (NodeId) os[1];
- Variant variant = (Variant) os[2];
- UaVariableNode node = new UaVariableNode.UaVariableNodeBuilder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "HelloWorld/ScalarTypes/" + name))
- .setAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setUserAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setBrowseName(new QualifiedName(namespaceIndex, name))
- .setDisplayName(LocalizedText.english(name))
- .setDataType(typeId)
- .setTypeDefinition(Identifiers.BaseDataVariableType)
- .build();
- node.setValue(new DataValue(variant));
- node.setAttributeDelegate(new ValueLoggingDelegate());
- server.getNodeMap().addNode(node);
- scalarTypesFolder.addOrganizes(node);
- }
- }
- private void addWriteOnlyNodes(UaFolderNode rootNode) {
- UaFolderNode writeOnlyFolder = new UaFolderNode(
- server.getNodeMap(),
- new NodeId(namespaceIndex, "HelloWorld/WriteOnly"),
- new QualifiedName(namespaceIndex, "WriteOnly"),
- LocalizedText.english("WriteOnly")
- );
- server.getNodeMap().addNode(writeOnlyFolder);
- rootNode.addOrganizes(writeOnlyFolder);
- String name = "String";
- UaVariableNode node = new UaVariableNode.UaVariableNodeBuilder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "HelloWorld/WriteOnly/" + name))
- .setAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.WRITE_ONLY)))
- .setUserAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.WRITE_ONLY)))
- .setBrowseName(new QualifiedName(namespaceIndex, name))
- .setDisplayName(LocalizedText.english(name))
- .setDataType(Identifiers.String)
- .setTypeDefinition(Identifiers.BaseDataVariableType)
- .build();
- node.setValue(new DataValue(new Variant("can't read this")));
- server.getNodeMap().addNode(node);
- writeOnlyFolder.addOrganizes(node);
- }
- private void addAdminReadableNodes(UaFolderNode rootNode) {
- UaFolderNode adminFolder = new UaFolderNode(
- server.getNodeMap(),
- new NodeId(namespaceIndex, "HelloWorld/OnlyAdminCanRead"),
- new QualifiedName(namespaceIndex, "OnlyAdminCanRead"),
- LocalizedText.english("OnlyAdminCanRead")
- );
- server.getNodeMap().addNode(adminFolder);
- rootNode.addOrganizes(adminFolder);
- String name = "String";
- UaVariableNode node = new UaVariableNode.UaVariableNodeBuilder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "HelloWorld/OnlyAdminCanRead/" + name))
- .setAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setBrowseName(new QualifiedName(namespaceIndex, name))
- .setDisplayName(LocalizedText.english(name))
- .setDataType(Identifiers.String)
- .setTypeDefinition(Identifiers.BaseDataVariableType)
- .build();
- node.setValue(new DataValue(new Variant("shh... don't tell the lusers")));
- node.setAttributeDelegate(new RestrictedAccessDelegate(identity -> {
- if ("admin".equals(identity)) {
- return AccessLevel.READ_WRITE;
- } else {
- return AccessLevel.NONE;
- }
- }));
- server.getNodeMap().addNode(node);
- adminFolder.addOrganizes(node);
- }
- private void addAdminWritableNodes(UaFolderNode rootNode) {
- UaFolderNode adminFolder = new UaFolderNode(
- server.getNodeMap(),
- new NodeId(namespaceIndex, "HelloWorld/OnlyAdminCanWrite"),
- new QualifiedName(namespaceIndex, "OnlyAdminCanWrite"),
- LocalizedText.english("OnlyAdminCanWrite")
- );
- server.getNodeMap().addNode(adminFolder);
- rootNode.addOrganizes(adminFolder);
- String name = "String";
- UaVariableNode node = new UaVariableNode.UaVariableNodeBuilder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "HelloWorld/OnlyAdminCanWrite/" + name))
- .setAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setBrowseName(new QualifiedName(namespaceIndex, name))
- .setDisplayName(LocalizedText.english(name))
- .setDataType(Identifiers.String)
- .setTypeDefinition(Identifiers.BaseDataVariableType)
- .build();
- node.setValue(new DataValue(new Variant("admin was here")));
- node.setAttributeDelegate(new RestrictedAccessDelegate(identity -> {
- if ("admin".equals(identity)) {
- return AccessLevel.READ_WRITE;
- } else {
- return AccessLevel.READ_ONLY;
- }
- }));
- server.getNodeMap().addNode(node);
- adminFolder.addOrganizes(node);
- }
- private void addDynamicNodes(UaFolderNode rootNode) {
- UaFolderNode dynamicFolder = new UaFolderNode(
- server.getNodeMap(),
- new NodeId(namespaceIndex, "HelloWorld/Dynamic"),
- new QualifiedName(namespaceIndex, "Dynamic"),
- LocalizedText.english("Dynamic")
- );
- server.getNodeMap().addNode(dynamicFolder);
- rootNode.addOrganizes(dynamicFolder);
- // Dynamic Boolean
- {
- String name = "Boolean";
- NodeId typeId = Identifiers.Boolean;
- Variant variant = new Variant(false);
- UaVariableNode node = new UaVariableNode.UaVariableNodeBuilder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "HelloWorld/Dynamic/" + name))
- .setAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setBrowseName(new QualifiedName(namespaceIndex, name))
- .setDisplayName(LocalizedText.english(name))
- .setDataType(typeId)
- .setTypeDefinition(Identifiers.BaseDataVariableType)
- .build();
- node.setValue(new DataValue(variant));
- AttributeDelegate delegate = AttributeDelegateChain.create(
- new AttributeDelegate() {
- @Override
- public DataValue getValue(AttributeContext context, VariableNode node) throws UaException {
- return new DataValue(new Variant(random.nextBoolean()));
- }
- },
- ValueLoggingDelegate::new
- );
- node.setAttributeDelegate(delegate);
- server.getNodeMap().addNode(node);
- dynamicFolder.addOrganizes(node);
- }
- // Dynamic Int32
- {
- String name = "Int32";
- NodeId typeId = Identifiers.Int32;
- Variant variant = new Variant(0);
- UaVariableNode node = new UaVariableNode.UaVariableNodeBuilder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "HelloWorld/Dynamic/" + name))
- .setAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setBrowseName(new QualifiedName(namespaceIndex, name))
- .setDisplayName(LocalizedText.english(name))
- .setDataType(typeId)
- .setTypeDefinition(Identifiers.BaseDataVariableType)
- .build();
- node.setValue(new DataValue(variant));
- AttributeDelegate delegate = AttributeDelegateChain.create(
- new AttributeDelegate() {
- @Override
- public DataValue getValue(AttributeContext context, VariableNode node) throws UaException {
- return new DataValue(new Variant(random.nextInt()));
- }
- },
- ValueLoggingDelegate::new
- );
- node.setAttributeDelegate(delegate);
- server.getNodeMap().addNode(node);
- dynamicFolder.addOrganizes(node);
- }
- // Dynamic Double
- {
- String name = "Double";
- NodeId typeId = Identifiers.Double;
- Variant variant = new Variant(0.0);
- UaVariableNode node = new UaVariableNode.UaVariableNodeBuilder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "HelloWorld/Dynamic/" + name))
- .setAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setBrowseName(new QualifiedName(namespaceIndex, name))
- .setDisplayName(LocalizedText.english(name))
- .setDataType(typeId)
- .setTypeDefinition(Identifiers.BaseDataVariableType)
- .build();
- node.setValue(new DataValue(variant));
- AttributeDelegate delegate = AttributeDelegateChain.create(
- new AttributeDelegate() {
- @Override
- public DataValue getValue(AttributeContext context, VariableNode node) throws UaException {
- return new DataValue(new Variant(random.nextDouble()));
- }
- },
- ValueLoggingDelegate::new
- );
- node.setAttributeDelegate(delegate);
- server.getNodeMap().addNode(node);
- dynamicFolder.addOrganizes(node);
- }
- }
- private void addDataAccessNodes(UaFolderNode rootNode) {
- // DataAccess folder
- UaFolderNode dataAccessFolder = new UaFolderNode(
- server.getNodeMap(),
- new NodeId(namespaceIndex, "HelloWorld/DataAccess"),
- new QualifiedName(namespaceIndex, "DataAccess"),
- LocalizedText.english("DataAccess")
- );
- server.getNodeMap().addNode(dataAccessFolder);
- rootNode.addOrganizes(dataAccessFolder);
- // AnalogItemType node
- AnalogItemNode node = nodeFactory.createVariable(
- new NodeId(namespaceIndex, "HelloWorld/DataAccess/AnalogValue"),
- new QualifiedName(namespaceIndex, "AnalogValue"),
- LocalizedText.english("AnalogValue"),
- Identifiers.AnalogItemType,
- AnalogItemNode.class
- );
- node.setDataType(Identifiers.Double);
- node.setValue(new DataValue(new Variant(3.14d)));
- node.setEURange(new Range(0.0, 100.0));
- server.getNodeMap().addNode(node);
- dataAccessFolder.addOrganizes(node);
- }
- private void addMethodNode(UaFolderNode folderNode) {
- UaMethodNode methodNode = UaMethodNode.builder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "HelloWorld/sqrt(x)"))
- .setBrowseName(new QualifiedName(namespaceIndex, "sqrt(x)"))
- .setDisplayName(new LocalizedText(null, "sqrt(x)"))
- .setDescription(
- LocalizedText.english("Returns the correctly rounded positive square root of a double value."))
- .build();
- try {
- AnnotationBasedInvocationHandler invocationHandler =
- AnnotationBasedInvocationHandler.fromAnnotatedObject(
- server.getNodeMap(), new SqrtMethod());
- methodNode.setProperty(UaMethodNode.InputArguments, invocationHandler.getInputArguments());
- methodNode.setProperty(UaMethodNode.OutputArguments, invocationHandler.getOutputArguments());
- methodNode.setInvocationHandler(invocationHandler);
- server.getNodeMap().addNode(methodNode);
- folderNode.addReference(new Reference(
- folderNode.getNodeId(),
- Identifiers.HasComponent,
- methodNode.getNodeId().expanded(),
- methodNode.getNodeClass(),
- true
- ));
- methodNode.addReference(new Reference(
- methodNode.getNodeId(),
- Identifiers.HasComponent,
- folderNode.getNodeId().expanded(),
- folderNode.getNodeClass(),
- false
- ));
- } catch (Exception e) {
- logger.error("Error creating sqrt() method.", e);
- }
- }
- private void addCustomObjectTypeAndInstance(UaFolderNode rootFolder) throws UaException {
- // Define a new ObjectType called "MyObjectType".
- UaObjectTypeNode objectTypeNode = UaObjectTypeNode.builder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "ObjectTypes/MyObjectType"))
- .setBrowseName(new QualifiedName(namespaceIndex, "MyObjectType"))
- .setDisplayName(LocalizedText.english("MyObjectType"))
- .setIsAbstract(false)
- .build();
- // "Foo" and "Bar" are members. These nodes are what are called "instance declarations" by the spec.
- UaVariableNode foo = UaVariableNode.builder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "ObjectTypes/MyObjectType.Foo"))
- .setAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setBrowseName(new QualifiedName(namespaceIndex, "Foo"))
- .setDisplayName(LocalizedText.english("Foo"))
- .setDataType(Identifiers.Int16)
- .setTypeDefinition(Identifiers.BaseDataVariableType)
- .build();
- foo.setValue(new DataValue(new Variant(0)));
- objectTypeNode.addComponent(foo);
- UaVariableNode bar = UaVariableNode.builder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "ObjectTypes/MyObjectType.Bar"))
- .setAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setBrowseName(new QualifiedName(namespaceIndex, "Bar"))
- .setDisplayName(LocalizedText.english("Bar"))
- .setDataType(Identifiers.String)
- .setTypeDefinition(Identifiers.BaseDataVariableType)
- .build();
- bar.setValue(new DataValue(new Variant("bar")));
- objectTypeNode.addComponent(bar);
- // Tell the ObjectTypeManager about our new type.
- // This let's us use NodeFactory to instantiate instances of the type.
- server.getObjectTypeManager().registerObjectType(
- objectTypeNode.getNodeId(),
- UaObjectNode.class,
- UaObjectNode::new
- );
- // Add our ObjectTypeNode as a subtype of BaseObjectType.
- server.getUaNamespace().addReference(
- Identifiers.BaseObjectType,
- Identifiers.HasSubtype,
- true,
- objectTypeNode.getNodeId().expanded(),
- NodeClass.ObjectType
- );
- // Add the inverse SubtypeOf relationship.
- objectTypeNode.addReference(new Reference(
- objectTypeNode.getNodeId(),
- Identifiers.HasSubtype,
- Identifiers.BaseObjectType.expanded(),
- NodeClass.ObjectType,
- false
- ));
- // Add it into the address space.
- server.getNodeMap().addNode(objectTypeNode);
- // Use NodeFactory to create instance of MyObjectType called "MyObject".
- // NodeFactory takes care of recursively instantiating MyObject member nodes
- // as well as adding all nodes to the address space.
- UaObjectNode myObject = nodeFactory.createObject(
- new NodeId(namespaceIndex, "HelloWorld/MyObject"),
- new QualifiedName(namespaceIndex, "MyObject"),
- LocalizedText.english("MyObject"),
- objectTypeNode.getNodeId()
- );
- // Add forward and inverse references from the root folder.
- rootFolder.addOrganizes(myObject);
- myObject.addReference(new Reference(
- myObject.getNodeId(),
- Identifiers.Organizes,
- rootFolder.getNodeId().expanded(),
- rootFolder.getNodeClass(),
- false
- ));
- }
- private void addCustomDataTypeVariable(UaFolderNode rootFolder) {
- // add a custom DataTypeNode as a subtype of the built-in Structure DataTypeNode
- NodeId dataTypeId = new NodeId(namespaceIndex, "DataType.CustomDataType");
- UaDataTypeNode dataTypeNode = new UaDataTypeNode(
- server.getNodeMap(),
- dataTypeId,
- new QualifiedName(namespaceIndex, "CustomDataType"),
- LocalizedText.english("CustomDataType"),
- LocalizedText.english("CustomDataType"),
- uint(0),
- uint(0),
- false
- );
- // Inverse ref to Structure
- dataTypeNode.addReference(new Reference(
- dataTypeId,
- Identifiers.HasSubtype,
- Identifiers.Structure.expanded(),
- NodeClass.DataType,
- false
- ));
- // Forward ref from Structure
- Optional<UaDataTypeNode> structureDataTypeNode = server.getNodeMap()
- .getNode(Identifiers.Structure)
- .map(UaDataTypeNode.class::cast);
- structureDataTypeNode.ifPresent(node ->
- node.addReference(new Reference(
- node.getNodeId(),
- Identifiers.HasSubtype,
- dataTypeId.expanded(),
- NodeClass.DataType,
- true
- ))
- );
- // Create a dictionary, binaryEncodingId, and register the codec under that id
- OpcUaBinaryDataTypeDictionary dictionary = new OpcUaBinaryDataTypeDictionary(
- "urn:eclipse:milo:example:custom-data-type"
- );
- NodeId binaryEncodingId = new NodeId(namespaceIndex, "DataType.CustomDataType.BinaryEncoding");
- dictionary.registerStructCodec(
- new CustomDataType.Codec().asBinaryCodec(),
- "CustomDataType",
- binaryEncodingId
- );
- // Register dictionary with the shared DataTypeManager instance
- OpcUaDataTypeManager.getInstance().registerTypeDictionary(dictionary);
- UaVariableNode customDataTypeVariable = UaVariableNode.builder(server.getNodeMap())
- .setNodeId(new NodeId(namespaceIndex, "HelloWorld/CustomDataTypeVariable"))
- .setAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setUserAccessLevel(ubyte(AccessLevel.getMask(AccessLevel.READ_WRITE)))
- .setBrowseName(new QualifiedName(namespaceIndex, "CustomDataTypeVariable"))
- .setDisplayName(LocalizedText.english("CustomDataTypeVariable"))
- .setDataType(dataTypeId)
- .setTypeDefinition(Identifiers.BaseDataVariableType)
- .build();
- CustomDataType value = new CustomDataType(
- "foo",
- uint(42),
- true
- );
- ExtensionObject xo = ExtensionObject.encode(value, binaryEncodingId);
- customDataTypeVariable.setValue(new DataValue(new Variant(xo)));
- rootFolder.addOrganizes(customDataTypeVariable);
- customDataTypeVariable.addReference(new Reference(
- customDataTypeVariable.getNodeId(),
- Identifiers.Organizes,
- rootFolder.getNodeId().expanded(),
- rootFolder.getNodeClass(),
- false
- ));
- }
- @Override
- public CompletableFuture<List<Reference>> browse(AccessContext context, NodeId nodeId) {
- ServerNode node = server.getNodeMap().get(nodeId);
- if (node != null) {
- return CompletableFuture.completedFuture(node.getReferences());
- } else {
- return FutureUtils.failedFuture(new UaException(StatusCodes.Bad_NodeIdUnknown));
- }
- }
- @Override
- public void read(
- ReadContext context,
- Double maxAge,
- TimestampsToReturn timestamps,
- List<ReadValueId> readValueIds) {
- List<DataValue> results = Lists.newArrayListWithCapacity(readValueIds.size());
- for (ReadValueId readValueId : readValueIds) {
- ServerNode node = server.getNodeMap().get(readValueId.getNodeId());
- if (node != null) {
- DataValue value = node.readAttribute(
- new AttributeContext(context),
- readValueId.getAttributeId(),
- timestamps,
- readValueId.getIndexRange(),
- readValueId.getDataEncoding()
- );
- results.add(value);
- } else {
- results.add(new DataValue(StatusCodes.Bad_NodeIdUnknown));
- }
- }
- context.complete(results);
- }
- @Override
- public void write(WriteContext context, List<WriteValue> writeValues) {
- List<StatusCode> results = Lists.newArrayListWithCapacity(writeValues.size());
- for (WriteValue writeValue : writeValues) {
- ServerNode node = server.getNodeMap().get(writeValue.getNodeId());
- if (node != null) {
- try {
- node.writeAttribute(
- new AttributeContext(context),
- writeValue.getAttributeId(),
- writeValue.getValue(),
- writeValue.getIndexRange()
- );
- results.add(StatusCode.GOOD);
- logger.info(
- "Wrote value {} to {} attribute of {}",
- writeValue.getValue().getValue(),
- AttributeId.from(writeValue.getAttributeId()).map(Object::toString).orElse("unknown"),
- node.getNodeId());
- } catch (UaException e) {
- logger.error("Unable to write value={}", writeValue.getValue(), e);
- results.add(e.getStatusCode());
- }
- } else {
- results.add(new StatusCode(StatusCodes.Bad_NodeIdUnknown));
- }
- }
- context.complete(results);
- }
- @Override
- public void onDataItemsCreated(List<DataItem> dataItems) {
- subscriptionModel.onDataItemsCreated(dataItems);
- }
- @Override
- public void onDataItemsModified(List<DataItem> dataItems) {
- subscriptionModel.onDataItemsModified(dataItems);
- }
- @Override
- public void onDataItemsDeleted(List<DataItem> dataItems) {
- subscriptionModel.onDataItemsDeleted(dataItems);
- }
- @Override
- public void onMonitoringModeChanged(List<MonitoredItem> monitoredItems) {
- subscriptionModel.onMonitoringModeChanged(monitoredItems);
- }
- @Override
- public Optional<MethodInvocationHandler> getInvocationHandler(NodeId methodId) {
- Optional<ServerNode> node = server.getNodeMap().getNode(methodId);
- return node.flatMap(n -> {
- if (n instanceof UaMethodNode) {
- return ((UaMethodNode) n).getInvocationHandler();
- } else {
- return Optional.empty();
- }
- });
- }
- }
|