Skip to main content

opcua_server/node_manager/
attributes.rs

1use opcua_types::{
2    AttributeId, DataEncoding, DataValue, DateTime, DiagnosticBits, DiagnosticInfo, NodeId,
3    NumericRange, ReadValueId, StatusCode, WriteValue,
4};
5
6use super::IntoResult;
7
8#[derive(Debug, Clone)]
9/// Parsed and validated version of a raw ReadValueId from OPC-UA.
10pub struct ParsedReadValueId {
11    /// ID of the node to read from.
12    pub node_id: NodeId,
13    /// Attribute ID to read.
14    pub attribute_id: AttributeId,
15    /// Index range to read.
16    pub index_range: NumericRange,
17    /// Requested data encoding.
18    pub data_encoding: DataEncoding,
19}
20
21impl ParsedReadValueId {
22    /// Try to parse from a `ReadValueId`.
23    pub fn parse(val: ReadValueId) -> Result<Self, StatusCode> {
24        let attribute_id = AttributeId::from_u32(val.attribute_id)
25            .map_err(|_| StatusCode::BadAttributeIdInvalid)?;
26
27        Ok(Self {
28            node_id: val.node_id,
29            attribute_id,
30            index_range: val.index_range,
31            data_encoding: DataEncoding::from_browse_name(val.data_encoding)?,
32        })
33    }
34
35    /// Create a "null" `ParsedReadValueId`, with no node ID.
36    pub fn null() -> Self {
37        Self {
38            node_id: NodeId::null(),
39            attribute_id: AttributeId::NodeId,
40            index_range: NumericRange::None,
41            data_encoding: DataEncoding::Binary,
42        }
43    }
44
45    /// Check whether this `ParsedReadValueId` is null.
46    pub fn is_null(&self) -> bool {
47        self.node_id.is_null()
48    }
49}
50
51impl Default for ParsedReadValueId {
52    fn default() -> Self {
53        Self::null()
54    }
55}
56
57#[derive(Debug)]
58/// Container for a single item in a `Read` service call.
59pub struct ReadNode {
60    node: ParsedReadValueId,
61    pub(crate) result: DataValue,
62    diagnostic_bits: DiagnosticBits,
63
64    diagnostic_info: Option<DiagnosticInfo>,
65}
66
67impl ReadNode {
68    /// Create a `ReadNode` from a `ReadValueId`.
69    pub(crate) fn new(node: ReadValueId, diagnostic_bits: DiagnosticBits) -> Self {
70        let mut status = StatusCode::BadNodeIdUnknown;
71
72        let node = match ParsedReadValueId::parse(node) {
73            Ok(r) => r,
74            Err(e) => {
75                status = e;
76                ParsedReadValueId::null()
77            }
78        };
79
80        Self {
81            node,
82            result: DataValue {
83                status: Some(status),
84                server_timestamp: Some(DateTime::now()),
85                ..Default::default()
86            },
87            diagnostic_bits,
88            diagnostic_info: None,
89        }
90    }
91
92    /// Get the current result status code.
93    pub fn status(&self) -> StatusCode {
94        self.result.status()
95    }
96
97    /// Get the node/attribute pair to read.
98    pub fn node(&self) -> &ParsedReadValueId {
99        &self.node
100    }
101
102    /// Set the result of this read operation.
103    pub fn set_result(&mut self, result: DataValue) {
104        self.result = result;
105    }
106
107    /// Set the result of this read operation to an error with no value or
108    /// timestamp. Use this not if the value is an error, but if the read
109    /// failed.
110    pub fn set_error(&mut self, status: StatusCode) {
111        self.result = DataValue {
112            status: Some(status),
113            server_timestamp: Some(DateTime::now()),
114            ..Default::default()
115        }
116    }
117
118    /// Header diagnostic bits for requesting operation-level diagnostics.
119    pub fn diagnostic_bits(&self) -> DiagnosticBits {
120        self.diagnostic_bits
121    }
122
123    /// Set diagnostic infos, you don't need to do this if
124    /// `diagnostic_bits` are not set.
125    pub fn set_diagnostic_info(&mut self, diagnostic_info: DiagnosticInfo) {
126        self.diagnostic_info = Some(diagnostic_info);
127    }
128}
129
130impl IntoResult for ReadNode {
131    type Result = DataValue;
132
133    fn into_result(self) -> (Self::Result, Option<DiagnosticInfo>) {
134        (self.result, self.diagnostic_info)
135    }
136}
137
138#[derive(Debug, Clone)]
139/// Parsed and validated version of the raw OPC-UA `WriteValue`.
140pub struct ParsedWriteValue {
141    /// ID of node to write to.
142    pub node_id: NodeId,
143    /// Attribute to write.
144    pub attribute_id: AttributeId,
145    /// Index range of value to write.
146    pub index_range: NumericRange,
147    /// Value to write.
148    pub value: DataValue,
149}
150
151impl ParsedWriteValue {
152    /// Try to parse from a `WriteValue`.
153    pub fn parse(val: WriteValue) -> Result<Self, StatusCode> {
154        let attribute_id = AttributeId::from_u32(val.attribute_id)
155            .map_err(|_| StatusCode::BadAttributeIdInvalid)?;
156
157        Ok(Self {
158            node_id: val.node_id,
159            attribute_id,
160            index_range: val.index_range,
161            value: val.value,
162        })
163    }
164
165    /// Create a "null" `ParsedWriteValue`.
166    pub fn null() -> Self {
167        Self {
168            node_id: NodeId::null(),
169            attribute_id: AttributeId::NodeId,
170            index_range: NumericRange::None,
171            value: DataValue::null(),
172        }
173    }
174
175    /// Check if this `ParsedWriteValue` is null.
176    pub fn is_null(&self) -> bool {
177        self.node_id.is_null()
178    }
179}
180
181impl Default for ParsedWriteValue {
182    fn default() -> Self {
183        Self::null()
184    }
185}
186
187/// Container for a single item in a `Write` service call.
188#[derive(Debug)]
189pub struct WriteNode {
190    value: ParsedWriteValue,
191    diagnostic_bits: DiagnosticBits,
192
193    status: StatusCode,
194    diagnostic_info: Option<DiagnosticInfo>,
195}
196
197impl WriteNode {
198    /// Create a `WriteNode` from a raw OPC-UA `WriteValue`.
199    pub(crate) fn new(value: WriteValue, diagnostic_bits: DiagnosticBits) -> Self {
200        let mut status = StatusCode::BadNodeIdUnknown;
201
202        let value = match ParsedWriteValue::parse(value) {
203            Ok(r) => r,
204            Err(e) => {
205                status = e;
206                ParsedWriteValue::null()
207            }
208        };
209
210        Self {
211            value,
212            status,
213            diagnostic_bits,
214            diagnostic_info: None,
215        }
216    }
217
218    /// Get the current status.
219    pub fn status(&self) -> StatusCode {
220        self.status
221    }
222
223    /// Set the status code result of this operation.
224    pub fn set_status(&mut self, status: StatusCode) {
225        self.status = status;
226    }
227
228    /// Get the value to write.
229    pub fn value(&self) -> &ParsedWriteValue {
230        &self.value
231    }
232
233    /// Header diagnostic bits for requesting operation-level diagnostics.
234    pub fn diagnostic_bits(&self) -> DiagnosticBits {
235        self.diagnostic_bits
236    }
237
238    /// Set diagnostic infos, you don't need to do this if
239    /// `diagnostic_bits` are not set.
240    pub fn set_diagnostic_info(&mut self, diagnostic_info: DiagnosticInfo) {
241        self.diagnostic_info = Some(diagnostic_info);
242    }
243}
244
245impl IntoResult for WriteNode {
246    type Result = StatusCode;
247
248    fn into_result(self) -> (Self::Result, Option<DiagnosticInfo>) {
249        (self.status(), self.diagnostic_info)
250    }
251}