1use opcua_types::{
8 Argument, AttributeId, AttributesMask, DataEncoding, DataTypeId, DataValue, ExtensionObject,
9 MethodAttributes, NumericRange, StatusCode, TimestampsToReturn, VariableTypeId, Variant,
10 VariantScalarTypeId,
11};
12use tracing::error;
13
14use crate::{FromAttributesError, NodeInsertTarget};
15
16use super::{
17 base::Base,
18 node::{Node, NodeBase},
19 variable::VariableBuilder,
20};
21
22node_builder_impl!(MethodBuilder, Method);
23node_builder_impl_component_of!(MethodBuilder);
24node_builder_impl_generates_event!(MethodBuilder);
25
26impl MethodBuilder {
27 pub fn output_args(
30 self,
31 address_space: &mut impl NodeInsertTarget,
32 node_id: &NodeId,
33 arguments: &[Argument],
34 ) -> Self {
35 self.insert_args(node_id, "OutputArguments", address_space, arguments);
36 self
37 }
38
39 pub fn input_args(
42 self,
43 address_space: &mut impl NodeInsertTarget,
44 node_id: &NodeId,
45 arguments: &[Argument],
46 ) -> Self {
47 self.insert_args(node_id, "InputArguments", address_space, arguments);
48 self
49 }
50
51 pub fn executable(mut self, executable: bool) -> Self {
54 self.node.set_executable(executable);
55 self
56 }
57
58 pub fn user_executable(mut self, executable: bool) -> Self {
62 self.node.set_user_executable(executable);
63 self
64 }
65
66 pub fn write_mask(mut self, write_mask: WriteMask) -> Self {
68 self.node.set_write_mask(write_mask);
69 self
70 }
71
72 fn args_to_variant(arguments: &[Argument]) -> Variant {
73 let arguments = arguments
74 .iter()
75 .map(|arg| Variant::from(ExtensionObject::from_message(arg.clone())))
76 .collect::<Vec<Variant>>();
77 Variant::from((VariantScalarTypeId::ExtensionObject, arguments))
78 }
79
80 fn insert_args(
81 &self,
82 node_id: &NodeId,
83 args_name: &str,
84 address_space: &mut impl NodeInsertTarget,
85 arguments: &[Argument],
86 ) {
87 let fn_node_id = self.node.node_id();
88 let args_value = Self::args_to_variant(arguments);
89 VariableBuilder::new(node_id, args_name, args_name)
90 .property_of(fn_node_id)
91 .has_type_definition(VariableTypeId::PropertyType)
92 .data_type(DataTypeId::Argument)
93 .value_rank(1)
94 .array_dimensions(&[arguments.len() as u32])
95 .value(args_value)
96 .insert(address_space);
97 }
98}
99
100#[derive(Debug)]
102pub struct Method {
103 pub(super) base: Base,
104 pub(super) executable: bool,
105 pub(super) user_executable: bool,
106}
107
108impl Default for Method {
109 fn default() -> Self {
110 Self {
111 base: Base::new(NodeClass::Method, &NodeId::null(), "", ""),
112 executable: true,
113 user_executable: true,
114 }
115 }
116}
117
118node_base_impl!(Method);
119
120impl Node for Method {
121 fn get_attribute_max_age(
122 &self,
123 timestamps_to_return: TimestampsToReturn,
124 attribute_id: AttributeId,
125 index_range: &NumericRange,
126 data_encoding: &DataEncoding,
127 max_age: f64,
128 ) -> Option<DataValue> {
129 match attribute_id {
130 AttributeId::Executable => Some(self.executable().into()),
131 AttributeId::UserExecutable => Some(self.user_executable().into()),
132 _ => self.base.get_attribute_max_age(
133 timestamps_to_return,
134 attribute_id,
135 index_range,
136 data_encoding,
137 max_age,
138 ),
139 }
140 }
141
142 fn set_attribute(
143 &mut self,
144 attribute_id: AttributeId,
145 value: Variant,
146 ) -> Result<(), StatusCode> {
147 match attribute_id {
148 AttributeId::Executable => {
149 if let Variant::Boolean(v) = value {
150 self.set_executable(v);
151 Ok(())
152 } else {
153 Err(StatusCode::BadTypeMismatch)
154 }
155 }
156 AttributeId::UserExecutable => {
157 if let Variant::Boolean(v) = value {
158 self.set_user_executable(v);
159 Ok(())
160 } else {
161 Err(StatusCode::BadTypeMismatch)
162 }
163 }
164 _ => self.base.set_attribute(attribute_id, value),
165 }
166 }
167}
168
169impl Method {
170 pub fn new(
172 node_id: &NodeId,
173 browse_name: impl Into<QualifiedName>,
174 display_name: impl Into<LocalizedText>,
175 executable: bool,
176 user_executable: bool,
177 ) -> Method {
178 Method {
179 base: Base::new(NodeClass::Method, node_id, browse_name, display_name),
180 executable,
181 user_executable,
182 }
183 }
184
185 pub fn new_full(base: Base, executable: bool, user_executable: bool) -> Self {
188 Self {
189 base,
190 executable,
191 user_executable,
192 }
193 }
194
195 pub fn from_attributes(
197 node_id: &NodeId,
198 browse_name: impl Into<QualifiedName>,
199 attributes: MethodAttributes,
200 ) -> Result<Self, FromAttributesError> {
201 let mandatory_attributes = AttributesMask::DISPLAY_NAME
202 | AttributesMask::EXECUTABLE
203 | AttributesMask::USER_EXECUTABLE;
204 let mask = AttributesMask::from_bits(attributes.specified_attributes)
205 .ok_or(FromAttributesError::InvalidMask)?;
206 if mask.contains(mandatory_attributes) {
207 let mut node = Self::new(
208 node_id,
209 browse_name,
210 attributes.display_name,
211 attributes.executable,
212 attributes.user_executable,
213 );
214 if mask.contains(AttributesMask::DESCRIPTION) {
215 node.set_description(attributes.description);
216 }
217 if mask.contains(AttributesMask::WRITE_MASK) {
218 node.set_write_mask(WriteMask::from_bits_truncate(attributes.write_mask));
219 }
220 if mask.contains(AttributesMask::USER_WRITE_MASK) {
221 node.set_user_write_mask(WriteMask::from_bits_truncate(attributes.user_write_mask));
222 }
223 Ok(node)
224 } else {
225 error!("Method cannot be created from attributes - missing mandatory values");
226 Err(FromAttributesError::MissingMandatoryValues)
227 }
228 }
229
230 pub fn is_valid(&self) -> bool {
232 self.base.is_valid()
233 }
234
235 pub fn executable(&self) -> bool {
237 self.executable
238 }
239
240 pub fn set_executable(&mut self, executable: bool) {
242 self.executable = executable;
243 }
244
245 pub fn user_executable(&self) -> bool {
247 self.executable && self.user_executable
249 }
250
251 pub fn set_user_executable(&mut self, user_executable: bool) {
253 self.user_executable = user_executable;
254 }
255}