1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
use async_trait::async_trait;
use crate::{
address_space::AddressSpace,
diagnostics::NamespaceMetadata,
node_manager::{
AddNodeItem, AddReferenceItem, DeleteNodeItem, DeleteReferenceItem, HistoryNode,
HistoryUpdateNode, MethodCall, MonitoredItemRef, MonitoredItemUpdateRef, ParsedReadValueId,
RegisterNodeItem, RequestContext, ServerContext, WriteNode,
},
subscriptions::CreateMonitoredItem,
};
use opcua_core::sync::RwLock;
use opcua_types::{
DataValue, ExpandedNodeId, MonitoringMode, NodeId, ReadAnnotationDataDetails,
ReadAtTimeDetails, ReadEventDetails, ReadProcessedDetails, ReadRawModifiedDetails, StatusCode,
TimestampsToReturn,
};
/// Trait for constructing an [InMemoryNodeManagerImpl].
///
/// Note that this is called with the lock on the [AddressSpace] held,
/// if you try to lock it again, it will deadlock.
pub trait InMemoryNodeManagerImplBuilder {
/// Type implementing [InMemoryNodeManagerImpl] constructed by this builder.
type Impl: InMemoryNodeManagerImpl;
/// Build the node manager impl.
fn build(self, context: ServerContext, address_space: &mut AddressSpace) -> Self::Impl;
}
impl<T, R: InMemoryNodeManagerImpl> InMemoryNodeManagerImplBuilder for T
where
T: FnOnce(ServerContext, &mut AddressSpace) -> R,
{
type Impl = R;
fn build(self, context: ServerContext, address_space: &mut AddressSpace) -> Self::Impl {
self(context, address_space)
}
}
#[async_trait]
#[allow(unused)]
/// Trait for user-provided implementation of the [InMemoryNodeManager](crate::node_manager::memory::InMemoryNodeManager)
pub trait InMemoryNodeManagerImpl: Send + Sync + 'static {
/// Populate the address space.
async fn init(&self, address_space: &mut AddressSpace, context: ServerContext);
/// Name of this node manager, for debug purposes.
fn name(&self) -> &str;
/// Return the static list of namespaces this node manager uses.
fn namespaces(&self) -> Vec<NamespaceMetadata>;
/// Return whether this node should handle requests to create a node
/// for the given parent ID. This is only called if no new node ID is
/// requested, otherwise owns_node is called on the requested node ID.
fn owns_server_events(&self) -> bool {
false
}
/// Return `true` if a node with no requested node ID and parent `parent_id`
/// should be created using this node manager.
///
/// This does not commit to actually allowing the node to be created, it just means
/// that no other node managers will be called to create the node.
fn handle_new_node(&self, parent_id: &ExpandedNodeId) -> bool {
false
}
/// Perform the register nodes service. The default behavior for this service is to
/// do nothing and pretend the nodes were registered.
async fn register_nodes(
&self,
context: &RequestContext,
address_space: &RwLock<AddressSpace>,
nodes: &mut [&mut RegisterNodeItem],
) -> Result<(), StatusCode> {
for node in nodes {
node.set_registered(true);
}
Ok(())
}
/// Read for variable values. Other attributes are handled by the parent
/// node ID. This should return a list of data values with the same length
/// and order as `nodes`.
async fn read_values(
&self,
context: &RequestContext,
address_space: &RwLock<AddressSpace>,
nodes: &[&ParsedReadValueId],
max_age: f64,
timestamps_to_return: TimestampsToReturn,
) -> Vec<DataValue> {
let address_space = address_space.read();
nodes
.iter()
.map(|n| address_space.read(context, n, max_age, timestamps_to_return))
.collect()
}
/// Create monitored items for the Value attribute, as needed.
/// This should, at the very least, read the current value of the nodes,
/// and set appropriate status on the monitored item request, see
/// default implementation.
///
/// It may also begin sampling as given by the monitored item request.
async fn create_value_monitored_items(
&self,
context: &RequestContext,
address_space: &RwLock<AddressSpace>,
items: &mut [&mut &mut CreateMonitoredItem],
) {
let to_read: Vec<_> = items.iter().map(|r| r.item_to_monitor()).collect();
let values = self
.read_values(
context,
address_space,
&to_read,
0.0,
TimestampsToReturn::Both,
)
.await;
for (value, node) in values.into_iter().zip(items.iter_mut()) {
if value.status() != StatusCode::BadAttributeIdInvalid {
node.set_initial_value(value);
}
node.set_status(StatusCode::Good);
}
}
/// Create monitored items for events.
///
/// This does not need to do anything.
async fn create_event_monitored_items(
&self,
context: &RequestContext,
address_space: &RwLock<AddressSpace>,
items: &mut [&mut &mut CreateMonitoredItem],
) {
// This is just a no-op by default.
}
/// Handle the SetMonitoringMode request, to pause or resume sampling.
///
/// This will only get monitored items for events or value.
async fn set_monitoring_mode(
&self,
context: &RequestContext,
mode: MonitoringMode,
items: &[&MonitoredItemRef],
) {
}
/// Handle modification of monitored items, this may adjust
/// sampling intervals or filters, and require action to update background
/// processes.
async fn modify_monitored_items(
&self,
context: &RequestContext,
items: &[&MonitoredItemUpdateRef],
) {
}
/// Handle deletion of monitored items.
async fn delete_monitored_items(&self, context: &RequestContext, items: &[&MonitoredItemRef]) {}
/// Perform the unregister nodes service. The default behavior for this service is to
/// do nothing.
async fn unregister_nodes(
&self,
context: &RequestContext,
address_space: &RwLock<AddressSpace>,
nodes: &[&NodeId],
) -> Result<(), StatusCode> {
// Again, just do nothing
Ok(())
}
/// Perform the history read raw modified service. This should write results
/// to the `nodes` list of type either `HistoryData` or `HistoryModifiedData`
///
/// Nodes are verified to be readable before this is called.
async fn history_read_raw_modified(
&self,
context: &RequestContext,
details: &ReadRawModifiedDetails,
nodes: &mut [&mut &mut HistoryNode],
timestamps_to_return: TimestampsToReturn,
) -> Result<(), StatusCode> {
Err(StatusCode::BadHistoryOperationUnsupported)
}
/// Perform the history read processed service. This should write results
/// to the `nodes` list of type `HistoryData`.
///
/// Nodes are verified to be readable before this is called.
async fn history_read_processed(
&self,
context: &RequestContext,
details: &ReadProcessedDetails,
nodes: &mut [&mut &mut HistoryNode],
timestamps_to_return: TimestampsToReturn,
) -> Result<(), StatusCode> {
Err(StatusCode::BadHistoryOperationUnsupported)
}
/// Perform the history read processed service. This should write results
/// to the `nodes` list of type `HistoryData`.
///
/// Nodes are verified to be readable before this is called.
async fn history_read_at_time(
&self,
context: &RequestContext,
details: &ReadAtTimeDetails,
nodes: &mut [&mut &mut HistoryNode],
timestamps_to_return: TimestampsToReturn,
) -> Result<(), StatusCode> {
Err(StatusCode::BadHistoryOperationUnsupported)
}
/// Perform the history read events service. This should write results
/// to the `nodes` list of type `HistoryEvent`.
///
/// Nodes are verified to be readable before this is called.
async fn history_read_events(
&self,
context: &RequestContext,
details: &ReadEventDetails,
nodes: &mut [&mut &mut HistoryNode],
timestamps_to_return: TimestampsToReturn,
) -> Result<(), StatusCode> {
Err(StatusCode::BadHistoryOperationUnsupported)
}
/// Perform the history read annotations data service. This should write
/// results to the `nodes` list of type `Annotation`.
///
/// Nodes are verified to be readable before this is called.
async fn history_read_annotations(
&self,
context: &RequestContext,
details: &ReadAnnotationDataDetails,
nodes: &mut [&mut &mut HistoryNode],
timestamps_to_return: TimestampsToReturn,
) -> Result<(), StatusCode> {
Err(StatusCode::BadHistoryOperationUnsupported)
}
/// Perform the HistoryUpdate service. This should write result
/// status codes to the `nodes` list as appropriate.
///
/// Nodes are verified to be writable before this is called.
async fn history_update(
&self,
context: &RequestContext,
nodes: &mut [&mut &mut HistoryUpdateNode],
) -> Result<(), StatusCode> {
Err(StatusCode::BadHistoryOperationUnsupported)
}
/// Perform the write service. This should write results
/// to the `nodes_to_write` list. The default result is `BadNodeIdUnknown`
///
/// Writing is left almost entirely up to the node manager impl. If you do write
/// values you should call `context.subscriptions.notify_data_change` to trigger
/// any monitored items subscribed to the updated values.
async fn write(
&self,
context: &RequestContext,
address_space: &RwLock<AddressSpace>,
nodes_to_write: &mut [&mut WriteNode],
) -> Result<(), StatusCode> {
Err(StatusCode::BadServiceUnsupported)
}
/// Call a list of methods.
///
/// The methods have already had their arguments verified to have valid length
/// and the method is verified to exist on the given object. This should try
/// to execute the methods, and set the result.
async fn call(
&self,
context: &RequestContext,
address_space: &RwLock<AddressSpace>,
methods_to_call: &mut [&mut &mut MethodCall],
) -> Result<(), StatusCode> {
Err(StatusCode::BadServiceUnsupported)
}
/// Add a list of nodes.
///
/// This should create the nodes, or set a failed status as appropriate.
/// If a node was created, the status should be set to Good.
async fn add_nodes(
&self,
context: &RequestContext,
address_space: &RwLock<AddressSpace>,
nodes_to_add: &mut [&mut AddNodeItem],
) -> Result<(), StatusCode> {
Err(StatusCode::BadServiceUnsupported)
}
/// Add a list of references.
///
/// This will be given all references where the source _or_
/// target belongs to this node manager. A reference is
/// considered successfully added if either source_status
/// or target_status are Good.
///
/// If you want to explicitly set the reference to failed,
/// set both source and target status. Note that it may
/// already have been added in a different node manager, you are
/// responsible for any cleanup if you do this.
async fn add_references(
&self,
context: &RequestContext,
address_space: &RwLock<AddressSpace>,
references_to_add: &mut [&mut AddReferenceItem],
) -> Result<(), StatusCode> {
Err(StatusCode::BadServiceUnsupported)
}
/// Delete a list of nodes.
///
/// This will be given all nodes that belong to this node manager.
///
/// Typically, you also want to implement `delete_node_references` if
/// there are other node managers that support deletes.
async fn delete_nodes(
&self,
context: &RequestContext,
address_space: &RwLock<AddressSpace>,
nodes_to_delete: &mut [&mut DeleteNodeItem],
) -> Result<(), StatusCode> {
Err(StatusCode::BadServiceUnsupported)
}
/// Delete references for the given list of nodes.
/// The node manager should respect `delete_target_references`.
///
/// This is not allowed to fail, you should make it impossible to delete
/// nodes with immutable references.
async fn delete_node_references(
&self,
context: &RequestContext,
address_space: &RwLock<AddressSpace>,
to_delete: &[&DeleteNodeItem],
) {
}
/// Delete a list of references.
///
/// This will be given all references where the source _or_
/// target belongs to this node manager. A reference is
/// considered successfully added if either source_status
/// or target_status are Good.
///
/// If you want to explicitly set the reference to failed,
/// set both source and target status. Note that it may
/// already have been deleted in a different node manager, you are
/// responsible for any cleanup if you do this.
async fn delete_references(
&self,
context: &RequestContext,
address_space: &RwLock<AddressSpace>,
references_to_delete: &mut [&mut DeleteReferenceItem],
) -> Result<(), StatusCode> {
Err(StatusCode::BadServiceUnsupported)
}
}