PicoOPC User's Guide and Reference
.NET Language Binding
In This Topic

PicoOPC language binding for .NET is embodied in the PicoOPC for .NET product.

PicoOPC for .NET works with any .NET runtime compatible with .NET Standard 2.0.

Types in PicoOPC for .NET do not follow the Common Language Specification (CLS). For example, PicoOPC makes use of unsigned integer 16-bit and 32-bit types (UInt16, UInt32). The programming language or tool used need to be able to cope with it. PicoOPC works well from C# .

Packaging

PicoOPC for .NET is distributed as NuGet package "OpcLabs.PicoOPC", and is available from https://www.nuget.org/packages/OpcLabs.PicoOpc/ .

Object Model

In PicoOPC for .NET, the data objects (see PicoOPC Types) are represented by .NET enumerations or value types (C# struct-s).

The Client type is represented by a reference type (C# class). 

Error Model

PicoOPC for .NET indicates errors (failures) by throwing .NET exceptions. Exceptions that can be thrown by each method are listed in the reference documentation.

Leaving aside the usage errors (see Operation Model), the synchronous methods on the Client object only throw AggregateException, for all kinds of operation errors. The inner exceptions of this AggregateException, or the exceptions thrown by the asynchronous methods, can be only of types listed below (and their derivatives):

Data Types

In PicoOPC for .NET, the OPC UA scalar built-in types are represented by various .NET types, according to the following table. 

OPC UA built-in type PicoOPC for .NET type
Null -
Boolean System.Boolean
SByte System.SByte
Byte System.Byte
Int16 System.Int16
UInt16 System.UInt16
Int32 System.Int32

UInt32

StatusCode

System.UInt32

Int64

DateTime

System.Int64
UInt64 System.UInt64
Float System.Single
Double System.Double

String

XmlElement

LocalizedText

System.String
Guid System.Guid
ByteString System.Byte[]
NodeId OpcLabs.PicoOpc.UA.NodeId
DataValue OpcLabs.PicoOpc.UA.DataValue
Variant OpcLabs.PicoOpc.UA.Variant (only in arrays)

Arrays of OPC UA built-in types are always represented as System.Object[], regardless of the underlying element type. The actual element types contained in the array, however, must conform to the above table. 

Asynchrony

PicoOPC for .NET supports task-based asynchronous pattern (TAP). This means that in addition to the synchronous methods on the Client object, there are also their asynchronous alternatives. Each such method is denoted by postfix "Async", i.e. ConnectAsync, DisconnectAsync, ReadAsync and WriteAsync.

With these methods, your code can start the operation and continue doing some independent work in parallel. Note, however, that this work must not involve more operations on the same Client object. You are not allowed to start more operations at once on the same Client object.

// This example shows how to asynchronously read and display data of an attribute.

using System;
using System.Threading.Tasks;
using OpcLabs.PicoOpc.UA;

namespace ConsoleApp._Client
{
    partial class Read
    {
        public static void Asynchrony()
        {
            // Instantiate the client object.
            var client = new Client();

            try
            {
                Async(client).Wait();
            }
            catch (AggregateException aggregateException)
            {
                Console.WriteLine("*** Failure: {0}", aggregateException.GetBaseException().Message);
            }
        }

        private static async Task Async(Client client)
        {
            try
            {
                // Asynchronously connect to the server.
                Console.WriteLine();
                Console.WriteLine("Connecting asynchronously...");
                Task connectTask = client.ConnectAsync(new Uri("opc.tcp://opcua.demo-this.com:51210/UA/SampleServer"));

                // Do some independent work.
                Console.WriteLine();
                Console.WriteLine("Doing some independent work...");

                // Await the connection.
                Console.WriteLine();
                Console.WriteLine("Awaiting the connection...");
                await connectTask;

                // Asynchronously read a node.
                Console.WriteLine();
                Console.WriteLine("Reading asynchronously ...");
                var nodeId = new NodeId(10221, namespaceIndex:2);
                Task<DataValue[]> readTask = client.ReadAsync(TimeSpan.Zero, new[] { new ReadValueId(nodeId) });

                // Do some independent work.
                Console.WriteLine();
                Console.WriteLine("Doing some independent work...");

                // Await the read.
                Console.WriteLine();
                Console.WriteLine("Awaiting the read...");
                DataValue dataValue = (await readTask)[0];

                // Display the result.
                Console.WriteLine();
                Console.WriteLine($"Status code: 0x{dataValue.StatusCode:X8}");
                Console.WriteLine($"Server timestamp: {DateTime.FromFileTimeUtc(dataValue.ServerTimestamp)}");
                Console.WriteLine($"Source timestamp: {DateTime.FromFileTimeUtc(dataValue.SourceTimestamp)}");
                if (dataValue.StatusCode == 0)
                {
                    Console.WriteLine($"Built-in type: {dataValue.Variant.BuiltInType}");
                    Console.WriteLine($"Is array: {dataValue.Variant.IsArray}");
                    Console.WriteLine($"Value: {dataValue.Variant.Value}");
                }
            }
            finally
            {
                if (client.IsConnected)
                {
                    // Disconnect from the server.
                    Console.WriteLine();
                    Console.WriteLine("Disconnecting...");
                    client.Disconnect();
                }
            }
        }
    }
}