Skip to main content

opcua_nodes/
type_tree.rs

1use std::{
2    borrow::Borrow,
3    collections::{HashMap, HashSet},
4};
5
6use crate::NamespaceMap;
7use opcua_types::{
8    DataTypeId, NodeClass, NodeId, ObjectTypeId, QualifiedName, ReferenceTypeId, VariableTypeId,
9};
10
11#[derive(PartialEq, Eq, Hash, Clone)]
12struct TypePropertyKey {
13    path: Vec<QualifiedName>,
14}
15// NOTE: This implementation means that TypePropertyKey must have the same
16// hash as an equivalent &[QualifiedName]
17impl Borrow<[QualifiedName]> for TypePropertyKey {
18    fn borrow(&self) -> &[QualifiedName] {
19        &self.path
20    }
21}
22
23#[derive(Clone, Debug)]
24/// A single property of a type in the type tree.
25pub struct TypeProperty {
26    /// Node ID of the property.
27    pub node_id: NodeId,
28    /// Node class of the property.
29    pub node_class: NodeClass,
30}
31
32#[derive(Clone, Debug)]
33/// Inverse reference to a type from a property.
34pub struct TypePropertyInverseRef {
35    /// Node ID of the type.
36    pub type_id: NodeId,
37    /// Path to the property.
38    pub path: Vec<QualifiedName>,
39}
40
41/// Type managing the types in an OPC-UA server.
42/// The server needs to know about all available types, to handle things like
43/// event filters, browse filtering, etc.
44///
45/// Each node manager is responsible for populating the type tree with
46/// its types.
47#[derive(Default, Clone)]
48pub struct DefaultTypeTree {
49    nodes: HashMap<NodeId, NodeClass>,
50    subtypes_by_source: HashMap<NodeId, HashSet<NodeId>>,
51    subtypes_by_target: HashMap<NodeId, NodeId>,
52    property_to_type: HashMap<NodeId, TypePropertyInverseRef>,
53    type_properties: HashMap<NodeId, HashMap<TypePropertyKey, TypeProperty>>,
54    namespaces: NamespaceMap,
55}
56
57#[derive(Clone, Debug)]
58/// A node in the type tree.
59pub enum TypeTreeNode<'a> {
60    /// A registered type.
61    Type(NodeClass),
62    /// A property of a type.
63    Property(&'a TypePropertyInverseRef),
64}
65
66/// Trait for a type tree, a structure that provides methods to
67/// inspect type relationships. This is used for getting reference sub-types,
68/// and for events.
69pub trait TypeTree {
70    /// Return `true` if `child` is a descendant of `ancestor`, meaning they
71    /// are connected with a chain of `HasSubtype` references.
72    fn is_subtype_of(&self, child: &NodeId, ancestor: &NodeId) -> bool;
73
74    /// Get a reference to the node with ID `node`.
75    fn get_node<'a>(&'a self, node: &NodeId) -> Option<TypeTreeNode<'a>>;
76
77    /// Get the node class of a type in the type tree given by `node`.
78    fn get(&self, node: &NodeId) -> Option<NodeClass>;
79
80    /// Find a property of a type from its browse path.
81    fn find_type_prop_by_browse_path(
82        &self,
83        type_id: &NodeId,
84        path: &[QualifiedName],
85    ) -> Option<&TypeProperty>;
86
87    /// Get the supertype of the given node.
88    fn get_supertype<'a>(&'a self, node: &NodeId) -> Option<&'a NodeId>;
89
90    /// Get the namespace map used by this type tree.
91    fn namespaces(&self) -> &NamespaceMap;
92}
93
94impl TypeTree for DefaultTypeTree {
95    /// Return `true` if `child` is a subtype of `ancestor`, or if `child` and
96    /// `ancestor` is the same node, i.e. subtype in the OPC-UA sense.
97    fn is_subtype_of(&self, child: &NodeId, ancestor: &NodeId) -> bool {
98        let mut node = child;
99        loop {
100            if node == ancestor {
101                break true;
102            }
103
104            let Some(class) = self.nodes.get(node) else {
105                break false;
106            };
107
108            if !matches!(
109                class,
110                NodeClass::DataType
111                    | NodeClass::ObjectType
112                    | NodeClass::ReferenceType
113                    | NodeClass::VariableType
114            ) {
115                break false;
116            }
117
118            match self.subtypes_by_target.get(node) {
119                Some(n) => node = n,
120                None => break false,
121            }
122        }
123    }
124
125    /// Get a reference to a node in the type tree.
126    fn get_node<'a>(&'a self, node: &NodeId) -> Option<TypeTreeNode<'a>> {
127        if let Some(n) = self.nodes.get(node) {
128            return Some(TypeTreeNode::Type(*n));
129        }
130        if let Some(p) = self.property_to_type.get(node) {
131            return Some(TypeTreeNode::Property(p));
132        }
133        None
134    }
135
136    /// Get a type from the type tree.
137    fn get(&self, node: &NodeId) -> Option<NodeClass> {
138        self.nodes.get(node).cloned()
139    }
140
141    /// Find a property by browse and type ID.
142    fn find_type_prop_by_browse_path(
143        &self,
144        type_id: &NodeId,
145        path: &[QualifiedName],
146    ) -> Option<&TypeProperty> {
147        self.type_properties.get(type_id).and_then(|p| p.get(path))
148    }
149
150    fn get_supertype<'a>(&'a self, node: &NodeId) -> Option<&'a NodeId> {
151        self.subtypes_by_target.get(node)
152    }
153
154    fn namespaces(&self) -> &NamespaceMap {
155        &self.namespaces
156    }
157}
158
159impl DefaultTypeTree {
160    /// Create a new type tree with just the root nodes added.
161    pub fn new() -> Self {
162        let mut type_tree = Self {
163            nodes: HashMap::new(),
164            subtypes_by_source: HashMap::new(),
165            subtypes_by_target: HashMap::new(),
166            type_properties: HashMap::new(),
167            property_to_type: HashMap::new(),
168            namespaces: NamespaceMap::new(),
169        };
170        type_tree
171            .namespaces
172            .add_namespace("http://opcfoundation.org/UA/");
173        type_tree
174            .nodes
175            .insert(ObjectTypeId::BaseObjectType.into(), NodeClass::ObjectType);
176        type_tree
177            .nodes
178            .insert(ReferenceTypeId::References.into(), NodeClass::ReferenceType);
179        type_tree.nodes.insert(
180            VariableTypeId::BaseVariableType.into(),
181            NodeClass::VariableType,
182        );
183        type_tree
184            .nodes
185            .insert(DataTypeId::BaseDataType.into(), NodeClass::DataType);
186        type_tree
187    }
188
189    /// Add a new type to the type tree.
190    pub fn add_type_node(&mut self, id: &NodeId, parent: &NodeId, node_class: NodeClass) {
191        self.nodes.insert(id.clone(), node_class);
192        self.subtypes_by_source
193            .entry(parent.clone())
194            .or_default()
195            .insert(id.clone());
196        self.subtypes_by_target.insert(id.clone(), parent.clone());
197    }
198
199    /// Add a new property to the type tree.
200    pub fn add_type_property(
201        &mut self,
202        id: &NodeId,
203        typ: &NodeId,
204        path: &[&QualifiedName],
205        node_class: NodeClass,
206    ) {
207        let props = match self.type_properties.get_mut(typ) {
208            Some(x) => x,
209            None => self.type_properties.entry(typ.clone()).or_default(),
210        };
211
212        let path_owned: Vec<_> = path.iter().map(|n| (*n).to_owned()).collect();
213
214        props.insert(
215            TypePropertyKey {
216                path: path_owned.clone(),
217            },
218            TypeProperty {
219                node_class,
220                node_id: id.clone(),
221            },
222        );
223
224        self.property_to_type.insert(
225            id.clone(),
226            TypePropertyInverseRef {
227                type_id: typ.clone(),
228                path: path_owned,
229            },
230        );
231    }
232
233    /// Remove a node from the type tree.
234    pub fn remove(&mut self, node_id: &NodeId) -> bool {
235        if self.nodes.remove(node_id).is_some() {
236            let props = self.type_properties.remove(node_id);
237            if let Some(props) = props {
238                for prop in props.values() {
239                    self.property_to_type.remove(&prop.node_id);
240                }
241            }
242            if let Some(parent) = self.subtypes_by_target.remove(node_id) {
243                if let Some(types) = self.subtypes_by_source.get_mut(&parent) {
244                    types.remove(node_id);
245                }
246            }
247            return true;
248        }
249        if let Some(prop) = self.property_to_type.remove(node_id) {
250            let props = self.type_properties.get_mut(&prop.type_id);
251            if let Some(props) = props {
252                props.remove(&prop.path as &[QualifiedName]);
253            }
254            return true;
255        }
256        false
257    }
258
259    /// Get a mutable reference to the namespaces used by this type tree.
260    pub fn namespaces_mut(&mut self) -> &mut NamespaceMap {
261        &mut self.namespaces
262    }
263
264    /// Get a reference to the namespaces used by this type tree.
265    pub fn namespaces(&self) -> &NamespaceMap {
266        &self.namespaces
267    }
268
269    /// Get a vector of all the descendants of the given root node.
270    pub fn get_all_children<'a>(&'a self, root: &'a NodeId) -> Vec<&'a NodeId> {
271        let mut res = Vec::new();
272        let mut roots = vec![root];
273        loop {
274            let Some(root) = roots.pop() else {
275                break;
276            };
277            res.push(root);
278            let Some(children) = self.subtypes_by_source.get(root) else {
279                continue;
280            };
281            for child in children.iter() {
282                roots.push(child);
283            }
284        }
285
286        res
287    }
288}