Client - Getting Started

An OPC UA client is a network client that interacts with an OPC UA server via a collection of abstract Remote Procedure Calls (RPC), which is a request/response protocol. An RPC is initiated by the client, which sends a request message to the server. The OPC UA server then sends a response to the client, and the application continues its process. While the OPC UA server is processing the call, the OPC UA client is blocked (it waits until the server has finished processing before resuming execution), unless the OPC UA client operates in asynchronous callback mode.

The OPC UA client can operate in two modes, standard blocking socket call (default) or non blocking cosocket mode. Blocking sockets must run in the context of a native thread such as a thread running an LSP page. See Asynchronous Cosocket Mode for details.

Connecting to Server

A client is created by first loading the OPC-UA stack and then creating a client as follows:

local ua = require("opcua.api")

local config = {
  applicationName = 'RealTimeLogic example',
  applicationUri = "urn:opcua-lua:example",
  productUri = "urn:opcua-lua:example",
  securityPolicyUri = ua.Types.SecurityPolicy.None,
}

local client = ua.newClient(config)

Next, connect to the server, open an OPC-UA channel and finally create an OPC-UA session:

local resp, err
local endpointUrl = "opc.tcp://localhost:4841"
err = client:connect(endpointUrl)
resp, err = client:openSecureChannel(120000)
resp, err = client:createSession("test_session", 3600000)
resp, err = client:activateSession()

Full source

You can start browsing the address space as soon as a session has been established.

Browsing Address Space

To browse the server’s address space, call the ‘browse’ method.

local nodeIds = require("opcua.node_ids")
-- Browse one node by ID.
resp, err = client:browse(nodeIds.RootFolder)
-- Browse array of Node IDs.
resp, err = client:browse({nodeIds.RootFolder, nodeIds.TypesFolder})
-- Full featured browsing
local browseParams = {
  requestedMaxReferencesPerNode = 0,
  nodesToBrowse = {
    {
      nodeId = nodeIds.RootFolder,
      referenceTypeId = nodeIds.HierarchicalReferences,
      browseDirection = ua.Types.BrowseDirection.Forward,
      nodeClassMask = ua.Types.NodeClass.Unspecified,
      resultMask = ua.Types.BrowseResultMask.All,
      includeSubtypes = 1,
    },
    {
      nodeId = nodeIds.RootFolder,
      referenceTypeId = nodeIds.HierarchicalReferences,
      browseDirection = ua.Types.BrowseDirection.Forward,
      nodeClassMask = ua.Types.NodeClass.Unspecified,
      resultMask = ua.Types.BrowseResultMask.All,
      includeSubtypes = 1,
    }
  },
}

local resp,err = client:browse(browseParams)

Full source

The Address Space Browsing user guide has detailed overview of all parameters.

Reading Node Attributes

-- Reading current time on server and product version.
local readParams = {
  nodesToRead = {
    {nodeId=nodeIds.ObjectsFolder, attributeId=ua.Types.AttributeId.DisplayName},
    {nodeId=nodeIds.ObjectsFolder, attributeId=ua.Types.AttributeId.BrowseName},
    {nodeId=nodeIds.ObjectsFolder, attributeId=ua.Types.AttributeId.NodeId},
  }
}

local resp,err = client:read(readParams)
for i,result in ipairs(resp.results) do
  if result.statusCode == 0 then
    ua.Tools.printTable("result", result.value)
  else
    trace(string.format("Read value '%s' error: 0x%X", nodes.nodesToRead[i].nodeId, result.statusCode))
  end
end

Full source

See Reading and Writing Data for paramerers and detailed information.

Closing Server Connection

When finished using the server, close the server connection as follows:

resp, err = client:closeSession()
client:disconnect()

Full source

Asynchronous Cosocket Mode

Clients, by default, use blocking sockets. However, a client can be configured to operate in cosocket mode, a lightweight Lua coroutine socket thread. All client methods can accept a callback that will be called when the corresponding request completes. The OPC UA methods can also be called without providing a callback when running in cosocket mode if the caller is running in the context of another cosocket. When another cosocket calls an OPC UA client in cosocket mode without providing a callback, the calling cosocket will block. Not having to provide a callback when operating in cosocket mode makes designing OPC UA clients easier.

We recommend reading the Barracuda App Server’s Socket Design User Guide for an introduction to cosockets and for details on the various socket modes.

The cosocket mode is set in the configuration table with the flag ‘cosocketMode’.

local ua = require("opcua.api")

local config = {
  applicationName = 'RealTimeLogic example',
  applicationUri = "urn:opcua-lua:example",
  productUri = "urn:opcua-lua:example",
  securityPolicyUri = ua.Types.SecurityPolicy.None,
  cosocketMode = true, -- Start socket read loop in separate cosocket
}

local client = ua.newClient(config)

All callbacks, except for the connect method must accept two arguments. The connect method receives one argument as shown in the two examples below.

  local endpointUrl = "opc.tcp://localhost:4841"
  client:connect(endpointUrl, connectCallback)

The connect method’s error argument is nil if the connection succeeded.

local function connectCallback(err)
  if err ~= nil then
    trace("connection error: "..err)
    return
  end

  trace("Opening secure channel")
  local secureChannelTimeout = 60000 -- ms
  client:openSecureChannel(secureChannelTimeout, onChannelOpened)
end

All other callbacks receive two arguments, the response and an error code. This corresponds to the two return values returned when operating in blocking mode (when not providing a callback). The methods return response,nil on success and nil,error on failure.

local function onChannelOpened(resp, err)
  if err ~= nil then
    trace("Secure channel error: "..tostring(err))
    return
  end
  ua.Tools.printTable("SecureChannel", resp)

  trace("Disconnecting from server")
  pcall(client.disconnect, client)
end

Full source