Skip to main content

opcua_nodes/
reference_type.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5//! Contains the implementation of `ReferenceType` and `ReferenceTypeBuilder`.
6
7use opcua_types::{
8    AttributeId, AttributesMask, DataEncoding, DataValue, NumericRange, ReferenceTypeAttributes,
9    StatusCode, TimestampsToReturn, Variant,
10};
11use tracing::error;
12
13use crate::FromAttributesError;
14
15use super::{base::Base, node::Node, node::NodeBase};
16
17node_builder_impl!(ReferenceTypeBuilder, ReferenceType);
18node_builder_impl_subtype!(ReferenceTypeBuilder);
19
20impl ReferenceTypeBuilder {
21    /// Set the `IsAbstract` attribute of this reference type,
22    /// indicating whether references in the instance hierarchy
23    /// are allowed to use this reference type.
24    pub fn is_abstract(mut self, is_abstract: bool) -> Self {
25        self.node.set_is_abstract(is_abstract);
26        self
27    }
28
29    /// Set the write mask of this type.
30    pub fn write_mask(mut self, write_mask: WriteMask) -> Self {
31        self.node.set_write_mask(write_mask);
32        self
33    }
34
35    /// Set whether references of this type are symmetric, meaning
36    /// they have the same name in both directions.
37    pub fn symmetric(mut self, symmetric: bool) -> Self {
38        self.node.set_symmetric(symmetric);
39        self
40    }
41
42    /// Set the inverse name of this reference type.
43    pub fn inverse_name(mut self, inverse_name: impl Into<LocalizedText>) -> Self {
44        self.node.set_inverse_name(inverse_name.into());
45        self
46    }
47}
48
49/// A `ReferenceType` is a type of node within the `AddressSpace`.
50#[derive(Debug)]
51pub struct ReferenceType {
52    pub(super) base: Base,
53    pub(super) symmetric: bool,
54    pub(super) is_abstract: bool,
55    pub(super) inverse_name: Option<LocalizedText>,
56}
57
58impl Default for ReferenceType {
59    fn default() -> Self {
60        Self {
61            base: Base::new(NodeClass::ReferenceType, &NodeId::null(), "", ""),
62            symmetric: false,
63            is_abstract: false,
64            inverse_name: None,
65        }
66    }
67}
68
69node_base_impl!(ReferenceType);
70
71impl Node for ReferenceType {
72    fn get_attribute_max_age(
73        &self,
74        timestamps_to_return: TimestampsToReturn,
75        attribute_id: AttributeId,
76        index_range: &NumericRange,
77        data_encoding: &DataEncoding,
78        max_age: f64,
79    ) -> Option<DataValue> {
80        match attribute_id {
81            AttributeId::Symmetric => Some(self.symmetric().into()),
82            AttributeId::IsAbstract => Some(self.is_abstract().into()),
83            AttributeId::InverseName => self.inverse_name().map(|v| v.into()),
84            _ => self.base.get_attribute_max_age(
85                timestamps_to_return,
86                attribute_id,
87                index_range,
88                data_encoding,
89                max_age,
90            ),
91        }
92    }
93
94    fn set_attribute(
95        &mut self,
96        attribute_id: AttributeId,
97        value: Variant,
98    ) -> Result<(), StatusCode> {
99        match attribute_id {
100            AttributeId::Symmetric => {
101                if let Variant::Boolean(v) = value {
102                    self.symmetric = v;
103                    Ok(())
104                } else {
105                    Err(StatusCode::BadTypeMismatch)
106                }
107            }
108            AttributeId::IsAbstract => {
109                if let Variant::Boolean(v) = value {
110                    self.is_abstract = v;
111                    Ok(())
112                } else {
113                    Err(StatusCode::BadTypeMismatch)
114                }
115            }
116            AttributeId::InverseName => {
117                if let Variant::LocalizedText(v) = value {
118                    self.inverse_name = Some(*v);
119                    Ok(())
120                } else {
121                    Err(StatusCode::BadTypeMismatch)
122                }
123            }
124            _ => self.base.set_attribute(attribute_id, value),
125        }
126    }
127}
128
129impl ReferenceType {
130    /// Create a new reference type.
131    pub fn new(
132        node_id: &NodeId,
133        browse_name: impl Into<QualifiedName>,
134        display_name: impl Into<LocalizedText>,
135        inverse_name: Option<LocalizedText>,
136        symmetric: bool,
137        is_abstract: bool,
138    ) -> ReferenceType {
139        ReferenceType {
140            base: Base::new(NodeClass::ReferenceType, node_id, browse_name, display_name),
141            symmetric,
142            is_abstract,
143            inverse_name,
144        }
145    }
146
147    /// Create a new reference type with all attributes, may change if
148    /// new attributes are added to the OPC-UA standard.
149    pub fn new_full(
150        base: Base,
151        symmetric: bool,
152        is_abstract: bool,
153        inverse_name: Option<LocalizedText>,
154    ) -> Self {
155        Self {
156            base,
157            symmetric,
158            is_abstract,
159            inverse_name,
160        }
161    }
162
163    /// Create a new reference type from [ReferenceTypeAttributes].
164    pub fn from_attributes(
165        node_id: &NodeId,
166        browse_name: impl Into<QualifiedName>,
167        attributes: ReferenceTypeAttributes,
168    ) -> Result<Self, FromAttributesError> {
169        let mandatory_attributes =
170            AttributesMask::DISPLAY_NAME | AttributesMask::IS_ABSTRACT | AttributesMask::SYMMETRIC;
171        let mask = AttributesMask::from_bits(attributes.specified_attributes)
172            .ok_or(FromAttributesError::InvalidMask)?;
173        if mask.contains(mandatory_attributes) {
174            let mut node = Self::new(
175                node_id,
176                browse_name,
177                attributes.display_name,
178                None,
179                false,
180                false,
181            );
182            if mask.contains(AttributesMask::DESCRIPTION) {
183                node.set_description(attributes.description);
184            }
185            if mask.contains(AttributesMask::WRITE_MASK) {
186                node.set_write_mask(WriteMask::from_bits_truncate(attributes.write_mask));
187            }
188            if mask.contains(AttributesMask::USER_WRITE_MASK) {
189                node.set_user_write_mask(WriteMask::from_bits_truncate(attributes.user_write_mask));
190            }
191            if mask.contains(AttributesMask::IS_ABSTRACT) {
192                node.set_is_abstract(attributes.is_abstract);
193            }
194            if mask.contains(AttributesMask::SYMMETRIC) {
195                node.set_symmetric(attributes.is_abstract);
196            }
197            if mask.contains(AttributesMask::INVERSE_NAME) {
198                node.set_inverse_name(attributes.inverse_name);
199            }
200            Ok(node)
201        } else {
202            error!("ReferenceType cannot be created from attributes - missing mandatory values");
203            Err(FromAttributesError::MissingMandatoryValues)
204        }
205    }
206
207    /// Get whether this reference type is valid.
208    pub fn is_valid(&self) -> bool {
209        self.base.is_valid()
210    }
211
212    /// Get whether this reference type is symmetric.
213    pub fn symmetric(&self) -> bool {
214        self.symmetric
215    }
216
217    /// Set whether this reference type is symmetric.
218    pub fn set_symmetric(&mut self, symmetric: bool) {
219        self.symmetric = symmetric;
220    }
221
222    /// Get whether this reference type is abstract.
223    pub fn is_abstract(&self) -> bool {
224        self.is_abstract
225    }
226
227    /// Get whether this reference type is abstract.
228    pub fn set_is_abstract(&mut self, is_abstract: bool) {
229        self.is_abstract = is_abstract;
230    }
231
232    /// Get the inverse name of this reference type.
233    pub fn inverse_name(&self) -> Option<LocalizedText> {
234        self.inverse_name.clone()
235    }
236
237    /// Set the inverse name of this reference type.
238    pub fn set_inverse_name(&mut self, inverse_name: LocalizedText) {
239        self.inverse_name = Some(inverse_name);
240    }
241}