Skip to main content

opcua_types/
argument.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5//! The [`Argument`] type, used for input and output arguments of methods.
6//!
7//! OPC UA Part 3, 8.6:
8//!
9//! This Structured DataType defines a Method input or output argument specification.
10//! It is for example used in the input and output argument Properties for Methods.
11//! Its elements are described in Table 28.
12
13use std::cmp::Ordering;
14use std::io::{Read, Write};
15
16use crate::{
17    encoding::{BinaryDecodable, BinaryEncodable, EncodingResult},
18    localized_text::LocalizedText,
19    node_id::NodeId,
20    string::UAString,
21    write_u32, Context, DataTypeId, Error, MessageInfo, ObjectId,
22};
23
24// From OPC UA Part 3 - Address Space Model 1.03 Specification
25//
26// This Structured DataType defines a Method input or output argument specification. It is for
27// example used in the input and output argument Properties for Methods. Its elements are described in
28// Table23
29
30#[allow(unused)]
31mod opcua {
32    pub(super) use crate as types;
33}
34
35#[derive(Clone, Debug, PartialEq, Default, crate::UaNullable)]
36#[cfg_attr(feature = "json", derive(crate::JsonEncodable, crate::JsonDecodable))]
37#[cfg_attr(
38    feature = "xml",
39    derive(crate::XmlEncodable, crate::XmlDecodable, crate::XmlType)
40)]
41/// OPC-UA method argument.
42pub struct Argument {
43    /// Argument name.
44    pub name: UAString,
45    /// Node ID of the argument data type.
46    pub data_type: NodeId,
47    /// Argument value rank.
48    pub value_rank: i32,
49    /// Argument array dimensions.
50    pub array_dimensions: Option<Vec<u32>>,
51    /// Argument description.
52    pub description: LocalizedText,
53}
54
55impl MessageInfo for Argument {
56    fn type_id(&self) -> ObjectId {
57        ObjectId::Argument_Encoding_DefaultBinary
58    }
59    fn json_type_id(&self) -> ObjectId {
60        ObjectId::Argument_Encoding_DefaultJson
61    }
62    fn xml_type_id(&self) -> ObjectId {
63        ObjectId::Argument_Encoding_DefaultXml
64    }
65    fn data_type_id(&self) -> crate::DataTypeId {
66        DataTypeId::Argument
67    }
68}
69
70impl BinaryEncodable for Argument {
71    fn byte_len(&self, ctx: &crate::Context<'_>) -> usize {
72        let mut size = 0;
73        size += self.name.byte_len(ctx);
74        size += self.data_type.byte_len(ctx);
75        size += self.value_rank.byte_len(ctx);
76        size += self.array_dimensions.byte_len(ctx);
77        size += self.description.byte_len(ctx);
78        size
79    }
80
81    fn encode<S: Write + ?Sized>(&self, stream: &mut S, ctx: &Context<'_>) -> EncodingResult<()> {
82        self.name.encode(stream, ctx)?;
83        self.data_type.encode(stream, ctx)?;
84        self.value_rank.encode(stream, ctx)?;
85        // Encode the array dimensions
86        if self.value_rank > 0 {
87            if let Some(ref array_dimensions) = self.array_dimensions {
88                if self.value_rank as usize != array_dimensions.len() {
89                    return Err(Error::encoding(
90                        format!("The array dimensions {} of the Argument should match value rank {} and they don't", array_dimensions.len(), self.value_rank)));
91                }
92                self.array_dimensions.encode(stream, ctx)?;
93            } else {
94                return Err(Error::encoding(format!("The array dimensions are expected in the Argument matching value rank {} and they aren't", self.value_rank)));
95            }
96        } else {
97            write_u32(stream, 0u32)?;
98        }
99
100        self.description.encode(stream, ctx)?;
101        Ok(())
102    }
103}
104
105impl BinaryDecodable for Argument {
106    fn decode<S: Read + ?Sized>(stream: &mut S, ctx: &Context<'_>) -> EncodingResult<Self> {
107        let name = UAString::decode(stream, ctx)?;
108        let data_type = NodeId::decode(stream, ctx)?;
109        let value_rank = i32::decode(stream, ctx)?;
110        // Decode array dimensions
111        let mut array_dimensions: Option<Vec<u32>> = BinaryDecodable::decode(stream, ctx)?;
112        if value_rank > 0 {
113            if let Some(array_dimensions) = array_dimensions.as_mut() {
114                let expected_rank = value_rank as usize;
115                match array_dimensions.len().cmp(&expected_rank) {
116                    Ordering::Less => {
117                        // If fewer dimensions are provided than rank, pad with 0 to reach rank length
118                        array_dimensions.resize(expected_rank, 0);
119                    }
120                    Ordering::Equal => {}
121                    Ordering::Greater => {
122                        // More dimensions than value_rank is internally inconsistent
123                        return Err(Error::decoding(format!("The array dimensions {} of the Argument should match value rank {} and they don't", array_dimensions.len(), value_rank)));
124                    }
125                }
126            }
127        }
128        let description = LocalizedText::decode(stream, ctx)?;
129        Ok(Argument {
130            name,
131            data_type,
132            value_rank,
133            array_dimensions,
134            description,
135        })
136    }
137}