There are various parameters that can be configured on the OPC UA Complex Data extension. Take following steps to read or modify them:
- Access the InstanceParameters Property of your EasyUAClient Class instance.
- In the InstanceParameters Property, access the PluginConfigurations Property.
- Call the Find Method of the ConfigurationPartCollection Class, passing it the name of the type of the configuration part you want to modify. The available type names and their purposes are listed further below.
- Read or modify the properties of the obtained object as needed.
Some typical scenarios for which the configuration might be needed are described below.
Configuring the extension only has effect when done before you attempt first OPC operation or
GetService Method call on the object instance.
Using shared data type model provider
The data type model provider (DTMP) is an object that provides access to the metadata about data type in the target OPC server(s). The data type model provider also caches the metadata obtained, so that repeated decodings of the same data types do not require retrieving the metadata from the OPC server(s) again and again.
By default, each instance of the EasyUAClient Class has its own data type model provider. If you use multiple instances of the EasyUAClient Class and do not require them be fully isolated in this respect, you should consider switching to a shared data type model provider.
Conceptually, there is no noticeable difference in the results from the default state in which the client objects are set to use per-instance data type model provider. But, with the shared data type model provider, the metadata obtained during the operations on one client object and cached inside the data type model provider are reused during operations on other client objects, making this and the subsequent operations more efficient.
The configuration part that you need to access is the UAComplexDataClientPluginParameters Class.
.NET
// Shows how to configure the OPC UA Complex Data plug-in to use a shared data type model provider.
using System;
using System.Diagnostics;
using OpcLabs.EasyOpc.UA;
using OpcLabs.EasyOpc.UA.OperationModel;
using OpcLabs.EasyOpc.UA.Plugins.ComplexData;
namespace UADocExamples.ComplexData._UAComplexDataClientPluginParameters
{
class IsolatedDataTypeModelProvider
{
public static void Main1()
{
// Define which server and node we will work with.
UAEndpointDescriptor endpointDescriptor =
"opc.tcp://opcua.demo-this.com:51210/UA/SampleServer";
// or "http://opcua.demo-this.com:51211/UA/SampleServer" (currently not supported)
// or "https://opcua.demo-this.com:51212/UA/SampleServer/"
UANodeDescriptor nodeDescriptor =
"nsu=http://test.org/UA/Data/ ;i=10239"; // [ObjectsFolder]/Data.Static.Scalar.StructureValue
// We will create two instances of EasyUAClient class, and configure each of them to use the shared data type
// model provider.
// Configure the first client object.
var client1 = new EasyUAClient();
UAComplexDataClientPluginParameters complexDataClientPluginParameters1 =
client1.InstanceParameters.PluginConfigurations.Find<UAComplexDataClientPluginParameters>();
Debug.Assert(complexDataClientPluginParameters1 != null);
complexDataClientPluginParameters1.IsolatedDataTypeModelProvider = false;
// Configure the second client object.
var client2 = new EasyUAClient();
UAComplexDataClientPluginParameters complexDataClientPluginParameters2 =
client2.InstanceParameters.PluginConfigurations.Find<UAComplexDataClientPluginParameters>();
Debug.Assert(complexDataClientPluginParameters2 != null);
complexDataClientPluginParameters2.IsolatedDataTypeModelProvider = false;
// We will now read the same complex data node using the two client objects.
//
// There is no noticeable difference in the results from the default state in which the client objects are
// set to use per-instance data type model provider. But, with the shared data type model provider, the metadata
// obtained during the read on the first client object and cached inside the data type model provider are reused
// during the read on the second client object, making this and the subsequent operations more efficient.
// Read the complex data node using the first client.
object value1;
try
{
value1 = client1.ReadValue(endpointDescriptor, nodeDescriptor);
}
catch (UAException uaException)
{
Console.WriteLine("*** Failure: {0}", uaException.GetBaseException().Message);
return;
}
Console.WriteLine(value1);
// Read the complex data node using the second client.
object value2;
try
{
value2 = client2.ReadValue(endpointDescriptor, nodeDescriptor);
}
catch (UAException uaException)
{
Console.WriteLine("*** Failure: {0}", uaException.GetBaseException().Message);
return;
}
Console.WriteLine(value2);
}
}
}
# Shows how to configure the OPC UA Complex Data plug-in to use a shared data type model provider.
# The QuickOPC package is needed. Install it using "pip install opclabs_quickopc".
import opclabs_quickopc
# Import .NET namespaces.
from System import *
from OpcLabs.EasyOpc.UA import *
from OpcLabs.EasyOpc.UA.OperationModel import *
from OpcLabs.EasyOpc.UA.Plugins.ComplexData import *
endpointDescriptor = UAEndpointDescriptor('opc.tcp://opcua.demo-this.com:51210/UA/SampleServer')
# or 'http://opcua.demo-this.com:51211/UA/SampleServer' (currently not supported)
# or 'https://opcua.demo-this.com:51212/UA/SampleServer/'
# [ObjectsFolder]/Data.Static.Scalar.StructureValue
nodeDescriptor = UANodeDescriptor('nsu=http://test.org/UA/Data/ ;i=10239')
# We will create two instances of EasyUAClient class, and configure each of them to use the shared data type
# model provider.
# Configure the first client object.
client1 = EasyUAClient()
complexDataClientPluginParameters1 = (
client1.InstanceParameters.PluginConfigurations.Find[UAComplexDataClientPluginParameters]())
assert complexDataClientPluginParameters1 is not None
complexDataClientPluginParameters1.IsolatedDataTypeModelProvider = False
# Configure the second client object.
client2 = EasyUAClient()
complexDataClientPluginParameters2 = (
client1.InstanceParameters.PluginConfigurations.Find[UAComplexDataClientPluginParameters]())
assert complexDataClientPluginParameters2 is not None
complexDataClientPluginParameters1.IsolatedDataTypeModelProvider = False
# We will now read the same complex data node using the two client objects.
#
# There is no noticeable difference in the results from the default state in which the client objects are
# set to use per-instance data type model provider. But, with the shared data type model provider, the metadata
# obtained during the read on the first client object and cached inside the data type model provider are reused
# during the read on the second client object, making this and the subsequent operations more efficient.
# Read the complex data node using the first client.
try:
value1 = IEasyUAClientExtension.ReadValue(client1, endpointDescriptor, nodeDescriptor)
except UAException as uaException:
print('*** Failure: ' + uaException.GetBaseException().Message)
exit()
print(value1)
# Read the complex data node using the second client.
try:
value2 = IEasyUAClientExtension.ReadValue(client2, endpointDescriptor, nodeDescriptor)
except UAException as uaException:
print('*** Failure: ' + uaException.GetBaseException().Message)
exit()
print(value1)
print()
print('Finished.')
' Shows how to configure the OPC UA Complex Data plug-in to use a shared data type model provider.
Imports System
Imports OpcLabs.EasyOpc.UA
Imports OpcLabs.EasyOpc.UA.OperationModel
Imports OpcLabs.EasyOpc.UA.Plugins.ComplexData
Namespace ComplexData._UAComplexDataClientPluginParameters
Friend Class IsolatedDataTypeModelProvider
Public Shared Sub Main1()
' Define which server we will work with.
Dim endpointDescriptor As UAEndpointDescriptor =
"opc.tcp://opcua.demo-this.com:51210/UA/SampleServer"
' or "http://opcua.demo-this.com:51211/UA/SampleServer" (currently not supported)
' or "https://opcua.demo-this.com:51212/UA/SampleServer/"
' Define which node we will work with.
Dim nodeDescriptor As UANodeDescriptor =
"nsu=http://test.org/UA/Data/ ;i=10239" ' [ObjectsFolder]/Data.Static.Scalar.StructureValue
' We will create two instances of EasyUAClient class, and configure each of them to use the shared data type
' model provider.
' Configure the first client object.
Dim client1 = New EasyUAClient
Dim complexDataClientPluginParameters1 As UAComplexDataClientPluginParameters =
client1.InstanceParameters.PluginConfigurations.Find(Of UAComplexDataClientPluginParameters)()
Debug.Assert(complexDataClientPluginParameters1 IsNot Nothing)
complexDataClientPluginParameters1.IsolatedDataTypeModelProvider = False
' Configure the second client object.
Dim client2 = New EasyUAClient
Dim complexDataClientPluginParameters2 As UAComplexDataClientPluginParameters =
client2.InstanceParameters.PluginConfigurations.Find(Of UAComplexDataClientPluginParameters)()
Debug.Assert(complexDataClientPluginParameters2 IsNot Nothing)
complexDataClientPluginParameters2.IsolatedDataTypeModelProvider = False
' We will now read the same complex data node using the two client objects.
'
' There is no noticeable difference in the results from the default state in which the client objects are
' set to use per-instance data type model provider. But, with the shared data type model provider, the metadata
' obtained during the read on the first client object and cached inside the data type model provider are reused
' during the read on the second client object, making this and the subsequent operations more efficient.
' Read the complex data node using the first client.
Dim value1 As Object
Try
value1 = client1.ReadValue(endpointDescriptor, nodeDescriptor)
Catch uaException As UAException
Console.WriteLine("*** Failure: {0}", uaException.GetBaseException.Message)
Exit Sub
End Try
Console.WriteLine(value1)
' Read the complex data node using the second client.
Dim value2 As Object
Try
value2 = client2.ReadValue(endpointDescriptor, nodeDescriptor)
Catch uaException As UAException
Console.WriteLine("*** Failure: {0}", uaException.GetBaseException.Message)
Exit Sub
End Try
Console.WriteLine(value2)
End Sub
End Class
End Namespace
COM
// Shows how to configure the OPC UA Complex Data plug-in to use a shared data type model provider.
class procedure IsolatedDataTypeModelProvider.Main;
var
Client1, Client2: _EasyUAClient;
ComplexDataClientPluginParameters1, ComplexDataClientPluginParameters2: OpcLabs_EasyOpcUA_TLB._UAComplexDataClientPluginParameters;
EndpointDescriptor: string;
NodeDescriptor: string;
Value1, Value2: OleVariant;
begin
// Define which server and node we will work with.
EndpointDescriptor :=
//'http://opcua.demo-this.com:51211/UA/SampleServer';
//'https://opcua.demo-this.com:51212/UA/SampleServer/';
'opc.tcp://opcua.demo-this.com:51210/UA/SampleServer';
NodeDescriptor := 'nsu=http://test.org/UA/Data/ ;i=10239'; // [ObjectsFolder]/Data.Static.Scalar.StructureValue
// We will create two instances of EasyUAClient class, and configure each of them to use the shared data type
// model provider.
// Configure the first client object.
Client1 := CoEasyUAClient.Create;
ComplexDataClientPluginParameters1 := IUnknown(Client1.InstanceParameters.PluginConfigurations.Find('OpcLabs.EasyOpc.UA.Plugins.ComplexData.UAComplexDataClientPluginParameters')) as OpcLabs_EasyOpcUA_TLB._UAComplexDataClientPluginParameters;
ComplexDataClientPluginParameters1.IsolatedDataTypeModelProvider := false;
// Configure the second client object.
Client2 := CoEasyUAClient.Create;
ComplexDataClientPluginParameters2 := IUnknown(Client2.InstanceParameters.PluginConfigurations.Find('OpcLabs.EasyOpc.UA.Plugins.ComplexData.UAComplexDataClientPluginParameters')) as OpcLabs_EasyOpcUA_TLB._UAComplexDataClientPluginParameters;
ComplexDataClientPluginParameters2.IsolatedDataTypeModelProvider := false;
// We will now read the same complex data node using the two client objects.
//
// There is no noticeable difference in the results from the default state in which the client objects are
// set to use per-instance data type model provider. But, with the shared data type model provider, the metadata
// obtained during the read on the first client object and cached inside the data type model provider are reused
// during the read on the second client object, making this and the subsequent operations more efficient.
// Read the complex data node using the first client.
try
Value1 := Client1.ReadValue(EndpointDescriptor, NodeDescriptor);
except
on E: EOleException do
begin
WriteLn(Format('*** Failure: %s', [E.GetBaseException.Message]));
Exit;
end;
end;
WriteLn(Value1);
// Read the complex data node using the second client.
try
Value2 := Client2.ReadValue(EndpointDescriptor, NodeDescriptor);
except
on E: EOleException do
begin
WriteLn(Format('*** Failure: %s', [E.GetBaseException.Message]));
Exit;
end;
end;
WriteLn(Value2);
end;
Rem Shows how to configure the OPC UA Complex Data plug-in to use a shared data type model provider.
Option Explicit
' Define which server and node we will work with.
Dim endpointDescriptor: endpointDescriptor = _
"opc.tcp://opcua.demo-this.com:51210/UA/SampleServer"
'"http://opcua.demo-this.com:51211/UA/SampleServer"
'"https://opcua.demo-this.com:51212/UA/SampleServer/"
Dim nodeDescriptor: nodeDescriptor = _
"nsu=http://test.org/UA/Data/ ;i=10239" ' [ObjectsFolder]/Data.Static.Scalar.StructureValue
' We will create two instances of EasyUAClient class, and configure each of them to use the shared data type
' model provider.
' Configure the first client object.
Dim Client1: Set Client1 = CreateObject("OpcLabs.EasyOpc.UA.EasyUAClient")
Dim ComplexDataClientPluginParameters1: Set ComplexDataClientPluginParameters1 = Client1.InstanceParameters.PluginConfigurations.Find( _
"OpcLabs.EasyOpc.UA.Plugins.ComplexData.UAComplexDataClientPluginParameters")
ComplexDataClientPluginParameters1.IsolatedDataTypeModelProvider = False
' Configure the second client object.
Dim Client2: Set Client2 = CreateObject("OpcLabs.EasyOpc.UA.EasyUAClient")
Dim ComplexDataClientPluginParameters2: Set ComplexDataClientPluginParameters2 = Client2.InstanceParameters.PluginConfigurations.Find( _
"OpcLabs.EasyOpc.UA.Plugins.ComplexData.UAComplexDataClientPluginParameters")
ComplexDataClientPluginParameters2.IsolatedDataTypeModelProvider = False
' We will now read the same complex data node using the two client objects.
'
' There is no noticeable difference in the results from the default state in which the client objects are
' set to use per-instance data type model provider. But, with the shared data type model provider, the metadata
' obtained during the read on the first client object and cached inside the data type model provider are reused
' during the read on the second client object, making this and the subsequent operations more efficient.
' Read the complex data node using the first client.
On Error Resume Next
Dim Value1: Set Value1 = Client1.ReadValue(endpointDescriptor, nodeDescriptor)
If Err.Number <> 0 Then
WScript.Echo "*** Failure: " & Err.Source & ": " & Err.Description
WScript.Quit
End If
On Error Goto 0
WScript.Echo Value1
' Read the complex data node using the second client.
On Error Resume Next
Dim Value2: Set Value2 = Client2.ReadValue(endpointDescriptor, nodeDescriptor)
If Err.Number <> 0 Then
WScript.Echo "*** Failure: " & Err.Source & ": " & Err.Description
WScript.Quit
End If
On Error Goto 0
WScript.Echo Value2
Configuring data type dictionary locations
In OPC Binary data type system, data type dictionaries that reside in the OPC server may use types from other data type dictionaries, and optionally specify location of these other dictionaries, using a string. The OPC UA specification does not define a format for such string.
The OPC UA Complex Data extension already contains data the standard data type dictionary defined in the OPC UA specification, and some others. It also knows how to resolve their namespaces to locations, therefore (by default), the data type dictionaries in the server do not have to specify the location for them.
For other namespaces, the OPC Binary data type system recognizes following syntax for the location string:
- If the string can be parsed as an OPC UA Node ID with explicitly specified namespace (by URI or namespace index), it is understood to be a reference to a node in the same OPC server, and resolved as such.
- Otherwise, the location string is understood to be a URI.
If the location string is understood to be a URI, it can be only:
- A URI with special "net.res" URI scheme, referring to a managed (.NET) resource embedded in some .NET assembly. The syntax of this URI is "net.res://assemblyName/resourceName".
- A file URI, or a universal naming convention (UNC) path.
For import directives that do not have their location string specified, the OPC Binary data type system tries to find the location in the WellKnownLocationTemplateDictionary Property. You can therefore access the UAOpcBinaryDtsParameters Class configuration part, and add your entry (entries) to this dictionary, in order to tell the OPC Binary data type system where to find the data type dictionaries. You can specify a node ID in the server if the data type dictionary resides there, or place the data type dictionary into a file, or embed it in a managed resource in your program.
Working around errors in data type dictionaries
The OPC UA Complex Data is commonly implemented with some bugs on the server side. In addition, at time of writing this article, it is not rigorously (or at all) checked during OPC compliance certification process, and only to very limited extent during OPC Interoperability Workshops. The client must therefore be prepared to handle situations in which the server behavior is erroneous.
We have tested the OPC UA Complex Data extension against several servers with complex data support available, and continue to expand the range of interoperable servers. We have found several issues in these servers (and in the OPC UA specifications) during these tests, and have already provided special coding and configuration inside the OPC UA Complex Data extension to work around these issues.
If you encounter a buggy server and the workaround is not already built into the OPC UA Complex Data extension, you might be able to work around the problem by configuring the extension accordingly. You are also welcome to report the problem to us, so that we can adjust the software and prevent the issue in future versions.
For errors related to the OPC Binary data type system, the configuration part you need to access is the UAOpcBinaryDtsParameters Class.
Here are some common issues with OPC Binary data type system, and their possible workarounds:
- The data type dictionary uses some external types, but fails to list the namespace (and possibly location) in the import directives. To rectify this, add the import directive to ImplicitImportDictionary Property.
- The data type dictionary uses incorrect (e.g. misspelled) namespace in some of its type references. To rectify this, redirect the namespace by adding an entry to the NamespaceRedirectionDictionary Property.
- The data type dictionary uses some incorrect (e.g. misspelled) type name, or the type name is correct but the type definition in the dictionary does not match the actual encoding used "on the wire". To rectify this, add an appropriate entry to TypeNameRemappingDictionary Property.
See Also