locka99 / opcua

A client and server implementation of the OPC UA specification written in Rust
Mozilla Public License 2.0
496 stars 131 forks source link

Explore child nodes from a root node #105

Closed Dazul closed 3 years ago

Dazul commented 3 years ago

Hi,

I did not found any examples, neither documentation about this. Maybe it's my fault. Anyway, here is what I want to do.

I have a "root" node, from where I want to explore the children. Something like:

Root Node
 |- Child Node 1
 |        |- Grand Child 1.1
 | - Child Node 2
 |        |- Grand Child 2.1

Is it a way to discover the child and grand children nodes knowing only the root node at the beginning?

locka99 commented 3 years ago

I don't have a direct sample, but assuming you're a client talking to a server you need to start with the root node which has a node id of "i=84" and then invoke Session::browse() with it as a node to browse and a BrowseDescription that says what nodes you wish to find from it.

So you'd call it with a BrowseDescription a bit like this pseudo example:

let results = session.browse(&[BrowseDescription {
  node_id: ObjectId::RootFolder.into(),
  browse_direction: BrowseDirection::Forward,
  reference_type_id:  ReferenceTypeId::Organizes,
  include_subtypes: true,
  node_class_mask: 0,
  result_mask: BrowseDescriptionResultMask::all().bits() as u32,
}]);

And then for the results, you'll get ReferenceDescription objects for each child.

The browse() is just a wrapper around the OPC UA Pt 4 BrowseRequest here https://reference.opcfoundation.org/v104/Core/docs/Part4/5.8.2/service .

Refer to the OPC UA spec but this should return you all the children of the RootFolder, i.e. everything it organizes. Then you repeat the call to browse with each child as the node id and do so for far as you want to go. Be aware, that a server may truncate the number of results, so you may have to call browse_next() with a continuation point.

Dazul commented 3 years ago

Thanks for the comment. However, when I try running this, I get this compilation error:

error[E0308]: mismatched types
  --> src/main.rs:79:28
   |
79 |         reference_type_id: ReferenceTypeId::Organizes,
   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `opcua_client::prelude::NodeId`, found enum `opcua_client::prelude::ReferenceTypeId`

And indeed, looking at the code of opcua/types/src/service_types/browse_description.rs (last commit while writing this comment: 52e171f ), it is expecting a Node Id.

#[derive(Debug, Clone, PartialEq)]
pub struct BrowseDescription {
    pub node_id: NodeId,
    pub browse_direction: BrowseDirection,
    pub reference_type_id: NodeId,
    pub include_subtypes: bool,
    pub node_class_mask: u32,
    pub result_mask: u32,
}

Just for fun, I tried using NodeId::default(), but as is probably expected, I get a BrowseResult with status code IS_ERROR.

locka99 commented 3 years ago

Hi, as I said it was a pseudo example but ReferenceTypeId::Organizes.into() fixes it.

The client browse() api is just a wrapper around OPC UA's BrowseRequest / BrowseResponse so the documentation for that is what you have to refer to for what the fields mean. Basically the function just allows you to traverse a reference from one node in the address space to another so get the "children" you have to use a reference type that uses that concept. I chose Organizes because if you were to use a client like UAExpert than that would be how it builds up its tree.

Dazul commented 3 years ago

Thanks for you help, I managed to do what I needed.