opcua_xml/schema/
ua_node_set.rs

1//! Definition of types representing OPC UA NodeSet2 files.
2
3use chrono::{DateTime, Utc};
4use roxmltree::{Document, Node};
5
6use crate::{
7    ext::{
8        children_with_name, first_child_with_name_opt, value_from_attr, value_from_attr_opt,
9        value_from_contents, NodeExt,
10    },
11    FromValue, XmlError, XmlLoad,
12};
13
14use super::opc_ua_types::Variant;
15
16#[derive(Debug)]
17/// Struct representing a NodeSet2.xml file.
18///
19/// NodeSet files are used as a portable format for OPC-UA node hierarchies.
20pub struct NodeSet2 {
21    /// Full node set.
22    pub node_set: Option<UANodeSet>,
23    /// Partial node set diff.
24    pub node_set_changes: Option<UANodeSetChanges>,
25    /// Status of node set changes.
26    pub node_set_changes_status: Option<UANodeSetChangesStatus>,
27}
28
29/// Load a NodeSet2 file from an XML file. `document` is the content of a NodeSet2.xml file.
30pub fn load_nodeset2_file(document: &str) -> Result<NodeSet2, XmlError> {
31    let document = Document::parse(document).map_err(|e| XmlError {
32        span: 0..1,
33        error: crate::error::XmlErrorInner::Xml(e),
34    })?;
35    let root = document.root();
36    Ok(NodeSet2 {
37        node_set: first_child_with_name_opt(&root, "UANodeSet")?,
38        node_set_changes: first_child_with_name_opt(&root, "UANodeSetChanges")?,
39        node_set_changes_status: first_child_with_name_opt(&root, "UANodeSetChangesStatus")?,
40    })
41}
42
43#[derive(Debug)]
44/// A NodeSet2 node.
45pub enum UANode {
46    /// Object
47    Object(UAObject),
48    /// Variable, can have value.
49    Variable(UAVariable),
50    /// Method.
51    Method(UAMethod),
52    /// View
53    View(UAView),
54    /// Object type.
55    ObjectType(UAObjectType),
56    /// Variable type, can have value.
57    VariableType(UAVariableType),
58    /// Data type
59    DataType(UADataType),
60    /// Reference type.
61    ReferenceType(UAReferenceType),
62}
63
64impl UANode {
65    pub(crate) fn from_node(node: &Node<'_, '_>) -> Result<Option<Self>, XmlError> {
66        Ok(Some(match node.tag_name().name() {
67            "UAObject" => Self::Object(XmlLoad::load(node)?),
68            "UAVariable" => Self::Variable(XmlLoad::load(node)?),
69            "UAMethod" => Self::Method(XmlLoad::load(node)?),
70            "UAView" => Self::View(XmlLoad::load(node)?),
71            "UAObjectType" => Self::ObjectType(XmlLoad::load(node)?),
72            "UAVariableType" => Self::VariableType(XmlLoad::load(node)?),
73            "UADataType" => Self::DataType(XmlLoad::load(node)?),
74            "UAReferenceType" => Self::ReferenceType(XmlLoad::load(node)?),
75            _ => return Ok(None),
76        }))
77    }
78
79    /// Get the base node, independent of node class.
80    pub fn base(&self) -> &UANodeBase {
81        match self {
82            UANode::Object(n) => &n.base.base,
83            UANode::Variable(n) => &n.base.base,
84            UANode::Method(n) => &n.base.base,
85            UANode::View(n) => &n.base.base,
86            UANode::ObjectType(n) => &n.base.base,
87            UANode::VariableType(n) => &n.base.base,
88            UANode::DataType(n) => &n.base.base,
89            UANode::ReferenceType(n) => &n.base.base,
90        }
91    }
92}
93
94#[derive(Debug, Default)]
95/// A full OPC-UA node set.
96pub struct UANodeSet {
97    /// List of namespace URIs covered by this node set.
98    pub namespace_uris: Option<UriTable>,
99    /// List of server URIs used in this node set.
100    pub server_uris: Option<UriTable>,
101    /// List of referenced models.
102    pub models: Option<ModelTable>,
103    /// List of aliases available in this node set.
104    pub aliases: Option<AliasTable>,
105    /// The full list of nodes.
106    pub nodes: Vec<UANode>,
107    /// Last modified time.
108    pub last_modified: Option<DateTime<Utc>>,
109}
110
111impl<'input> XmlLoad<'input> for UANodeSet {
112    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
113        // Special case this one, we really want to avoid seeking through the entire
114        // document for the optional elements. It's fine elsewhere since most nodes
115        // have few children.
116        let mut namespace_uris = None;
117        let mut server_uris = None;
118        let mut models = None;
119        let mut aliases = None;
120        let mut nodes = Vec::new();
121        for child in node.children() {
122            match child.tag_name().name() {
123                "NamespaceUris" => namespace_uris = Some(XmlLoad::load(&child)?),
124                "ServerUris" => server_uris = Some(XmlLoad::load(&child)?),
125                "Models" => models = Some(XmlLoad::load(&child)?),
126                "Aliases" => aliases = Some(XmlLoad::load(&child)?),
127                _ => {
128                    if let Some(node) = UANode::from_node(&child)? {
129                        nodes.push(node);
130                    }
131                }
132            }
133        }
134
135        Ok(Self {
136            namespace_uris,
137            server_uris,
138            models,
139            aliases,
140            nodes,
141            last_modified: value_from_attr_opt(node, "LastModified")?,
142        })
143    }
144}
145
146#[derive(Debug)]
147/// Differential update of a node set.
148pub struct UANodeSetChanges {
149    /// List of namespace URIs in this node set.
150    pub namespace_uris: Option<UriTable>,
151    /// List of server URIs used in this node set.
152    pub server_uris: Option<UriTable>,
153    /// List of aliases available in this node set.
154    pub aliases: Option<AliasTable>,
155    /// New nodes.
156    pub nodes_to_add: Option<NodesToAdd>,
157    /// New references.
158    pub references_to_add: Option<ReferencesToChange>,
159    /// Nodes that should be deleted.
160    pub nodes_to_delete: Option<NodesToDelete>,
161    /// References that should be deleted.
162    pub references_to_delete: Option<ReferencesToChange>,
163    /// Last modified time.
164    pub last_modified: Option<DateTime<Utc>>,
165    /// Change transaction ID. Used to identify this change.
166    pub transaction_id: String,
167    /// If `true`, applications loading this should either accept all nodes in the change set,
168    /// or fail completely, applying no changes at all.
169    pub accept_all_or_nothing: bool,
170}
171
172impl<'input> XmlLoad<'input> for UANodeSetChanges {
173    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
174        Ok(Self {
175            namespace_uris: first_child_with_name_opt(node, "NamespaceUris")?,
176            server_uris: first_child_with_name_opt(node, "ServerUris")?,
177            aliases: first_child_with_name_opt(node, "Aliases")?,
178            nodes_to_add: first_child_with_name_opt(node, "NodesToAdd")?,
179            references_to_add: first_child_with_name_opt(node, "ReferencesToAdd")?,
180            nodes_to_delete: first_child_with_name_opt(node, "NodesToDelete")?,
181            references_to_delete: first_child_with_name_opt(node, "ReferencesToDelete")?,
182            last_modified: value_from_attr_opt(node, "LastModified")?,
183            transaction_id: value_from_attr(node, "TransactionId")?,
184            accept_all_or_nothing: value_from_attr_opt(node, "AcceptAllOrNothing")?
185                .unwrap_or(false),
186        })
187    }
188}
189
190#[derive(Debug)]
191/// Status of a node set change.
192pub struct UANodeSetChangesStatus {
193    /// Status of nodes being added.
194    pub nodes_to_add: Option<NodeSetStatusList>,
195    /// Status of references being added.
196    pub references_to_add: Option<NodeSetStatusList>,
197    /// Status of nodes being deleted.
198    pub nodes_to_delete: Option<NodeSetStatusList>,
199    /// Status of references being deleted.
200    pub references_to_delete: Option<NodeSetStatusList>,
201    /// Last modified time.
202    pub last_modified: Option<DateTime<Utc>>,
203    /// Change transaction ID. Used to identify this change.
204    pub transaction_id: String,
205}
206
207impl<'input> XmlLoad<'input> for UANodeSetChangesStatus {
208    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
209        Ok(Self {
210            nodes_to_add: first_child_with_name_opt(node, "NodesToAdd")?,
211            references_to_add: first_child_with_name_opt(node, "ReferencesToAdd")?,
212            nodes_to_delete: first_child_with_name_opt(node, "NodesToDelete")?,
213            references_to_delete: first_child_with_name_opt(node, "ReferencesToDelete")?,
214            last_modified: value_from_attr_opt(node, "LastModified")?,
215            transaction_id: value_from_attr(node, "TransactionId")?,
216        })
217    }
218}
219
220#[derive(Debug)]
221/// List of nodes to add.
222pub struct NodesToAdd {
223    /// Nodes to add.
224    pub nodes: Vec<UANode>,
225}
226
227impl<'input> XmlLoad<'input> for NodesToAdd {
228    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
229        Ok(Self {
230            nodes: node
231                .children()
232                .filter_map(|n| UANode::from_node(&n).transpose())
233                .collect::<Result<Vec<_>, _>>()?,
234        })
235    }
236}
237
238#[derive(Debug)]
239/// Node that should be deleted.
240pub struct NodeToDelete {
241    /// Node ID of node being deleted.
242    pub node_id: NodeId,
243    /// Whether to delete references _to_ this node. References _from_ this node
244    /// should always be deleted.
245    pub delete_reverse_references: bool,
246}
247
248impl<'input> XmlLoad<'input> for NodeToDelete {
249    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
250        Ok(Self {
251            node_id: NodeId::load(node)?,
252            delete_reverse_references: value_from_attr_opt(node, "DeleteReverseReferences")?
253                .unwrap_or(true),
254        })
255    }
256}
257
258#[derive(Debug)]
259/// List of nodes to delete.
260pub struct NodesToDelete {
261    /// Nodes to delete.
262    pub nodes: Vec<NodeToDelete>,
263}
264
265impl<'input> XmlLoad<'input> for NodesToDelete {
266    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
267        Ok(Self {
268            nodes: children_with_name(node, "Node")?,
269        })
270    }
271}
272
273#[derive(Debug)]
274/// Reference being created or deleted.
275pub struct ReferenceChange {
276    /// Target node ID.
277    pub node_id: NodeId,
278    /// Source node ID.
279    pub source: NodeId,
280    /// Reference type ID.
281    pub reference_type: NodeId,
282    /// Whether this is a forward or inverse reference.
283    pub is_forward: bool,
284}
285
286impl<'input> XmlLoad<'input> for ReferenceChange {
287    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
288        Ok(Self {
289            node_id: NodeId::load(node)?,
290            source: value_from_attr(node, "Source")?,
291            reference_type: value_from_attr(node, "ReferenceType")?,
292            is_forward: value_from_attr_opt(node, "IsForward")?.unwrap_or(true),
293        })
294    }
295}
296
297#[derive(Debug)]
298/// List of references to add or remove.
299pub struct ReferencesToChange {
300    /// References to change.
301    pub references: Vec<ReferenceChange>,
302}
303
304impl<'input> XmlLoad<'input> for ReferencesToChange {
305    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
306        Ok(Self {
307            references: children_with_name(node, "Reference")?,
308        })
309    }
310}
311
312#[derive(Debug)]
313/// Status of a node set change element.
314pub struct NodeSetStatus {
315    /// Status symbol.
316    pub status: String,
317    /// Status code.
318    pub code: u64,
319}
320
321impl<'input> XmlLoad<'input> for NodeSetStatus {
322    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
323        Ok(Self {
324            status: String::load(node)?,
325            code: value_from_attr_opt(node, "Code")?.unwrap_or(0),
326        })
327    }
328}
329
330#[derive(Debug)]
331/// List of statuses for a node set change.
332pub struct NodeSetStatusList {
333    /// Node set statuses.
334    pub statuses: Vec<NodeSetStatus>,
335}
336
337impl<'input> XmlLoad<'input> for NodeSetStatusList {
338    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
339        Ok(Self {
340            statuses: children_with_name(node, "Status")?,
341        })
342    }
343}
344
345#[derive(Debug)]
346/// List of URIs.
347pub struct UriTable {
348    /// URIs.
349    pub uris: Vec<String>,
350}
351
352impl<'input> XmlLoad<'input> for UriTable {
353    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
354        Ok(Self {
355            uris: node
356                .with_name("Uri")
357                .map(|v| v.try_contents().map(|v| v.to_owned()))
358                .collect::<Result<Vec<_>, _>>()?,
359        })
360    }
361}
362
363macro_rules! value_wrapper {
364    ($key:ident, $doc:expr, $ty:ident) => {
365        #[derive(Debug, Default, Clone)]
366        #[doc = $doc]
367        pub struct $key(pub $ty);
368
369        impl FromValue for $key {
370            fn from_value(node: &Node<'_, '_>, attr: &str, v: &str) -> Result<Self, XmlError> {
371                Ok(Self($ty::from_value(node, attr, v)?))
372            }
373        }
374    };
375}
376
377#[derive(Debug, Clone)]
378/// Description of a model contained in a nodeset file.
379pub struct ModelTableEntry {
380    /// Role permissions that apply to this entry.
381    pub role_permissions: Option<ListOfRolePermissions>,
382    /// List of required models.
383    pub required_model: Vec<ModelTableEntry>,
384    /// Model URI.
385    pub model_uri: String,
386    /// Model version.
387    pub version: Option<String>,
388    /// Model publication date.
389    pub publication_date: Option<DateTime<Utc>>,
390    /// Default access restrictions for this model.
391    pub access_restrictions: AccessRestriction,
392}
393
394impl<'input> XmlLoad<'input> for ModelTableEntry {
395    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
396        Ok(Self {
397            role_permissions: first_child_with_name_opt(node, "RolePermissions")?,
398            required_model: children_with_name(node, "RequiredModel")?,
399            model_uri: node.try_attribute("ModelUri")?.to_owned(),
400            version: node.attribute("Version").map(|v| v.to_owned()),
401            publication_date: value_from_attr_opt(node, "PublicationDate")?,
402            access_restrictions: value_from_attr_opt(node, "AccessRestrictions")?
403                .unwrap_or(AccessRestriction(0)),
404        })
405    }
406}
407
408#[derive(Debug, Clone)]
409/// Table containing models defined in a nodeset file.
410pub struct ModelTable {
411    /// List of models.
412    pub models: Vec<ModelTableEntry>,
413}
414
415impl<'input> XmlLoad<'input> for ModelTable {
416    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
417        Ok(Self {
418            models: children_with_name(node, "Model")?,
419        })
420    }
421}
422
423value_wrapper!(NodeId, "An OPC-UA node ID or alias", String);
424value_wrapper!(
425    QualifiedName,
426    "An OPC-UA QualifiedName on the form Name:Index",
427    String
428);
429value_wrapper!(Locale, "A text locale", String);
430value_wrapper!(WriteMask, "A node write mask", u32);
431value_wrapper!(EventNotifier, "Node event notifier", u8);
432value_wrapper!(ValueRank, "Variable value rank", i32);
433value_wrapper!(AccessRestriction, "Access restriction flags", u8);
434value_wrapper!(
435    ArrayDimensions,
436    "Array dimensions as a comma separated list of lengths",
437    String
438);
439value_wrapper!(
440    Duration,
441    "Duration as a floating point number of seconds",
442    f64
443);
444value_wrapper!(AccessLevel, "Access level flags", u8);
445
446impl FromValue for chrono::DateTime<Utc> {
447    fn from_value(node: &Node<'_, '_>, attr: &str, v: &str) -> Result<Self, XmlError> {
448        let v = chrono::DateTime::parse_from_rfc3339(v)
449            .map_err(|e| XmlError::parse_date_time(node, attr, e))?;
450        Ok(v.with_timezone(&Utc))
451    }
452}
453
454#[derive(Debug)]
455/// Entry in the alias table.
456pub struct NodeIdAlias {
457    /// Node ID.
458    pub id: NodeId,
459    /// Alias name.
460    pub alias: String,
461}
462
463impl<'input> XmlLoad<'input> for NodeIdAlias {
464    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
465        Ok(Self {
466            id: NodeId::load(node)?,
467            alias: node.try_attribute("Alias")?.to_owned(),
468        })
469    }
470}
471
472#[derive(Debug, Default)]
473/// List of aliases used in a nodeset.
474pub struct AliasTable {
475    /// Alias list.
476    pub aliases: Vec<NodeIdAlias>,
477}
478
479impl<'input> XmlLoad<'input> for AliasTable {
480    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
481        Ok(Self {
482            aliases: children_with_name(node, "Alias")?,
483        })
484    }
485}
486
487#[derive(Debug, Default, Clone)]
488/// A localized text with a body and a locale.
489pub struct LocalizedText {
490    /// Localized text body.
491    pub text: String,
492    /// Localized text locale.
493    pub locale: Locale,
494}
495impl<'input> XmlLoad<'input> for LocalizedText {
496    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
497        Ok(Self {
498            text: node.text().map(|v| v.to_owned()).unwrap_or_default(),
499            locale: value_from_attr_opt(node, "Locale")?.unwrap_or_else(|| Locale("".to_owned())),
500        })
501    }
502}
503
504#[derive(Debug, Clone)]
505/// Symbolic name.
506pub struct SymbolicName {
507    /// Name alternatives.
508    pub names: Vec<String>,
509}
510
511impl FromValue for SymbolicName {
512    fn from_value(_node: &Node<'_, '_>, _attr: &str, v: &str) -> Result<Self, XmlError> {
513        Ok(Self {
514            names: v.split_whitespace().map(|v| v.to_owned()).collect(),
515        })
516    }
517}
518
519#[derive(Debug)]
520/// A reference defined inside a node.
521pub struct Reference {
522    /// Target node ID.
523    pub node_id: NodeId,
524    /// Reference type ID.
525    pub reference_type: NodeId,
526    /// Whether this is a forward or inverse reference.
527    pub is_forward: bool,
528}
529
530impl<'input> XmlLoad<'input> for Reference {
531    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
532        Ok(Self {
533            node_id: value_from_contents(node)?,
534            reference_type: value_from_attr(node, "ReferenceType")?,
535            is_forward: value_from_attr_opt(node, "IsForward")?.unwrap_or(true),
536        })
537    }
538}
539
540#[derive(Debug)]
541/// List of references in a node definition.
542pub struct ListOfReferences {
543    /// References.
544    pub references: Vec<Reference>,
545}
546impl<'input> XmlLoad<'input> for ListOfReferences {
547    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
548        Ok(Self {
549            references: children_with_name(node, "Reference")?,
550        })
551    }
552}
553
554#[derive(Debug, Clone)]
555/// Role permission for a node.
556pub struct RolePermission {
557    /// Role ID.
558    pub node_id: NodeId,
559    /// Permission flags.
560    pub permissions: u64,
561}
562
563impl<'input> XmlLoad<'input> for RolePermission {
564    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
565        Ok(Self {
566            node_id: value_from_contents(node)?,
567            permissions: value_from_attr_opt(node, "Permissions")?.unwrap_or(0),
568        })
569    }
570}
571
572#[derive(Debug, Clone)]
573/// List of role permissions.
574pub struct ListOfRolePermissions {
575    /// Role permissions.
576    pub role_permissions: Vec<RolePermission>,
577}
578impl<'input> XmlLoad<'input> for ListOfRolePermissions {
579    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
580        Ok(Self {
581            role_permissions: children_with_name(node, "RolePermission")?,
582        })
583    }
584}
585
586#[derive(Debug)]
587/// Status of a node set.
588pub enum ReleaseStatus {
589    /// Node set has been released.
590    Released,
591    /// Node set is a draft.
592    Draft,
593    /// Node set has been deprecated.
594    Deprecated,
595}
596
597impl FromValue for ReleaseStatus {
598    fn from_value(node: &Node<'_, '_>, attr: &str, v: &str) -> Result<Self, XmlError> {
599        match v {
600            "Released" => Ok(Self::Released),
601            "Draft" => Ok(Self::Draft),
602            "Deprecated" => Ok(Self::Deprecated),
603            r => Err(XmlError::other(
604                node,
605                &format!("Unexpected value for {attr}: {r}"),
606            )),
607        }
608    }
609}
610
611#[derive(Debug)]
612/// Common fields for nodeset nodes.
613pub struct UANodeBase {
614    /// Display name alternatives.
615    pub display_names: Vec<LocalizedText>,
616    /// Description alternatives.
617    pub description: Vec<LocalizedText>,
618    /// Category alternatives.
619    pub category: Vec<String>,
620    /// Documentation about this node.
621    pub documentation: Option<String>,
622    /// List of references.
623    pub references: Option<ListOfReferences>,
624    /// List of required role permissions.
625    pub role_permissions: Option<ListOfRolePermissions>,
626    /// Node ID of this node.
627    pub node_id: NodeId,
628    /// Browse name of this node.
629    pub browse_name: QualifiedName,
630    /// Default write mask.
631    pub write_mask: WriteMask,
632    /// Default user write mask.
633    pub user_write_mask: WriteMask,
634    /// Default access restrictions.
635    pub access_restrictions: AccessRestriction,
636    /// Symbolic name for this node.
637    pub symbolic_name: Option<SymbolicName>,
638    /// Release status of this node.
639    pub release_status: ReleaseStatus,
640}
641
642impl<'input> XmlLoad<'input> for UANodeBase {
643    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
644        Ok(Self {
645            display_names: children_with_name(node, "DisplayName")?,
646            description: children_with_name(node, "Description")?,
647            category: children_with_name(node, "Category")?,
648            documentation: first_child_with_name_opt(node, "Documentation")?,
649            references: first_child_with_name_opt(node, "References")?,
650            role_permissions: first_child_with_name_opt(node, "RolePermissions")?,
651            node_id: value_from_attr(node, "NodeId")?,
652            browse_name: value_from_attr(node, "BrowseName")?,
653            write_mask: value_from_attr_opt(node, "WriteMask")?.unwrap_or(WriteMask(0)),
654            user_write_mask: value_from_attr_opt(node, "UserWriteMask")?.unwrap_or(WriteMask(0)),
655            access_restrictions: value_from_attr_opt(node, "AccessRestrictions")?
656                .unwrap_or(AccessRestriction(0)),
657            symbolic_name: value_from_attr_opt(node, "SymbolicName")?,
658            release_status: value_from_attr_opt(node, "ReleaseStatus")?
659                .unwrap_or(ReleaseStatus::Released),
660        })
661    }
662}
663
664#[derive(Debug)]
665/// Base type for node instances.
666pub struct UAInstance {
667    /// Common fields.
668    pub base: UANodeBase,
669    /// Parent node ID, not required.
670    pub parent_node_id: Option<NodeId>,
671}
672
673impl<'input> XmlLoad<'input> for UAInstance {
674    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
675        Ok(Self {
676            base: UANodeBase::load(node)?,
677            parent_node_id: value_from_attr_opt(node, "ParentNodeId")?,
678        })
679    }
680}
681
682#[derive(Debug)]
683/// OPC UA Object in a nodeset.
684pub struct UAObject {
685    /// Base data.
686    pub base: UAInstance,
687    /// Default node event notifier.
688    pub event_notifier: EventNotifier,
689}
690
691impl<'input> XmlLoad<'input> for UAObject {
692    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
693        Ok(Self {
694            base: UAInstance::load(node)?,
695            event_notifier: value_from_attr_opt(node, "EventNotifier")?.unwrap_or(EventNotifier(0)),
696        })
697    }
698}
699
700#[derive(Debug)]
701/// Variable initial value.
702pub struct Value(pub Variant);
703
704impl<'input> XmlLoad<'input> for Value {
705    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
706        Ok(Self(
707            node.children()
708                .find(|n| !n.tag_name().name().is_empty())
709                .map(|n| Variant::load(&n))
710                .transpose()?
711                .ok_or_else(|| XmlError::other(node, "Empty value, expected variant"))?,
712        ))
713    }
714}
715
716#[derive(Debug)]
717/// Variable defined in a nodeset.
718pub struct UAVariable {
719    /// Base data.
720    pub base: UAInstance,
721    /// Initial or default value.
722    pub value: Option<Value>,
723    /// Data type ID.
724    pub data_type: NodeId,
725    /// Node value rank.
726    pub value_rank: ValueRank,
727    /// Array dimensions.
728    pub array_dimensions: ArrayDimensions,
729    /// Default access level.
730    pub access_level: AccessLevel,
731    /// Default user access level.
732    pub user_access_level: AccessLevel,
733    /// Default minimum sampling interval.
734    pub minimum_sampling_interval: Duration,
735    /// Default value of "historizing", whether this node stores its history.
736    pub historizing: bool,
737}
738
739impl<'input> XmlLoad<'input> for UAVariable {
740    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
741        Ok(Self {
742            base: UAInstance::load(node)?,
743            value: first_child_with_name_opt(node, "Value")?,
744            data_type: value_from_attr_opt(node, "DataType")?
745                .unwrap_or_else(|| NodeId("i=24".to_owned())),
746            value_rank: value_from_attr_opt(node, "ValueRank")?.unwrap_or(ValueRank(-1)),
747            array_dimensions: value_from_attr_opt(node, "ArrayDimensions")?
748                .unwrap_or_else(|| ArrayDimensions("".to_owned())),
749            access_level: value_from_attr_opt(node, "AccessLevel")?.unwrap_or(AccessLevel(1)),
750            user_access_level: value_from_attr_opt(node, "UserAccessLevel")?
751                .unwrap_or(AccessLevel(1)),
752            minimum_sampling_interval: value_from_attr_opt(node, "MinimumSamplingInterval")?
753                .unwrap_or(Duration(0.0)),
754            historizing: value_from_attr_opt(node, "Historizing")?.unwrap_or(false),
755        })
756    }
757}
758
759#[derive(Debug)]
760/// Argument of a method in a node set file.
761pub struct UAMethodArgument {
762    /// Method argument name.
763    pub name: Option<String>,
764    /// List of possible descriptions (in different locales).
765    pub descriptions: Vec<LocalizedText>,
766}
767
768impl<'input> XmlLoad<'input> for UAMethodArgument {
769    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
770        Ok(Self {
771            name: first_child_with_name_opt(node, "Name")?,
772            descriptions: children_with_name(node, "Description")?,
773        })
774    }
775}
776
777#[derive(Debug)]
778/// Method defined in a node set file.
779pub struct UAMethod {
780    /// Base data.
781    pub base: UAInstance,
782    /// List of method arguments.
783    pub arguments: Vec<UAMethodArgument>,
784    /// Whether this method is executable.
785    pub executable: bool,
786    /// Default value of user executable.
787    pub user_executable: bool,
788    /// ID of another node serving as the method declaration in the type hierarchy.
789    pub method_declaration_id: Option<NodeId>,
790}
791
792impl<'input> XmlLoad<'input> for UAMethod {
793    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
794        Ok(Self {
795            base: UAInstance::load(node)?,
796            arguments: children_with_name(node, "ArgumentDescription")?,
797            executable: value_from_attr_opt(node, "Executable")?.unwrap_or(false),
798            user_executable: value_from_attr_opt(node, "UserExecutable")?.unwrap_or(false),
799            method_declaration_id: value_from_attr_opt(node, "MethodDeclarationId")?,
800        })
801    }
802}
803
804#[derive(Debug)]
805/// Structure translation.
806pub struct StructureTranslationType {
807    /// Possible translations.
808    pub text: Vec<LocalizedText>,
809    /// Name of the translation field.
810    pub name: String,
811}
812
813impl<'input> XmlLoad<'input> for StructureTranslationType {
814    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
815        Ok(Self {
816            text: children_with_name(node, "Text")?,
817            name: value_from_attr(node, "Name")?,
818        })
819    }
820}
821
822#[derive(Debug)]
823/// Translation variant.
824pub enum TranslationType {
825    /// Raw list of alternative translations.
826    Text(Vec<LocalizedText>),
827    /// Named list of translations.
828    Field(Vec<StructureTranslationType>),
829    /// No translation.
830    None,
831}
832
833impl<'input> XmlLoad<'input> for TranslationType {
834    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
835        let texts = children_with_name(node, "Text")?;
836        if !texts.is_empty() {
837            return Ok(Self::Text(texts));
838        }
839        let fields = children_with_name(node, "Field")?;
840        if !fields.is_empty() {
841            return Ok(Self::Field(fields));
842        }
843        Ok(Self::None)
844    }
845}
846
847#[derive(Debug)]
848/// View defined in a node set file.
849pub struct UAView {
850    /// Base data.
851    pub base: UAInstance,
852    /// Whether this view contains no loops.
853    pub contains_no_loops: bool,
854    /// Event notifier.
855    pub event_notifier: EventNotifier,
856}
857
858impl<'input> XmlLoad<'input> for UAView {
859    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
860        Ok(Self {
861            base: UAInstance::load(node)?,
862            contains_no_loops: value_from_attr_opt(node, "ContainsNoLoops")?.unwrap_or(false),
863            event_notifier: value_from_attr(node, "EventNotifier")?,
864        })
865    }
866}
867
868#[derive(Debug)]
869/// Base type for node set types.
870pub struct UAType {
871    /// Base data.
872    pub base: UANodeBase,
873    /// Whether this type is abstract, i.e. it cannot be used in the instance hierarchy.
874    pub is_abstract: bool,
875}
876
877impl<'input> XmlLoad<'input> for UAType {
878    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
879        Ok(Self {
880            base: UANodeBase::load(node)?,
881            is_abstract: value_from_attr_opt(node, "IsAbstract")?.unwrap_or(false),
882        })
883    }
884}
885
886#[derive(Debug)]
887/// Object type defined in a node set file.
888pub struct UAObjectType {
889    /// Base data.
890    pub base: UAType,
891}
892
893impl<'input> XmlLoad<'input> for UAObjectType {
894    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
895        Ok(Self {
896            base: UAType::load(node)?,
897        })
898    }
899}
900
901#[derive(Debug)]
902/// Variable type defined in a node set file.
903pub struct UAVariableType {
904    /// Base data.
905    pub base: UAType,
906    /// Default value of instances of this type.
907    pub value: Option<Value>,
908    /// Data type, implementing types may use a subtype of this.
909    pub data_type: NodeId,
910    /// Value rank.
911    pub value_rank: ValueRank,
912    /// Array dimensions.
913    pub array_dimensions: ArrayDimensions,
914}
915
916impl<'input> XmlLoad<'input> for UAVariableType {
917    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
918        Ok(Self {
919            base: UAType::load(node)?,
920            value: first_child_with_name_opt(node, "Value")?,
921            data_type: value_from_attr_opt(node, "DataType")?
922                .unwrap_or_else(|| NodeId("i=24".to_owned())),
923            value_rank: value_from_attr_opt(node, "ValueRank")?.unwrap_or(ValueRank(-1)),
924            array_dimensions: value_from_attr_opt(node, "ArrayDimensions")?
925                .unwrap_or_else(|| ArrayDimensions("".to_owned())),
926        })
927    }
928}
929
930#[derive(Debug)]
931/// Purpose of a data type in a node set.
932pub enum DataTypePurpose {
933    /// Normal OPC-UA type.
934    Normal,
935    /// Only used as part of service calls, not intended to be in ExtensionObjects.
936    ServicesOnly,
937    /// Used for code generation.
938    CodeGenerator,
939}
940
941impl FromValue for DataTypePurpose {
942    fn from_value(node: &Node<'_, '_>, attr: &str, v: &str) -> Result<Self, XmlError> {
943        match v {
944            "Normal" => Ok(Self::Normal),
945            "ServicesOnly" => Ok(Self::ServicesOnly),
946            "CodeGenerator" => Ok(Self::CodeGenerator),
947            r => Err(XmlError::other(
948                node,
949                &format!("Unexpected value for {attr}: {r}"),
950            )),
951        }
952    }
953}
954
955#[derive(Debug, Clone)]
956/// Field in a data type definition.
957pub struct DataTypeField {
958    /// Possible display name translations.
959    pub display_names: Vec<LocalizedText>,
960    /// Possible description translations.
961    pub descriptions: Vec<LocalizedText>,
962    /// Field name, required.
963    pub name: String,
964    /// Field symbolic name.
965    pub symbolic_name: Option<SymbolicName>,
966    /// Field data type, required.
967    pub data_type: NodeId,
968    /// Value rank, default -1.
969    pub value_rank: ValueRank,
970    /// Array dimensions.
971    pub array_dimensions: ArrayDimensions,
972    /// Max string length, can be 0 for no limit.
973    pub max_string_length: u64,
974    /// Value, only applies to enum fields.
975    pub value: i64,
976    /// Whether this is an optional structure field.
977    pub is_optional: bool,
978    /// Whether to allow sub types of the field.
979    pub allow_sub_types: bool,
980}
981
982impl<'input> XmlLoad<'input> for DataTypeField {
983    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
984        Ok(Self {
985            display_names: children_with_name(node, "DisplayName")?,
986            descriptions: children_with_name(node, "Description")?,
987            name: value_from_attr(node, "Name")?,
988            symbolic_name: value_from_attr_opt(node, "SymbolicName")?,
989            data_type: value_from_attr_opt(node, "DataType")?
990                .unwrap_or_else(|| NodeId("i=24".to_owned())),
991            value_rank: value_from_attr_opt(node, "ValueRank")?.unwrap_or(ValueRank(-1)),
992            array_dimensions: value_from_attr_opt(node, "ArrayDimensions")?
993                .unwrap_or_else(|| ArrayDimensions("".to_owned())),
994            max_string_length: value_from_attr_opt(node, "MaxStringLength")?.unwrap_or(0),
995            value: value_from_attr_opt(node, "Value")?.unwrap_or(-1),
996            is_optional: value_from_attr_opt(node, "IsOptional")?.unwrap_or(false),
997            allow_sub_types: value_from_attr_opt(node, "AllowSubTypes")?.unwrap_or(false),
998        })
999    }
1000}
1001
1002#[derive(Debug, Clone)]
1003/// Data type definition.
1004pub struct DataTypeDefinition {
1005    /// Fields in this data type.
1006    pub fields: Vec<DataTypeField>,
1007    /// Qualified name of this data type.
1008    pub name: QualifiedName,
1009    /// Symbolic name.
1010    pub symbolic_name: SymbolicName,
1011    /// Whether this defines a union.
1012    pub is_union: bool,
1013    /// Whether this defines an option set.
1014    pub is_option_set: bool,
1015}
1016
1017impl<'input> XmlLoad<'input> for DataTypeDefinition {
1018    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
1019        Ok(Self {
1020            fields: children_with_name(node, "Field")?,
1021            name: value_from_attr(node, "Name")?,
1022            symbolic_name: value_from_attr_opt(node, "SymbolicName")?
1023                .unwrap_or_else(|| SymbolicName { names: Vec::new() }),
1024            is_union: value_from_attr_opt(node, "IsUnion")?.unwrap_or(false),
1025            is_option_set: value_from_attr_opt(node, "IsOptionSet")?.unwrap_or(false),
1026        })
1027    }
1028}
1029
1030#[derive(Debug)]
1031/// Data type defined in a node set file.
1032pub struct UADataType {
1033    /// Base data.
1034    pub base: UAType,
1035    /// Data type definition.
1036    pub definition: Option<DataTypeDefinition>,
1037    /// Purpose of this data type.
1038    pub purpose: DataTypePurpose,
1039}
1040
1041impl<'input> XmlLoad<'input> for UADataType {
1042    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
1043        Ok(Self {
1044            base: UAType::load(node)?,
1045            definition: first_child_with_name_opt(node, "Definition")?,
1046            purpose: value_from_attr_opt(node, "Purpose")?.unwrap_or(DataTypePurpose::Normal),
1047        })
1048    }
1049}
1050
1051#[derive(Debug)]
1052/// Reference type defined in a ndoe set file.
1053pub struct UAReferenceType {
1054    /// Base data.
1055    pub base: UAType,
1056    /// Possible inverse name translations.
1057    pub inverse_names: Vec<LocalizedText>,
1058    /// Whether this uses the same name for forward and inverse references.
1059    pub symmetric: bool,
1060}
1061
1062impl<'input> XmlLoad<'input> for UAReferenceType {
1063    fn load(node: &Node<'_, 'input>) -> Result<Self, XmlError> {
1064        Ok(Self {
1065            base: UAType::load(node)?,
1066            inverse_names: children_with_name(node, "InverseName")?,
1067            symmetric: value_from_attr_opt(node, "Symmetric")?.unwrap_or(false),
1068        })
1069    }
1070}