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