Skip to main content

opcua_nodes/
view.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 `View` and `ViewBuilder`.
6use opcua_types::{
7    AttributeId, AttributesMask, DataEncoding, DataValue, NumericRange, StatusCode,
8    TimestampsToReturn, Variant, ViewAttributes,
9};
10use tracing::error;
11
12use crate::FromAttributesError;
13
14use super::{base::Base, node::Node, node::NodeBase, EventNotifier};
15
16node_builder_impl!(ViewBuilder, View);
17
18node_builder_impl_generates_event!(ViewBuilder);
19
20impl ViewBuilder {
21    /// Set whether the view contains no loops.
22    pub fn contains_no_loops(mut self, contains_no_loops: bool) -> Self {
23        self.node.set_contains_no_loops(contains_no_loops);
24        self
25    }
26
27    /// Set view event notifier.
28    pub fn event_notifier(mut self, event_notifier: EventNotifier) -> Self {
29        self.node.set_event_notifier(event_notifier);
30        self
31    }
32
33    /// Set view write mask.
34    pub fn write_mask(mut self, write_mask: WriteMask) -> Self {
35        self.node.set_write_mask(write_mask);
36        self
37    }
38}
39
40/// A `View` is a type of node within the `AddressSpace`.
41#[derive(Debug)]
42pub struct View {
43    pub(super) base: Base,
44    pub(super) event_notifier: EventNotifier,
45    pub(super) contains_no_loops: bool,
46}
47
48impl Default for View {
49    fn default() -> Self {
50        Self {
51            base: Base::new(NodeClass::View, &NodeId::null(), "", ""),
52            event_notifier: EventNotifier::empty(),
53            contains_no_loops: true,
54        }
55    }
56}
57
58node_base_impl!(View);
59
60impl Node for View {
61    fn get_attribute_max_age(
62        &self,
63        timestamps_to_return: TimestampsToReturn,
64        attribute_id: AttributeId,
65        index_range: &NumericRange,
66        data_encoding: &DataEncoding,
67        max_age: f64,
68    ) -> Option<DataValue> {
69        match attribute_id {
70            AttributeId::EventNotifier => Some(Variant::from(self.event_notifier().bits()).into()),
71            AttributeId::ContainsNoLoops => Some(Variant::from(self.contains_no_loops()).into()),
72            _ => self.base.get_attribute_max_age(
73                timestamps_to_return,
74                attribute_id,
75                index_range,
76                data_encoding,
77                max_age,
78            ),
79        }
80    }
81
82    fn set_attribute(
83        &mut self,
84        attribute_id: AttributeId,
85        value: Variant,
86    ) -> Result<(), StatusCode> {
87        match attribute_id {
88            AttributeId::EventNotifier => {
89                if let Variant::Byte(v) = value {
90                    self.set_event_notifier(EventNotifier::from_bits_truncate(v));
91                    Ok(())
92                } else {
93                    Err(StatusCode::BadTypeMismatch)
94                }
95            }
96            AttributeId::ContainsNoLoops => {
97                if let Variant::Boolean(v) = value {
98                    self.set_contains_no_loops(v);
99                    Ok(())
100                } else {
101                    Err(StatusCode::BadTypeMismatch)
102                }
103            }
104            _ => self.base.set_attribute(attribute_id, value),
105        }
106    }
107}
108
109impl View {
110    /// Create a new view.
111    pub fn new(
112        node_id: &NodeId,
113        browse_name: impl Into<QualifiedName>,
114        display_name: impl Into<LocalizedText>,
115        event_notifier: EventNotifier,
116        contains_no_loops: bool,
117    ) -> View {
118        View {
119            base: Base::new(NodeClass::View, node_id, browse_name, display_name),
120            event_notifier,
121            contains_no_loops,
122        }
123    }
124
125    /// Create a new view from the full `Base` node.
126    pub fn new_full(base: Base, event_notifier: EventNotifier, contains_no_loops: bool) -> Self {
127        Self {
128            base,
129            event_notifier,
130            contains_no_loops,
131        }
132    }
133
134    /// Create a new view from [ViewAttributes].
135    pub fn from_attributes(
136        node_id: &NodeId,
137        browse_name: impl Into<QualifiedName>,
138        attributes: ViewAttributes,
139    ) -> Result<Self, FromAttributesError> {
140        let mandatory_attributes = AttributesMask::DISPLAY_NAME
141            | AttributesMask::EVENT_NOTIFIER
142            | AttributesMask::CONTAINS_NO_LOOPS;
143        let mask = AttributesMask::from_bits_truncate(attributes.specified_attributes);
144        if mask.contains(mandatory_attributes) {
145            let event_notifier = EventNotifier::from_bits_truncate(attributes.event_notifier);
146            let mut node = Self::new(
147                node_id,
148                browse_name,
149                attributes.display_name,
150                event_notifier,
151                attributes.contains_no_loops,
152            );
153            if mask.contains(AttributesMask::DESCRIPTION) {
154                node.set_description(attributes.description);
155            }
156            if mask.contains(AttributesMask::WRITE_MASK) {
157                node.set_write_mask(WriteMask::from_bits_truncate(attributes.write_mask));
158            }
159            if mask.contains(AttributesMask::USER_WRITE_MASK) {
160                node.set_user_write_mask(WriteMask::from_bits_truncate(attributes.user_write_mask));
161            }
162            Ok(node)
163        } else {
164            error!("View cannot be created from attributes - missing mandatory values");
165            Err(FromAttributesError::MissingMandatoryValues)
166        }
167    }
168
169    /// Check whether this node is valid.
170    pub fn is_valid(&self) -> bool {
171        self.base.is_valid()
172    }
173
174    /// Get the event notifier of this view.
175    pub fn event_notifier(&self) -> EventNotifier {
176        self.event_notifier
177    }
178
179    /// Set the event notifier of this view.
180    pub fn set_event_notifier(&mut self, event_notifier: EventNotifier) {
181        self.event_notifier = event_notifier;
182    }
183
184    /// Get the `ContainsNoLoops` attribute of this view.
185    pub fn contains_no_loops(&self) -> bool {
186        self.contains_no_loops
187    }
188
189    /// Set the `ContainsNoLoops` attribute on this view.
190    pub fn set_contains_no_loops(&mut self, contains_no_loops: bool) {
191        self.contains_no_loops = contains_no_loops
192    }
193}