Skip to main content

opcua_client/session/services/
view.rs

1use std::time::Duration;
2
3use crate::{
4    session::{
5        process_service_result, process_unexpected_response,
6        request_builder::{builder_base, builder_debug, builder_error, RequestHeaderBuilder},
7    },
8    Session, UARequest,
9};
10use opcua_core::ResponseMessage;
11use opcua_types::{
12    BrowseDescription, BrowseNextRequest, BrowseNextResponse, BrowsePath, BrowsePathResult,
13    BrowseRequest, BrowseResponse, BrowseResult, ByteString, IntegerId, NodeId,
14    RegisterNodesRequest, RegisterNodesResponse, StatusCode, TranslateBrowsePathsToNodeIdsRequest,
15    TranslateBrowsePathsToNodeIdsResponse, UnregisterNodesRequest, UnregisterNodesResponse,
16    ViewDescription,
17};
18
19#[derive(Debug, Clone)]
20/// Discover the references to the specified nodes by sending a [`BrowseRequest`] to the server.
21///
22/// See OPC UA Part 4 - Services 5.8.2 for complete description of the service and error responses.
23pub struct Browse {
24    nodes_to_browse: Vec<BrowseDescription>,
25    view: ViewDescription,
26    max_references_per_node: u32,
27
28    header: RequestHeaderBuilder,
29}
30
31builder_base!(Browse);
32
33impl Browse {
34    /// Construct a new call to the `Browse` service.
35    pub fn new(session: &Session) -> Self {
36        Self {
37            nodes_to_browse: Vec::new(),
38            view: ViewDescription::default(),
39            max_references_per_node: 0,
40
41            header: RequestHeaderBuilder::new_from_session(session),
42        }
43    }
44
45    /// Construct a new call to the `Browse` service, setting header parameters manually.
46    pub fn new_manual(
47        session_id: u32,
48        timeout: Duration,
49        auth_token: NodeId,
50        request_handle: IntegerId,
51    ) -> Self {
52        Self {
53            nodes_to_browse: Vec::new(),
54            view: ViewDescription::default(),
55            max_references_per_node: 0,
56
57            header: RequestHeaderBuilder::new(session_id, timeout, auth_token, request_handle),
58        }
59    }
60
61    /// Set the view to browse.
62    pub fn view(mut self, view: ViewDescription) -> Self {
63        self.view = view;
64        self
65    }
66
67    /// Set max references per node. The default is zero, meaning server-defined.
68    pub fn max_references_per_node(mut self, max_references_per_node: u32) -> Self {
69        self.max_references_per_node = max_references_per_node;
70        self
71    }
72
73    /// Set nodes to browse, overwriting any that were set previously.
74    pub fn nodes_to_browse(mut self, nodes_to_browse: Vec<BrowseDescription>) -> Self {
75        self.nodes_to_browse = nodes_to_browse;
76        self
77    }
78
79    /// Add a node to browse.
80    pub fn browse_node(mut self, node_to_browse: impl Into<BrowseDescription>) -> Self {
81        self.nodes_to_browse.push(node_to_browse.into());
82        self
83    }
84}
85
86impl UARequest for Browse {
87    type Out = BrowseResponse;
88
89    async fn send<'a>(self, channel: &'a crate::AsyncSecureChannel) -> Result<Self::Out, StatusCode>
90    where
91        Self: 'a,
92    {
93        if self.nodes_to_browse.is_empty() {
94            builder_error!(self, "browse was not supplied with any nodes to browse");
95            return Err(StatusCode::BadNothingToDo);
96        }
97        let request = BrowseRequest {
98            request_header: self.header.header,
99            view: self.view,
100            requested_max_references_per_node: self.max_references_per_node,
101            nodes_to_browse: Some(self.nodes_to_browse),
102        };
103        let response = channel.send(request, self.header.timeout).await?;
104        if let ResponseMessage::Browse(response) = response {
105            builder_debug!(self, "browse, success");
106            process_service_result(&response.response_header)?;
107            Ok(*response)
108        } else {
109            builder_error!(self, "browse failed");
110            Err(process_unexpected_response(response))
111        }
112    }
113}
114
115#[derive(Debug, Clone)]
116/// Continue to discover references to nodes by sending continuation points in a [`BrowseNextRequest`]
117/// to the server. This function may have to be called repeatedly to process the initial query.
118///
119/// See OPC UA Part 4 - Services 5.8.3 for complete description of the service and error responses.
120pub struct BrowseNext {
121    continuation_points: Vec<ByteString>,
122    release_continuation_points: bool,
123
124    header: RequestHeaderBuilder,
125}
126
127builder_base!(BrowseNext);
128
129impl BrowseNext {
130    /// Construct a new call to the `BrowseNext` service.
131    pub fn new(session: &Session) -> Self {
132        Self {
133            continuation_points: Vec::new(),
134            release_continuation_points: false,
135
136            header: RequestHeaderBuilder::new_from_session(session),
137        }
138    }
139
140    /// Construct a new call to the `BrowseNext` service, setting header parameters manually.
141    pub fn new_manual(
142        session_id: u32,
143        timeout: Duration,
144        auth_token: NodeId,
145        request_handle: IntegerId,
146    ) -> Self {
147        Self {
148            continuation_points: Vec::new(),
149            release_continuation_points: false,
150
151            header: RequestHeaderBuilder::new(session_id, timeout, auth_token, request_handle),
152        }
153    }
154
155    /// Set release continuation points. Default is false, if this is true,
156    /// continuation points will be released and no results will be returned.
157    pub fn release_continuation_points(mut self, release_continuation_points: bool) -> Self {
158        self.release_continuation_points = release_continuation_points;
159        self
160    }
161
162    /// Set continuation points, overwriting any that were set previously.
163    pub fn continuation_points(mut self, continuation_points: Vec<ByteString>) -> Self {
164        self.continuation_points = continuation_points;
165        self
166    }
167
168    /// Add a continuation point to the request.
169    pub fn continuation_point(mut self, continuation_point: ByteString) -> Self {
170        self.continuation_points.push(continuation_point);
171        self
172    }
173}
174
175impl UARequest for BrowseNext {
176    type Out = BrowseNextResponse;
177
178    async fn send<'a>(self, channel: &'a crate::AsyncSecureChannel) -> Result<Self::Out, StatusCode>
179    where
180        Self: 'a,
181    {
182        if self.continuation_points.is_empty() {
183            builder_error!(
184                self,
185                "browse_next was not supplied with any continuation points"
186            );
187            return Err(StatusCode::BadNothingToDo);
188        }
189        let request = BrowseNextRequest {
190            request_header: self.header.header,
191            continuation_points: Some(self.continuation_points),
192            release_continuation_points: self.release_continuation_points,
193        };
194        let response = channel.send(request, self.header.timeout).await?;
195        if let ResponseMessage::BrowseNext(response) = response {
196            builder_debug!(self, "browse_next, success");
197            process_service_result(&response.response_header)?;
198            Ok(*response)
199        } else {
200            builder_error!(self, "browse_next failed");
201            Err(process_unexpected_response(response))
202        }
203    }
204}
205
206#[derive(Debug, Clone)]
207/// Translate browse paths to NodeIds by sending a [`TranslateBrowsePathsToNodeIdsRequest`] request to the Server
208/// Each [`BrowsePath`] is constructed of a starting node and a `RelativePath`. The specified starting node
209/// identifies the node from which the RelativePath is based. The RelativePath contains a sequence of
210/// ReferenceTypes and BrowseNames.
211///
212/// See OPC UA Part 4 - Services 5.8.4 for complete description of the service and error responses.
213pub struct TranslateBrowsePaths {
214    browse_paths: Vec<BrowsePath>,
215
216    header: RequestHeaderBuilder,
217}
218
219builder_base!(TranslateBrowsePaths);
220
221impl TranslateBrowsePaths {
222    /// Construct a new call to the `TranslateBrowsePaths` service.
223    pub fn new(session: &Session) -> Self {
224        Self {
225            browse_paths: Vec::new(),
226
227            header: RequestHeaderBuilder::new_from_session(session),
228        }
229    }
230
231    /// Construct a new call to the `TranslateBrowsePaths` service, setting header parameters manually.
232    pub fn new_manual(
233        session_id: u32,
234        timeout: Duration,
235        auth_token: NodeId,
236        request_handle: IntegerId,
237    ) -> Self {
238        Self {
239            browse_paths: Vec::new(),
240
241            header: RequestHeaderBuilder::new(session_id, timeout, auth_token, request_handle),
242        }
243    }
244
245    /// Set browse paths, overwriting any that were set previously.
246    pub fn browse_paths(mut self, browse_paths: Vec<BrowsePath>) -> Self {
247        self.browse_paths = browse_paths;
248        self
249    }
250
251    /// Add a browse path to the request.
252    pub fn browse_path(mut self, browse_path: BrowsePath) -> Self {
253        self.browse_paths.push(browse_path);
254        self
255    }
256}
257
258impl UARequest for TranslateBrowsePaths {
259    type Out = TranslateBrowsePathsToNodeIdsResponse;
260
261    async fn send<'a>(self, channel: &'a crate::AsyncSecureChannel) -> Result<Self::Out, StatusCode>
262    where
263        Self: 'a,
264    {
265        if self.browse_paths.is_empty() {
266            builder_error!(
267                self,
268                "translate_browse_paths_to_node_ids was not supplied with any browse paths"
269            );
270            return Err(StatusCode::BadNothingToDo);
271        }
272        let request = TranslateBrowsePathsToNodeIdsRequest {
273            request_header: self.header.header,
274            browse_paths: Some(self.browse_paths),
275        };
276        let response = channel.send(request, self.header.timeout).await?;
277        if let ResponseMessage::TranslateBrowsePathsToNodeIds(response) = response {
278            builder_debug!(self, "translate_browse_paths_to_node_ids, success");
279            process_service_result(&response.response_header)?;
280            Ok(*response)
281        } else {
282            builder_error!(self, "translate_browse_paths_to_node_ids failed");
283            Err(process_unexpected_response(response))
284        }
285    }
286}
287
288#[derive(Debug, Clone)]
289/// Register nodes on the server by sending a [`RegisterNodesRequest`]. The purpose of this
290/// call is server-dependent but allows a client to ask a server to create nodes which are
291/// otherwise expensive to set up or maintain, e.g. nodes attached to hardware.
292///
293/// See OPC UA Part 4 - Services 5.8.5 for complete description of the service and error responses.
294pub struct RegisterNodes {
295    nodes_to_register: Vec<NodeId>,
296
297    header: RequestHeaderBuilder,
298}
299
300builder_base!(RegisterNodes);
301
302impl RegisterNodes {
303    /// Construct a new call to the `RegisterNodes` service.
304    pub fn new(session: &Session) -> Self {
305        Self {
306            nodes_to_register: Vec::new(),
307
308            header: RequestHeaderBuilder::new_from_session(session),
309        }
310    }
311
312    /// Construct a new call to the `RegisterNodes` service, setting header parameters manually.
313    pub fn new_manual(
314        session_id: u32,
315        timeout: Duration,
316        auth_token: NodeId,
317        request_handle: IntegerId,
318    ) -> Self {
319        Self {
320            nodes_to_register: Vec::new(),
321
322            header: RequestHeaderBuilder::new(session_id, timeout, auth_token, request_handle),
323        }
324    }
325
326    /// Set nodes to register, overwriting any that were set previously.
327    pub fn nodes_to_register(mut self, nodes_to_register: Vec<NodeId>) -> Self {
328        self.nodes_to_register = nodes_to_register;
329        self
330    }
331
332    /// Add a node to the request.
333    pub fn node_to_register(mut self, node_to_register: impl Into<NodeId>) -> Self {
334        self.nodes_to_register.push(node_to_register.into());
335        self
336    }
337}
338
339impl UARequest for RegisterNodes {
340    type Out = RegisterNodesResponse;
341
342    async fn send<'a>(self, channel: &'a crate::AsyncSecureChannel) -> Result<Self::Out, StatusCode>
343    where
344        Self: 'a,
345    {
346        if self.nodes_to_register.is_empty() {
347            builder_error!(self, "register_nodes was not supplied with any node IDs");
348            return Err(StatusCode::BadNothingToDo);
349        }
350        let request = RegisterNodesRequest {
351            request_header: self.header.header,
352            nodes_to_register: Some(self.nodes_to_register),
353        };
354        let response = channel.send(request, self.header.timeout).await?;
355        if let ResponseMessage::RegisterNodes(response) = response {
356            builder_debug!(self, "register_nodes, success");
357            process_service_result(&response.response_header)?;
358            Ok(*response)
359        } else {
360            builder_error!(self, "register_nodes failed");
361            Err(process_unexpected_response(response))
362        }
363    }
364}
365
366#[derive(Debug, Clone)]
367/// Unregister nodes on the server by sending a [`UnregisterNodesRequest`]. This indicates to
368/// the server that the client relinquishes any need for these nodes. The server will ignore
369/// unregistered nodes.
370///
371/// See OPC UA Part 4 - Services 5.8.5 for complete description of the service and error responses.
372pub struct UnregisterNodes {
373    nodes_to_unregister: Vec<NodeId>,
374
375    header: RequestHeaderBuilder,
376}
377
378builder_base!(UnregisterNodes);
379
380impl UnregisterNodes {
381    /// Construct a new call to the `UnregisterNodes` service.
382    pub fn new(session: &Session) -> Self {
383        Self {
384            nodes_to_unregister: Vec::new(),
385
386            header: RequestHeaderBuilder::new_from_session(session),
387        }
388    }
389
390    /// Construct a new call to the `UnregisterNodes` service, setting header parameters manually.
391    pub fn new_manual(
392        session_id: u32,
393        timeout: Duration,
394        auth_token: NodeId,
395        request_handle: IntegerId,
396    ) -> Self {
397        Self {
398            nodes_to_unregister: Vec::new(),
399
400            header: RequestHeaderBuilder::new(session_id, timeout, auth_token, request_handle),
401        }
402    }
403
404    /// Set nodes to register, overwriting any that were set previously.
405    pub fn nodes_to_unregister(mut self, nodes_to_unregister: Vec<NodeId>) -> Self {
406        self.nodes_to_unregister = nodes_to_unregister;
407        self
408    }
409
410    /// Add a continuation point to the request.
411    pub fn node_to_unregister(mut self, node_to_unregister: impl Into<NodeId>) -> Self {
412        self.nodes_to_unregister.push(node_to_unregister.into());
413        self
414    }
415}
416
417impl UARequest for UnregisterNodes {
418    type Out = UnregisterNodesResponse;
419
420    async fn send<'a>(self, channel: &'a crate::AsyncSecureChannel) -> Result<Self::Out, StatusCode>
421    where
422        Self: 'a,
423    {
424        if self.nodes_to_unregister.is_empty() {
425            builder_error!(self, "unregister_nodes was not supplied with any node IDs");
426            return Err(StatusCode::BadNothingToDo);
427        }
428        let request = UnregisterNodesRequest {
429            request_header: self.header.header,
430            nodes_to_unregister: Some(self.nodes_to_unregister),
431        };
432        let response = channel.send(request, self.header.timeout).await?;
433        if let ResponseMessage::UnregisterNodes(response) = response {
434            builder_debug!(self, "unregister_nodes, success");
435            process_service_result(&response.response_header)?;
436            Ok(*response)
437        } else {
438            builder_error!(self, "unregister_nodes failed");
439            Err(process_unexpected_response(response))
440        }
441    }
442}
443
444impl Session {
445    /// Discover the references to the specified nodes by sending a [`BrowseRequest`] to the server.
446    ///
447    /// See OPC UA Part 4 - Services 5.8.2 for complete description of the service and error responses.
448    ///
449    /// # Arguments
450    ///
451    /// * `nodes_to_browse` - A list of [`BrowseDescription`] describing nodes to browse.
452    ///
453    /// # Returns
454    ///
455    /// * `Ok(Vec<BrowseResult>)` - A list [`BrowseResult`] corresponding to each node to browse. A browse result
456    ///   may contain a continuation point, for use with `browse_next()`.
457    /// * `Err(StatusCode)` - Request failed, [Status code](StatusCode) is the reason for failure.
458    ///
459    pub async fn browse(
460        &self,
461        nodes_to_browse: &[BrowseDescription],
462        max_references_per_node: u32,
463        view: Option<ViewDescription>,
464    ) -> Result<Vec<BrowseResult>, StatusCode> {
465        Ok(Browse::new(self)
466            .nodes_to_browse(nodes_to_browse.to_vec())
467            .view(view.unwrap_or_default())
468            .max_references_per_node(max_references_per_node)
469            .send(&self.channel)
470            .await?
471            .results
472            .unwrap_or_default())
473    }
474
475    /// Continue to discover references to nodes by sending continuation points in a [`BrowseNextRequest`]
476    /// to the server. This function may have to be called repeatedly to process the initial query.
477    ///
478    /// See OPC UA Part 4 - Services 5.8.3 for complete description of the service and error responses.
479    ///
480    /// # Arguments
481    ///
482    /// * `release_continuation_points` - Flag indicating if the continuation points should be released by the server
483    /// * `continuation_points` - A list of [`BrowseDescription`] continuation points
484    ///
485    /// # Returns
486    ///
487    /// * `Ok(Option<Vec<BrowseResult>)` - A list [`BrowseResult`] corresponding to each node to browse. A browse result
488    ///   may contain a continuation point, for use with `browse_next()`.
489    /// * `Err(StatusCode)` - Request failed, [Status code](StatusCode) is the reason for failure.
490    ///
491    pub async fn browse_next(
492        &self,
493        release_continuation_points: bool,
494        continuation_points: &[ByteString],
495    ) -> Result<Vec<BrowseResult>, StatusCode> {
496        Ok(BrowseNext::new(self)
497            .continuation_points(continuation_points.to_vec())
498            .release_continuation_points(release_continuation_points)
499            .send(&self.channel)
500            .await?
501            .results
502            .unwrap_or_default())
503    }
504
505    /// Translate browse paths to NodeIds by sending a [`TranslateBrowsePathsToNodeIdsRequest`] request to the Server
506    /// Each [`BrowsePath`] is constructed of a starting node and a `RelativePath`. The specified starting node
507    /// identifies the node from which the RelativePath is based. The RelativePath contains a sequence of
508    /// ReferenceTypes and BrowseNames.
509    ///
510    /// See OPC UA Part 4 - Services 5.8.4 for complete description of the service and error responses.
511    ///
512    /// # Arguments
513    ///
514    /// * `browse_paths` - A list of [`BrowsePath`] node + relative path for the server to look up
515    ///
516    /// # Returns
517    ///
518    /// * `Ok(Vec<BrowsePathResult>>)` - List of [`BrowsePathResult`] for the list of browse
519    ///   paths. The size and order of the list matches the size and order of the `browse_paths`
520    ///   parameter.
521    /// * `Err(StatusCode)` - Request failed, [Status code](StatusCode) is the reason for failure.
522    ///
523    pub async fn translate_browse_paths_to_node_ids(
524        &self,
525        browse_paths: &[BrowsePath],
526    ) -> Result<Vec<BrowsePathResult>, StatusCode> {
527        Ok(TranslateBrowsePaths::new(self)
528            .browse_paths(browse_paths.to_vec())
529            .send(&self.channel)
530            .await?
531            .results
532            .unwrap_or_default())
533    }
534
535    /// Register nodes on the server by sending a [`RegisterNodesRequest`]. The purpose of this
536    /// call is server-dependent but allows a client to ask a server to create nodes which are
537    /// otherwise expensive to set up or maintain, e.g. nodes attached to hardware.
538    ///
539    /// See OPC UA Part 4 - Services 5.8.5 for complete description of the service and error responses.
540    ///
541    /// # Arguments
542    ///
543    /// * `nodes_to_register` - A list of [`NodeId`] nodes for the server to register
544    ///
545    /// # Returns
546    ///
547    /// * `Ok(Vec<NodeId>)` - A list of [`NodeId`] corresponding to size and order of the input. The
548    ///   server may return an alias for the input `NodeId`
549    /// * `Err(StatusCode)` - Request failed, [Status code](StatusCode) is the reason for failure.
550    ///
551    pub async fn register_nodes(
552        &self,
553        nodes_to_register: &[NodeId],
554    ) -> Result<Vec<NodeId>, StatusCode> {
555        Ok(RegisterNodes::new(self)
556            .nodes_to_register(nodes_to_register.to_vec())
557            .send(&self.channel)
558            .await?
559            .registered_node_ids
560            .unwrap_or_default())
561    }
562
563    /// Unregister nodes on the server by sending a [`UnregisterNodesRequest`]. This indicates to
564    /// the server that the client relinquishes any need for these nodes. The server will ignore
565    /// unregistered nodes.
566    ///
567    /// See OPC UA Part 4 - Services 5.8.5 for complete description of the service and error responses.
568    ///
569    /// # Arguments
570    ///
571    /// * `nodes_to_unregister` - A list of [`NodeId`] nodes for the server to unregister
572    ///
573    /// # Returns
574    ///
575    /// * `Ok(())` - Request succeeded, server ignores invalid nodes
576    /// * `Err(StatusCode)` - Request failed, [Status code](StatusCode) is the reason for failure.
577    ///
578    pub async fn unregister_nodes(&self, nodes_to_unregister: &[NodeId]) -> Result<(), StatusCode> {
579        UnregisterNodes::new(self)
580            .nodes_to_unregister(nodes_to_unregister.to_vec())
581            .send(&self.channel)
582            .await?;
583        Ok(())
584    }
585}