1mod utils;
4
5pub use opcua_nodes::*;
6pub use utils::*;
7
8#[cfg(feature = "generated-address-space")]
9pub use opcua_core_namespace::CoreNamespace;
10
11use std::collections::VecDeque;
12
13use hashbrown::{HashMap, HashSet};
14use tracing::{debug, error, info, warn};
15
16use crate::node_manager::{ParsedReadValueId, ParsedWriteValue, RequestContext};
17use opcua_types::{
18 node_id::IntoNodeIdRef, BrowseDirection, DataValue, LocalizedText, NodeClass, NodeId,
19 QualifiedName, ReferenceTypeId, StatusCode, TimestampsToReturn,
20};
21
22#[derive(Default)]
24pub struct AddressSpace {
25 node_map: HashMap<NodeId, NodeType>,
26 namespaces: HashMap<u16, String>,
27 references: References,
28}
29
30impl AddressSpace {
31 pub fn new() -> Self {
33 Self {
34 node_map: HashMap::new(),
35 namespaces: HashMap::new(),
36 references: References::new(),
37 }
38 }
39
40 pub fn import_node_set<T: NodeSetImport + ?Sized>(
43 &mut self,
44 import: &T,
45 namespaces: &mut NamespaceMap,
46 ) {
47 let mut map = NodeSetNamespaceMapper::new(namespaces);
48 import.register_namespaces(&mut map);
49 let owned_namespaces = import.get_own_namespaces();
50 for ns in owned_namespaces {
51 let idx = map
52 .namespaces()
53 .known_namespaces()
54 .get(&ns)
55 .expect("Node import returned owned namespace not added to the namespace map");
56 self.add_namespace(&ns, *idx);
57 }
58 let mut count = 0;
59 for item in import.load(&map) {
60 count += 1;
61 self.import_node(item);
62 }
63 info!("Imported {count} nodes");
64 }
65
66 pub fn load_into_type_tree(&self, type_tree: &mut DefaultTypeTree) {
68 let mut found_ids = VecDeque::new();
69 for node in self.node_map.values() {
71 let nc = node.node_class();
72 if !matches!(
73 nc,
74 NodeClass::DataType
75 | NodeClass::ObjectType
76 | NodeClass::VariableType
77 | NodeClass::ReferenceType
78 ) {
79 continue;
80 }
81
82 let node_id = node.node_id();
83
84 let parent = self
85 .references
86 .find_references(
87 node_id,
88 Some((ReferenceTypeId::HasSubtype, false)),
89 type_tree,
90 BrowseDirection::Inverse,
91 )
92 .next();
93
94 let parent_id = if let Some(parent) = parent {
96 parent.target_node.clone()
97 } else {
98 continue;
99 };
100
101 type_tree.add_type_node(node_id, &parent_id, nc);
102 found_ids.push_back((node_id, node_id, Vec::new(), nc));
103 }
104
105 let mut seen_nodes = HashSet::new();
106
107 while let Some((node, root_type, path, node_class)) = found_ids.pop_front() {
109 for child in self.find_references(
110 node,
111 Some((ReferenceTypeId::HierarchicalReferences, true)),
112 type_tree,
113 BrowseDirection::Forward,
114 ) {
115 if child
116 .reference_type
117 .as_reference_type_id()
118 .is_ok_and(|r| r == ReferenceTypeId::HasSubtype)
119 {
120 continue;
121 }
122 let Some(node_type) = self.node_map.get(child.target_node) else {
123 continue;
124 };
125
126 let nc = node_type.node_class();
127
128 if matches!(
129 nc,
130 NodeClass::DataType
131 | NodeClass::ObjectType
132 | NodeClass::VariableType
133 | NodeClass::ReferenceType
134 ) {
135 continue;
136 }
137 let mut path = path.clone();
138 path.push(node_type.as_node().browse_name());
139
140 if !seen_nodes.insert(child.target_node) {
141 warn!(
142 "Found node {} more than once when browsing hierarchically",
143 child.target_node
144 );
145 continue;
146 }
147
148 found_ids.push_back((child.target_node, root_type, path, nc));
149 }
150
151 if !path.is_empty() {
152 type_tree.add_type_property(node, root_type, &path, node_class);
153 }
154 }
155 }
156
157 pub fn add_namespace(&mut self, namespace: &str, index: u16) {
159 self.namespaces.insert(index, namespace.to_string());
160 }
161
162 pub fn insert<'a, T, S>(
164 &mut self,
165 node: T,
166 references: Option<&'a [(&'a NodeId, &S, ReferenceDirection)]>,
167 ) -> bool
168 where
169 T: Into<NodeType>,
170 S: Into<NodeId> + Clone,
171 {
172 let node_type = node.into();
173 let node_id = node_type.node_id().clone();
174
175 self.assert_namespace(&node_id);
176
177 if self.node_exists(&node_id) {
178 error!("This node {} already exists", node_id);
179 false
180 } else {
181 if let Some(references) = references {
183 self.references.insert::<S>(&node_id, references);
184 }
185 self.node_map.insert(node_id, node_type);
186
187 true
188 }
189 }
190
191 pub fn import_node(&mut self, node: ImportedItem) -> bool {
193 let node_id = node.node.node_id().clone();
194
195 self.assert_namespace(&node_id);
196 if self.node_exists(&node_id) {
197 error!("This node {} already exists", node_id);
198 false
199 } else {
200 self.node_map.insert(node_id.clone(), node.node);
201 for r in node.references {
202 self.references.import_reference(node_id.clone(), r);
203 }
204
205 true
206 }
207 }
208
209 pub fn namespace_index(&self, namespace: &str) -> Option<u16> {
211 self.namespaces
212 .iter()
213 .find(|(_, ns)| namespace == ns.as_str())
214 .map(|(i, _)| *i)
215 }
216
217 fn assert_namespace(&self, node_id: &NodeId) {
218 if !self.namespaces.contains_key(&node_id.namespace) {
219 panic!("Namespace index {} not in address space", node_id.namespace);
220 }
221 }
222
223 pub fn node_exists(&self, node_id: &NodeId) -> bool {
225 self.node_map.contains_key(node_id)
226 }
227
228 pub fn insert_reference(
231 &mut self,
232 source_node: &NodeId,
233 target_node: &NodeId,
234 reference_type: impl Into<NodeId>,
235 ) {
236 self.references
237 .insert_reference(source_node, target_node, reference_type)
238 }
239
240 pub fn insert_references<'a>(
242 &mut self,
243 references: impl Iterator<Item = (&'a NodeId, &'a NodeId, impl Into<NodeId>)>,
244 ) {
245 self.references.insert_references(references)
246 }
247
248 pub fn delete_reference<'a>(
250 &mut self,
251 source_node: impl IntoNodeIdRef<'a>,
252 target_node: impl IntoNodeIdRef<'a>,
253 reference_type: impl IntoNodeIdRef<'a>,
254 ) -> bool {
255 self.references
256 .delete_reference(source_node, target_node, reference_type)
257 }
258
259 pub fn delete_node_references(
261 &mut self,
262 source_node: &NodeId,
263 delete_target_references: bool,
264 ) -> bool {
265 self.references
266 .delete_node_references(source_node, delete_target_references)
267 }
268
269 pub fn has_reference<'a>(
272 &self,
273 source_node: impl IntoNodeIdRef<'a>,
274 target_node: impl IntoNodeIdRef<'a>,
275 reference_type: impl IntoNodeIdRef<'a>,
276 ) -> bool {
277 self.references
278 .has_reference(source_node, target_node, reference_type)
279 }
280
281 pub fn find_references<'a: 'b, 'b>(
284 &'a self,
285 source_node: impl IntoNodeIdRef<'b>,
286 filter: Option<(impl Into<NodeId>, bool)>,
287 type_tree: &'b dyn TypeTree,
288 direction: BrowseDirection,
289 ) -> impl Iterator<Item = ReferenceRef<'a>> + 'b {
290 self.references
291 .find_references(source_node, filter, type_tree, direction)
292 }
293
294 pub fn find_node_by_browse_name<'a: 'b, 'b>(
297 &'a self,
298 source_node: impl IntoNodeIdRef<'b>,
299 filter: Option<(impl Into<NodeId>, bool)>,
300 type_tree: &'b dyn TypeTree,
301 direction: BrowseDirection,
302 browse_name: impl Into<QualifiedName>,
303 ) -> Option<&'a NodeType> {
304 let name = browse_name.into();
305 for rf in self.find_references(source_node, filter, type_tree, direction) {
306 let node = self.find_node(rf.target_node);
307 if let Some(node) = node {
308 if node.as_node().browse_name() == &name {
309 return Some(node);
310 }
311 }
312 }
313 None
314 }
315
316 pub fn find_node_by_browse_path<'a: 'b, 'b>(
319 &'a self,
320 source_node: impl IntoNodeIdRef<'b>,
321 filter: Option<(impl Into<NodeId>, bool)>,
322 type_tree: &'b dyn TypeTree,
323 direction: BrowseDirection,
324 browse_path: &[QualifiedName],
325 ) -> Option<&'a NodeType> {
326 let mut node = self.find_node(source_node)?;
327 let filter: Option<(NodeId, bool)> = filter.map(|(id, c)| (id.into(), c));
328 for path_elem in browse_path {
329 let mut found = false;
330 for rf in self.find_references(node.node_id(), filter.clone(), type_tree, direction) {
331 let child = self.find_node(rf.target_node);
332 if let Some(child) = child {
333 if child.as_node().browse_name() == path_elem {
334 node = child;
335 found = true;
336 break;
337 }
338 }
339 }
340 if !found {
341 return None;
342 }
343 }
344 Some(node)
345 }
346
347 pub fn namespaces(&self) -> &HashMap<u16, String> {
349 &self.namespaces
350 }
351
352 pub fn find<'b>(&self, node_id: impl IntoNodeIdRef<'b>) -> Option<&NodeType> {
354 self.find_node(node_id)
355 }
356
357 pub fn find_mut<'b>(&mut self, node_id: impl IntoNodeIdRef<'b>) -> Option<&mut NodeType> {
359 self.find_node_mut(node_id)
360 }
361
362 pub fn find_node<'b>(&self, node_id: impl IntoNodeIdRef<'b>) -> Option<&NodeType> {
364 self.node_map.get(&node_id.into_node_id_ref())
365 }
366
367 pub fn find_node_mut<'b>(&mut self, node_id: impl IntoNodeIdRef<'b>) -> Option<&mut NodeType> {
369 self.node_map.get_mut(&node_id.into_node_id_ref())
370 }
371
372 pub fn validate_node_read<'a>(
374 &'a self,
375 context: &RequestContext,
376 node_to_read: &ParsedReadValueId,
377 ) -> Result<&'a NodeType, StatusCode> {
378 let Some(node) = self.find(&node_to_read.node_id) else {
379 debug!(
380 "read_node_value result for read node id {}, attribute {:?} cannot find node",
381 node_to_read.node_id, node_to_read.attribute_id
382 );
383 return Err(StatusCode::BadNodeIdUnknown);
384 };
385
386 validate_node_read(node, context, node_to_read)?;
387
388 Ok(node)
389 }
390
391 pub fn read(
394 &self,
395 context: &RequestContext,
396 node_to_read: &ParsedReadValueId,
397 max_age: f64,
398 timestamps_to_return: TimestampsToReturn,
399 ) -> DataValue {
400 let node = match self.validate_node_read(context, node_to_read) {
401 Ok(n) => n,
402 Err(e) => {
403 return DataValue {
404 status: Some(e),
405 ..Default::default()
406 };
407 }
408 };
409
410 read_node_value(node, context, node_to_read, max_age, timestamps_to_return)
411 }
412
413 pub fn validate_node_write<'a>(
415 &'a mut self,
416 context: &RequestContext,
417 node_to_write: &ParsedWriteValue,
418 type_tree: &dyn TypeTree,
419 ) -> Result<&'a mut NodeType, StatusCode> {
420 let Some(node) = self.find_mut(&node_to_write.node_id) else {
421 debug!(
422 "write_node_value result for read node id {}, attribute {:?} cannot find node",
423 node_to_write.node_id, node_to_write.attribute_id
424 );
425 return Err(StatusCode::BadNodeIdUnknown);
426 };
427
428 validate_node_write(node, context, node_to_write, type_tree)?;
429
430 Ok(node)
431 }
432
433 pub fn delete(&mut self, node_id: &NodeId, delete_target_references: bool) -> Option<NodeType> {
435 let n = self.node_map.remove(node_id);
436 self.references
437 .delete_node_references(node_id, delete_target_references);
438
439 n
440 }
441
442 pub fn add_folder(
444 &mut self,
445 node_id: &NodeId,
446 browse_name: impl Into<QualifiedName>,
447 display_name: impl Into<LocalizedText>,
448 parent_node_id: &NodeId,
449 ) -> bool {
450 self.assert_namespace(node_id);
451 ObjectBuilder::new(node_id, browse_name, display_name)
452 .is_folder()
453 .organized_by(parent_node_id.clone())
454 .insert(self)
455 }
456
457 pub fn add_variables(
459 &mut self,
460 variables: Vec<Variable>,
461 parent_node_id: &NodeId,
462 ) -> Vec<bool> {
463 variables
464 .into_iter()
465 .map(|v| {
466 self.insert(
467 v,
468 Some(&[(
469 parent_node_id,
470 &ReferenceTypeId::Organizes,
471 ReferenceDirection::Inverse,
472 )]),
473 )
474 })
475 .collect()
476 }
477}
478
479impl NodeInsertTarget for AddressSpace {
480 fn insert<'a>(
481 &mut self,
482 node: impl Into<NodeType>,
483 references: Option<&'a [(&'a NodeId, &NodeId, opcua_nodes::ReferenceDirection)]>,
484 ) -> bool {
485 let node_type = node.into();
486 let node_id = node_type.node_id().clone();
487
488 self.assert_namespace(&node_id);
489
490 if self.node_exists(&node_id) {
491 error!("This node {} already exists", node_id);
492 false
493 } else {
494 if let Some(references) = references {
496 self.references.insert(&node_id, references);
497 }
498 self.node_map.insert(node_id, node_type);
499
500 true
501 }
502 }
503}
504
505#[cfg(test)]
506mod tests {
507 use crate::address_space::{
508 CoreNamespace, EventNotifier, MethodBuilder, NodeBase, NodeType, Object, ObjectBuilder,
509 ObjectTypeBuilder, Variable, VariableBuilder,
510 };
511 use opcua_nodes::{DefaultTypeTree, NamespaceMap, TypeTree};
512 use opcua_types::{
513 argument::Argument, Array, BrowseDirection, DataTypeId, LocalizedText, NodeClass, NodeId,
514 NumericRange, ObjectId, ObjectTypeId, QualifiedName, ReferenceTypeId, TimestampsToReturn,
515 UAString, Variant, VariantScalarTypeId,
516 };
517
518 use super::AddressSpace;
519
520 fn make_sample_address_space() -> AddressSpace {
521 let mut address_space = AddressSpace::new();
522 address_space.add_namespace("http://opcfoundation.org/UA/", 0);
523 let mut namespaces = NamespaceMap::default();
524 address_space.import_node_set(&CoreNamespace, &mut namespaces);
525 add_sample_vars_to_address_space(&mut address_space);
526 address_space
527 }
528
529 fn add_sample_vars_to_address_space(address_space: &mut AddressSpace) {
530 address_space.add_namespace("urn:test", 1);
531 let ns = 1;
532
533 let sample_folder_id = NodeId::next_numeric(ns);
535 ObjectBuilder::new(&sample_folder_id, "Sample", "Sample")
536 .organized_by(ObjectId::ObjectsFolder)
537 .insert(address_space);
538
539 let vars = vec![
541 Variable::new(&NodeId::new(ns, "v1"), "v1", "v1", 30i32),
542 Variable::new(&NodeId::new(ns, 300), "v2", "v2", true),
543 Variable::new(&NodeId::new(ns, "v3"), "v3", "v3", "Hello world"),
544 Variable::new(&NodeId::new(ns, "v4"), "v4", "v4", 100.123f64),
545 ];
546 for var in vars {
547 let node_id = var.node_id().clone();
548 address_space.insert::<_, NodeId>(var, None);
549 address_space.insert_reference(
550 &sample_folder_id,
551 &node_id,
552 ReferenceTypeId::HasComponent,
553 );
554 }
555 }
556
557 #[test]
558 fn find_root_folder() {
559 let address_space = make_sample_address_space();
560 let node_type = address_space.find_node(&NodeId::new(0, 84));
561 assert!(node_type.is_some());
562
563 let node = node_type.unwrap().as_node();
564 assert_eq!(node.node_id(), &NodeId::new(0, 84));
565 assert_eq!(node.node_id(), &ObjectId::RootFolder);
566 }
567
568 #[test]
569 fn find_objects_folder() {
570 let address_space = make_sample_address_space();
571 let node_type = address_space.find(ObjectId::ObjectsFolder);
572 assert!(node_type.is_some());
573 }
574
575 #[test]
576 fn find_types_folder() {
577 let address_space = make_sample_address_space();
578 let node_type = address_space.find(ObjectId::TypesFolder);
579 assert!(node_type.is_some());
580 }
581
582 #[test]
583 fn find_views_folder() {
584 let address_space = make_sample_address_space();
585 let node_type = address_space.find(ObjectId::ViewsFolder);
586 assert!(node_type.is_some());
587 }
588
589 #[test]
590 fn find_common_nodes() {
591 let address_space = make_sample_address_space();
592 let nodes: Vec<NodeId> = vec![
593 ObjectId::RootFolder.into(),
594 ObjectId::ObjectsFolder.into(),
595 ObjectId::TypesFolder.into(),
596 ObjectId::ViewsFolder.into(),
597 ObjectId::DataTypesFolder.into(),
598 DataTypeId::BaseDataType.into(),
599 DataTypeId::Boolean.into(),
601 DataTypeId::ByteString.into(),
602 DataTypeId::DataValue.into(),
603 DataTypeId::DateTime.into(),
604 DataTypeId::DiagnosticInfo.into(),
605 DataTypeId::Enumeration.into(),
606 DataTypeId::ExpandedNodeId.into(),
607 DataTypeId::Guid.into(),
608 DataTypeId::LocalizedText.into(),
609 DataTypeId::NodeId.into(),
610 DataTypeId::Number.into(),
611 DataTypeId::QualifiedName.into(),
612 DataTypeId::StatusCode.into(),
613 DataTypeId::String.into(),
614 DataTypeId::Structure.into(),
615 DataTypeId::XmlElement.into(),
616 DataTypeId::Double.into(),
617 DataTypeId::Float.into(),
618 DataTypeId::Integer.into(),
619 DataTypeId::SByte.into(),
620 DataTypeId::Int16.into(),
621 DataTypeId::Int32.into(),
622 DataTypeId::Int64.into(),
623 DataTypeId::Byte.into(),
624 DataTypeId::UInt16.into(),
625 DataTypeId::UInt32.into(),
626 DataTypeId::UInt64.into(),
627 ObjectId::OPCBinarySchema_TypeSystem.into(),
628 ObjectTypeId::DataTypeSystemType.into(),
629 ObjectId::ReferenceTypesFolder.into(),
631 ReferenceTypeId::References.into(),
632 ReferenceTypeId::HierarchicalReferences.into(),
633 ReferenceTypeId::HasChild.into(),
634 ReferenceTypeId::HasSubtype.into(),
635 ReferenceTypeId::Organizes.into(),
636 ReferenceTypeId::NonHierarchicalReferences.into(),
637 ReferenceTypeId::HasTypeDefinition.into(),
638 ];
639 for n in nodes {
640 assert!(address_space.find_node(&n).is_some());
641 }
642 }
643
644 #[test]
645 fn object_attributes() {
646 let on = NodeId::new(1, "o1");
647 let o = Object::new(&on, "Browse01", "Display01", EventNotifier::empty());
648 assert_eq!(o.node_class(), NodeClass::Object);
649 assert_eq!(o.node_id(), &on);
650 assert_eq!(o.browse_name(), &QualifiedName::new(0, "Browse01"));
651 assert_eq!(o.display_name(), &"Display01".into());
652 }
653
654 #[test]
655 fn find_node_by_id() {
656 let address_space = make_sample_address_space();
657 let ns = 1;
658
659 assert!(!address_space.node_exists(&NodeId::null()));
660 assert!(!address_space.node_exists(&NodeId::new(11, "v3")));
661
662 assert!(address_space.node_exists(&NodeId::new(ns, "v1")));
663 assert!(address_space.node_exists(&NodeId::new(ns, 300)));
664 assert!(address_space.node_exists(&NodeId::new(ns, "v3")));
665 }
666
667 #[test]
668 fn find_references() {
669 let address_space = make_sample_address_space();
670
671 let references: Vec<_> = address_space
672 .find_references(
673 &NodeId::root_folder_id(),
674 Some((ReferenceTypeId::Organizes, false)),
675 &DefaultTypeTree::new(),
676 BrowseDirection::Forward,
677 )
678 .collect();
679 assert_eq!(references.len(), 3);
680
681 let references: Vec<_> = address_space
682 .find_references(
683 &NodeId::root_folder_id(),
684 None::<(NodeId, bool)>,
685 &DefaultTypeTree::new(),
686 BrowseDirection::Forward,
687 )
688 .collect();
689 assert_eq!(references.len(), 4);
690
691 let references: Vec<_> = address_space
692 .find_references(
693 &NodeId::objects_folder_id(),
694 Some((ReferenceTypeId::Organizes, false)),
695 &DefaultTypeTree::new(),
696 BrowseDirection::Forward,
697 )
698 .collect();
699 assert_eq!(references.len(), 4);
700
701 let r1 = &references[0];
702 assert_eq!(r1.reference_type, &ReferenceTypeId::Organizes);
703 let child_node_id = r1.target_node.clone();
704
705 let child = address_space.find_node(&child_node_id);
706 assert!(child.is_some());
707 }
708
709 #[test]
710 fn find_inverse_references() {
711 let address_space = make_sample_address_space();
712
713 let references: Vec<_> = address_space
715 .find_references(
716 &NodeId::root_folder_id(),
717 Some((ReferenceTypeId::Organizes, false)),
718 &DefaultTypeTree::new(),
719 BrowseDirection::Inverse,
720 )
721 .collect();
722 assert!(references.is_empty());
723
724 let references: Vec<_> = address_space
725 .find_references(
726 &NodeId::objects_folder_id(),
727 Some((ReferenceTypeId::Organizes, false)),
728 &DefaultTypeTree::new(),
729 BrowseDirection::Inverse,
730 )
731 .collect();
732 assert_eq!(references.len(), 1);
733 }
734
735 #[test]
736 fn find_reference_subtypes() {
737 let address_space = make_sample_address_space();
738 let mut type_tree = DefaultTypeTree::new();
739 address_space.load_into_type_tree(&mut type_tree);
740
741 let reference_types = [
742 (
743 ReferenceTypeId::References,
744 ReferenceTypeId::HierarchicalReferences,
745 ),
746 (ReferenceTypeId::References, ReferenceTypeId::HasChild),
747 (ReferenceTypeId::References, ReferenceTypeId::HasSubtype),
748 (ReferenceTypeId::References, ReferenceTypeId::Organizes),
749 (ReferenceTypeId::References, ReferenceTypeId::Aggregates),
750 (ReferenceTypeId::References, ReferenceTypeId::HasProperty),
751 (ReferenceTypeId::References, ReferenceTypeId::HasComponent),
752 (
753 ReferenceTypeId::References,
754 ReferenceTypeId::HasOrderedComponent,
755 ),
756 (ReferenceTypeId::References, ReferenceTypeId::HasEventSource),
757 (ReferenceTypeId::References, ReferenceTypeId::HasNotifier),
758 (ReferenceTypeId::References, ReferenceTypeId::GeneratesEvent),
759 (
760 ReferenceTypeId::References,
761 ReferenceTypeId::AlwaysGeneratesEvent,
762 ),
763 (ReferenceTypeId::References, ReferenceTypeId::HasEncoding),
764 (
765 ReferenceTypeId::References,
766 ReferenceTypeId::HasModellingRule,
767 ),
768 (ReferenceTypeId::References, ReferenceTypeId::HasDescription),
769 (
770 ReferenceTypeId::References,
771 ReferenceTypeId::HasTypeDefinition,
772 ),
773 (
774 ReferenceTypeId::HierarchicalReferences,
775 ReferenceTypeId::HasChild,
776 ),
777 (
778 ReferenceTypeId::HierarchicalReferences,
779 ReferenceTypeId::HasSubtype,
780 ),
781 (
782 ReferenceTypeId::HierarchicalReferences,
783 ReferenceTypeId::Organizes,
784 ),
785 (
786 ReferenceTypeId::HierarchicalReferences,
787 ReferenceTypeId::Aggregates,
788 ),
789 (
790 ReferenceTypeId::HierarchicalReferences,
791 ReferenceTypeId::HasProperty,
792 ),
793 (
794 ReferenceTypeId::HierarchicalReferences,
795 ReferenceTypeId::HasComponent,
796 ),
797 (
798 ReferenceTypeId::HierarchicalReferences,
799 ReferenceTypeId::HasOrderedComponent,
800 ),
801 (
802 ReferenceTypeId::HierarchicalReferences,
803 ReferenceTypeId::HasEventSource,
804 ),
805 (
806 ReferenceTypeId::HierarchicalReferences,
807 ReferenceTypeId::HasNotifier,
808 ),
809 (ReferenceTypeId::HasChild, ReferenceTypeId::Aggregates),
810 (ReferenceTypeId::HasChild, ReferenceTypeId::HasComponent),
811 (
812 ReferenceTypeId::HasChild,
813 ReferenceTypeId::HasHistoricalConfiguration,
814 ),
815 (ReferenceTypeId::HasChild, ReferenceTypeId::HasProperty),
816 (
817 ReferenceTypeId::HasChild,
818 ReferenceTypeId::HasOrderedComponent,
819 ),
820 (ReferenceTypeId::HasChild, ReferenceTypeId::HasSubtype),
821 (ReferenceTypeId::Aggregates, ReferenceTypeId::HasComponent),
822 (
823 ReferenceTypeId::Aggregates,
824 ReferenceTypeId::HasHistoricalConfiguration,
825 ),
826 (ReferenceTypeId::Aggregates, ReferenceTypeId::HasProperty),
827 (
828 ReferenceTypeId::Aggregates,
829 ReferenceTypeId::HasOrderedComponent,
830 ),
831 (
832 ReferenceTypeId::HasComponent,
833 ReferenceTypeId::HasOrderedComponent,
834 ),
835 (
836 ReferenceTypeId::HasEventSource,
837 ReferenceTypeId::HasNotifier,
838 ),
839 (
840 ReferenceTypeId::HierarchicalReferences,
841 ReferenceTypeId::HasNotifier,
842 ),
843 (
844 ReferenceTypeId::References,
845 ReferenceTypeId::NonHierarchicalReferences,
846 ),
847 (
848 ReferenceTypeId::NonHierarchicalReferences,
849 ReferenceTypeId::GeneratesEvent,
850 ),
851 (
852 ReferenceTypeId::NonHierarchicalReferences,
853 ReferenceTypeId::AlwaysGeneratesEvent,
854 ),
855 (
856 ReferenceTypeId::NonHierarchicalReferences,
857 ReferenceTypeId::HasEncoding,
858 ),
859 (
860 ReferenceTypeId::NonHierarchicalReferences,
861 ReferenceTypeId::HasModellingRule,
862 ),
863 (
864 ReferenceTypeId::NonHierarchicalReferences,
865 ReferenceTypeId::HasDescription,
866 ),
867 (
868 ReferenceTypeId::NonHierarchicalReferences,
869 ReferenceTypeId::HasTypeDefinition,
870 ),
871 (
872 ReferenceTypeId::GeneratesEvent,
873 ReferenceTypeId::AlwaysGeneratesEvent,
874 ),
875 ];
876
877 reference_types.iter().for_each(|r| {
880 let r1 = r.0.into();
881 let r2 = r.1.into();
882 assert!(type_tree.is_subtype_of(&r2, &r1));
883 });
884 }
885
886 #[test]
889 fn array_as_variable() {
890 let values = (0..100).map(Variant::Int32).collect::<Vec<Variant>>();
892
893 let node_id = NodeId::new(2, 1);
895 let v = Variable::new(&node_id, "x", "x", (VariantScalarTypeId::Int32, values));
896
897 let value_rank = v.value_rank();
898 assert_eq!(value_rank, 1);
899 let array_dimensions = v.array_dimensions().unwrap();
900 assert_eq!(array_dimensions, vec![100u32]);
901 }
902
903 #[test]
906 fn multi_dimension_array_as_variable() {
907 let values = (0..100).map(Variant::Int32).collect::<Vec<Variant>>();
910 let mda = Array::new_multi(VariantScalarTypeId::Int32, values, vec![10u32, 10u32]).unwrap();
911 assert!(mda.is_valid());
912
913 let node_id = NodeId::new(2, 1);
915 let v = Variable::new(&node_id, "x", "x", mda);
916
917 let value_rank = v.value_rank();
918 assert_eq!(value_rank, 2);
919 let array_dimensions = v.array_dimensions().unwrap();
920 assert_eq!(array_dimensions, vec![10u32, 10u32]);
921 }
922
923 #[test]
924 fn browse_nodes() {
925 let address_space = make_sample_address_space();
926
927 let object_id = ObjectId::RootFolder.into();
929 let result = address_space.find_node_by_browse_path(
930 &object_id,
931 None::<(NodeId, bool)>,
932 &DefaultTypeTree::new(),
933 BrowseDirection::Forward,
934 &["Objects".into(), "Sample".into(), "v1".into()],
935 );
936 let node = result.unwrap();
937 assert_eq!(node.as_node().browse_name(), &QualifiedName::from("v1"));
938
939 let result = address_space.find_node_by_browse_path(
941 &object_id,
942 None::<(NodeId, bool)>,
943 &DefaultTypeTree::new(),
944 BrowseDirection::Forward,
945 &["Objects".into(), "Sample".into(), "vxxx".into()],
946 );
947 assert!(result.is_none());
948 }
949
950 #[test]
951 fn object_builder() {
952 let mut address_space = make_sample_address_space();
953
954 let node_type_id = NodeId::new(1, "HelloType");
955 let _ot = ObjectTypeBuilder::new(&node_type_id, "HelloType", "HelloType")
956 .subtype_of(ObjectTypeId::BaseObjectType)
957 .insert(&mut address_space);
958
959 let node_id = NodeId::new(1, "Hello");
960 let _o = ObjectBuilder::new(&node_id, "Foo", "Foo")
961 .event_notifier(EventNotifier::SUBSCRIBE_TO_EVENTS)
962 .organized_by(ObjectId::ObjectsFolder)
963 .has_type_definition(node_type_id.clone())
964 .insert(&mut address_space);
965
966 let _o = match address_space.find_node(&node_id).unwrap() {
968 NodeType::Object(o) => o,
969 _ => panic!(),
970 };
971
972 assert!(address_space.has_reference(
974 &ObjectId::ObjectsFolder.into(),
975 &node_id,
976 ReferenceTypeId::Organizes
977 ));
978 assert!(address_space.has_reference(
979 &node_id,
980 &node_type_id,
981 ReferenceTypeId::HasTypeDefinition
982 ));
983 }
984
985 #[test]
986 fn object_type_builder() {
987 let mut address_space = make_sample_address_space();
988
989 let node_type_id = NodeId::new(1, "HelloType");
990 let _ot = ObjectTypeBuilder::new(&node_type_id, "HelloType", "HelloType")
991 .subtype_of(ObjectTypeId::BaseObjectType)
992 .insert(&mut address_space);
993
994 let _ot = match address_space.find_node(&node_type_id).unwrap() {
995 NodeType::ObjectType(ot) => ot,
996 _ => panic!(),
997 };
998
999 assert!(address_space.has_reference(
1000 &ObjectTypeId::BaseObjectType.into(),
1001 &node_type_id,
1002 ReferenceTypeId::HasSubtype
1003 ));
1004 }
1005
1006 #[test]
1007 fn variable_builder() {
1008 let result = std::panic::catch_unwind(|| {
1009 let _v = VariableBuilder::new(&NodeId::null(), "", "").build();
1011 });
1012 assert!(result.is_err());
1013
1014 let _v = VariableBuilder::new(&NodeId::new(1, 1), "", "")
1016 .data_type(DataTypeId::Boolean)
1017 .build();
1018
1019 let v = VariableBuilder::new(&NodeId::new(1, "Hello"), "BrowseName", "DisplayName")
1021 .description("Desc")
1022 .data_type(DataTypeId::UInt32)
1023 .value_rank(10)
1024 .array_dimensions(&[1, 2, 3])
1025 .historizing(true)
1026 .value(Variant::from(999))
1027 .minimum_sampling_interval(123.0)
1028 .build();
1029
1030 assert_eq!(v.node_id(), &NodeId::new(1, "Hello"));
1031 assert_eq!(v.browse_name(), &QualifiedName::new(0, "BrowseName"));
1032 assert_eq!(v.display_name(), &"DisplayName".into());
1033 assert_eq!(v.data_type(), DataTypeId::UInt32);
1034 assert_eq!(v.description().unwrap(), &"Desc".into());
1035 assert_eq!(v.value_rank(), 10);
1036 assert_eq!(v.array_dimensions().unwrap(), vec![1, 2, 3]);
1037 assert!(v.historizing());
1038 assert_eq!(
1039 v.value(
1040 TimestampsToReturn::Neither,
1041 &NumericRange::None,
1042 &opcua_types::DataEncoding::Binary,
1043 0.0
1044 )
1045 .value
1046 .unwrap(),
1047 Variant::from(999)
1048 );
1049 assert_eq!(v.minimum_sampling_interval().unwrap(), 123.0);
1050
1051 let mut address_space = make_sample_address_space();
1054 let node_id = NodeId::new(1, "Hello");
1055 let _v = VariableBuilder::new(&node_id, "BrowseName", "DisplayName")
1056 .description("Desc")
1057 .value_rank(10)
1058 .data_type(DataTypeId::UInt32)
1059 .array_dimensions(&[1, 2, 3])
1060 .historizing(true)
1061 .value(Variant::from(999))
1062 .minimum_sampling_interval(123.0)
1063 .organized_by(ObjectId::ObjectsFolder)
1064 .insert(&mut address_space);
1065
1066 assert!(address_space.find_node(&node_id).is_some());
1068 assert!(address_space.has_reference(
1070 &ObjectId::ObjectsFolder.into(),
1071 &node_id,
1072 ReferenceTypeId::Organizes
1073 ));
1074 }
1075
1076 #[test]
1077 fn method_builder() {
1078 let mut address_space = make_sample_address_space();
1079
1080 address_space.add_namespace("urn:test", 1);
1081 let ns = 1;
1082
1083 let object_id: NodeId = ObjectId::ObjectsFolder.into();
1084
1085 let fn_node_id = NodeId::new(ns, "HelloWorld");
1086 let out_args = NodeId::new(ns, "HelloWorldOut");
1087
1088 let inserted = MethodBuilder::new(&fn_node_id, "HelloWorld", "HelloWorld")
1089 .component_of(object_id.clone())
1090 .output_args(
1091 &mut address_space,
1092 &out_args,
1093 &[("Result", DataTypeId::String).into()],
1094 )
1095 .insert(&mut address_space);
1096 assert!(inserted);
1097
1098 assert!(matches!(
1099 address_space.find_node(&fn_node_id),
1100 Some(NodeType::Method(_))
1101 ));
1102
1103 let refs: Vec<_> = address_space
1104 .find_references(
1105 &fn_node_id,
1106 Some((ReferenceTypeId::HasProperty, false)),
1107 &DefaultTypeTree::new(),
1108 BrowseDirection::Forward,
1109 )
1110 .collect();
1111 assert_eq!(refs.len(), 1);
1112
1113 let child = address_space
1114 .find_node(refs.first().unwrap().target_node)
1115 .unwrap();
1116 if let NodeType::Variable(v) = child {
1117 assert_eq!(v.data_type(), DataTypeId::Argument);
1120 assert_eq!(v.display_name(), &LocalizedText::from("OutputArguments"));
1121 let v = v
1122 .value(
1123 TimestampsToReturn::Neither,
1124 &NumericRange::None,
1125 &opcua_types::DataEncoding::Binary,
1126 0.0,
1127 )
1128 .value
1129 .unwrap();
1130 if let Variant::Array(array) = v {
1131 let v = array.values;
1132 assert_eq!(v.len(), 1);
1133 let v = v.first().unwrap().clone();
1134 if let Variant::ExtensionObject(v) = v {
1135 let argument = v.inner_as::<Argument>().unwrap();
1137 assert_eq!(argument.name, UAString::from("Result"));
1138 assert_eq!(argument.data_type, DataTypeId::String);
1139 assert_eq!(argument.value_rank, -1);
1140 assert_eq!(argument.array_dimensions, None);
1141 assert_eq!(argument.description, LocalizedText::null());
1142 } else {
1143 panic!("Variant was expected to be extension object, was {v:?}");
1144 }
1145 } else {
1146 panic!("Variant was expected to be array, was {v:?}");
1147 }
1148 } else {
1149 panic!();
1150 }
1151 }
1152
1153 #[test]
1154 fn simple_delete_node() {
1155 let mut address_space = make_sample_address_space();
1161
1162 let root_node = NodeId::root_folder_id();
1164
1165 let node = Object::new(&root_node, "Root", "", EventNotifier::empty());
1166 let _ = address_space.insert::<Object, ReferenceTypeId>(node, None);
1167
1168 let node_id = NodeId::new(1, "Hello");
1169 let _o = ObjectBuilder::new(&node_id, "Foo", "Foo")
1170 .organized_by(root_node.clone())
1171 .insert(&mut address_space);
1172
1173 assert!(address_space.find_node(&node_id).is_some());
1175 assert!(address_space.has_reference(&root_node, &node_id, ReferenceTypeId::Organizes));
1176
1177 address_space.delete(&node_id, true);
1179 assert!(address_space.find_node(&node_id).is_none());
1181 assert!(address_space.find_node(&root_node).is_some());
1182 assert!(!address_space.has_reference(&root_node, &node_id, ReferenceTypeId::Organizes));
1183 }
1184
1185 #[test]
1186 fn delete_node() {
1187 (0..2).for_each(|i| {
1189 let mut address_space = make_sample_address_space();
1190
1191 let node_type_id = NodeId::new(1, "HelloType");
1192 let _ot = ObjectTypeBuilder::new(&node_type_id, "HelloType", "HelloType")
1193 .subtype_of(ObjectTypeId::BaseObjectType)
1194 .insert(&mut address_space);
1195
1196 let node_id = NodeId::new(1, "Hello");
1197 let _o = ObjectBuilder::new(&node_id, "Foo", "Foo")
1198 .event_notifier(EventNotifier::SUBSCRIBE_TO_EVENTS)
1199 .organized_by(ObjectId::ObjectsFolder)
1200 .has_type_definition(node_type_id.clone())
1201 .insert(&mut address_space);
1202
1203 assert!(address_space.find_node(&node_id).is_some());
1205 assert!(address_space.has_reference(
1206 &ObjectId::ObjectsFolder.into(),
1207 &node_id,
1208 ReferenceTypeId::Organizes
1209 ));
1210 assert!(!address_space.has_reference(
1211 &node_id,
1212 &ObjectId::ObjectsFolder.into(),
1213 ReferenceTypeId::Organizes
1214 ));
1215 assert!(address_space.has_reference(
1216 &node_id,
1217 &node_type_id,
1218 ReferenceTypeId::HasTypeDefinition
1219 ));
1220
1221 let delete_target_references = i == 1;
1223 address_space.delete(&node_id, delete_target_references);
1224 if !delete_target_references {
1225 assert!(address_space.find_node(&node_id).is_none());
1227 assert!(address_space.has_reference(
1228 &ObjectId::ObjectsFolder.into(),
1229 &node_id,
1230 ReferenceTypeId::Organizes
1231 ));
1232 assert!(!address_space.has_reference(
1233 &node_id,
1234 &node_type_id,
1235 ReferenceTypeId::HasTypeDefinition
1236 ));
1237 } else {
1238 assert!(address_space.find_node(&node_id).is_none());
1240 assert!(!address_space.has_reference(
1241 &ObjectId::ObjectsFolder.into(),
1242 &node_id,
1243 ReferenceTypeId::Organizes
1244 ));
1245 assert!(!address_space.has_reference(
1246 &node_id,
1247 &node_type_id,
1248 ReferenceTypeId::HasTypeDefinition
1249 ));
1250 }
1251 });
1252 }
1253}