Page 1 of 1

Read structure array with a model

Posted: 01 Dec 2022, 11:34
by Nathan
Hello

I wish to read an array of a custom structure using a model.
I would expect that I could do something like the following:

Code: Select all

var usersNodeId = new NodeId(OpcNodeIds.UsersPropertyId, this.NamespaceIndex());
var users = new List<UserModel>();
session.Model.Read(usersNodeId, users);
With the users model looking like this.

Code: Select all

[UaTypeDefinition(NamespaceUri = OpcNamespaces.ApplicationUri, NodeId = OpcNodeIds.UserDataType)]
public class UserModel
{
    [UaInstanceDeclaration(NamespaceUri = OpcNamespaces.ApplicationUri, BrowseName = "UserName")]
    public string Username { get; set; }
    
    [UaInstanceDeclaration(NamespaceUri = OpcNamespaces.ApplicationUri, BrowseName = "Description")]
    public string Description { get; set; }
}
This does of course not work.
So how do I accomplish what I am trying to do?

I am using the Nuget package version 3.1.1 on .NET Framework 4.6.2.

Thank you

Re: Read structure array with a model

Posted: 01 Dec 2022, 16:26
by Support Team
Hi,

I think you are mixing here two concepts. In OPC UA, there is the address space in which the whole node hierarchy lives and there are attributes and values of those nodes. There are no structures and arrays in the address space. The Read method of the model manager, however, reads the values/attributes of several nodes into a C# class, so it works on the address space. To read an array of structured data types of one (single) node you simply use the Read method of the session.

In order for the decoder to know how to decode the value, you must create a class for the structure that implements the IEncodeable interface and register that class. There are many pitfalls you can run into, when implementing an IEncodeable class by hand. Fortunately, the UaModeler will create those DataTypes for you. Simply load the nodeset file of the server and let it generate the client code for you.

Re: Read structure array with a model

Posted: 02 Dec 2022, 15:59
by Nathan
That helped a lot, thank you.

For anybody stumbling across this thread with the same/similar question a little more info:

A type implementing the IEncodeable interface (and therefore representing a type on the server), can be registered on the application instance after calling the start method.

Code: Select all

applicationInstance.Start();

// register a single type
applicationInstance.KnownTypeFactory.AddEncodeableType(typeof(AccUserType));

// register all IEncodeable types of the assembly
applicationInstance.KnownTypeFactory.AddEncodeableTypes(typeof(AccUserType).Assembly);
Any normal read call will then return an object of type ExtensionObject or an ExtensionObject array (in case the property on the server is an array).
This object then carries an instance the previously registered type.

Code: Select all

List<DataValue> dataValues = s.Read(new[] { new ReadValueId { NodeId = usersNodeId, AttributeId = Attributes.Value } });
ExtensionObject[] extensionObjects = (ExtensionObject[])dataValues[0].Value;
List<AccUserType> users = extensionObjects.Select(v => (AccUserType)v.Body).ToList();

Re: Read structure array with a model

Posted: 26 Dec 2023, 13:07
by @rt
I'm working with PLC server
Is there any way to generate nodeset file of the server's exposed data types?

Re: Read structure array with a model

Posted: 17 Jan 2024, 19:19
by Support Team
Hi,

yes there is. UaExpert has plugin that (recursively) browses the complete server, and exports all the nodes being found into XML file (one file per each namespace).

With that you have "copy" of the actual type system, and can use it to program (specialize) you client implementation against this PLC server. You may re-use (import) in UaModeler and use for client-side code generation with Unified Automation Client SDKs.

In UaExpert you go: Menu -> Document -> Add... -> XML Nodeset Export View