Skip to content

Client Methods

Kevin Herron edited this page Oct 5, 2026 · 10 revisions

Call a Method in the context of its owning Object, with values matching the advertised argument signature. A connected and authorized Session is required.

Milo 1.2.0-SNAPSHOT, Java 17. Source baseline 7faaf4f0516c.

Select ObjectId and MethodId

A CallMethodRequest contains both the ObjectId and MethodId. The ObjectId identifies the instance on whose behalf the operation runs; the MethodId identifies the callable Method. Browse the Object's HasComponent relationships and the Method's InputArguments and OutputArguments properties. Do not substitute the Method's type-definition object or assume the same MethodId means the same object binding everywhere.

Executable and UserExecutable describe availability, but the Call result remains authoritative. Server state or permissions can change after those attributes were read. A server can share a Method node among objects and dispatch to different per-object handlers.

Use Call and inspect the result

This helper expects a square-root Method with one Double input and one Double output. The caller supplies the correct object and method NodeIds. The local fixture attaches it to NodeIds.ObjectsFolder; another server's model will use different identifiers.

static double callSquareRoot(OpcUaClient client, NodeId objectId, NodeId methodId, double input)
    throws UaException {
  Variant[] inputs = {new Variant(input)};
  var request = new CallMethodRequest(objectId, methodId, inputs);

  CallResponse response = client.call(List.of(request));
  CallMethodResult result = requireNonNull(response.getResults())[0];
  if (!result.getStatusCode().isGood()) {
    throw new UaException(result.getStatusCode());
  }

  Variant[] outputs = requireNonNull(result.getOutputArguments());
  return (Double) outputs[0].value();
}

A successful call with 16.0 yields 4.0 in the fixture. Supplying a String instead produces Bad_InvalidArgument with a corresponding Bad_TypeMismatch input result. The helper throws on a non-Good method result; use the raw result when you need per-argument status for a UI or log.

The batch client.call(List<CallMethodRequest>) returns a CallResponse. Validate the service result, expected result count, and each CallMethodResult separately. A Good service result only means that the Call service request was processed. getInputArgumentResults() explains per-argument status. High-level calls set ReturnDiagnostics=0 and do not request getInputArgumentDiagnosticInfos(). Request diagnostics with a custom CallRequest/RequestHeader and sendRequest(...) or sendRequestAsync(...); servers may still omit them. In particular, absent arrays do not establish successful argument validation. Outputs must be decoded only when the method result permits it, and checked against the signature.

Typed helpers and value types

UaObjectNode.getMethod(String) assumes the Object's namespace for the Method BrowseName. Use getMethod(QualifiedName) when the Method belongs to a different namespace, as with the fixture's sqrt(x) under namespace-zero ObjectsFolder. Inspect its getInputArguments() and getOutputArguments() for names, DataTypes, ValueRanks, dimensions, and descriptions. An empty array can also mean the argument property could not be read; it does not prove the Method takes no arguments. UaMethod.call(Variant[]) returns output Variants on success and throws UaMethodException for a non-Good operation result. The exception retains input-argument results and any supplied diagnostics, but the high-level call requests no DiagnosticInfos. Its asynchronous call completes exceptionally for both service and operation failure.

Build inputs in the declared order, using the correct Milo/Java representation. A missing argument, extra argument, wrong scalar/array rank, or invalid value may fail differently. Types alone do not establish a valid application value; a server may reject a negative speed or a state transition with the correct Java type. For structures and ExtensionObjects, use Data types.

Failures, retries, and ownership

A method may perform side effects before a connection breaks. A timeout or exceptional completion does not prove it did nothing. Retry only when the operation is idempotent or the application protocol supplies a transaction identifier or another way to determine the outcome. A batch of methods is not atomic.

Method wrappers hold client/model references and need no separate shutdown. The caller owns the connection and any asynchronous work. Do not invoke a blocking method from an SDK callback if it can stall that callback's delivery path. Hand long work to a bounded application executor and preserve the result and its error cause.

Examples and reference

Related pages

Client SDK · Server methods · Data types

Clone this wiki locally