Skip to main content

opcua_server/node_manager/
method.rs

1use opcua_types::{
2    CallMethodRequest, CallMethodResult, DiagnosticBits, DiagnosticInfo, NodeId, StatusCode,
3    Variant,
4};
5
6use super::IntoResult;
7
8#[derive(Debug)]
9/// Container for a single method call in a `Call` service call.
10pub struct MethodCall {
11    object_id: NodeId,
12    method_id: NodeId,
13    arguments: Vec<Variant>,
14    diagnostic_bits: DiagnosticBits,
15
16    status: StatusCode,
17    argument_results: Vec<StatusCode>,
18    outputs: Vec<Variant>,
19    diagnostic_info: Option<DiagnosticInfo>,
20}
21
22impl MethodCall {
23    pub(crate) fn new(request: CallMethodRequest, diagnostic_bits: DiagnosticBits) -> Self {
24        Self {
25            object_id: request.object_id,
26            method_id: request.method_id,
27            arguments: request.input_arguments.unwrap_or_default(),
28            status: StatusCode::BadMethodInvalid,
29            argument_results: Vec::new(),
30            outputs: Vec::new(),
31            diagnostic_bits,
32            diagnostic_info: None,
33        }
34    }
35
36    /// Set the argument results to a list of errors.
37    /// This will update the `status` to `BadInvalidArgument`.
38    ///
39    /// The length of `argument_results` must be equal to the length of `arguments`.
40    pub fn set_argument_error(&mut self, argument_results: Vec<StatusCode>) {
41        self.argument_results = argument_results;
42        self.status = StatusCode::BadInvalidArgument;
43    }
44
45    /// Set the result of this method call.
46    pub fn set_status(&mut self, status: StatusCode) {
47        self.status = status;
48    }
49
50    /// Set the outputs of this method call.
51    pub fn set_outputs(&mut self, outputs: Vec<Variant>) {
52        self.outputs = outputs;
53    }
54
55    /// Get the arguments to this method call.
56    pub fn arguments(&self) -> &[Variant] {
57        &self.arguments
58    }
59
60    /// Get the ID of the method to call.
61    pub fn method_id(&self) -> &NodeId {
62        &self.method_id
63    }
64
65    /// Get the ID of the object the method is a part of.
66    pub fn object_id(&self) -> &NodeId {
67        &self.object_id
68    }
69
70    /// Get the current status.
71    pub fn status(&self) -> StatusCode {
72        self.status
73    }
74
75    /// Header diagnostic bits for requesting operation-level diagnostics.
76    pub fn diagnostic_bits(&self) -> DiagnosticBits {
77        self.diagnostic_bits
78    }
79
80    /// Set diagnostic infos, you don't need to do this if
81    /// `diagnostic_bits` are not set.
82    pub fn set_diagnostic_info(&mut self, diagnostic_info: DiagnosticInfo) {
83        self.diagnostic_info = Some(diagnostic_info);
84    }
85}
86
87impl IntoResult for MethodCall {
88    type Result = CallMethodResult;
89
90    fn into_result(self) -> (Self::Result, Option<DiagnosticInfo>) {
91        (
92            CallMethodResult {
93                status_code: self.status,
94                input_argument_diagnostic_infos: None,
95                input_argument_results: if !self.argument_results.is_empty() {
96                    Some(self.argument_results)
97                } else {
98                    None
99                },
100                output_arguments: Some(self.outputs),
101            },
102            self.diagnostic_info,
103        )
104    }
105}
106
107/// Convenient macro for performing an _implicit_ cast of
108/// each argument to the expected method argument type, and returning
109/// the arguments as a tuple.
110///
111/// This macro will produce `Result<(Arg1, Arg2, ...), StatusCode>`.
112///
113/// The types in the argument list must be enum variants of the `Variant` type.
114///
115/// # Example
116///
117/// ```ignore
118/// let (arg1, arg2) = load_method_args!(method_call, Int32, String)?;
119/// ```
120#[macro_export]
121macro_rules! load_method_args {
122    ($call:expr, $($type:ident),+) => {
123        {
124            let mut arguments = $call.arguments().iter();
125            (move || {
126                Ok($(
127                    match arguments.next().map(|v| v.convert(VariantTypeId::Scalar(VariantScalarTypeId::$type))) {
128                        Some(Variant::$type(val)) => val,
129                        _ => return Err(StatusCode::BadInvalidArgument),
130                    }
131                ),*)
132            })()
133        }
134
135    };
136}