Avatar

Please consider registering
guest

sp_LogInOut Log In sp_Registration Register

Register | Lost password?
Advanced Search

— Forum Scope —




— Match —





— Forum Options —





Minimum search word length is 3 characters - maximum search word length is 84 characters

sp_Feed Topic RSS sp_TopicIcon
Access Nodes in ObjectsFolder via ExtendedNodeId
November 11, 2019
19:33, EET
Avatar
stefan.bck
Member
Members
Forum Posts: 5
Member Since:
November 5, 2019
sp_UserOfflineSmall Offline

Hello,

in our project (server-side only) we have a generated xml-model (from UAModeler) that we use for code generation.
I can see in the generated code, that there are some helper classes (e.g. PrefixObjectIds, PrefixVariableIds, etc.), which contain ExpandedNodeIds, which are very handy for quickly referencing types and variables.
I am however, still looking for a convenient way to quickly access objects that live inside the objects part (objects folder) of the address space and are already defined in the xml-model and have a defined NodeId.
Since the NodeIds do not change as long as the model itself does not change (and if, the code would have to be re-generated anyways), I would have expected there to be ExpandedNodeIds for instances as well.
What are we missing? How can we conveniently access a predefined instance that sits below e.g. 6 folders and 4 objects? Navigating there from the top seems quite cumbersome.

Any help would be greatly appreciated.

Best Regards,

Stefan

Edit: The title says “ExtendedNodeIds”, but what I really meant was “ExpandedNodeIds”.

November 12, 2019
13:06, EET
Avatar
Bjarne Boström
Moderator
Moderators
Forum Posts: 1026
Member Since:
April 3, 2012
sp_UserOfflineSmall Offline

Hi,

(sorry, this is a bit long)

Generally we have been recommending to define the types in the nodesets and instances in code. I would say outside of trivial examples, it would usually be quite pain to model all the instances in the models. Additionally typically you will have some external system which will dictate what the instances should be. That is one of the reasons why we currently generate Ids for type-space only.

Generally I would say due to modelling the types, you should usually have a well known entry point (i.e. you know the NodeId or BrowsePath) to the instances you are interested in. Then you should have instances of some types, and those types TypeDefinition does define the rest of the structure of the nodes. The codegen will create more specific versions of the UaNode interface (+ the impls) which help to access the rest of the node-tree.

For example, if you would know an id is of AnalogItemType, then you could do the following.

ExpandedNodeId id = …
AnalogItemType analogItem = client.getAddressSpace().getNode(id, AnalogItemType.class);

Range euRange = analogItem.getEURange(); // Values of sub-nodes
UaProperty euRangeProperty = analogItem.getEURangeNode(); // The actual sub-node

But to return to your “below 6 folders and 4 objects” example. Typically in these skenarios, you will have the (Expanded)NodeId for the node stored somewhere (conf files, hardcoded values etc.) and use it directly. Note that this NodeId would not be generated by the codegen, you would need to look that up (or e.g. the user of your software would either use generic clients to find it, or you could have an address-space tree selector built-in).

If possible, I would like to have a more concrete example, if there is a specific use-case where you feel this is not enough, as this answer is getting quite long and in some cases some other approach might be better. For example in some cases the UaClient.getAddressSpace().translateBrowsePathsToNodeIds(…) might be better.

P.S.
We will investigate at some point if an option to generate Ids from instances would be added. There are some not-so-obvious constraints to consider for a generic solution (a lot of low-lvl tech stuff below):

1. A Java classfile can contain maximum of 65535 or so bytes of bytecode or it cannot be compiled. At bare minimum, initialing a static constant will take up 6 bytes of bytecode (invokestatic + putstatic both take op code + 2 bytes). This is without having any parameters for the static method (which would usually be 3 or more bytes). For the standard model, which basically just defines types (+the Server instance node), is a bit over 10000 identifiers. We have somewhat solved this by abusing the fact that java interfaces can be multiple inherited and statics are accessed via inheritance (so we split the ids per NodeClass and for backwards compability the Ids will inherit all of them). Additionally the initialization static methods are created for each id in a separate class (as having a parameter would increate bytecode count). If we were to generate ids for instances as well, we would need to prepare for a larger id count. But an option to turn this on would probably be the best.

