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