Client and Subscriber Development > Extensions > Integrated Extensions > OPC UA Complex Data Extension > Generic data and data types > Working with data types > Processing data types |
In order to process a data type (a sub-type of DataType Class), you need to first determine its kind (see Data type kinds). You can do it by
There are also some common properties that all data types have, such as Name Property, FullName Property and Description Property.
After you have determined the kind of data type you are dealing with, you can use the data type object cast to appropriate type, and work with properties and methods that are specific to that data type kind. For example, a SequenceDataType contains the length of the sequence (the Length Property), and the data type of its elements (the ElementDataType Property).
The following example shows how a data type can be processed.
// Shows how to process a data type, displaying some of its properties, recursively. // // Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html . using System; using System.Linq; using OpcLabs.BaseLib.DataTypeModel; using OpcLabs.EasyOpc.UA; using OpcLabs.EasyOpc.UA.ComplexData; using OpcLabs.EasyOpc.UA.OperationModel; namespace UADocExamples.ComplexData._DataType { class Kind { 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 // Instantiate the client object. var client = new EasyUAClient(); // Read a node. We know that this node returns complex data, so we can type cast to UAGenericObject. UAGenericObject genericObject; try { genericObject = (UAGenericObject)client.ReadValue(endpointDescriptor, nodeDescriptor); } catch (UAException uaException) { Console.WriteLine("*** Failure: {0}", uaException.GetBaseException().Message); return; } // The data type is in the GenericData.DataType property of the UAGenericObject. DataType dataType = genericObject.GenericData.DataType; // Process the data type. We will inspect some of its properties, and dump them. ProcessDataType(dataType, maximumDepth: 3); } // Process the data type. It can be recursive in itself, so if you do not know the data type you are dealing with, // it is recommended to make safeguards against infinite looping or recursion - here, the maximumDepth. public static void ProcessDataType(DataType dataType, int maximumDepth) { if (maximumDepth == 0) return; Console.WriteLine(); Console.WriteLine("dataType.Name: {0}", dataType.Name); switch (dataType.Kind) { case DataTypeKind.Enumeration: Console.WriteLine("The data type is an enumeration."); var enumerationDataType = (EnumerationDataType) dataType; Console.WriteLine("It has {0} enumeration members.", enumerationDataType.EnumerationMembers.Count); Console.WriteLine("The names of the enumeration members are: {0}.", String.Join(", ", enumerationDataType.EnumerationMembers.Select(member => member.Name))); // Here you can process the members, or inspect SizeInBits etc. break; case DataTypeKind.Opaque: Console.WriteLine("The data type is opaque."); var opaqueDataType = (OpaqueDataType) dataType; Console.WriteLine("Its size is {0} bits.", opaqueDataType.SizeInBits); // There isn't much more you can learn about an opaque data type (well, it may have Description and // other common members). It is, after all, opaque... break; case DataTypeKind.Primitive: Console.WriteLine("The data type is primitive."); var primitiveDataType = (PrimitiveDataType) dataType; Console.WriteLine("Its .NET value type is \"{0}\".", primitiveDataType.ValueType); // There isn't much more you can learn about the primitive data type. break; case DataTypeKind.Sequence: Console.WriteLine("The data type is a sequence."); var sequenceDataType = (SequenceDataType) dataType; Console.WriteLine("Its length is {0} (-1 means that the length can vary).", sequenceDataType.Length); Console.WriteLine("A dump of the element data type follows."); ProcessDataType(sequenceDataType.ElementDataType, maximumDepth - 1); break; case DataTypeKind.Structured: Console.WriteLine("The data type is structured."); var structuredDataType = (StructuredDataType) dataType; Console.WriteLine("It has {0} data fields.", structuredDataType.DataFields.Count); Console.WriteLine("The names of the data fields are: {0}.", String.Join(", ", structuredDataType.DataFields.Select(field => field.Name))); Console.WriteLine("A dump of each of the data fields follows."); foreach (DataField dataField in structuredDataType.DataFields) { Console.WriteLine(); Console.WriteLine("dataField.Name: {0}", dataField.Name); // Note that every data field also has properties like IsLength, IsOptional, IsSwitch which might // be of interest, but we are not dumping them here. ProcessDataType(dataField.DataType, maximumDepth - 1); } break; case DataTypeKind.Union: Console.WriteLine("The data type is union."); var unionDataType = (UnionDataType)dataType; Console.WriteLine("It has {0} data fields.", unionDataType.DataFields.Count); Console.WriteLine("The names of the data fields are: {0}.", String.Join(", ", unionDataType.DataFields.Select(field => field.Name))); break; } } } }
# Shows how to process a data type, displaying some of its properties, recursively. # # Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html . # OPC client and subscriber examples in Python on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-Python . # The QuickOPC package is needed. Install it using "pip install opclabs_quickopc". import opclabs_quickopc # Import .NET namespaces. from System import * from OpcLabs.BaseLib.DataTypeModel import * from OpcLabs.EasyOpc.UA import * from OpcLabs.EasyOpc.UA.OperationModel import * def processDataType(dataType, maximumDepth): if maximumDepth == 0: print('* Reached maximum depth *') return print() print('dataType.Name: ', dataType.Name, sep='') if dataType.Kind == DataTypeKind.Enumeration: print('The data type is an enumeration.') enumerationDataType = dataType print('It has ', enumerationDataType.EnumerationMembers.Count, ' enumeration members.', sep='') print('The names of the enumeration members are: ', end='') for i, member in enumerate(enumerationDataType.EnumerationMembers): if i != 0: print(', ', end='') print(member.Name, end='') print('.') # Here you can process the members, or inspect SizeInBits etc. elif dataType.Kind == DataTypeKind.Opaque: print('The data type is opaque.') opaqueDataType = dataType print('Its size is ', opaqueDataType.SizeInBits, ' bits.', sep='') # There isn't much more you can learn about an opaque data type (well, it may have Description and # other common members). It is, after all, opaque... elif dataType.Kind == DataTypeKind.Primitive: print('The data type is primitive.') primitiveDataType = dataType print('Its .NET value type is "', primitiveDataType.ValueType, '".', sep='') # There isn't much more you can learn about the primitive data type. elif dataType.Kind == DataTypeKind.Sequence: print('The data type is a sequence.') sequenceDataType = dataType print('Its length is ', sequenceDataType.Length, ' (-1 means that the length can vary).', sep='') print('A dump of the element data type follows.') processDataType(sequenceDataType.ElementDataType, maximumDepth - 1) elif dataType.Kind == DataTypeKind.Structured: print('The data type is structured.') structuredDataType = dataType print('It has ', structuredDataType.DataFields.Count, ' data fields.', sep='') print('The names of the data fields are: ', end='') for i, field in enumerate(structuredDataType.DataFields): if i != 0: print(', ', end='') print(field.Name, end='') print('.') print('A dump of each of the data fields follows.') for dataField in structuredDataType.DataFields: print() print('dataField.Name: ', dataField.Name, sep='') # Note that every data field also has properties like IsLength, IsOptional, IsSwitch which might # be of interest, but we are not dumping them here. processDataType(dataField.DataType, maximumDepth - 1) elif dataType.Kind == DataTypeKind.Union: print('The data type is union.') unionDataType = dataType print('It has ', unionDataType.DataFields.Count, ' data fields.', sep='') print('The names of the data fields are: ', end='') for i, field in enumerate(unionDataType.DataFields): if i != 0: print(', ', end='') print(field.Name, end='') print('.') # Define which server and node we will work with. 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') # Instantiate the client object. client = EasyUAClient() # Read a node. We know that this node returns complex data, so we can type cast to UAGenericObject. try: print('Reading...') genericObject = IEasyUAClientExtension.ReadValue(client, endpointDescriptor, nodeDescriptor) except UAException as uaException: print('*** Failure: ' + uaException.GetBaseException().Message) exit() print('Reading successful.') # The data type is in the GenericData.DataType property of the UAGenericObject. dataType = genericObject.GenericData.DataType # Process the data type. We will inspect some of its properties, and dump them. processDataType(dataType, 3) print() print('Finished.')
' Shows how to process a data type, displaying some of its properties, recursively. ' ' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html . Imports System Imports System.Linq Imports OpcLabs.BaseLib.DataTypeModel Imports OpcLabs.EasyOpc.UA Imports OpcLabs.EasyOpc.UA.ComplexData Imports OpcLabs.EasyOpc.UA.OperationModel Namespace ComplexData._DataType Friend Class Kind 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 ' Instantiate the client object. Dim client = New EasyUAClient ' Read a node. We know that this node returns complex data, so we can type cast to UAGenericObject. Dim genericObject As UAGenericObject Try genericObject = CType(client.ReadValue(endpointDescriptor, nodeDescriptor), UAGenericObject) Catch uaException As UAException Console.WriteLine("*** Failure: {0}", uaException.GetBaseException.Message) Exit Sub End Try ' The data type is in the GenericData.DataType property of the UAGenericObject. Dim dataType As DataType = genericObject.GenericData.DataType ' Process the data type. We will inspect some of its properties, and dump them. ProcessDataType(dataType, maximumDepth:=2) End Sub ' Process the data type. It can be recursive in itself, so if you do not know the data type you are dealing with, ' it is recommended to make safeguards against infinite looping or recursion - here, the maximumDepth. Public Shared Sub ProcessDataType(dataType As DataType, ByVal maximumDepth As Integer) If (maximumDepth = 0) Then Return End If Console.WriteLine() Console.WriteLine("dataType.Name: {0}", dataType.Name) Select Case (dataType.Kind) Case DataTypeKind.Enumeration Console.WriteLine("The data type is an enumeration.") Dim enumerationDataType = CType(dataType, EnumerationDataType) Console.WriteLine("It has {0} enumeration members.", enumerationDataType.EnumerationMembers.Count) Console.WriteLine("The names of the enumeration members are: {0}.", _ String.Join(", ", enumerationDataType.EnumerationMembers.Select(Function(member) member.Name))) ' Here you can process the members, or inspect SizeInBits etc. Case DataTypeKind.Opaque Console.WriteLine("The data type is opaque.") Dim opaqueDataType = CType(dataType, OpaqueDataType) Console.WriteLine("Its size is {0} bits.", opaqueDataType.SizeInBits) ' There isn't much more you can learn about an opaque data type (well, it may have Description and ' other common members). It is, after all, opaque... Case DataTypeKind.Primitive Console.WriteLine("The data type is primitive.") Dim primitiveDataType = CType(dataType, PrimitiveDataType) Console.WriteLine("Its .NET value type is ""{0}"".", primitiveDataType.ValueType) ' There isn't much more you can learn about the primitive data type. Case DataTypeKind.Sequence Console.WriteLine("The data type is a sequence.") Dim sequenceDataType = CType(dataType, SequenceDataType) Console.WriteLine("Its length is {0} (-1 means that the length can vary).", sequenceDataType.Length) Console.WriteLine("A dump of the element data type follows.") ProcessDataType(sequenceDataType.ElementDataType, (maximumDepth - 1)) Case DataTypeKind.Structured Console.WriteLine("The data type is structured.") Dim structuredDataType = CType(dataType, StructuredDataType) Console.WriteLine("It has {0} data fields.", structuredDataType.DataFields.Count) Console.WriteLine("The names of the data fields are: {0}.", _ String.Join(", ", structuredDataType.DataFields.Select(Function(field) field.Name))) Console.WriteLine("A dump of each of the data fields follows.") For Each dataField As DataField In structuredDataType.DataFields Console.WriteLine() Console.WriteLine("dataField.Name: {0}", dataField.Name) ' Note that every data field also has properties like IsLength, IsOptional, IsSwitch which might ' be of interest but we are not dumping them here. ProcessDataType(dataField.DataType, (maximumDepth - 1)) Next Case DataTypeKind.Union Console.WriteLine("The data type is union.") Dim unionDataType = CType(dataType, UnionDataType) Console.WriteLine("It has {0} data fields.", unionDataType.DataFields.Count) Console.WriteLine("The names of the data fields are: {0}.", String.Join(", ", unionDataType.DataFields.Select(Function(field) field.Name))) End Select End Sub End Class End Namespace
// Shows how to process a data type, displaying some of its properties, recursively. // // Find all latest examples here : https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html . class procedure Kind.Main; var Client: _EasyUAClient; DataType: OpcLabs_BaseLib_TLB._DataType; EndpointDescriptor: string; GenericObject: _UAGenericObject; NodeDescriptor: string; 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 // Instantiate the client object Client := CoEasyUAClient.Create; // Read a node. We know that this node returns complex data, so we can type cast to UAGenericObject. try GenericObject := IUnknown(Client.ReadValue(EndpointDescriptor, NodeDescriptor)) as _UAGenericObject; except on E: EOleException do begin WriteLn(Format('*** Failure: %s', [E.GetBaseException.Message])); Exit; end; end; // The data type is in the GenericData.DataType property of the UAGenericObject. DataType := genericObject.GenericData.DataType; // Process the data type. We will inspect some of its properties, and dump them. ProcessDataType(DataType, 2); end; // Process the data type. It can be recursive in itself, so if you do not know the data type you are dealing with, // it is recommended to make safeguards against infinite looping or recursion - here, the maximumDepth. class procedure Kind.ProcessDataType(DataType: OpcLabs_BaseLib_TLB._DataType; MaximumDepth: Cardinal); var Count: Cardinal; DataField: _DataField; Element: OleVariant; ElementEnumerator: IEnumVARIANT; EnumerationMember: _EnumerationMember; EnumerationDataType: _EnumerationDataType; FieldNames: string; First: boolean; MemberNames: string; OpaqueDataType: _OpaqueDataType; PrimitiveDataType: _PrimitiveDataType; SequenceDataType: _SequenceDataType; StructuredDataType: _StructuredDataType; TypeName: WideString; begin if MaximumDepth = 0 then Exit; WriteLn; WriteLn('dataType.Name: ', DataType.Name); case DataType.Kind of DataTypeKind_Enumeration: begin WriteLn('The data type is an enumeration.'); EnumerationDataType := DataType as _EnumerationDataType; WriteLn(Format('It has %s enumeration members.', [EnumerationDataType.EnumerationMembers.Count])); ElementEnumerator := EnumerationDataType.EnumerationMembers.GetEnumerator; MemberNames := ''; First := True; while (ElementEnumerator.Next(1, Element, Count) = S_OK) do begin EnumerationMember := IUnknown(Element) as _EnumerationMember; if First then First := False else MemberNames := MemberNames + ', '; MemberNames := MemberNames + EnumerationMember.Name; end; WriteLn(Format('The names of the enumeration members are: %s.', [MemberNames])); // Here you can process the members, or inspect SizeInBits etc. end; DataTypeKind_Opaque: begin WriteLn('The data type is opaque.'); OpaqueDataType := DataType as _OpaqueDataType; WriteLn(Format('Its size is %s bits.', [OpaqueDataType.SizeInBits])); // There isn't much more you can learn about an opaque data type (well, it may have Description and // other common members). It is, after all, opaque... end; DataTypeKind_Primitive: begin WriteLn('The data type is primitive.'); PrimitiveDataType := DataType as _PrimitiveDataType; PrimitiveDataType.ValueType.Get_ToString(TypeName); WriteLn(Format('Its .NET value type is "%s".', [TypeName])); // There isn't much more you can learn about the primitive data type. end; DataTypeKind_Sequence: begin WriteLn('The data type is a sequence.'); SequenceDataType := DataType as _SequenceDataType; WriteLn(Format('Its length is %s (-1 means that the length can vary).', [SequenceDataType.Length.ToString])); WriteLn('A dump of the element data type follows.'); ProcessDataType(SequenceDataType.ElementDataType, MaximumDepth - 1); end; DataTypeKind_Structured: begin WriteLn('The data type is structured.'); StructuredDataType := DataType as _StructuredDataType; WriteLn(Format('It has %s data fields.', [StructuredDataType.DataFields.Count.ToString])); ElementEnumerator := StructuredDataType.DataFields.GetEnumerator; FieldNames := ''; First := True; while (ElementEnumerator.Next(1, Element, Count) = S_OK) do begin if First then First := False else FieldNames := FieldNames + ', '; FieldNames := FieldNames + Element.Name; end; WriteLn(Format('The names of the data fields are: %s.', [FieldNames])); WriteLn('A dump of each of the data fields follows.'); ElementEnumerator := StructuredDataType.DataFields.GetEnumerator; while (ElementEnumerator.Next(1, Element, Count) = S_OK) do begin DataField := IUnknown(Element) as _DataField; WriteLn; WriteLn(Format('dataField.Name: %s', [DataField.Name])); // Note that every data field also has properties like IsLength, IsOptional, IsSwitch which might // be of interest but we are not dumping them here. ProcessDataType(DataField.DataType, MaximumDepth - 1); end; end; end; end;
In many cases, you will not be processing the data type alone, but in parallel with the generic data. See Processing generic data for details.
Copyright © 2004-2024 CODE Consulting and Development, s.r.o., Plzen. All rights reserved. Web page: www.opclabs.com
Send Documentation Feedback. Resources: Knowledge Base, Product Downloads. Technical support: Online Forums, FAQ.Missing some example? Ask us for it on our Online Forums! You do not have to own a commercial license in order to use Online Forums, and we reply to every post.