Skip to main content

opcua_nodes/
object_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 `ObjectType` and `ObjectTypeBuilder`.
6
7use opcua_types::{
8    AttributeId, AttributesMask, DataEncoding, DataValue, NumericRange, ObjectTypeAttributes,
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!(ObjectTypeBuilder, ObjectType);
18
19node_builder_impl_generates_event!(ObjectTypeBuilder);
20node_builder_impl_subtype!(ObjectTypeBuilder);
21node_builder_impl_component_of!(ObjectTypeBuilder);
22node_builder_impl_property_of!(ObjectTypeBuilder);
23
24impl ObjectTypeBuilder {
25    /// Set whether the object type is abstract, meaning
26    /// it cannot be used by types in the instance hierarchy.
27    pub fn is_abstract(mut self, is_abstract: bool) -> Self {
28        self.node.set_is_abstract(is_abstract);
29        self
30    }
31
32    /// Set the object type write mask.
33    pub fn write_mask(mut self, write_mask: WriteMask) -> Self {
34        self.node.set_write_mask(write_mask);
35        self
36    }
37}
38
39/// An `ObjectType` is a type of node within the `AddressSpace`.
40#[derive(Debug)]
41pub struct ObjectType {
42    pub(super) base: Base,
43    pub(super) is_abstract: bool,
44}
45
46impl Default for ObjectType {
47    fn default() -> Self {
48        Self {
49            base: Base::new(NodeClass::ObjectType, &NodeId::null(), "", ""),
50            is_abstract: false,
51        }
52    }
53}
54
55node_base_impl!(ObjectType);
56
57impl Node for ObjectType {
58    fn get_attribute_max_age(
59        &self,
60        timestamps_to_return: TimestampsToReturn,
61        attribute_id: AttributeId,
62        index_range: &NumericRange,
63        data_encoding: &DataEncoding,
64        max_age: f64,
65    ) -> Option<DataValue> {
66        match attribute_id {
67            AttributeId::IsAbstract => Some(self.is_abstract().into()),
68            _ => self.base.get_attribute_max_age(
69                timestamps_to_return,
70                attribute_id,
71                index_range,
72                data_encoding,
73                max_age,
74            ),
75        }
76    }
77
78    fn set_attribute(
79        &mut self,
80        attribute_id: AttributeId,
81        value: Variant,
82    ) -> Result<(), StatusCode> {
83        match attribute_id {
84            AttributeId::IsAbstract => {
85                if let Variant::Boolean(v) = value {
86                    self.set_is_abstract(v);
87                    Ok(())
88                } else {
89                    Err(StatusCode::BadTypeMismatch)
90                }
91            }
92            _ => self.base.set_attribute(attribute_id, value),
93        }
94    }
95}
96
97impl ObjectType {
98    /// Create a new object type.
99    pub fn new(
100        node_id: &NodeId,
101        browse_name: impl Into<QualifiedName>,
102        display_name: impl Into<LocalizedText>,
103        is_abstract: bool,
104    ) -> ObjectType {
105        ObjectType {
106            base: Base::new(NodeClass::ObjectType, node_id, browse_name, display_name),
107            is_abstract,
108        }
109    }
110
111    /// Create a new object type with all attributes, may change if
112    /// new attributes are added to the OPC-UA standard.
113    pub fn new_full(base: Base, is_abstract: bool) -> Self {
114        Self { base, is_abstract }
115    }
116
117    /// Create a new object type from [ObjectTypeAttributes].
118    pub fn from_attributes(
119        node_id: &NodeId,
120        browse_name: impl Into<QualifiedName>,
121        attributes: ObjectTypeAttributes,
122    ) -> Result<Self, FromAttributesError> {
123        let mandatory_attributes = AttributesMask::DISPLAY_NAME | AttributesMask::IS_ABSTRACT;
124        let mask = AttributesMask::from_bits(attributes.specified_attributes)
125            .ok_or(FromAttributesError::InvalidMask)?;
126        if mask.contains(mandatory_attributes) {
127            let mut node = Self::new(
128                node_id,
129                browse_name,
130                attributes.display_name,
131                attributes.is_abstract,
132            );
133            if mask.contains(AttributesMask::DESCRIPTION) {
134                node.set_description(attributes.description);
135            }
136            if mask.contains(AttributesMask::WRITE_MASK) {
137                node.set_write_mask(WriteMask::from_bits_truncate(attributes.write_mask));
138            }
139            if mask.contains(AttributesMask::USER_WRITE_MASK) {
140                node.set_user_write_mask(WriteMask::from_bits_truncate(attributes.user_write_mask));
141            }
142            Ok(node)
143        } else {
144            error!("ObjectType cannot be created from attributes - missing mandatory values");
145            Err(FromAttributesError::MissingMandatoryValues)
146        }
147    }
148
149    /// Get whether this object type is valid.
150    pub fn is_valid(&self) -> bool {
151        self.base.is_valid()
152    }
153
154    /// Get the `IsAbstract` attribute for this object type.
155    pub fn is_abstract(&self) -> bool {
156        self.is_abstract
157    }
158
159    /// Set the `IsAbstract` attribute for this object type.
160    pub fn set_is_abstract(&mut self, is_abstract: bool) {
161        self.is_abstract = is_abstract;
162    }
163}