Skip to main content

opcua_nodes/
data_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 `Method` and `MethodBuilder`.
6
7use opcua_types::{
8    AttributeId, AttributesMask, DataEncoding, DataTypeAttributes, DataTypeDefinition, DataValue,
9    NumericRange, StatusCode, TimestampsToReturn, Variant,
10};
11use tracing::error;
12
13use crate::FromAttributesError;
14
15use super::{base::Base, node::Node, node::NodeBase};
16
17node_builder_impl!(DataTypeBuilder, DataType);
18node_builder_impl_subtype!(DataTypeBuilder);
19
20impl DataTypeBuilder {
21    /// Set whether the data type is abstract, meaning
22    /// it cannot be used by nodes in the instance hierarchy.
23    pub fn is_abstract(mut self, is_abstract: bool) -> Self {
24        self.node.set_is_abstract(is_abstract);
25        self
26    }
27
28    /// Set the data type write mask.
29    pub fn write_mask(mut self, write_mask: WriteMask) -> Self {
30        self.node.set_write_mask(write_mask);
31        self
32    }
33
34    /// Set the data type definition.
35    pub fn data_type_definition(mut self, data_type_definition: DataTypeDefinition) -> Self {
36        self.node
37            .set_data_type_definition(Some(data_type_definition));
38        self
39    }
40}
41
42/// A `DataType` is a type of node within the `AddressSpace`.
43#[derive(Debug)]
44pub struct DataType {
45    pub(super) base: Base,
46    pub(super) is_abstract: bool,
47    pub(super) data_type_definition: Option<DataTypeDefinition>,
48}
49
50impl Default for DataType {
51    fn default() -> Self {
52        Self {
53            base: Base::new(NodeClass::DataType, &NodeId::null(), "", ""),
54            is_abstract: false,
55            data_type_definition: None,
56        }
57    }
58}
59
60node_base_impl!(DataType);
61
62impl Node for DataType {
63    fn get_attribute_max_age(
64        &self,
65        timestamps_to_return: TimestampsToReturn,
66        attribute_id: AttributeId,
67        index_range: &NumericRange,
68        data_encoding: &DataEncoding,
69        max_age: f64,
70    ) -> Option<DataValue> {
71        match attribute_id {
72            AttributeId::IsAbstract => Some(self.is_abstract().into()),
73            AttributeId::DataTypeDefinition => self.data_type_definition.as_ref().map(|dt| {
74                let v: Variant = dt.clone().into();
75                v.into()
76            }),
77            _ => self.base.get_attribute_max_age(
78                timestamps_to_return,
79                attribute_id,
80                index_range,
81                data_encoding,
82                max_age,
83            ),
84        }
85    }
86
87    fn set_attribute(
88        &mut self,
89        attribute_id: AttributeId,
90        value: Variant,
91    ) -> Result<(), StatusCode> {
92        match attribute_id {
93            AttributeId::IsAbstract => {
94                if let Variant::Boolean(v) = value {
95                    self.set_is_abstract(v);
96                    Ok(())
97                } else {
98                    Err(StatusCode::BadTypeMismatch)
99                }
100            }
101            AttributeId::DataTypeDefinition => {
102                if matches!(value, Variant::Empty) {
103                    self.set_data_type_definition(None);
104                    Ok(())
105                } else if let Variant::ExtensionObject(v) = value {
106                    let def = DataTypeDefinition::from_extension_object(v)?;
107                    self.set_data_type_definition(Some(def));
108                    Ok(())
109                } else {
110                    Err(StatusCode::BadTypeMismatch)
111                }
112            }
113            _ => self.base.set_attribute(attribute_id, value),
114        }
115    }
116}
117
118impl DataType {
119    /// Create a new data type.
120    pub fn new(
121        node_id: &NodeId,
122        browse_name: impl Into<QualifiedName>,
123        display_name: impl Into<LocalizedText>,
124        is_abstract: bool,
125    ) -> DataType {
126        DataType {
127            base: Base::new(NodeClass::DataType, node_id, browse_name, display_name),
128            is_abstract,
129            data_type_definition: None,
130        }
131    }
132
133    /// Create a new data type with all attributes, may change if
134    /// new attributes are added to the OPC-UA standard.
135    pub fn new_full(
136        base: Base,
137        is_abstract: bool,
138        data_type_definition: Option<DataTypeDefinition>,
139    ) -> Self {
140        Self {
141            base,
142            is_abstract,
143            data_type_definition,
144        }
145    }
146
147    /// Create a new data type from [DataTypeAttributes].
148    pub fn from_attributes(
149        node_id: &NodeId,
150        browse_name: impl Into<QualifiedName>,
151        attributes: DataTypeAttributes,
152    ) -> Result<Self, FromAttributesError> {
153        let mask = AttributesMask::from_bits(attributes.specified_attributes)
154            .ok_or(FromAttributesError::InvalidMask)?;
155        if mask.contains(AttributesMask::DISPLAY_NAME | AttributesMask::IS_ABSTRACT) {
156            let mut node = Self::new(
157                node_id,
158                browse_name,
159                attributes.display_name,
160                attributes.is_abstract,
161            );
162            if mask.contains(AttributesMask::DESCRIPTION) {
163                node.set_description(attributes.description);
164            }
165            if mask.contains(AttributesMask::WRITE_MASK) {
166                node.set_write_mask(WriteMask::from_bits_truncate(attributes.write_mask));
167            }
168            if mask.contains(AttributesMask::USER_WRITE_MASK) {
169                node.set_user_write_mask(WriteMask::from_bits_truncate(attributes.user_write_mask));
170            }
171            Ok(node)
172        } else {
173            error!("DataType cannot be created from attributes - missing mandatory values");
174            Err(FromAttributesError::MissingMandatoryValues)
175        }
176    }
177
178    /// Get whether this data type is valid.
179    pub fn is_valid(&self) -> bool {
180        self.base.is_valid()
181    }
182
183    /// Get the `IsAbstract` attribute for this data type.
184    pub fn is_abstract(&self) -> bool {
185        self.is_abstract
186    }
187
188    /// Set the `IsAbstract` attribute for this data type.
189    pub fn set_is_abstract(&mut self, is_abstract: bool) {
190        self.is_abstract = is_abstract;
191    }
192
193    /// Set the data type definition of this data type.
194    pub fn set_data_type_definition(&mut self, data_type_definition: Option<DataTypeDefinition>) {
195        self.data_type_definition = data_type_definition;
196    }
197
198    /// Get the data type definition of this data type.
199    pub fn data_type_definition(&self) -> Option<&DataTypeDefinition> {
200        self.data_type_definition.as_ref()
201    }
202}