opcua_nodes/
reference_type.rs1use 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 pub fn is_abstract(mut self, is_abstract: bool) -> Self {
25 self.node.set_is_abstract(is_abstract);
26 self
27 }
28
29 pub fn write_mask(mut self, write_mask: WriteMask) -> Self {
31 self.node.set_write_mask(write_mask);
32 self
33 }
34
35 pub fn symmetric(mut self, symmetric: bool) -> Self {
38 self.node.set_symmetric(symmetric);
39 self
40 }
41
42 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#[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 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 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 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 pub fn is_valid(&self) -> bool {
209 self.base.is_valid()
210 }
211
212 pub fn symmetric(&self) -> bool {
214 self.symmetric
215 }
216
217 pub fn set_symmetric(&mut self, symmetric: bool) {
219 self.symmetric = symmetric;
220 }
221
222 pub fn is_abstract(&self) -> bool {
224 self.is_abstract
225 }
226
227 pub fn set_is_abstract(&mut self, is_abstract: bool) {
229 self.is_abstract = is_abstract;
230 }
231
232 pub fn inverse_name(&self) -> Option<LocalizedText> {
234 self.inverse_name.clone()
235 }
236
237 pub fn set_inverse_name(&mut self, inverse_name: LocalizedText) {
239 self.inverse_name = Some(inverse_name);
240 }
241}