Skip to main content

opcua_server/node_manager/
query.rs

1use crate::session::{
2    continuation_points::{ContinuationPoint, EmptyContinuationPoint},
3    instance::Session,
4};
5use opcua_crypto::random;
6use opcua_nodes::ParsedContentFilter;
7use opcua_types::{
8    AttributeId, ByteString, ExpandedNodeId, NodeTypeDescription, NumericRange, ParsingResult,
9    QueryDataDescription, QueryDataSet, RelativePath, StatusCode,
10};
11
12pub(crate) struct QueryContinuationPoint {
13    pub node_manager_index: usize,
14    pub continuation_point: ContinuationPoint,
15    pub id: ByteString,
16
17    node_types: Vec<ParsedNodeTypeDescription>,
18    filter: ParsedContentFilter,
19    max_data_sets_to_return: usize,
20    max_references_to_return: usize,
21}
22
23#[derive(Debug)]
24/// Parsed and validated version of the OPC-UA `QueryDataDescription`.
25pub struct ParsedQueryDataDescription {
26    /// The relative path to the node being referenced.
27    pub relative_path: RelativePath,
28    /// Attribute ID of the attribute being referenced.
29    pub attribute_id: AttributeId,
30    /// Index range for the read.
31    pub index_range: NumericRange,
32}
33
34impl ParsedQueryDataDescription {
35    pub(crate) fn parse(desc: QueryDataDescription) -> Result<Self, StatusCode> {
36        let attribute_id = AttributeId::from_u32(desc.attribute_id)
37            .map_err(|_| StatusCode::BadAttributeIdInvalid)?;
38
39        Ok(Self {
40            relative_path: desc.relative_path,
41            attribute_id,
42            index_range: desc.index_range,
43        })
44    }
45}
46
47#[derive(Debug)]
48/// Parsed and validated version of the OPC-UA `NodeTypeDescription`.
49pub struct ParsedNodeTypeDescription {
50    /// Type definition to query.
51    pub type_definition_node: ExpandedNodeId,
52    /// Whether to include sub types of the type definition.
53    pub include_sub_types: bool,
54    /// List of values to return.
55    pub data_to_return: Vec<ParsedQueryDataDescription>,
56}
57
58impl ParsedNodeTypeDescription {
59    pub(crate) fn parse(desc: NodeTypeDescription) -> (ParsingResult, Result<Self, StatusCode>) {
60        let num_descs = desc
61            .data_to_return
62            .as_ref()
63            .map(|d| d.len())
64            .unwrap_or_default();
65        let mut desc_results = Vec::with_capacity(num_descs);
66        let mut final_descs = Vec::with_capacity(num_descs);
67        for child in desc.data_to_return.into_iter().flatten() {
68            match ParsedQueryDataDescription::parse(child) {
69                Ok(c) => {
70                    desc_results.push(StatusCode::Good);
71                    final_descs.push(c);
72                }
73                Err(e) => desc_results.push(e),
74            }
75        }
76
77        if final_descs.len() < num_descs {
78            return (
79                ParsingResult {
80                    status_code: StatusCode::BadInvalidArgument,
81                    data_status_codes: Some(desc_results),
82                    data_diagnostic_infos: None,
83                },
84                Err(StatusCode::BadInvalidArgument),
85            );
86        }
87
88        (
89            ParsingResult {
90                status_code: StatusCode::Good,
91                data_diagnostic_infos: None,
92                data_status_codes: None,
93            },
94            Ok(ParsedNodeTypeDescription {
95                type_definition_node: desc.type_definition_node,
96                include_sub_types: desc.include_sub_types,
97                data_to_return: final_descs,
98            }),
99        )
100    }
101}
102
103/// Container for a `Query` service call.
104pub struct QueryRequest {
105    node_types: Vec<ParsedNodeTypeDescription>,
106    filter: ParsedContentFilter,
107    max_data_sets_to_return: usize,
108    max_references_to_return: usize,
109    continuation_point: Option<ContinuationPoint>,
110    next_continuation_point: Option<ContinuationPoint>,
111    status: StatusCode,
112    node_manager_index: usize,
113
114    data_sets: Vec<QueryDataSet>,
115}
116
117impl QueryRequest {
118    pub(crate) fn new(
119        node_types: Vec<ParsedNodeTypeDescription>,
120        filter: ParsedContentFilter,
121        max_data_sets_to_return: usize,
122        max_references_to_return: usize,
123    ) -> Self {
124        Self {
125            node_types,
126            filter,
127            max_data_sets_to_return,
128            max_references_to_return,
129            continuation_point: None,
130            next_continuation_point: None,
131            data_sets: Vec::new(),
132            status: StatusCode::Good,
133            node_manager_index: 0,
134        }
135    }
136
137    pub(crate) fn from_continuation_point(point: QueryContinuationPoint) -> Self {
138        Self {
139            node_types: point.node_types,
140            filter: point.filter,
141            max_data_sets_to_return: point.max_data_sets_to_return,
142            max_references_to_return: point.max_references_to_return,
143            continuation_point: Some(point.continuation_point),
144            next_continuation_point: None,
145            status: StatusCode::Good,
146            data_sets: Vec::new(),
147            node_manager_index: point.node_manager_index,
148        }
149    }
150
151    /// Data sets to query.
152    pub fn data_sets(&self) -> &[QueryDataSet] {
153        &self.data_sets
154    }
155
156    /// Continuation point, if present.
157    pub fn continuation_point(&self) -> Option<&ContinuationPoint> {
158        self.continuation_point.as_ref()
159    }
160
161    /// Maximum number of references to return.
162    pub fn max_references_to_return(&self) -> usize {
163        self.max_references_to_return
164    }
165
166    /// Maximum number of data sets to return.
167    pub fn max_data_sets_to_return(&self) -> usize {
168        self.max_data_sets_to_return
169    }
170
171    /// Content filter that the results must match.
172    pub fn filter(&self) -> &ParsedContentFilter {
173        &self.filter
174    }
175
176    /// Node types to query.
177    pub fn node_types(&self) -> &[ParsedNodeTypeDescription] {
178        &self.node_types
179    }
180
181    /// Space for data sets left.
182    pub fn remaining_data_sets(&self) -> usize {
183        if self.data_sets.len() >= self.max_data_sets_to_return {
184            0
185        } else {
186            self.max_data_sets_to_return - self.data_sets.len()
187        }
188    }
189
190    /// Whether this query is completed.
191    pub fn is_completed(&self) -> bool {
192        self.remaining_data_sets() == 0 || self.next_continuation_point.is_some()
193    }
194
195    pub(crate) fn into_result(
196        self,
197        node_manager_index: usize,
198        node_manager_count: usize,
199        session: &mut Session,
200    ) -> (Vec<QueryDataSet>, ByteString, StatusCode) {
201        // If the status is bad, assume the results are suspect and return nothing.
202        if self.status.is_bad() {
203            return (Vec::new(), ByteString::null(), self.status);
204        }
205        // There may be a continuation point defined for the current node manager,
206        // in that case return that. There is also a corner case here where
207        // remaining == 0 and there is no continuation point.
208        // In this case we need to pass an empty continuation point
209        // to the next node manager.
210        let inner = self
211            .next_continuation_point
212            .map(|c| (c, node_manager_index))
213            .or_else(|| {
214                if node_manager_index < node_manager_count - 1 {
215                    Some((
216                        ContinuationPoint::new(Box::new(EmptyContinuationPoint)),
217                        node_manager_index + 1,
218                    ))
219                } else {
220                    None
221                }
222            });
223
224        let continuation_point = inner.map(|(p, node_manager_index)| QueryContinuationPoint {
225            node_manager_index,
226            continuation_point: p,
227            id: random::byte_string(6),
228            node_types: self.node_types,
229            filter: self.filter,
230            max_data_sets_to_return: self.max_data_sets_to_return,
231            max_references_to_return: self.max_references_to_return,
232        });
233
234        let mut status = self.status;
235        let mut cp_id = continuation_point
236            .as_ref()
237            .map(|c| c.id.clone())
238            .unwrap_or_default();
239
240        // If we're out of continuation points, the correct response is to not store it, and
241        // set the status code to BadNoContinuationPoints.
242        if let Some(c) = continuation_point {
243            if session.add_query_continuation_point(&cp_id, c).is_err() {
244                status = StatusCode::BadNoContinuationPoints;
245                cp_id = ByteString::null();
246            }
247        }
248
249        (self.data_sets, cp_id, status)
250    }
251
252    /// Current result status code.
253    pub fn status(&self) -> StatusCode {
254        self.status
255    }
256
257    /// Set the general result of this query.
258    pub fn set_status(&mut self, status: StatusCode) {
259        self.status = status;
260    }
261
262    /// Set the next continuation point for this query.
263    pub fn set_next_continuation_point(
264        &mut self,
265        next_continuation_point: Option<ContinuationPoint>,
266    ) {
267        self.next_continuation_point = next_continuation_point;
268    }
269
270    pub(crate) fn node_manager_index(&self) -> usize {
271        self.node_manager_index
272    }
273}