2. As far as I’m aware, in type-“space” each BrowsePath must be unique. This means if we generate the ids with name like Type_InstanceDeclaration1_InstanceDeclaration2_LeafInstanceDeclaration we should get all type-related-ids and all of they should have unique name for the code. This restriction does not apply to instance-“space”, multiple nodes can have the same names i.e. a BrowsePath can target multiple nodes in the instance-space. For code-generation this means either the files wont compile due to duplicate fields or we need to leave some out (or come up with some other naming scheme). Also if we start Objects_X_Y_Z_A_B_C_Leaf we could still get conflicts and the names would probably end up absurdly long. The same can somewhat be said for the type-ids naming scheme, but that is at least somewhat consistent.

P.S.2.
I guess I should clarify that instances that would be like the “DeviceSet” folder of the DI spec would not be a problem and would be something we would like to generate, but there is no easy way to recognize them easily from normal instances. But those models usually wont have any “real” instances anyway, so for them at least it shouldn’t be a problem.

P.S.3.
For modelling purposes, it might be best to separate the type-model from the instance model anyway, i.e. they probably should be in separate namespaces. This is because clients can be programmed vs. a model, but unless those instances should be part of every server having that (type) namespace (like the “DeviceSet” is in the DI model), then I recommend putting the instances to a separate model.

November 12, 2019
16:10, EET
Avatar
stefan.bck
Member
Members
Forum Posts: 5
Member Since:
November 5, 2019
sp_UserOfflineSmall Offline

Hello Bjarne,

thank you for taking the time to write this detailed response.

You are suggesting to hardcode the NodeIds (may it be directly or via config files), yet this is exactly what we are trying to avoid. We already know that our model is subject to (multiple) change(s), and we really want to avoid doing this manually, since it is prone to errors.

Choosing to generate the instances in the code instead of the model itself is something we may be able to do, but it only partly helps us make things easier.
The problem here is that we have a couple of pretty big and nested type declarations. To give you a minimized example:

Type A
..- Folder A1
….- Folder A11
……- Type A11A
……..- Folder A11A1
……….- Type A11A1A
……..- Type A11AA
……- Type A11B
….- Folder A12
..- Folder A2
….- Type A2A
….- Type A2B
..- Folder A3
..- Type AA
….- Folder AA1
……- Type AA1A
..- Type AB

I am not even getting started listing the variables here, but they are not the problem anyways. Navigating from Type to type is also not a problem, but we are still looking for a convenient way to navigate through all the folders.
If I have this Type A and I create an instance of it, what is the best / quickest way to get to its Node A11A1A for example?
The structure is already defined so I would have thought that this is easy.

Thanks in advance and best regards,
Stefan

November 12, 2019
17:40, EET
Avatar
Bjarne Boström
Moderator
Moderators
Forum Posts: 1026
Member Since:
April 3, 2012
sp_UserOfflineSmall Offline

I would maybe argue at least a bit that that is not proper information modeling. Technically yes, you can do this, but from the type’s perspective it lacks semantic information if it is just raw folders. This is unless the folders actually represent e.g. a filesystem (the there is the FileDirectoryType) or equivalent real nesting system. Then again I’m not sure if this just the case in this example. I’ll probably follow-up in some post depending how it was. This has implications on the answers I can give, i.e. if they are just raw folders, then codegen wouldn’t help much (except the Ids generation part). If they are actual types describing the node-structure of them, then the codegen would generate the helper methods like seen above in the case of the AnalogItemType.

Additionally, from the example I’m not 100% sure do you actually have an ObjectType, that has an InstanceDeclaration of the FolderType, and below that InstanceDeclaration, you would have additional ObjectType nodes. As I’m not sure if that is allowed, i.e. each XXXType NodeClass nodes must be linked with HasSubType references to form the type hierarchy. Therefore in these kinds of examples I would model them as such:

(ID meaning InstanceDeclaration)

AType
– B (ID, FolderType)
– C (ID, FolderType)
– D (ID, DType)
– E (ID, FolderType) <– This comes from the DType TypeDefinition
– Potential subtypes of AType etc.

DType
– E (ID, FolderType)

