opcua_nodes/
object_type.rs1use 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 pub fn is_abstract(mut self, is_abstract: bool) -> Self {
28 self.node.set_is_abstract(is_abstract);
29 self
30 }
31
32 pub fn write_mask(mut self, write_mask: WriteMask) -> Self {
34 self.node.set_write_mask(write_mask);
35 self
36 }
37}
38
39#[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 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 pub fn new_full(base: Base, is_abstract: bool) -> Self {
114 Self { base, is_abstract }
115 }
116
117 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 pub fn is_valid(&self) -> bool {
151 self.base.is_valid()
152 }
153
154 pub fn is_abstract(&self) -> bool {
156 self.is_abstract
157 }
158
159 pub fn set_is_abstract(&mut self, is_abstract: bool) {
161 self.is_abstract = is_abstract;
162 }
163}