Skip to main content

opcua_server/node_manager/memory/
simple.rs

1use std::{collections::HashMap, sync::Arc, time::Duration};
2
3use async_trait::async_trait;
4use opcua_core::{trace_read_lock, trace_write_lock};
5use opcua_nodes::{HasNodeId, NodeSetImport};
6
7use crate::{
8    address_space::{read_node_value, write_node_value, AddressSpace},
9    node_manager::{
10        DefaultTypeTree, MethodCall, MonitoredItemRef, MonitoredItemUpdateRef, NodeManagerBuilder,
11        NodeManagersRef, ParsedReadValueId, RequestContext, ServerContext, SyncSampler, WriteNode,
12    },
13    CreateMonitoredItem,
14};
15use opcua_core::sync::RwLock;
16use opcua_types::{
17    AttributeId, DataValue, MonitoringMode, NodeClass, NodeId, NumericRange, StatusCode,
18    TimestampsToReturn, Variant,
19};
20
21use super::{
22    InMemoryNodeManager, InMemoryNodeManagerBuilder, InMemoryNodeManagerImpl,
23    InMemoryNodeManagerImplBuilder, NamespaceMetadata,
24};
25
26/// A simple in-memory node manager with utility methods for updating the address space,
27/// and a mechanism for setting callbacks on `Read` and `Write` of values.
28pub type SimpleNodeManager = InMemoryNodeManager<SimpleNodeManagerImpl>;
29
30type WriteCB = Arc<dyn Fn(DataValue, &NumericRange) -> StatusCode + Send + Sync + 'static>;
31type ReadCB = Arc<
32    dyn Fn(&NumericRange, TimestampsToReturn, f64) -> Result<DataValue, StatusCode>
33        + Send
34        + Sync
35        + 'static,
36>;
37type MethodCB = Arc<dyn Fn(&[Variant]) -> Result<Vec<Variant>, StatusCode> + Send + Sync + 'static>;
38
39/// Builder for the [SimpleNodeManager].
40pub struct SimpleNodeManagerBuilder {
41    namespaces: Vec<NamespaceMetadata>,
42    imports: Vec<Box<dyn NodeSetImport>>,
43    name: String,
44}
45
46impl SimpleNodeManagerBuilder {
47    /// Create a new simple node manager builder with the given namespace
48    /// and name.
49    pub fn new(namespace: NamespaceMetadata, name: &str) -> Self {
50        Self {
51            namespaces: vec![namespace],
52            imports: Vec::new(),
53            name: name.to_owned(),
54        }
55    }
56
57    /// Create a new simple node manager that imports from the given list
58    /// of [NodeSetImport]s.
59    pub fn new_imports(imports: Vec<Box<dyn NodeSetImport>>, name: &str) -> Self {
60        Self {
61            namespaces: Vec::new(),
62            imports,
63            name: name.to_owned(),
64        }
65    }
66}
67
68impl InMemoryNodeManagerImplBuilder for SimpleNodeManagerBuilder {
69    type Impl = SimpleNodeManagerImpl;
70
71    fn build(mut self, context: ServerContext, address_space: &mut AddressSpace) -> Self::Impl {
72        {
73            let mut type_tree = context.type_tree.write();
74            for import in self.imports {
75                address_space.import_node_set(&*import, type_tree.namespaces_mut());
76                let nss = import.get_own_namespaces();
77                for ns in nss {
78                    if !self.namespaces.iter().any(|n| n.namespace_uri == ns) {
79                        self.namespaces.push(NamespaceMetadata {
80                            namespace_uri: ns,
81                            ..Default::default()
82                        });
83                    }
84                }
85            }
86            for ns in &mut self.namespaces {
87                ns.namespace_index = type_tree.namespaces_mut().add_namespace(&ns.namespace_uri);
88            }
89        }
90        for ns in &self.namespaces {
91            address_space.add_namespace(&ns.namespace_uri, ns.namespace_index);
92        }
93        SimpleNodeManagerImpl::new(self.namespaces, &self.name, context.node_managers.clone())
94    }
95}
96
97/// Create a node manager builder for the simple node manager with the given
98/// namespace and name.
99pub fn simple_node_manager(namespace: NamespaceMetadata, name: &str) -> impl NodeManagerBuilder {
100    InMemoryNodeManagerBuilder::new(SimpleNodeManagerBuilder::new(namespace, name))
101}
102
103/// Create a new simple node manager that imports from the given list
104/// of [NodeSetImport]s.
105pub fn simple_node_manager_imports(
106    imports: Vec<Box<dyn NodeSetImport>>,
107    name: &str,
108) -> impl NodeManagerBuilder {
109    InMemoryNodeManagerBuilder::new(SimpleNodeManagerBuilder::new_imports(imports, name))
110}
111
112/// Node manager designed to deal with simple, entirely in-memory, synchronous OPC-UA servers.
113///
114/// Use this if
115///
116///  - Your node hierarchy is known and small enough to fit in memory.
117///  - No read, write, or method call operations are async or particularly time consuming.
118///  - and you don't need to be able to write attributes other than `Value`.
119pub struct SimpleNodeManagerImpl {
120    write_cbs: RwLock<HashMap<NodeId, WriteCB>>,
121    read_cbs: RwLock<HashMap<NodeId, ReadCB>>,
122    method_cbs: RwLock<HashMap<NodeId, MethodCB>>,
123    namespaces: Vec<NamespaceMetadata>,
124    #[allow(unused)]
125    node_managers: NodeManagersRef,
126    name: String,
127    samplers: SyncSampler,
128}
129
130#[async_trait]
131impl InMemoryNodeManagerImpl for SimpleNodeManagerImpl {
132    async fn init(&self, _address_space: &mut AddressSpace, context: ServerContext) {
133        self.samplers.run(
134            Duration::from_micros(
135                // If this is set too low the server will just spin at 100% CPU. Cap it at
136                // 100 ms. In custom node manager implementations using the sync sampler
137                // users are free to set whatever minimum they want.
138                // In practice, if you need sampling rates much lower than 100ms you
139                // will likely want a different mechanism than the sync sampler.
140                (context
141                    .info
142                    .config
143                    .limits
144                    .subscriptions
145                    .min_sampling_interval_ms
146                    .max(100.0)
147                    * 1000.0) as u64,
148            ),
149            context.subscriptions.clone(),
150        );
151    }
152
153    fn namespaces(&self) -> Vec<NamespaceMetadata> {
154        self.namespaces.clone()
155    }
156
157    fn name(&self) -> &str {
158        &self.name
159    }
160
161    async fn read_values(
162        &self,
163        context: &RequestContext,
164        address_space: &RwLock<AddressSpace>,
165        nodes: &[&ParsedReadValueId],
166        max_age: f64,
167        timestamps_to_return: TimestampsToReturn,
168    ) -> Vec<DataValue> {
169        let address_space = address_space.read();
170        let cbs = trace_read_lock!(self.read_cbs);
171
172        nodes
173            .iter()
174            .map(|n| {
175                self.read_node_value(
176                    &cbs,
177                    context,
178                    &address_space,
179                    n,
180                    max_age,
181                    timestamps_to_return,
182                )
183            })
184            .collect()
185    }
186
187    async fn create_value_monitored_items(
188        &self,
189        context: &RequestContext,
190        address_space: &RwLock<AddressSpace>,
191        items: &mut [&mut &mut CreateMonitoredItem],
192    ) {
193        let to_read: Vec<_> = items.iter().map(|r| r.item_to_monitor()).collect();
194        let values = self
195            .read_values(
196                context,
197                address_space,
198                &to_read,
199                0.0,
200                TimestampsToReturn::Both,
201            )
202            .await;
203
204        let cbs = trace_read_lock!(self.read_cbs);
205
206        for (value, node) in values.into_iter().zip(items.iter_mut()) {
207            if value.status() != StatusCode::BadAttributeIdInvalid {
208                node.set_initial_value(value);
209            }
210            node.set_status(StatusCode::Good);
211            let rf = &node.item_to_monitor().node_id;
212
213            if let Some(cb) = cbs.get(rf).cloned() {
214                let tss = node.timestamps_to_return();
215                let index_range = node.item_to_monitor().index_range.clone();
216
217                self.samplers.add_sampler(
218                    node.item_to_monitor().node_id.clone(),
219                    AttributeId::Value,
220                    move || {
221                        Some(match cb(&index_range, tss, 0.0) {
222                            Err(e) => DataValue {
223                                status: Some(e),
224                                ..Default::default()
225                            },
226                            Ok(v) => v,
227                        })
228                    },
229                    node.monitoring_mode(),
230                    node.handle(),
231                    Duration::from_millis(node.sampling_interval() as u64),
232                )
233            }
234        }
235    }
236
237    async fn modify_monitored_items(
238        &self,
239        _context: &RequestContext,
240        items: &[&MonitoredItemUpdateRef],
241    ) {
242        for it in items {
243            self.samplers.update_sampler(
244                it.node_id(),
245                it.attribute(),
246                it.handle(),
247                Duration::from_millis(it.update().revised_sampling_interval as u64),
248            );
249        }
250    }
251
252    async fn set_monitoring_mode(
253        &self,
254        _context: &RequestContext,
255        mode: MonitoringMode,
256        items: &[&MonitoredItemRef],
257    ) {
258        for it in items {
259            self.samplers
260                .set_sampler_mode(it.node_id(), it.attribute(), it.handle(), mode);
261        }
262    }
263
264    async fn delete_monitored_items(&self, _context: &RequestContext, items: &[&MonitoredItemRef]) {
265        for it in items {
266            self.samplers
267                .remove_sampler(it.node_id(), it.attribute(), it.handle());
268        }
269    }
270
271    async fn write(
272        &self,
273        context: &RequestContext,
274        address_space: &RwLock<AddressSpace>,
275        nodes_to_write: &mut [&mut WriteNode],
276    ) -> Result<(), StatusCode> {
277        let mut address_space = trace_write_lock!(address_space);
278        let type_tree = trace_read_lock!(context.type_tree);
279        let cbs = trace_read_lock!(self.write_cbs);
280
281        for write in nodes_to_write {
282            self.write_node_value(&cbs, context, &mut address_space, &type_tree, write);
283        }
284
285        Ok(())
286    }
287
288    async fn call(
289        &self,
290        _context: &RequestContext,
291        _address_space: &RwLock<AddressSpace>,
292        methods_to_call: &mut [&mut &mut MethodCall],
293    ) -> Result<(), StatusCode> {
294        let cbs = trace_read_lock!(self.method_cbs);
295        for method in methods_to_call {
296            if let Some(cb) = cbs.get(method.method_id()) {
297                match cb(method.arguments()) {
298                    Ok(r) => {
299                        method.set_outputs(r);
300                        method.set_status(StatusCode::Good);
301                    }
302                    Err(e) => method.set_status(e),
303                }
304            }
305        }
306
307        Ok(())
308    }
309}
310
311impl SimpleNodeManagerImpl {
312    fn new(namespaces: Vec<NamespaceMetadata>, name: &str, node_managers: NodeManagersRef) -> Self {
313        Self {
314            write_cbs: Default::default(),
315            read_cbs: Default::default(),
316            method_cbs: Default::default(),
317            namespaces,
318            name: name.to_owned(),
319            node_managers,
320            samplers: SyncSampler::new(),
321        }
322    }
323
324    fn read_node_value(
325        &self,
326        cbs: &HashMap<NodeId, ReadCB>,
327        context: &RequestContext,
328        address_space: &AddressSpace,
329        node_to_read: &ParsedReadValueId,
330        max_age: f64,
331        timestamps_to_return: TimestampsToReturn,
332    ) -> DataValue {
333        let mut result_value = DataValue::null();
334        // Check that the read is permitted.
335        let node = match address_space.validate_node_read(context, node_to_read) {
336            Ok(n) => n,
337            Err(e) => {
338                result_value.status = Some(e);
339                return result_value;
340            }
341        };
342
343        // If there is a callback registered, call that, otherwise read it from the node hierarchy.
344        if let Some(cb) = cbs.get(&node_to_read.node_id) {
345            match cb(&node_to_read.index_range, timestamps_to_return, max_age) {
346                Err(e) => DataValue {
347                    status: Some(e),
348                    ..Default::default()
349                },
350                Ok(v) => v,
351            }
352        } else {
353            // If it can't be found, read it from the node hierarchy.
354            read_node_value(node, context, node_to_read, max_age, timestamps_to_return)
355        }
356    }
357
358    fn write_node_value(
359        &self,
360        cbs: &HashMap<NodeId, WriteCB>,
361        context: &RequestContext,
362        address_space: &mut AddressSpace,
363        type_tree: &DefaultTypeTree,
364        write: &mut WriteNode,
365    ) {
366        let node = match address_space.validate_node_write(context, write.value(), type_tree) {
367            Ok(v) => v,
368            Err(e) => {
369                write.set_status(e);
370                return;
371            }
372        };
373
374        if node.node_class() != NodeClass::Variable
375            || write.value().attribute_id != AttributeId::Value
376        {
377            write.set_status(StatusCode::BadNotWritable);
378            return;
379        }
380
381        if let Some(cb) = cbs.get(node.as_node().node_id()) {
382            // If there is a callback registered, call that.
383            write.set_status(cb(write.value().value.clone(), &write.value().index_range));
384        } else if write.value().value.value.is_some() {
385            // If not, write the value to the node hierarchy.
386            match write_node_value(node, write.value()) {
387                Ok(_) => write.set_status(StatusCode::Good),
388                Err(e) => write.set_status(e),
389            }
390        } else {
391            // If no value is passed return an error.
392            write.set_status(StatusCode::BadNothingToDo);
393        }
394        if write.status().is_good() {
395            if let Some(val) = node.as_mut_node().get_attribute(
396                TimestampsToReturn::Both,
397                write.value().attribute_id,
398                &NumericRange::None,
399                &opcua_types::DataEncoding::Binary,
400            ) {
401                context.subscriptions.notify_data_change(
402                    [(val, node.node_id(), write.value().attribute_id)].into_iter(),
403                );
404            }
405        }
406    }
407
408    /// Add a callback called on `Write` for the node given by `id`.
409    pub fn add_write_callback(
410        &self,
411        id: NodeId,
412        cb: impl Fn(DataValue, &NumericRange) -> StatusCode + Send + Sync + 'static,
413    ) {
414        let mut cbs = trace_write_lock!(self.write_cbs);
415        cbs.insert(id, Arc::new(cb));
416    }
417
418    /// Add a callback for `Read` on the node given by `id`.
419    pub fn add_read_callback(
420        &self,
421        id: NodeId,
422        cb: impl Fn(&NumericRange, TimestampsToReturn, f64) -> Result<DataValue, StatusCode>
423            + Send
424            + Sync
425            + 'static,
426    ) {
427        let mut cbs = trace_write_lock!(self.read_cbs);
428        cbs.insert(id, Arc::new(cb));
429    }
430
431    /// Add a callback for `Call` on the method given by `id`.
432    pub fn add_method_callback(
433        &self,
434        id: NodeId,
435        cb: impl Fn(&[Variant]) -> Result<Vec<Variant>, StatusCode> + Send + Sync + 'static,
436    ) {
437        let mut cbs = trace_write_lock!(self.method_cbs);
438        cbs.insert(id, Arc::new(cb));
439    }
440}