Topic RSS14:23, EET
October 15, 2020
OfflineHi,
I’m trying to define custom object types and use them to create objects but run into some problems with that. I know the standard way is to have a NodeSet.xml defining the types import that with LoadModel into the AddressSpace, the part after that is a bit fuzzy for me though.
Do I need to actually create custom classes derived from TUaObject to use those? Or how is that supposed to work?
The concrete example I’m working on right now would be the PackML Specification:
https://reference.opcfoundatio…..odesets/56
Specifically the PackMLStatusObjectType
https://reference.opcfoundatio…..s/56/22059
The other topic related to this is, how can I dynamically, at runtime, create new ObjectTypes and use them? The definition for those types stem from data a user enters as a configuration for the type that should be created. I tried using TUaType like this:
MyType := TUaType.Create(MyNodeManager);
MyType.SuperType := MyNodeManager.GetType(Id_BaseObjectType);
p := TUaVariable.Create(MyNodeManager);
p.Name := ‘Test’;
p.DataTypeId := Id_Int32;
MyType.AddComponent(p);
MyType.Name := ‘MyTestType’;
MyType.NodeId := TUaNodeId.Create(MyNodeManager.Namespace, 9999);
MyNodeManager.RegisterType(MyType.NodeId, TUaObject);
MyObj := MyNodeManager.CreateObject(‘MyTestObj’);
MyObjectsFolder.AddOrganizes(MyObj);
MyObj.TypeDefinition := MyType;
but that runs into an abstract error at runtime. I guess because TUaType has the abstract Method GetNodeClass.
So that seems to be the wrong approach. Any hints on how this could be done?
Greetings
Patrick
16:00, EET
December 21, 2011
OfflineOnce you have the type loaded into the address space, you can create new instances of that type with ‘TUaNodeManager.CreateInstance’.
Take a look at ‘TUaSampleServerForm.CreateAlarmNode’ in the UaSampleServer, for an example.
You will just need the NodeId of the type for that.
Creating types dynamically at runtime is a bit more tricky, but basically, you just need to construct the nodes as you have done. Just use ‘TUaObjectType’ (in ProsysOPC.UaServer.Nodes), instead of ‘TUaType’. We haven’t tried that very much, but I believe it should work…
And again, you should be able to use ‘CreateInstance’ to create the whole structure that corresponds to the type definition.
For types, you should also define the ModellingRule for each InstanceDeclaration (Object and Variable component), to instruct how it should be created: only Mandatory nodes are created by default. For example:
17:11, EET
October 15, 2020
OfflineUsing the CreateInstance works if the NodeSet.xml is present. It does not create optional entries though, even when I add the NodeBuilderConfiguration as shown in the CreateAlarmNode function.
the following code:
UaServer.AddressSpace.LoadModel(‘PackMLNamespace.xml’);
PackMLNodeManager := UaServer.AddressSpace.GetNodeManager(
‘http://opcfoundation.org/UA/PackML/’)
as TUaNodeManagerUaNode;
PackMLNodeManager.NodeBuilderConfiguration :=
TUaTypeDefinitionBasedNodeBuilderConfiguration.Builder
.AddOptional(‘Parameter’)
.AddOptional(‘Product’)
.AddOptional(‘MaterialInterlocked’)
.Build;
MyStatus := PackMLNodeManager.CreateInstance(TUaNodeId.Create(PackMLNodeManager.Namespace, 4), ‘Status’);
MyObjectsFolder.AddOrganizes(MyStatus);
leads to the following object being created:
https://ibb.co/dwxK492R
It’s not adding the optional elements. Guess I misread something there?
For the dynamic type creation, just using TUaObjectType will eliminate the runtime error but it does not seem to work either. The type will not be visible in the type library of the server and creating an instance just creates an empty object.
If I do this on the other hand it does work a bit but still not completely:
MyType := MyNodeManager.GetType(Id_BaseObjectType).AddSubtype(TUaNodeId.Create(MyNodeManager.Namespace, 9999), ‘MyTestType’);
// MyType := TUaObjectType.Create(MyNodeManager);
// MyType.SuperType := MyNodeManager.GetType(id_BaseObjectType);
// MyType.NodeId := TUaNodeId.Create(MyNodeManager.Namespace, 9999);
p := TUaVariable.Create(MyNodeManager);
p.Name := ‘Test’;
p.DataTypeId := Id_Int32;
p.AddReference(Id_ModellingRule_Mandatory, Id_HasModellingRule);
MyType.AddComponent(p);
MyType.AddReference(p, Id_HasComponent);
MyType.AddProperty(‘TestProperty’);
MyObj2 := MyNodeManager.CreateInstance(MyType.NodeId, ‘MyTestObj’);
MyObjectsFolder.AddOrganizes(MyObj2);
Now the Type does show up under the type definition.
https://ibb.co/4w16DCT6
But the variable under it does not, only the property is there as you can see.
And the created instance looks like this:
https://ibb.co/4ZcFF1Rv
It is missing the property and the variable.
As loading a NodeSet.xml is basically creating types at runtime too, I figured it should be possible to simulate that without a NodeSet.xml as well.
What is already working today is to simply go with a BasicObjectType and manually add all the variables and stuff I want to build the type I need, just without a dedicated type. The problem I ran into with that is if I need an array of objects it should really be an array of a specific object type instead of an array of basic objects. Otherwise the client will have a hard time figuring out how the structure actually is supposed to be.
PS: I’m on version 7.8.0 Build 881 if that has any relevance for this topic.
Greetings
Patrick
18:36, EET
December 21, 2011
OfflineGood that you figured out the Variable creation. I don’t even recall what was the exact difference here, but CreateVariable is the proper way to do it…
When you create your object instances, you should not place them in the PackMLNodeManager, but your own. So always use
MyNodeManager.CreateInstance and TUaNodeId.Create(MyNodeManager, …)
for them. The PackMLNodeManager is hosting the types defined in that model and you should not extend it yourself.
Note that CreateInstance will also define a new NodeId based on the name, by default, if you omit the NodeId. But you can see which strategy works the best for you.
For the optional members, you need to define the names using the PackML Namespace. The default is the OPC UA Standard namespace, which works for those types only. And again, modify your own NodeManager. So, try this:
MyNodeManager.NodeBuilderConfiguration :=
TUaTypeDefinitionBasedNodeBuilderConfiguration.Builder
.AddOptional(TUaQualifiedName.Create(PackMLNodeManager.Namespace, ‘Parameter’))
.AddOptional(TUaQualifiedName.Create(PackMLNodeManager.Namespace, ‘Product’))
.AddOptional(TUaQualifiedName.Create(PackMLNodeManager.Namespace, ‘MaterialInterlocked’))
.Build;
1 Guest(s)

Log In
Register