opcua_client/session/services/
method.rs1use std::time::Duration;
2
3use crate::{
4 session::{
5 process_unexpected_response,
6 request_builder::{builder_base, builder_debug, builder_error, RequestHeaderBuilder},
7 session_error,
8 },
9 AsyncSecureChannel, Session, UARequest,
10};
11use opcua_core::ResponseMessage;
12use opcua_types::{
13 CallMethodRequest, CallMethodResult, CallRequest, CallResponse, IntegerId, MethodId, NodeId,
14 ObjectId, StatusCode, TryFromVariant, Variant,
15};
16
17#[derive(Debug, Clone)]
18pub struct Call {
22 methods: Vec<CallMethodRequest>,
23
24 header: RequestHeaderBuilder,
25}
26
27builder_base!(Call);
28
29impl Call {
30 pub fn new(session: &Session) -> Self {
32 Self {
33 methods: Vec::new(),
34 header: RequestHeaderBuilder::new_from_session(session),
35 }
36 }
37
38 pub fn new_manual(
40 session_id: u32,
41 timeout: Duration,
42 auth_token: NodeId,
43 request_handle: IntegerId,
44 ) -> Self {
45 Self {
46 methods: Vec::new(),
47 header: RequestHeaderBuilder::new(session_id, timeout, auth_token, request_handle),
48 }
49 }
50
51 pub fn methods_to_call(mut self, methods: Vec<CallMethodRequest>) -> Self {
53 self.methods = methods;
54 self
55 }
56
57 pub fn method(mut self, method: impl Into<CallMethodRequest>) -> Self {
59 self.methods.push(method.into());
60 self
61 }
62}
63
64impl UARequest for Call {
65 type Out = CallResponse;
66
67 async fn send<'a>(self, channel: &'a AsyncSecureChannel) -> Result<Self::Out, StatusCode>
68 where
69 Self: 'a,
70 {
71 if self.methods.is_empty() {
72 builder_error!(self, "call(), was not supplied with any methods to call");
73 return Err(StatusCode::BadNothingToDo);
74 }
75
76 builder_debug!(self, "call()");
77 let cnt = self.methods.len();
78 let request = CallRequest {
79 request_header: self.header.header,
80 methods_to_call: Some(self.methods),
81 };
82 let response = channel.send(request, self.header.timeout).await?;
83 if let ResponseMessage::Call(response) = response {
84 if let Some(results) = &response.results {
85 if results.len() != cnt {
86 builder_error!(
87 self,
88 "call(), expecting {cnt} results from the call to the server, got {} results",
89 results.len()
90 );
91 Err(StatusCode::BadUnexpectedError)
92 } else {
93 Ok(*response)
94 }
95 } else {
96 builder_error!(
97 self,
98 "call(), expecting a result from the call to the server, got nothing"
99 );
100 Err(StatusCode::BadUnexpectedError)
101 }
102 } else {
103 Err(process_unexpected_response(response))
104 }
105 }
106}
107
108impl Session {
109 pub async fn call(
123 &self,
124 methods: Vec<CallMethodRequest>,
125 ) -> Result<Vec<CallMethodResult>, StatusCode> {
126 Ok(Call::new(self)
127 .methods_to_call(methods)
128 .send(&self.channel)
129 .await?
130 .results
131 .unwrap_or_default())
132 }
133
134 pub async fn call_one(
150 &self,
151 method: impl Into<CallMethodRequest>,
152 ) -> Result<CallMethodResult, StatusCode> {
153 Ok(self
154 .call(vec![method.into()])
155 .await?
156 .into_iter()
157 .next()
158 .unwrap())
159 }
160
161 pub async fn call_get_monitored_items(
173 &self,
174 subscription_id: u32,
175 ) -> Result<(Vec<u32>, Vec<u32>), StatusCode> {
176 let args = Some(vec![Variant::from(subscription_id)]);
177 let object_id: NodeId = ObjectId::Server.into();
178 let method_id: NodeId = MethodId::Server_GetMonitoredItems.into();
179 let request: CallMethodRequest = (object_id, method_id, args).into();
180 let response = self.call_one(request).await?;
181 if let Some(mut result) = response.output_arguments {
182 if result.len() == 2 {
183 let server_handles = <Vec<u32>>::try_from_variant(result.remove(0))
184 .map_err(|_| StatusCode::BadUnexpectedError)?;
185 let client_handles = <Vec<u32>>::try_from_variant(result.remove(0))
186 .map_err(|_| StatusCode::BadUnexpectedError)?;
187 Ok((server_handles, client_handles))
188 } else {
189 session_error!(
190 self,
191 "Expected a result with 2 args but got {}",
192 result.len()
193 );
194 Err(StatusCode::BadUnexpectedError)
195 }
196 } else {
197 session_error!(self, "Expected output arguments but got null");
198 Err(StatusCode::BadUnexpectedError)
199 }
200 }
201}