Skip to main content

opcua_client/session/services/
node_management.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};
10
11use opcua_core::ResponseMessage;
12use opcua_types::{
13    AddNodesItem, AddNodesRequest, AddNodesResponse, AddNodesResult, AddReferencesItem,
14    AddReferencesRequest, AddReferencesResponse, DeleteNodesItem, DeleteNodesRequest,
15    DeleteNodesResponse, DeleteReferencesItem, DeleteReferencesRequest, DeleteReferencesResponse,
16    Error, IntegerId, NodeId, StatusCode,
17};
18use tracing::{debug_span, Instrument};
19
20#[derive(Debug, Clone)]
21/// Add nodes by sending a [`AddNodesRequest`] to the server.
22///
23/// See OPC UA Part 4 - Services 5.7.2 for complete description of the service and error responses.
24pub struct AddNodes {
25    nodes_to_add: Vec<AddNodesItem>,
26
27    header: RequestHeaderBuilder,
28}
29
30builder_base!(AddNodes);
31
32impl AddNodes {
33    /// Construct a new call to the `AddNodes` service.
34    pub fn new(session: &Session) -> Self {
35        Self {
36            nodes_to_add: Vec::new(),
37            header: RequestHeaderBuilder::new_from_session(session),
38        }
39    }
40
41    /// Construct a new call to the `AddNodes` service, setting header parameters manually.
42    pub fn new_manual(
43        session_id: u32,
44        timeout: Duration,
45        auth_token: NodeId,
46        request_handle: IntegerId,
47    ) -> Self {
48        Self {
49            nodes_to_add: Vec::new(),
50            header: RequestHeaderBuilder::new(session_id, timeout, auth_token, request_handle),
51        }
52    }
53
54    /// Set nodes to add, overwriting any that were set previously.
55    pub fn nodes_to_add(mut self, nodes_to_add: Vec<AddNodesItem>) -> Self {
56        self.nodes_to_add = nodes_to_add;
57        self
58    }
59
60    /// Add a node to create.
61    pub fn node(mut self, node: impl Into<AddNodesItem>) -> Self {
62        self.nodes_to_add.push(node.into());
63        self
64    }
65}
66
67impl UARequest for AddNodes {
68    type Out = AddNodesResponse;
69
70    async fn send<'a>(self, channel: &'a crate::AsyncSecureChannel) -> Result<Self::Out, Error>
71    where
72        Self: 'a,
73    {
74        let span = debug_span!(
75            "Sending AddNodes request",
76            num_nodes_to_add = self.nodes_to_add.len()
77        );
78        let request = {
79            let _h = span.enter();
80            if self.nodes_to_add.is_empty() {
81                builder_error!(self, "add_nodes, called with no nodes to add");
82                return Err(Error::new(
83                    StatusCode::BadNothingToDo,
84                    "add_nodes called with no nodes to add",
85                ));
86            }
87            AddNodesRequest {
88                request_header: self.header.header,
89                nodes_to_add: Some(self.nodes_to_add),
90            }
91        };
92
93        let response = channel
94            .send(request, self.header.timeout)
95            .instrument(span.clone())
96            .await?;
97        let _h = span.enter();
98        if let ResponseMessage::AddNodes(response) = response {
99            builder_debug!(self, "add_nodes, success");
100            process_service_result(&response.response_header)?;
101            Ok(*response)
102        } else {
103            builder_error!(self, "add_nodes failed");
104            Err(process_unexpected_response(response))
105        }
106    }
107}
108
109#[derive(Debug, Clone)]
110/// Add references by sending a [`AddReferencesRequest`] to the server.
111///
112/// See OPC UA Part 4 - Services 5.7.3 for complete description of the service and error responses.
113pub struct AddReferences {
114    references_to_add: Vec<AddReferencesItem>,
115
116    header: RequestHeaderBuilder,
117}
118
119builder_base!(AddReferences);
120
121impl AddReferences {
122    /// Construct a new call to the `AddReferences` service.
123    pub fn new(session: &Session) -> Self {
124        Self {
125            references_to_add: Vec::new(),
126            header: RequestHeaderBuilder::new_from_session(session),
127        }
128    }
129
130    /// Construct a new call to the `AddReferences` service, setting header parameters manually.
131    pub fn new_manual(
132        session_id: u32,
133        timeout: Duration,
134        auth_token: NodeId,
135        request_handle: IntegerId,
136    ) -> Self {
137        Self {
138            references_to_add: Vec::new(),
139            header: RequestHeaderBuilder::new(session_id, timeout, auth_token, request_handle),
140        }
141    }
142
143    /// Set references to add, overwriting any that were set previously.
144    pub fn references_to_add(mut self, references_to_add: Vec<AddReferencesItem>) -> Self {
145        self.references_to_add = references_to_add;
146        self
147    }
148
149    /// Add a reference to create.
150    pub fn reference(mut self, reference: impl Into<AddReferencesItem>) -> Self {
151        self.references_to_add.push(reference.into());
152        self
153    }
154}
155
156impl UARequest for AddReferences {
157    type Out = AddReferencesResponse;
158
159    async fn send<'a>(self, channel: &'a crate::AsyncSecureChannel) -> Result<Self::Out, Error>
160    where
161        Self: 'a,
162    {
163        let span = debug_span!(
164            "Sending AddReferences request",
165            num_references_to_add = self.references_to_add.len()
166        );
167        let request = {
168            let _h = span.enter();
169            if self.references_to_add.is_empty() {
170                builder_error!(self, "add_references, called with no references to add");
171                return Err(Error::new(
172                    StatusCode::BadNothingToDo,
173                    "add_references called with no references to add",
174                ));
175            }
176            AddReferencesRequest {
177                request_header: self.header.header,
178                references_to_add: Some(self.references_to_add),
179            }
180        };
181        let response = channel
182            .send(request, self.header.timeout)
183            .instrument(span.clone())
184            .await?;
185        let _h = span.enter();
186        if let ResponseMessage::AddReferences(response) = response {
187            builder_debug!(self, "add_references, success");
188            process_service_result(&response.response_header)?;
189            Ok(*response)
190        } else {
191            builder_error!(self, "add_references failed");
192            Err(process_unexpected_response(response))
193        }
194    }
195}
196
197#[derive(Debug, Clone)]
198/// Delete nodes by sending a [`DeleteNodesRequest`] to the server.
199///
200/// See OPC UA Part 4 - Services 5.7.4 for complete description of the service and error responses.
201pub struct DeleteNodes {
202    nodes_to_delete: Vec<DeleteNodesItem>,
203
204    header: RequestHeaderBuilder,
205}
206
207builder_base!(DeleteNodes);
208
209impl DeleteNodes {
210    /// Construct a new call to the `DeleteNodes` service.
211    pub fn new(session: &Session) -> Self {
212        Self {
213            nodes_to_delete: Vec::new(),
214            header: RequestHeaderBuilder::new_from_session(session),
215        }
216    }
217
218    /// Construct a new call to the `DeleteNodes` service, setting header parameters manually.
219    pub fn new_manual(
220        session_id: u32,
221        timeout: Duration,
222        auth_token: NodeId,
223        request_handle: IntegerId,
224    ) -> Self {
225        Self {
226            nodes_to_delete: Vec::new(),
227            header: RequestHeaderBuilder::new(session_id, timeout, auth_token, request_handle),
228        }
229    }
230
231    /// Set nodes to delete, overwriting any that were set previously.
232    pub fn nodes_to_delete(mut self, nodes_to_delete: Vec<DeleteNodesItem>) -> Self {
233        self.nodes_to_delete = nodes_to_delete;
234        self
235    }
236
237    /// Add a node to delete.
238    pub fn node(mut self, reference: impl Into<DeleteNodesItem>) -> Self {
239        self.nodes_to_delete.push(reference.into());
240        self
241    }
242}
243
244impl UARequest for DeleteNodes {
245    type Out = DeleteNodesResponse;
246
247    async fn send<'a>(self, channel: &'a crate::AsyncSecureChannel) -> Result<Self::Out, Error>
248    where
249        Self: 'a,
250    {
251        let span = debug_span!(
252            "Sending DeleteNodes request",
253            num_nodes_to_delete = self.nodes_to_delete.len()
254        );
255        let request = {
256            let _h = span.enter();
257            if self.nodes_to_delete.is_empty() {
258                builder_error!(self, "delete_nodes, called with no nodes to delete");
259                return Err(Error::new(
260                    StatusCode::BadNothingToDo,
261                    "delete_nodes called with no nodes to delete",
262                ));
263            }
264            DeleteNodesRequest {
265                request_header: self.header.header,
266                nodes_to_delete: Some(self.nodes_to_delete),
267            }
268        };
269
270        let response = channel
271            .send(request, self.header.timeout)
272            .instrument(span.clone())
273            .await?;
274        let _h = span.enter();
275        if let ResponseMessage::DeleteNodes(response) = response {
276            builder_debug!(self, "delete_nodes, success");
277            process_service_result(&response.response_header)?;
278            Ok(*response)
279        } else {
280            builder_error!(self, "delete_nodes failed");
281            Err(process_unexpected_response(response))
282        }
283    }
284}
285
286#[derive(Debug, Clone)]
287/// Delete references by sending a [`DeleteReferencesRequest`] to the server.
288///
289/// See OPC UA Part 4 - Services 5.7.5 for complete description of the service and error responses.
290pub struct DeleteReferences {
291    references_to_delete: Vec<DeleteReferencesItem>,
292
293    header: RequestHeaderBuilder,
294}
295
296builder_base!(DeleteReferences);
297
298impl DeleteReferences {
299    /// Construct a new call to the `DeleteReferences` service.
300    pub fn new(session: &Session) -> Self {
301        Self {
302            references_to_delete: Vec::new(),
303            header: RequestHeaderBuilder::new_from_session(session),
304        }
305    }
306
307    /// Construct a new call to the `DeleteReferences` service, setting header parameters manually.
308    pub fn new_manual(
309        session_id: u32,
310        timeout: Duration,
311        auth_token: NodeId,
312        request_handle: IntegerId,
313    ) -> Self {
314        Self {
315            references_to_delete: Vec::new(),
316            header: RequestHeaderBuilder::new(session_id, timeout, auth_token, request_handle),
317        }
318    }
319
320    /// Set nodes to delete, overwriting any that were set previously.
321    pub fn references_to_delete(mut self, references_to_delete: Vec<DeleteReferencesItem>) -> Self {
322        self.references_to_delete = references_to_delete;
323        self
324    }
325
326    /// Add a reference to delete.
327    pub fn reference(mut self, reference: impl Into<DeleteReferencesItem>) -> Self {
328        self.references_to_delete.push(reference.into());
329        self
330    }
331}
332
333impl UARequest for DeleteReferences {
334    type Out = DeleteReferencesResponse;
335
336    async fn send<'a>(self, channel: &'a crate::AsyncSecureChannel) -> Result<Self::Out, Error>
337    where
338        Self: 'a,
339    {
340        let span = debug_span!(
341            "Sending DeleteReferences request",
342            num_references_to_delete = self.references_to_delete.len()
343        );
344        let request = {
345            let _h = span.enter();
346            if self.references_to_delete.is_empty() {
347                builder_error!(
348                    self,
349                    "delete_references, called with no references to delete"
350                );
351                return Err(Error::new(
352                    StatusCode::BadNothingToDo,
353                    "delete_references called with no references to delete",
354                ));
355            }
356            DeleteReferencesRequest {
357                request_header: self.header.header,
358                references_to_delete: Some(self.references_to_delete),
359            }
360        };
361        let response = channel
362            .send(request, self.header.timeout)
363            .instrument(span.clone())
364            .await?;
365        let _h = span.enter();
366        if let ResponseMessage::DeleteReferences(response) = response {
367            builder_debug!(self, "delete_references, success");
368            process_service_result(&response.response_header)?;
369            Ok(*response)
370        } else {
371            builder_error!(self, "delete_references failed");
372            Err(process_unexpected_response(response))
373        }
374    }
375}
376
377impl Session {
378    /// Add nodes by sending a [`AddNodesRequest`] to the server.
379    ///
380    /// See OPC UA Part 4 - Services 5.7.2 for complete description of the service and error responses.
381    ///
382    /// # Arguments
383    ///
384    /// * `nodes_to_add` - A list of [`AddNodesItem`] to be added to the server.
385    ///
386    /// # Returns
387    ///
388    /// * `Ok(Vec<AddNodesResult>)` - A list of [`AddNodesResult`] corresponding to each add node operation.
389    /// * `Err(Error)` - Request failed, [Status code](StatusCode) is the reason for failure.
390    ///
391    pub async fn add_nodes(
392        &self,
393        nodes_to_add: &[AddNodesItem],
394    ) -> Result<Vec<AddNodesResult>, Error> {
395        Ok(AddNodes::new(self)
396            .nodes_to_add(nodes_to_add.to_vec())
397            .send(&self.channel)
398            .await?
399            .results
400            .unwrap_or_default())
401    }
402
403    /// Add references by sending a [`AddReferencesRequest`] to the server.
404    ///
405    /// See OPC UA Part 4 - Services 5.7.3 for complete description of the service and error responses.
406    ///
407    /// # Arguments
408    ///
409    /// * `references_to_add` - A list of [`AddReferencesItem`] to be sent to the server.
410    ///
411    /// # Returns
412    ///
413    /// * `Ok(Vec<StatusCode>)` - A list of `StatusCode` corresponding to each add reference operation.
414    /// * `Err(Error)` - Request failed, [Status code](StatusCode) is the reason for failure.
415    ///
416    pub async fn add_references(
417        &self,
418        references_to_add: &[AddReferencesItem],
419    ) -> Result<Vec<StatusCode>, Error> {
420        Ok(AddReferences::new(self)
421            .references_to_add(references_to_add.to_vec())
422            .send(&self.channel)
423            .await?
424            .results
425            .unwrap_or_default())
426    }
427
428    /// Delete nodes by sending a [`DeleteNodesRequest`] to the server.
429    ///
430    /// See OPC UA Part 4 - Services 5.7.4 for complete description of the service and error responses.
431    ///
432    /// # Arguments
433    ///
434    /// * `nodes_to_delete` - A list of [`DeleteNodesItem`] to be sent to the server.
435    ///
436    /// # Returns
437    ///
438    /// * `Ok(Vec<StatusCode>)` - A list of `StatusCode` corresponding to each delete node operation.
439    /// * `Err(Error)` - Request failed, [Status code](StatusCode) is the reason for failure.
440    ///
441    pub async fn delete_nodes(
442        &self,
443        nodes_to_delete: &[DeleteNodesItem],
444    ) -> Result<Vec<StatusCode>, Error> {
445        Ok(DeleteNodes::new(self)
446            .nodes_to_delete(nodes_to_delete.to_vec())
447            .send(&self.channel)
448            .await?
449            .results
450            .unwrap_or_default())
451    }
452
453    /// Delete references by sending a [`DeleteReferencesRequest`] to the server.
454    ///
455    /// See OPC UA Part 4 - Services 5.7.5 for complete description of the service and error responses.
456    ///
457    /// # Arguments
458    ///
459    /// * `nodes_to_delete` - A list of [`DeleteReferencesItem`] to be sent to the server.
460    ///
461    /// # Returns
462    ///
463    /// * `Ok(Vec<StatusCode>)` - A list of `StatusCode` corresponding to each delete node operation.
464    /// * `Err(Error)` - Request failed, [Status code](StatusCode) is the reason for failure.
465    ///
466    pub async fn delete_references(
467        &self,
468        references_to_delete: &[DeleteReferencesItem],
469    ) -> Result<Vec<StatusCode>, Error> {
470        Ok(DeleteReferences::new(self)
471            .references_to_delete(references_to_delete.to_vec())
472            .send(&self.channel)
473            .await?
474            .results
475            .unwrap_or_default())
476    }
477}