### Browse References and Nodes with UaNode API Source: https://github.com/eclipse-milo/milo/wiki/Client Use UaNode#browse to get ReferenceDescriptions or UaNode#browseNodes to get target UaNode instances. This example demonstrates browsing for HasProperty references. ```java BrowseOptions browseOptions = BrowseOptions.builder() .setReferenceType(Identifiers.HasProperty) .build(); // Browse for HasProperty references List references = node.browse(browseOptions); // Browse for HasProperty references and get the UaNodes they target List nodes = node.browseNodes(browseOptions); ``` -------------------------------- ### Browse Address Space with OpcUaClient.browse() Source: https://context7.com/eclipse-milo/milo/llms.txt Performs a low-level browse operation on the OPC UA address space starting from a given node. Requires manual handling of continuation points if the result set is large. ```java import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned; import org.eclipse.milo.opcua.stack.core.types.enumerated.*; import org.eclipse.milo.opcua.stack.core.types.structured.*; client.connect(); BrowseDescription browse = new BrowseDescription( NodeIds.RootFolder, BrowseDirection.Forward, NodeIds.References, true, Unsigned.uint(NodeClass.Object.getValue() | NodeClass.Variable.getValue()), Unsigned.uint(BrowseResultMask.All.getValue()) ); BrowseResult result = client.browse(browse); for (ReferenceDescription ref : result.getReferences()) { System.out.println("Node: " + ref.getBrowseName().name()); } // Node: Objects // Node: Types // Node: Views ``` -------------------------------- ### Build and Start OPC UA Server with Configurable Endpoints Source: https://context7.com/eclipse-milo/milo/llms.txt This snippet constructs an OPC UA server with a minimal endpoint configuration, allowing anonymous and username/password authentication. It sets up the server with application details, security policies, and identity validation. ```java import org.eclipse.milo.opcua.sdk.server.*; import org.eclipse.milo.opcua.sdk.server.identity.*; import org.eclipse.milo.opcua.stack.core.security.*; import org.eclipse.milo.opcua.stack.core.transport.TransportProfile; import org.eclipse.milo.opcua.stack.core.types.builtin.*; import org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode; import org.eclipse.milo.opcua.stack.core.types.structured.BuildInfo; import org.eclipse.milo.opcua.stack.transport.server.tcp.*; import java.util.Set; // Identity validator accepting anonymous and username/password var usernameValidator = new UsernameIdentityValidator(challenge -> { String u = challenge.getUsername(); String p = challenge.getPassword(); return ("admin".equals(u) && "secret".equals(p)) || ("user".equals(u) && "pass".equals(p)); }); var identityValidator = new CompositeValidator( AnonymousIdentityValidator.INSTANCE, usernameValidator); // Minimal endpoint: no security, anonymous + username auth EndpointConfig noSecEndpoint = EndpointConfig.newBuilder() .setBindAddress("0.0.0.0") .setHostname("localhost") .setPath("/milo") .setSecurityPolicy(SecurityPolicy.None) .setSecurityMode(MessageSecurityMode.None) .setTransportProfile(TransportProfile.TCP_UASC_UABINARY) .setBindPort(4840) .addTokenPolicies( OpcUaServerConfig.USER_TOKEN_POLICY_ANONYMOUS, OpcUaServerConfig.USER_TOKEN_POLICY_USERNAME) .build(); OpcUaServerConfig serverConfig = OpcUaServerConfig.builder() .setApplicationName(LocalizedText.english("My OPC UA Server")) .setApplicationUri("urn:example:my-server") .setProductUri("urn:example:my-server") .setEndpoints(Set.of(noSecEndpoint)) .setCertificateManager(new DefaultCertificateManager(null)) .setIdentityValidator(identityValidator) .setBuildInfo(new BuildInfo("urn:example", "Example", "Server", "1.0", "", DateTime.now())) .build(); OpcUaServer server = new OpcUaServer(serverConfig, profile -> { assert profile == TransportProfile.TCP_UASC_UABINARY; return new OpcTcpServerTransport(OpcTcpServerTransportConfig.newBuilder().build()); }); server.startup().get(); System.out.println("Server started at opc.tcp://localhost:4840/milo"); // Graceful shutdown server.shutdown().get(); ``` -------------------------------- ### Get Endpoints with DiscoveryClient Source: https://github.com/eclipse-milo/milo/wiki/Client Retrieves a list of EndpointDescriptions from a remote OPC UA server using the DiscoveryClient. This is a prerequisite for establishing a connection. ```java List endpoints = DiscoveryClient.getEndpoints(endpointUrl).get(); ``` -------------------------------- ### Browse Nodes with Custom Options Source: https://github.com/eclipse-milo/milo/wiki/Client Use AddressSpace#browseNodes with custom BrowseOptions to filter the types of references to retrieve. This example specifically retrieves nodes referenced by HasProperty. ```java AddressSpace addressSpace = client.getAddressSpace(); UaNode serverNode = addressSpace.getNode(Identifiers.Server); BrowseOptions browseOptions = addressSpace.getBrowseOptions().copy( b -> b.setReferenceType(BuiltinReferenceType.HasProperty) ); // just UaNodes referenced by HasProperty references List nodes = addressSpace.browseNodes(serverNode, browseOptions); ``` -------------------------------- ### OpcUaClient.translateBrowsePaths() Source: https://context7.com/eclipse-milo/milo/llms.txt Resolves hierarchical browse paths relative to a starting node into NodeId(s) using the TranslateBrowsePathsToNodeIds service. ```APIDOC ## OpcUaClient.translateBrowsePaths() ### Description Resolves a hierarchical browse path relative to a starting node into the corresponding NodeId(s) using the TranslateBrowsePathsToNodeIds service. ### Method `translateBrowsePaths(List)` ### Parameters #### `translateBrowsePaths(List)` - **List** (List) - Required - A list of `BrowsePath` objects to resolve. - **BrowsePath**: - **startingNode** (NodeId) - The node from which the browse path starts. - **relativePath** (RelativePath) - The hierarchical path to resolve. - **relativePathElements** (RelativePathElement[]) - An array of elements defining the path. - **targetName** (QualifiedName) - The name of the target reference. - **isInverse** (boolean) - Whether to follow the reference in the inverse direction. - **includeSubtypes** (boolean) - Whether to include subtypes of the reference type. - **referenceTypeId** (NodeId) - The NodeId of the reference type to follow. ### Request Example ```java import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; import org.eclipse.milo.opcua.stack.core.types.structured.*; import java.util.List; // Assuming 'client' is an initialized OpcUaClient instance TranslateBrowsePathsToNodeIdsResponse response = client.translateBrowsePaths( List.of( new BrowsePath( NodeIds.ObjectsFolder, new RelativePath(new RelativePathElement[]{ new RelativePathElement( NodeIds.HierarchicalReferences, false, true, new QualifiedName(2, "HelloWorld")), new RelativePathElement( NodeIds.HierarchicalReferences, false, true, new QualifiedName(2, "ScalarTypes")), new RelativePathElement( NodeIds.HierarchicalReferences, false, true, new QualifiedName(2, "UInt64")) }) ) ) ); BrowsePathResult result = response.getResults()[0]; System.out.println("Status=" + result.getStatusCode()); result.getTargets().forEach(t -> System.out.println("TargetId=" + t.getTargetId())); ``` ### Response - **TranslateBrowsePathsToNodeIdsResponse** (TranslateBrowsePathsToNodeIdsResponse) - Contains an array of `BrowsePathResult` objects, one for each input `BrowsePath`. - **BrowsePathResult**: - **statusCode** (StatusCode) - The status code of the operation for this browse path. - **targets** (TargetResult[]) - An array of `TargetResult` objects, each representing a resolved target NodeId. ``` -------------------------------- ### Read and Write Node Values with UaVariableNode Source: https://context7.com/eclipse-milo/milo/llms.txt Demonstrates reading a variable's value and writing a new value to a variable node. Includes examples of single writes and batch writes via the client. ```java import org.eclipse.milo.opcua.sdk.client.nodes.UaVariableNode; import org.eclipse.milo.opcua.stack.core.types.builtin.*; client.connect(); // --- READ --- UaVariableNode node = client.getAddressSpace().getVariableNode(NodeIds.Server_ServerStatus_StartTime); DataValue value = node.readValue(); System.out.println("StartTime=" + value.value().value()); // StartTime=2025-01-01T00:00:00.000Z // --- WRITE --- UaVariableNode int32Node = client.getAddressSpace().getVariableNode(new NodeId(2, "HelloWorld/ScalarTypes/Int32")); int32Node.writeValue(new DataValue(Variant.ofInt32(42))); System.out.println("Write successful"); // --- BATCH WRITE via OpcUaClient --- List writeIds = List.of(new NodeId(2, "HelloWorld/ScalarTypes/Int32")); List results = client.writeValues(writeIds, List.of(DataValue.valueOnly(Variant.ofInt32(99)))); System.out.println("WriteStatus=" + results.get(0)); // WriteStatus=0x00000000 (Good) ``` -------------------------------- ### Define a Custom OPC UA Namespace with ManagedNamespaceWithLifecycle Source: https://context7.com/eclipse-milo/milo/llms.txt Extend ManagedNamespaceWithLifecycle to create and manage OPC UA nodes. This example demonstrates creating a folder and a read-write Int32 variable within the custom namespace. ```java import org.eclipse.milo.opcua.sdk.server.ManagedNamespaceWithLifecycle; import org.eclipse.milo.opcua.sdk.server.OpcUaServer; import org.eclipse.milo.opcua.sdk.server.nodes.*; import org.eclipse.milo.opcua.sdk.core.AccessLevel; import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel; import org.eclipse.milo.opcua.stack.core.NodeIds; import org.eclipse.milo.opcua.sdk.core.Reference; import org.eclipse.milo.opcua.stack.core.types.builtin.*; import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned; public class MyNamespace extends ManagedNamespaceWithLifecycle { public static final String URI = "urn:example:my-namespace"; private final SubscriptionModel subscriptionModel; public MyNamespace(OpcUaServer server) { super(server, URI); subscriptionModel = new SubscriptionModel(server, this); getLifecycleManager().addLifecycle(subscriptionModel); getLifecycleManager().addStartupTask(this::populateAddressSpace); } private void populateAddressSpace() { // Create folder node UaFolderNode folder = new UaFolderNode( getNodeContext(), newNodeId("MyFolder"), newQualifiedName("MyFolder"), LocalizedText.english("MyFolder") ); getNodeManager().addNode(folder); // Attach folder under Objects folder.addReference(new Reference( folder.getNodeId(), NodeIds.Organizes, NodeIds.ObjectsFolder.expanded(), false )); // Create a read-write Int32 variable UaVariableNode variable = new UaVariableNode.UaVariableNodeBuilder(getNodeContext()) .setNodeId(newNodeId("MyFolder/MyInt32")) .setAccessLevel(AccessLevel.READ_WRITE) .setUserAccessLevel(AccessLevel.READ_WRITE) .setBrowseName(newQualifiedName("MyInt32")) .setDisplayName(LocalizedText.english("MyInt32")) .setDataType(NodeIds.Int32) .setTypeDefinition(NodeIds.BaseDataVariableType) .build(); variable.setValue(new DataValue(new Variant(0))); getNodeManager().addNode(variable); folder.addOrganizes(variable); } @Override public void onDataItemsCreated(java.util.List items) { subscriptionModel.onDataItemsCreated(items); } @Override public void onDataItemsModified(java.util.List items) { subscriptionModel.onDataItemsModified(items); } @Override public void onDataItemsDeleted(java.util.List items) { subscriptionModel.onDataItemsDeleted(items); } @Override public void onMonitoringModeChanged(java.util.List items) { subscriptionModel.onMonitoringModeChanged(items); } } ``` -------------------------------- ### Translate Browse Paths to NodeIds with OpcUaClient.translateBrowsePaths() Source: https://context7.com/eclipse-milo/milo/llms.txt Resolves hierarchical browse paths relative to a starting node into their corresponding NodeIds using the TranslateBrowsePathsToNodeIds service. Useful for navigating the address space when NodeIds are not known directly. ```java import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; import org.eclipse.milo.opcua.stack.core.types.structured.*; import java.util.List; client.connect(); TranslateBrowsePathsToNodeIdsResponse response = client.translateBrowsePaths( List.of( new BrowsePath( NodeIds.ObjectsFolder, new RelativePath(new RelativePathElement[]{ new RelativePathElement( NodeIds.HierarchicalReferences, false, true, new QualifiedName(2, "HelloWorld")), new RelativePathElement( NodeIds.HierarchicalReferences, false, true, new QualifiedName(2, "ScalarTypes")), new RelativePathElement( NodeIds.HierarchicalReferences, false, true, new QualifiedName(2, "UInt64")) })))); BrowsePathResult result = response.getResults()[0]; System.out.println("Status=" + result.getStatusCode()); // Status=0x00000000 (Good) result.getTargets().forEach(t -> System.out.println("TargetId=" + t.getTargetId())); // TargetId=ExpandedNodeId{ns=2, id=HelloWorld/ScalarTypes/UInt64} ``` -------------------------------- ### Configure Server with WiresharkKeyLogWriter Source: https://github.com/eclipse-milo/milo/blob/main/docs/features/wireshark-key-log.md Set up an OPC UA server to use WiresharkKeyLogWriter by providing a file path for the key log. Ensure the writer is closed when done to flush and close the file. ```java Path keyLogFile = Path.of("/tmp/opcua_server_keys.log"); var writer = new WiresharkKeyLogWriter(keyLogFile); OpcUaServerConfig config = OpcUaServerConfig.builder() .setSecurityKeysListener(writer) // ... other config ... .build(); OpcUaServer server = new OpcUaServer(config, transportFactory); server.startup().get(); // ... server runs ... writer.close(); ``` -------------------------------- ### Create Data Change Subscription Source: https://context7.com/eclipse-milo/milo/llms.txt Set up data change subscriptions using `OpcUaSubscription` and `OpcUaMonitoredItem`. Supports both subscription-level and per-item listeners. Remember to synchronize monitored items and delete the subscription when done. ```java import org.eclipse.milo.opcua.sdk.client.subscriptions.*; import org.eclipse.milo.opcua.stack.core.NodeIds; import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; import java.util.List; client.connect(); OpcUaSubscription subscription = new OpcUaSubscription(client); // Subscription-level listener for all data changes subscription.setSubscriptionListener(new OpcUaSubscription.SubscriptionListener() { @Override public void onDataReceived(OpcUaSubscription sub, List items, List values) { for (int i = 0; i < items.size(); i++) { System.out.printf("nodeId=%s value=%s%n", items.get(i).getReadValueId().getNodeId(), values.get(i).value()); } } }); subscription.create(); // Per-item listener OpcUaMonitoredItem item = OpcUaMonitoredItem.newDataItem(NodeIds.Server_ServerStatus_CurrentTime); item.setDataValueListener((mi, v) -> System.out.println("item value: " + v.value())); subscription.addMonitoredItem(item); try { subscription.synchronizeMonitoredItems(); // pushes changes to server } catch (MonitoredItemSynchronizationException e) { e.getCreateResults().forEach(r -> System.err.println("Failed to create: " + r.serviceResult())); } Thread.sleep(3000); // receive a few notifications subscription.delete(); ``` -------------------------------- ### Configure Client with WiresharkKeyLogWriter Source: https://github.com/eclipse-milo/milo/blob/main/docs/features/wireshark-key-log.md Set up an OPC UA client to use WiresharkKeyLogWriter by providing a file path for the key log. Ensure the writer is closed when done to flush and close the file. ```java Path keyLogFile = Path.of("/tmp/opcua_client_keys.log"); var writer = new WiresharkKeyLogWriter(keyLogFile); OpcUaClientConfig config = OpcUaClientConfig.builder() .setSecurityKeysListener(writer) // ... other config ... .build(); OpcUaClient client = OpcUaClient.create(config); client.connect(); // ... use the client ... writer.close(); // flush and close when done ``` -------------------------------- ### Access Local UaNode Attributes Source: https://github.com/eclipse-milo/milo/wiki/Client Access local copies of UaNode attributes using get() methods. These values are available without a server round-trip. ```java Use the `get()` methods to access these values. ``` -------------------------------- ### Create and Connect an OpcUaClient Source: https://context7.com/eclipse-milo/milo/llms.txt Create an OpcUaClient by discovering endpoints at a given URL, selecting one, and applying optional configuration. Call connect() to establish the session. The client automatically reconnects after connection loss until disconnect() is called. ```java import org.eclipse.milo.opcua.sdk.client.OpcUaClient; import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy; import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; // Create a client connecting to the public Milo demo server (no security) OpcUaClient client = OpcUaClient.create( "opc.tcp://milo.digitalpetri.com:62541/milo", endpoints -> endpoints.stream() .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri())) .findFirst(), transportConfigBuilder -> {}, // use default transport settings configBuilder -> configBuilder .setApplicationName(LocalizedText.english("my opc-ua client")) .setApplicationUri("urn:my:opc-ua:client") .build() ); client.connect(); // blocks until session is established System.out.println("Connected: " + client.getSession().getSessionId()); // Expected: Connected: NodeId{ns=1, id=...} client.disconnectAsync().get(); ``` -------------------------------- ### Write Node Value with UaNode API Source: https://github.com/eclipse-milo/milo/wiki/Client Write node values using UaNode#writeValue() or UaNode#writeAttribute(). This example demonstrates writing a new value to the Value attribute. ```java UaVariableNode testNode = (UaVariableNode) addressSpace.getNode( new NodeId(2, "TestInt32") ); // Write the Value attribute; throws UaException if the write fails testNode.writeValue(new Variant(42)); // Most servers don't allow quality or timestamps to be written, // hence the use of DataValue.valueOnly(), but this method could // be used to write to a server that did support it if necessary StatusCode statusCode = testNode.writeAttribute( AttributeId.Value, DataValue.valueOnly(new Variant(42)) ); ``` -------------------------------- ### Read Node Attributes with UaNode API Source: https://github.com/eclipse-milo/milo/wiki/Client Read node attributes directly using UaNode methods like readValue() or readAttribute(). This example shows reading the Value and BrowseName attributes. ```java UaVariableNode testNode = (UaVariableNode) addressSpace.getNode( new NodeId(2, "TestInt32") ); // Read the Value attribute DataValue value = testNode.readValue(); // Read the BrowseName attribute QualifiedName browseName = testNode.readBrowseName(); // Read the Description attribute, with timestamps and quality intact DataValue descriptionValue = testNode.readAttribute(AttributeId.Description); ``` -------------------------------- ### Calling Methods Source: https://github.com/eclipse-milo/milo/wiki/Client Demonstrates three ways to call methods on an OPC UA server: using UaObjectNode#callMethod, finding a UaMethod and invoking call, and using the lower-level OpcUaClient#call API. ```APIDOC ## Calling Methods Methods can be called using either the high or low level APIs: 1. Using `UaObjectNode#callMethod`: ```java UaObjectNode serverNode = addressSpace.getObjectNode(Identifiers.Server); Variant[] outputs = serverNode.callMethod( "GetMonitoredItems", new Variant[]{ new Variant(subscription.getSubscription().getSubscriptionId()) } ); ``` 2. Finding a `UaMethod` and invoking `UaMethod#call`: ```java UaObjectNode serverNode = addressSpace.getObjectNode(Identifiers.Server); UaMethod getMonitoredItems = serverNode.getMethod("GetMonitoredItems"); Variant[] outputs = getMonitoredItems.call( new Variant[]{ new Variant(subscription.getSubscription().getSubscriptionId()) } ); ``` The `UaMethod` object also has a copy of the input and output defined by the method: ```java Argument[] inputArguments = getMonitoredItems.getInputArguments(); Argument[] outputArguments = getMonitoredItems.getOutputArguments(); ``` When a method does not define input or output arguments the corresponding `Argument[]` will be empty. 3. `OpcUaClient#call`, a lower level API That takes parameters corresponding directly to those defined by the Call service in the OPC UA spec: ```java NodeId objectId = NodeId.parse("ns=2;s=HelloWorld"); NodeId methodId = NodeId.parse("ns=2;s=HelloWorld/sqrt(x)"); CallMethodRequest request = new CallMethodRequest( objectId, methodId, new Variant[]{new Variant(input)} ); return client.call(request).thenCompose(result -> { StatusCode statusCode = result.getStatusCode(); if (statusCode.isGood()) { Double value = (Double) l(result.getOutputArguments()).get(0).getValue(); return CompletableFuture.completedFuture(value); } else { StatusCode[] inputArgumentResults = result.getInputArgumentResults(); for (int i = 0; i < inputArgumentResults.length; i++) { logger.error("inputArgumentResults[{}]={}", i, inputArgumentResults[i]); } CompletableFuture f = new CompletableFuture<>(); f.completeExceptionally(new UaException(statusCode)); return f; } }); ``` See `MethodExample.java` in the `client-examples` Maven module for the full source code. ``` -------------------------------- ### Configure Triggered Sampling with OpcUaClient.setTriggering() Source: https://context7.com/eclipse-milo/milo/llms.txt Use this to configure an item in Sampling mode to report values only when a designated triggering item changes. This reduces unnecessary network traffic. Ensure the client and subscription are connected and created before proceeding. ```java import org.eclipse.milo.opcua.sdk.client.subscriptions.*; import org.eclipse.milo.opcua.stack.core.*; import org.eclipse.milo.opcua.stack.core.types.builtin.*; import org.eclipse.milo.opcua.stack.core.types.enumerated.MonitoringMode; import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId; import java.util.Collections; import java.util.List; client.connect(); OpcUaSubscription subscription = new OpcUaSubscription(client); subscription.create(); // Triggering item: reports on every publish cycle ReadValueId triggerReadId = new ReadValueId( new NodeId(2, "HelloWorld/ScalarTypes/Float"), AttributeId.Value.uid(), null, QualifiedName.NULL_VALUE); OpcUaMonitoredItem triggerItem = new OpcUaMonitoredItem(triggerReadId); subscription.addMonitoredItem(triggerItem); // Sampling item: only sampled, not reported (until triggered) ReadValueId sampleReadId = new ReadValueId( NodeIds.Server_ServerStatus_CurrentTime, AttributeId.Value.uid(), null, QualifiedName.NULL_VALUE); OpcUaMonitoredItem samplingItem = new OpcUaMonitoredItem(sampleReadId, MonitoringMode.Sampling); samplingItem.setDataValueListener((item, v) -> System.out.println("triggered value: " + v)); subscription.addMonitoredItem(samplingItem); subscription.synchronizeMonitoredItems(); // Link the triggering item to force sampling item to report client.setTriggering( subscription.getSubscriptionId().orElseThrow(), triggerItem.getMonitoredItemId().orElseThrow(), List.of(samplingItem.getMonitoredItemId().orElseThrow()), Collections.emptyList() ); // Write to trigger item to cause both to report client.writeValues( List.of(new NodeId(2, "HelloWorld/ScalarTypes/Float")), List.of(new DataValue(Variant.ofFloat(3.14f)))); Thread.sleep(3000); subscription.delete(); ``` -------------------------------- ### Create ManagedSubscription Source: https://github.com/eclipse-milo/milo/wiki/Client Instantiate a `ManagedSubscription` using `ManagedSubscription.create(client)` for managing subscriptions and monitored items at a higher level. ```java ManagedSubscription subscription = ManagedSubscription.create(client); ``` ```java ManagedSubscription subscription = ManagedSubscription.create(client, 250.0); ``` -------------------------------- ### Browse Nodes with Default Options Source: https://github.com/eclipse-milo/milo/wiki/Client Use AddressSpace#browseNodes with default BrowseOptions to retrieve nodes referenced by a given node. ```java AddressSpace addressSpace = client.getAddressSpace(); UaNode serverNode = addressSpace.getNode(Identifiers.Server); List nodes = addressSpace.browseNodes(serverNode); ``` -------------------------------- ### Low-Level Browse Service Source: https://github.com/eclipse-milo/milo/wiki/Client Use OpcUaClient#browse for direct interaction with the Browse service as defined in the OPC UA spec. This method requires manual handling of continuation points for full results. ```java // Browse for forward hierarchal references from the Objects folder // that lead to other Object and Variable nodes. BrowseDescription browse = new BrowseDescription( Identifiers.ObjectsFolder, BrowseDirection.Forward, Identifiers.References, true, uint(NodeClass.Object.getValue() | NodeClass.Variable.getValue()), uint(BrowseResultMask.All.getValue()) ); BrowseResult browseResult = client.browse(browse).get(); ``` -------------------------------- ### Create Subscription with OpcUaSubscriptionManager Source: https://github.com/eclipse-milo/milo/wiki/Client Use OpcUaSubscriptionManager to create a UaSubscription object, which is the entry point for creating MonitoredItems. ```java `OpcUaSubscriptionManager#createSubscription` is used to create a `UaSubscription` object. MonitoredItems can then be created using `UaSubscription#createMonitoredItems`. The parameters and data structures used in these API calls correspond directly to those defined by the Subscription and MonitoredItems services in the OPC UA spec. ``` -------------------------------- ### Run Wireshark Key Log Tests Source: https://github.com/eclipse-milo/milo/blob/main/docs/features/wireshark-key-log.md Execute specific test classes for Wireshark key log functionality using Maven. This command targets the stack-core module and runs tests related to security keysets and Wireshark key log writing. ```bash mvn -q verify -pl opc-ua-stack/stack-core \ -Dtest="SecurityKeysetTest,WiresharkKeyLogWriterTest" ``` -------------------------------- ### Low-Level Write Service Source: https://github.com/eclipse-milo/milo/wiki/Client Use OpcUaClient#write for direct interaction with the Write service. This requires constructing a list of WriteValue objects specifying the nodes, attributes, and data to write. ```java List writeValues = new ArrayList<>(); writeValues.add( new WriteValue( new NodeId(2, "TestInt32"), AttributeId.Value.uid(), null, // indexRange DataValue.valueOnly(new Variant(42)) ) ); WriteResponse writeResponse = client.write(writeValues).get(); ``` -------------------------------- ### OpcUaSubscription + OpcUaMonitoredItem - Data Change Subscriptions Source: https://context7.com/eclipse-milo/milo/llms.txt Creates a subscription on the server and adds monitored items whose data changes are delivered to registered listeners. Supports both subscription-level and per-item listeners. ```APIDOC ## Data Change Subscriptions ### Description Creates a subscription on the server and adds monitored items whose data changes are delivered to registered listeners. Supports both subscription-level and per-item listeners. ### Usage 1. Create an `OpcUaSubscription` instance. 2. Optionally, set a subscription-level listener for all data changes. 3. Create and add `OpcUaMonitoredItem` instances for specific nodes. 4. Optionally, set per-item listeners for individual monitored items. 5. Synchronize monitored items with the server. 6. Process received data changes. 7. Delete the subscription when done. ### Code Example ```java import org.eclipse.milo.opcua.sdk.client.subscriptions.*; import org.eclipse.milo.opcua.stack.core.NodeIds; import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue; import java.util.List; // Assuming 'client' is an instance of OpcUaClient client.connect(); OpcUaSubscription subscription = new OpcUaSubscription(client); // Subscription-level listener for all data changes subscription.setSubscriptionListener(new OpcUaSubscription.SubscriptionListener() { @Override public void onDataReceived(OpcUaSubscription sub, List items, List values) { for (int i = 0; i < items.size(); i++) { System.out.printf("nodeId=%s value=%s%n", items.get(i).getReadValueId().getNodeId(), values.get(i).value()); } } }); subscription.create(); // Per-item listener OpcUaMonitoredItem item = OpcUaMonitoredItem.newDataItem(NodeIds.Server_ServerStatus_CurrentTime); item.setDataValueListener((mi, v) -> System.out.println("item value: " + v.value())); subscription.addMonitoredItem(item); try { subscription.synchronizeMonitoredItems(); // pushes changes to server } catch (MonitoredItemSynchronizationException e) { e.getCreateResults().forEach(r -> System.err.println("Failed to create: " + r.serviceResult())); } Thread.sleep(3000); // receive a few notifications subscription.delete(); ``` ``` -------------------------------- ### Discover Server Endpoints with DiscoveryClient Source: https://context7.com/eclipse-milo/milo/llms.txt Fetch the list of EndpointDescription objects advertised by a server to choose one before creating a client. This is useful for understanding available security policies and modes. ```java import org.eclipse.milo.opcua.sdk.client.DiscoveryClient; import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription; import java.util.List; List endpoints = DiscoveryClient.getEndpoints("opc.tcp://milo.digitalpetri.com:62541/milo").get(); endpoints.forEach(ep -> System.out.printf("URL=%s policy=%s mode=%s%n", ep.getEndpointUrl(), ep.getSecurityPolicyUri(), ep.getSecurityMode()) ); // Expected output (one line per endpoint): // URL=opc.tcp://... policy=http://opcfoundation.org/UA/SecurityPolicy#None mode=None // URL=opc.tcp://... policy=http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256 mode=SignAndEncrypt ``` -------------------------------- ### OpcUaClient.create() Source: https://context7.com/eclipse-milo/milo/llms.txt Creates and connects an OpcUaClient instance to an OPC UA server. It discovers endpoints, selects one, and allows for optional configuration. The client automatically attempts to reconnect after a connection loss until explicitly disconnected. ```APIDOC ## OpcUaClient.create() ### Description Creates an `OpcUaClient` by discovering endpoints at the given URL, selecting one, and applying optional configuration. Once created, call `connect()` to establish the session. The client automatically reconnects after connection loss until `disconnect()` is called. ### Method `OpcUaClient.create(String url, java.util.function.Function, org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription> endpointSelector, java.util.function.Consumer transportConfigBuilder, java.util.function.Consumer configBuilder) ### Endpoint N/A (SDK method) ### Parameters * **url** (String) - The URL of the OPC UA server. * **endpointSelector** (Function) - A function to select the desired endpoint from the discovered list. * **transportConfigBuilder** (Consumer) - A consumer to configure transport settings. * **configBuilder** (Consumer) - A consumer to configure the client settings, such as application name and URI. ### Request Example ```java import org.eclipse.milo.opcua.sdk.client.OpcUaClient; import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy; import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; // Create a client connecting to the public Milo demo server (no security) OpcUaClient client = OpcUaClient.create( "opc.tcp://milo.digitalpetri.com:62541/milo", endpoints -> endpoints.stream() .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri())) .findFirst(), transportConfigBuilder -> {}, // use default transport settings configBuilder -> configBuilder .setApplicationName(LocalizedText.english("my opc-ua client")) .setApplicationUri("urn:my:opc-ua:client") .build() ); client.connect(); // blocks until session is established System.out.println("Connected: " + client.getSession().getSessionId()); // Expected: Connected: NodeId{ns=1, id=...} client.disconnectAsync().get(); ``` ### Response * **OpcUaClient** - An instance of the configured OpcUaClient. ### Response Example ```java // Connected: NodeId{ns=1, id=...} ``` ``` -------------------------------- ### Create Event Subscription Source: https://context7.com/eclipse-milo/milo/llms.txt Subscribe to OPC UA Events using `OpcUaMonitoredItem.newEventItem()` with an `EventFilter`. Events are delivered to a registered `EventValueListener` or the subscription-level listener. Ensure the client is connected and the subscription is created. ```java import org.eclipse.milo.opcua.sdk.client.subscriptions.*; import org.eclipse.milo.opcua.stack.core.*; import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; import org.eclipse.milo.opcua.stack.core.types.structured.*; client.connect(); OpcUaSubscription subscription = new OpcUaSubscription(client); subscription.create(); EventFilter eventFilter = new EventFilter( new SimpleAttributeOperand[]{ new SimpleAttributeOperand(NodeIds.BaseEventType, new QualifiedName[]{new QualifiedName(0, "EventId")}, AttributeId.Value.uid(), null), new SimpleAttributeOperand(NodeIds.BaseEventType, new QualifiedName[]{new QualifiedName(0, "Severity")}, AttributeId.Value.uid(), null), new SimpleAttributeOperand(NodeIds.BaseEventType, new QualifiedName[]{new QualifiedName(0, "Message")}, AttributeId.Value.uid(), null) }, new ContentFilter(null) ); OpcUaMonitoredItem eventItem = OpcUaMonitoredItem.newEventItem(NodeIds.Server, eventFilter); eventItem.setEventValueListener((item, fields) -> { System.out.println("Event received:"); for (int i = 0; i < fields.length; i++) { System.out.println(" field[" + i + "]=" + fields[i].value()); } }); subscription.addMonitoredItem(eventItem); subscription.synchronizeMonitoredItems(); Thread.sleep(10000); // wait for events subscription.delete(); ``` -------------------------------- ### DiscoveryClient.getEndpoints() Source: https://context7.com/eclipse-milo/milo/llms.txt Fetches a list of EndpointDescription objects advertised by an OPC UA server, allowing selection of a specific endpoint before client creation. ```APIDOC ## DiscoveryClient.getEndpoints() ### Description Fetches the list of `EndpointDescription` objects advertised by a server so you can choose one before creating a client. ### Method `DiscoveryClient.getEndpoints(String url) ### Endpoint N/A (SDK method) ### Parameters * **url** (String) - The URL of the OPC UA server to discover endpoints from. ### Request Example ```java import org.eclipse.milo.opcua.sdk.client.DiscoveryClient; import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription; import java.util.List; List endpoints = DiscoveryClient.getEndpoints("opc.tcp://milo.digitalpetri.com:62541/milo").get(); endpoints.forEach(ep -> System.out.printf("URL=%s policy=%s mode=%s%n", ep.getEndpointUrl(), ep.getSecurityPolicyUri(), ep.getSecurityMode()) ); // Expected output (one line per endpoint): // URL=opc.tcp://... policy=http://opcfoundation.org/UA/SecurityPolicy#None mode=None // URL=opc.tcp://... policy=http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256 mode=SignAndEncrypt ``` ### Response * **List** - A list of available endpoint descriptions from the server. ### Response Example ```java URL=opc.tcp://milo.digitalpetri.com:62541/milo policy=http://opcfoundation.org/UA/SecurityPolicy#None mode=None URL=opc.tcp://milo.digitalpetri.com:62541/milo policy=http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256 mode=SignAndEncrypt ``` ``` -------------------------------- ### Call OPC UA Method with Arguments Source: https://context7.com/eclipse-milo/milo/llms.txt Use `OpcUaClient.callAsync()` to invoke a server-side method. Input arguments are passed as `Variant[]` and output arguments are returned in the `CallMethodResult`. Ensure the client is connected before calling. ```java import org.eclipse.milo.opcua.stack.core.types.builtin.*; import org.eclipse.milo.opcua.stack.core.types.structured.*; import java.util.List; client.connect(); NodeId objectId = NodeId.parse("ns=2;s=HelloWorld"); NodeId methodId = NodeId.parse("ns=2;s=HelloWorld/sqrt(x)"); CallMethodRequest request = new CallMethodRequest( objectId, methodId, new Variant[]{Variant.ofDouble(16.0)} ); CallMethodResult result = client.callAsync(List.of(request)) .thenApply(r -> r.getResults()[0]) .get(); if (result.getStatusCode().isGood()) { Double sqrtValue = (Double) result.getOutputArguments()[0].value(); System.out.println("sqrt(16) = " + sqrtValue); // sqrt(16) = 4.0 } else { System.err.println("Method call failed: " + result.getStatusCode()); } ``` -------------------------------- ### Create and Fire OPC UA Event - Java Source: https://context7.com/eclipse-milo/milo/llms.txt Creates and fires an OPC UA event from a namespace, visible to all subscribed clients monitoring the Server node's EventNotifier. Ensure the node is released after firing. ```java import org.eclipse.milo.opcua.sdk.server.model.objects.BaseEventTypeNode; import org.eclipse.milo.opcua.stack.core.NodeIds; import org.eclipse.milo.opcua.stack.core.types.builtin.*; import java.util.UUID; // -- Inside a running namespace / background thread -- BaseEventTypeNode eventNode = getServer() .getEventFactory() .createEvent(newNodeId(UUID.randomUUID()), NodeIds.BaseEventType); eventNode.setBrowseName(new QualifiedName(1, "my-event")); eventNode.setDisplayName(LocalizedText.english("my-event")); eventNode.setEventId(ByteString.of(new byte[]{0, 1, 2, 3})); eventNode.setEventType(NodeIds.BaseEventType); eventNode.setSourceNode(NodeIds.Server); eventNode.setSourceName("Server"); eventNode.setTime(DateTime.now()); eventNode.setReceiveTime(DateTime.NULL_VALUE); eventNode.setMessage(LocalizedText.english("Something happened!")); eventNode.setSeverity(org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.ushort(500)); getServer().getEventNotifier().fire(eventNode); eventNode.delete(); // release the node after firing // Subscribed clients with an EventFilter will receive fields specified in their filter ``` -------------------------------- ### Async Recursive Browse with AddressSpace.browseNodesAsync() Source: https://context7.com/eclipse-milo/milo/llms.txt Asynchronously browses child nodes of a given node using the high-level AddressSpace API. Returns a CompletableFuture that resolves to a list of UaNode objects. ```java import org.eclipse.milo.opcua.sdk.client.nodes.UaNode; import java.util.List; import java.util.concurrent.CompletableFuture; client.connect(); UaNode rootNode = client.getAddressSpace().getNode(NodeIds.RootFolder); // Async browse of direct children CompletableFuture> future = client.getAddressSpace().browseNodesAsync(rootNode); List children = future.get(); children.forEach(n -> System.out.println("Child: " + n.getBrowseName().name())); // Child: Objects // Child: Types // Child: Views ``` -------------------------------- ### OpcUaClient#browse Source: https://github.com/eclipse-milo/milo/wiki/Client Low-level API for performing a single browse request, corresponding directly to the OPC UA Browse service. Requires manual handling of continuation points for subsequent requests. ```APIDOC ## OpcUaClient#browse ### Description Low-level API for performing a single browse request, corresponding directly to the OPC UA Browse service. Requires manual handling of continuation points for subsequent requests. ### Method ```java CompletableFuture client.browse(BrowseDescription browseDescription) ``` ### Parameters - **browseDescription** (BrowseDescription) - The description of the browse request, including node ID, direction, reference type, node class mask, and result mask. ### Request Example ```java // Browse for forward hierarchal references from the Objects folder // that lead to other Object and Variable nodes. BrowseDescription browse = new BrowseDescription( Identifiers.ObjectsFolder, BrowseDirection.Forward, Identifiers.References, true, uint(NodeClass.Object.getValue() | NodeClass.Variable.getValue()), uint(BrowseResultMask.All.getValue()) ); BrowseResult browseResult = client.browse(browse).get(); ``` ### Response #### Success Response (200) - **browseResult** (BrowseResult) - The result of the browse operation, which may include a continuation point. ``` -------------------------------- ### Create ManagedDataItem Source: https://github.com/eclipse-milo/milo/wiki/Client Create a `ManagedDataItem` to monitor an attribute value using `ManagedSubscription#createDataItem`. Always verify the returned `StatusCode` for success. ```java ManagedDataItem dataItem = subscription.createDataItem(Identifiers.Server_ServerStatus_CurrentTime); if (!dataItem.getStatusCode.isGood()) { throw new RuntimeException("uh oh!") } ``` -------------------------------- ### ManagedSubscription Source: https://github.com/eclipse-milo/milo/wiki/Client Explains how to create and use ManagedSubscription for managing OPC UA subscriptions and monitored items. It covers adding data change listeners. ```APIDOC ## Subscriptions ### ManagedSubscription `ManagedSubscription` is the entry point in the higher level API for creating Subscriptions and MonitoredItems. Create a `ManagedSubscription` using an `OpcUaClient` instance like this: ```java ManagedSubscription subscription = ManagedSubscription.create(client); ``` Optionally, specify a publishing interval when creating the subscription: ```java ManagedSubscription subscription = ManagedSubscription.create(client, 250.0); ``` Once the Subscription is created you can add a data or event listener to it. This listener will receive all data changes for all items belonging to the subscription. ```java subscription.addChangeListener(new ChangeListener() { @Override public void onDataReceived(List dataItems, List dataValues) { // Each item in the dataItems list has a corresponding value at // the same index in the dataValues list. // Some items may appear multiple times if the item has a queue // size greater than 1 and the value changed more than once within // the publishing interval of the subscription. // The items and values appear in the order of the changes. } }); ``` ``` -------------------------------- ### Call Method using UaMethod Source: https://github.com/eclipse-milo/milo/wiki/Client Find a `UaMethod` by name and then invoke it using `UaMethod#call`. This approach allows inspection of method input and output arguments. ```java UaObjectNode serverNode = addressSpace.getObjectNode(Identifiers.Server); UaMethod getMonitoredItems = serverNode.getMethod("GetMonitoredItems"); Variant[] outputs = getMonitoredItems.call( new Variant[]{ new Variant(subscription.getSubscription().getSubscriptionId()) } ); ``` ```java Argument[] inputArguments = getMonitoredItems.getInputArguments(); Argument[] outputArguments = getMonitoredItems.getOutputArguments(); ``` -------------------------------- ### Low-Level Read Service Source: https://github.com/eclipse-milo/milo/wiki/Client Use OpcUaClient#read for direct interaction with the Read service. This requires constructing a list of ReadValueId objects specifying the nodes and attributes to read. ```java List readValueIds = new ArrayList<>(); readValueIds.add( new ReadValueId( new NodeId(2, "TestInt32"), AttributeId.Value.uid(), null, // indexRange QualifiedName.NULL_VALUE ) ); ReadResponse readResponse = client.read( 0.0, // maxAge TimestampsToReturn.Both, readValueIds ).get(); ``` -------------------------------- ### Create and Connect OpcUaClient Source: https://github.com/eclipse-milo/milo/wiki/Client Creates an OpcUaClient instance by selecting an endpoint that uses SecurityPolicy.None and automatically attempts to reconnect if the connection is lost. Call disconnect() to stop this behavior. ```java OpcUaClient client = OpcUaClient.create( "opc.tcp://milo.digitalpetri.com:62541/milo", endpoints -> endpoints.stream() .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri())) .findFirst(), configBuilder -> configBuilder.build() ); client.connect().get(); ``` -------------------------------- ### Implement Custom SecurityKeysListener Source: https://github.com/eclipse-milo/milo/blob/main/docs/features/wireshark-key-log.md Implement the SecurityKeysListener interface to handle key material as needed. This listener is lambda-compatible and must be thread-safe as multiple channels may derive keys concurrently. ```java SecurityKeysListener listener = keyset -> { logger.debug( "Keys derived for channel={}, token={}", keyset.channelId(), keyset.tokenId() ); // write to database, send to remote service, etc. }; OpcUaClientConfig config = OpcUaClientConfig.builder() .setSecurityKeysListener(listener) .build(); ```