But anyway, assuming you know the BrowsePath, which this looks like and assuming there are no duplicates (or you can handle them), I would say in this case the TranslateBrowsePathToNodeId service call would probably be the easiest or at least this fits for it’s use-case. Therefore you could do something like (this assumes a bit from your example, but should be enough to explain it):

    ExpandedNodeId expandedNodeId = ... //For the instance of A
	NamespaceTable namespaceTable = client.getNamespaceTable(); //or some other way
	
	// This or ExpandedNodeId can be used for the UaBrowsePath.from(...)
    UaNodeId startId = UaNodeId.fromLocal(expandedNodeId, namespaceTable);

    // Assumed, and might be different per "hop"
    int namespaceIndex = 2; // need to know the index or uri
    String namespaceUri = namespaceTable.getUri(namespaceIndex);
    UaNamespace ns = UaNamespace.from(namespaceUri);

    // Different ways to construct
    UaQualifiedName hop1 = UaQualifiedName.from(namespaceUri, "A1");
    UaQualifiedName hop2 = UaQualifiedName.from(ns, "A11");
    UaQualifiedName hop3 = UaQualifiedName.from(new QualifiedName(namespaceIndex, "A11A"), namespaceTable);
    UaQualifiedName hop4 = UaQualifiedName.standard("A11A1"); // (if it would be in the 0-namespace)

    UaQualifiedName targetNode = UaQualifiedName.from(namespaceUri, "A11A1A");

    UaBrowsePath uabp = UaBrowsePath.from(startId, hop1, hop2, hop3, hop4, targetNode);

    boolean isInverse = false; // Look only forward direction
    ExpandedNodeId referenceType = ReferenceTypeIds.HierarchicalReferences;
    boolean includeSubTypes = true; // Look additionally all sub-reference types of the above

    BrowsePathResult[] pathsResults = client.getAddressSpace()
        .translateBrowsePathsToNodeIds(uabp.toBrowsePath(namespaceTable, referenceType, isInverse, includeSubTypes));

    // we did pass a single BrowsePath to the service call
    BrowsePathResult pathsResult = pathsResults[0];
	
    for (BrowsePathTarget target : pathsResult.getTargets()) {
      ExpandedNodeId targetId = target.getTargetId(); // <-- should be id for targetNode A11A1A
      // And as mentioned, duplicates are a possibility.
    }
November 12, 2019
18:11, EET
Avatar
Bjarne Boström
Moderator
Moderators
Forum Posts: 1026
Member Since:
April 3, 2012
sp_UserOfflineSmall Offline

I guess I should mention, that if the sub-nodes are linked with HasComponent ReferenceType, then you can use UaNode.getComponent(QualifiedName) to get it via the BrowseName from the parent node. Typically nodes that are part of a larger type are reference with that ReferenceType. Additionally for HasProperty child nodes we have getProperty(QualifiedName). For other types currently the getReferences (or getForwardReferences) must be used and then the node from inside the UaReference object.

But anyway, where possible and makes sense, if there are sub-nodes for a type more than 1-level deep, then I would hope and recommend that each such InstanceDeclaration would actually have a TypeDefinition that defines the second level (and so on). Because like in object oriented programming, once you go 1-level deep, it’s their objects definition what defines what fields etc. they do have. While you could override them also e.g. in Java by overriding e.g. getters to return more precise types, the client of that object would need to do the extra work to use any extra information if they only know the super-type.

Assuming you had a hierarchy with the following types and their InstanceDeclarations:

AType
- B of BType
	- C of CType
		- D of DType

BType
- C of CType
	- D of DType

CType
- D of DType

DType
- (no additional nodes)

Codegen would generate AType, BType, CType, DType.

Then if you get NodeId for an instance A of AType, you should be able to do (ignoring error handling, Optional ModellingRules etc.):

AType aInstance = client.getAddressSpace().getNode(idForAInstance, AType.class)
aInstance.getBNode().getCNode().getD() (or .getDNode().getValue())

If instead B,C would be of FolderType, that type is defined in the standard model, thus no way to have it somehow the getBNode() method, so then you would need to use the more generic getComponent etc. methods.

EDIT: indentation to the example

Forum Timezone: Europe/Helsinki

Most Users Ever Online: 1919

Currently Online:
17 Guest(s)

Currently Browsing this Page:
1 Guest(s)

Top Posters:

Heikki Tahvanainen: 402

hbrackel: 144

rocket science: 88

pramanj: 86

Francesco Zambon: 83

Ibrahim: 78

Sabari: 62

kapsl: 57

gjevremovic: 49

Xavier: 43

Member Stats:

Guest Posters: 0

Members: 736

Moderators: 7

Admins: 1

Forum Stats:

Groups: 3

Forums: 15

Topics: 1524

Posts: 6450

Newest Members:

kristiewinkle8, rust, christamcdowall, redaahern07571, nigelbdhmp, travistimmons, AnnelCib, dalenegettinger, howardkennerley, Thomassnism

Moderators: Jouni Aro: 1026, Pyry: 1, Petri: 0, Bjarne Boström: 1026, Jimmy Ni: 26, Matti Siponen: 346, Lusetti: 0

Administrators: admin: 1