Skip to content
Kevin Herron edited this page Jul 3, 2020 · 4 revisions

Client SDK

Connecting

The entry point for access to a remote server is via an instance of OpcUaClient. An instance can be created by providing an OpcUaClientConfig to the static OpcUaClient#create() function.

Creating an OpcUaClientConfig requires, at a minimum, a valid EndpointDescription for the server this client will connect to. An EndpointDescription can be obtained by calling the GetEndpoints service and selecting an endpoint that matches the criteria of the client application.

Each EndpointDescription contains details about an endpoint such as the endpoint URL, SecurityPolicy, MessageSecurityMode, and UserTokenPolicys supported by that endpoint.

Putting this together, the process looks like:

  1. Given an endpoint URL, call the GetEndpoints service to get a list of endpoints.
  2. Select an endpoint that meets your application criteria.
  3. Create and configure an OpcUaClientConfig using the selected endpoint.
  4. Create an OpcUaClient instance and call connect().

OpcUaClient#create has an overload that combines the above steps into a single call. The following example creates an OpcUaClient after selecting the first endpoint that uses SecurityPolicy.None.

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()
);

Now the OpcUaClient just needs to be connected:

client.connect().get();

From this call forward the OpcUaClient instance will automatically attempt to reconnect any time the connection is lost, including if the initial connect() call failed. This behavior persists until disconnect() is called.

Discovery

Endpoints can be discovered by calling the GetEndpoints service on a remote server.

The client SDK provides a DiscoveryClient with convenient static helper methods to make this easier.

A list of EndpointDescriptions can be obtained from DiscoveryClient#getEndpoints:

List<EndpointDescription> endpoints =
    DiscoveryClient.getEndpoints(endpointUrl).get();

Configuration

The above example showed an extremely minimal configuriation for a client, but properly configured client applications need a little more configuration to identify them.

  • Application Name
  • Application URI
  • Product URI

Security

In order to connect to an endpoint that uses security additional configuration is needed.

  • Public+Private Key Pair
  • Certificate or Certificate Chain
  • Certificate Validator

Authentication

Authentication is configured by setting an IdentityProvider when building an OpcUaClientConfig. You must configure an IdentityProvider that the selected endpoint indicates it supports in its UserTokenPolicy array.

Anonymous

No additional configuration is necessary to connect anonymously as AnonymousProvider is the default IdentityProvider when one is not explicitly configured.

The selected endpoint must contain a UserTokenPolicy with a UserTokenType of UserTokenType.Anonymous.

Username and Password

To connect with a username and password set an instance of UsernameProvider when building the OpcUaClientConfig.

The selected endpoint must contain a UserTokenPolicy with a UserTokenType of UserTokenType.UserName.

X509 Certificate

To connect using an X509Certificate as the identity set an instance of X509IdentityProvider

The selected endpoint must contain a UserTokenPolicy with a UserTokenType of UserTokenType.Certificate.

Browsing

Browsing can be done in three ways:

  1. AddressSpace#browse, a higher level API that provides blocking or non-blocking options and a reasonable set of default parameters.

    Using the default BrowseOptions:

    AddressSpace addressSpace = client.getAddressSpace();
    
    UaNode serverNode = addressSpace.getNode(Identifiers.Server);
    
    List<? extends UaNode> nodes = addressSpace.browseNodes(serverNode);

    Using a custom BrowseOptions:

    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<? extends UaNode> nodes = addressSpace.browseNodes(serverNode, browseOptions);
  2. UaNode#browse and UaNode#browseNodes, a higher level API that makes getting References or other UaNode instances referenced by a UaNode instance easy.

    BrowseOptions browseOptions = BrowseOptions.builder()
        .setReferenceType(Identifiers.HasProperty)
        .build();
    
    // Browse for HasProperty references
    List<ReferenceDescription> references = node.browse(browseOptions);
    
    // Browse for HasProperty references and get the UaNodes they target
    List<? extends UaNode> nodes = node.browseNodes(browseOptions);
  3. OpcUaClient#browse, a lower level API that takes parameters corresponding directly to those defined for the Browse service in the OPC UA spec.

     // 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();

    Unlike the previous browse methods, this one does not check whether a continuation point was returned and follow up with calls to the BrowseNext service until all References have been retrieved. Subsequent calls to BrowseNext are the responsibility of the caller.

Reading

Attributes can be read using either the high or low level APIs:

  1. Using UaNode, which provides a variety of high level calls to read each attribute directly or provide an AttributeId to read.

    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);
  2. Using OpcUaClient#read, a lower level API that takes parameters corresponding directly to those defined for the Read service in the OPC UA spec.

    List<ReadValueId> 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();

Writing

Attributes can be written using either the high or low level APIs:

  1. Using UaNode, which provides a variety of high level calls to write each attribute directly or provide an AttributeId and DataValue to write.

    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))
    );
  2. Using OpcUaClient#write, a lower level API that takes parameters corresponding directly to those defined for the Write service in the OPC UA spec.

    List<WriteValue> 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();

Methods

Subscriptions

ManagedSubscription

ManagedDataItem

ManagedEventItem

UaNode

Attributes

Refresh

Synchronize

Browsing

Clone this wiki locally