1use crate::NamespaceMap;
2use opcua_types::{
3 event_field::EventField, AttributeId, ByteString, DateTime, LocalizedText, NodeId,
4 NumericRange, ObjectTypeId, QualifiedName, TimeZoneDataType, UAString, Variant,
5};
6
7pub trait Event: EventField {
12 fn get_field(
15 &self,
16 type_definition_id: &NodeId,
17 attribute_id: AttributeId,
18 index_range: &NumericRange,
19 browse_path: &[QualifiedName],
20 ) -> Variant;
21
22 fn time(&self) -> &DateTime;
24
25 fn event_type_id(&self) -> &NodeId;
27}
28
29#[derive(Debug, Default)]
30pub struct BaseEventType {
32 pub event_id: ByteString,
34 pub event_type: NodeId,
36 pub source_node: NodeId,
38 pub source_name: UAString,
41 pub time: DateTime,
44 pub receive_time: DateTime,
47 pub local_time: Option<TimeZoneDataType>,
50 pub message: LocalizedText,
53 pub severity: u16,
64 pub condition_class_id: Option<NodeId>,
66 pub condition_class_name: Option<LocalizedText>,
68 pub condition_sub_class_id: Option<Vec<NodeId>>,
71 pub condition_sub_class_name: Option<Vec<LocalizedText>>,
73}
74
75impl Event for BaseEventType {
76 fn time(&self) -> &DateTime {
77 &self.time
78 }
79
80 fn get_field(
81 &self,
82 type_definition_id: &NodeId,
83 attribute_id: AttributeId,
84 index_range: &NumericRange,
85 browse_path: &[QualifiedName],
86 ) -> Variant {
87 if type_definition_id == &ObjectTypeId::BaseEventType {
88 self.get_value(attribute_id, index_range, browse_path)
89 } else {
90 Variant::Empty
91 }
92 }
93
94 fn event_type_id(&self) -> &NodeId {
95 &self.event_type
96 }
97}
98
99impl EventField for BaseEventType {
100 fn get_value(
101 &self,
102 attribute_id: AttributeId,
103 index_range: &NumericRange,
104 remaining_path: &[QualifiedName],
105 ) -> Variant {
106 if remaining_path.len() != 1 || attribute_id != AttributeId::Value {
107 return Variant::Empty;
109 }
110 let field = &remaining_path[0];
111 if field.namespace_index != 0 {
112 return Variant::Empty;
113 }
114 match field.name.as_ref() {
115 "EventId" => self.event_id.get_value(attribute_id, index_range, &[]),
116 "EventType" => self.event_type.get_value(attribute_id, index_range, &[]),
117 "SourceNode" => self.source_node.get_value(attribute_id, index_range, &[]),
118 "SourceName" => self.source_name.get_value(attribute_id, index_range, &[]),
119 "Time" => self.time.get_value(attribute_id, index_range, &[]),
120 "ReceiveTime" => self.receive_time.get_value(attribute_id, index_range, &[]),
121 "LocalTime" => self.local_time.get_value(attribute_id, index_range, &[]),
122 "Message" => self.message.get_value(attribute_id, index_range, &[]),
123 "Severity" => self.severity.get_value(attribute_id, index_range, &[]),
124 "ConditionClassId" => self
125 .condition_class_id
126 .get_value(attribute_id, index_range, &[]),
127 "ConditionClassName" => {
128 self.condition_class_name
129 .get_value(attribute_id, index_range, &[])
130 }
131 "ConditionSubClassId" => {
132 self.condition_sub_class_id
133 .get_value(attribute_id, index_range, &[])
134 }
135 "ConditionSubClassName" => {
136 self.condition_sub_class_name
137 .get_value(attribute_id, index_range, &[])
138 }
139 _ => Variant::Empty,
140 }
141 }
142}
143
144impl BaseEventType {
145 pub fn new_now(
147 type_id: impl Into<NodeId>,
148 event_id: ByteString,
149 message: impl Into<LocalizedText>,
150 ) -> Self {
151 let time = DateTime::now();
152 Self::new(type_id, event_id, message, time)
153 }
154
155 pub fn new(
157 type_id: impl Into<NodeId>,
158 event_id: ByteString,
159 message: impl Into<LocalizedText>,
160 time: DateTime,
161 ) -> Self {
162 Self {
163 event_id,
164 event_type: type_id.into(),
165 message: message.into(),
166 time,
167 receive_time: time,
168 ..Default::default()
169 }
170 }
171
172 pub fn new_event(
174 type_id: impl Into<NodeId>,
175 event_id: ByteString,
176 message: impl Into<LocalizedText>,
177 _namespace: &NamespaceMap,
178 time: DateTime,
179 ) -> Self {
180 Self::new(type_id, event_id, message, time)
181 }
182
183 pub fn set_source_node(mut self, source_node: NodeId) -> Self {
185 self.source_node = source_node;
186 self
187 }
188
189 pub fn set_source_name(mut self, source_name: UAString) -> Self {
191 self.source_name = source_name;
192 self
193 }
194
195 pub fn set_receive_time(mut self, receive_time: DateTime) -> Self {
197 self.receive_time = receive_time;
198 self
199 }
200
201 pub fn set_severity(mut self, severity: u16) -> Self {
203 self.severity = severity;
204 self
205 }
206}
207
208pub use method_event_field::MethodEventField;
209
210mod method_event_field {
211 use opcua_macros::EventField;
212 use opcua_types::NodeId;
213
214 mod opcua {
215 pub(super) use crate as nodes;
216 pub(super) use opcua_types as types;
217 }
218 #[derive(Default, EventField, Debug)]
219 pub struct MethodEventField {
221 pub node_id: NodeId,
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use crate::NamespaceMap;
229
230 mod opcua {
231 pub(super) use crate as nodes;
232 pub(super) use opcua_types as types;
233 }
234
235 use crate::{BaseEventType, Event, EventField};
236 use opcua_types::event_field::PlaceholderEventField;
237 use opcua_types::{
238 AttributeId, ByteString, EUInformation, KeyValuePair, LocalizedText, NodeId, NumericRange,
239 ObjectTypeId, QualifiedName, StatusCode, UAString, Variant,
240 };
241 #[derive(Event)]
242 #[opcua(identifier = "s=myevent", namespace = "uri:my:namespace")]
243 struct BasicValueEvent {
244 base: BaseEventType,
245 own_namespace_index: u16,
246 float: f32,
248 double: f64,
249 string: String,
250 status: StatusCode,
251 int: Option<i64>,
253 int2: Option<u64>,
254 vec: Vec<i64>,
256 optvec: Option<Vec<i32>>,
258 kvp: KeyValuePair,
260 euinfo: EUInformation,
261 }
262
263 fn namespace_map() -> NamespaceMap {
264 let mut map = NamespaceMap::new();
265 map.add_namespace("uri:my:namespace");
266 map
267 }
268
269 fn get(id: &NodeId, evt: &dyn Event, field: &str) -> Variant {
270 evt.get_field(id, AttributeId::Value, &NumericRange::None, &[field.into()])
271 }
272
273 fn get_nested(id: &NodeId, evt: &dyn Event, fields: &[&str]) -> Variant {
274 let fields: Vec<QualifiedName> = fields.iter().map(|f| (*f).into()).collect();
275 evt.get_field(id, AttributeId::Value, &NumericRange::None, &fields)
276 }
277
278 #[test]
279 fn test_basic_values() {
280 let namespaces = namespace_map();
281 let mut evt = BasicValueEvent::new_event_now(
282 BasicValueEvent::event_type_id(&namespaces),
283 ByteString::from_base64("dGVzdA==").unwrap(),
284 "Some message",
285 &namespaces,
286 );
287 evt.float = 1.0;
288 evt.double = 2.0;
289 evt.string = "foo".to_owned();
290 evt.status = StatusCode::BadMaxAgeInvalid;
291 evt.kvp = KeyValuePair {
292 key: "Key".into(),
293 value: 123.into(),
294 };
295 evt.int = None;
296 evt.int2 = Some(5);
297 evt.vec = vec![1, 2, 3];
298 evt.optvec = Some(vec![3, 2, 1]);
299 evt.euinfo = EUInformation {
300 namespace_uri: "uri:my:namespace".into(),
301 unit_id: 15,
302 display_name: "Some unit".into(),
303 description: "Some unit desc".into(),
304 };
305 let id = BasicValueEvent::event_type_id(&namespaces);
306
307 assert_eq!(
309 evt.get_field(
310 &ObjectTypeId::ProgressEventType.into(),
311 AttributeId::Value,
312 &NumericRange::None,
313 &["Message".into()],
314 ),
315 Variant::Empty
316 );
317 assert_eq!(
319 evt.get_field(
320 &id,
321 AttributeId::Value,
322 &NumericRange::None,
323 &["FooBar".into()],
324 ),
325 Variant::Empty
326 );
327 assert_eq!(
329 evt.get_field(
330 &id,
331 AttributeId::Value,
332 &NumericRange::None,
333 &["Float".into(), "Child".into()],
334 ),
335 Variant::Empty
336 );
337 assert_eq!(
339 evt.get_field(
340 &id,
341 AttributeId::NodeId,
342 &NumericRange::None,
343 &["Float".into()],
344 ),
345 Variant::Empty
346 );
347
348 assert_eq!(get(&id, &evt, "Float"), Variant::from(1f32));
350 assert_eq!(get(&id, &evt, "Double"), Variant::from(2.0));
351 assert_eq!(get(&id, &evt, "String"), Variant::from("foo"));
352 assert_eq!(
353 get(&id, &evt, "Status"),
354 Variant::from(StatusCode::BadMaxAgeInvalid)
355 );
356 let kvp: KeyValuePair = match get(&id, &evt, "Kvp") {
357 Variant::ExtensionObject(o) => *o.into_inner_as().unwrap(),
358 _ => panic!("Wrong variant type"),
359 };
360 assert_eq!(kvp.key, "Key".into());
361 assert_eq!(kvp.value, 123.into());
362
363 assert_eq!(get(&id, &evt, "Int"), Variant::Empty);
364 assert_eq!(get(&id, &evt, "Int2"), Variant::from(5u64));
365 assert_eq!(get(&id, &evt, "Vec"), Variant::from(vec![1i64, 2i64, 3i64]));
366 assert_eq!(
367 get(&id, &evt, "Optvec"),
368 Variant::from(vec![3i32, 2i32, 1i32])
369 );
370 let euinfo: EUInformation = match get(&id, &evt, "Euinfo") {
371 Variant::ExtensionObject(o) => *o.into_inner_as().unwrap(),
372 _ => panic!("Wrong variant type"),
373 };
374 assert_eq!(euinfo.namespace_uri.as_ref(), "uri:my:namespace");
375 assert_eq!(euinfo.unit_id, 15);
376 assert_eq!(euinfo.display_name, "Some unit".into());
377 assert_eq!(euinfo.description, "Some unit desc".into());
378 }
379
380 #[derive(EventField, Default, Debug)]
381 struct ComplexEventField {
382 float: f32,
383 }
384
385 #[derive(EventField, Default, Debug)]
386 struct SubComplexEventField {
387 base: ComplexEventField,
388 node_id: NodeId,
389 #[opcua(rename = "gnirtS")]
390 string: UAString,
391 #[opcua(ignore)]
392 data: i32,
393 }
394
395 #[derive(EventField, Default, Debug)]
396 struct ComplexVariable {
397 node_id: NodeId,
398 value: i32,
399 id: u32,
400 #[opcua(placeholder)]
401 extra: PlaceholderEventField<i32>,
402 }
403
404 #[derive(Event)]
405 #[opcua(identifier = "s=mynestedevent", namespace = "uri:my:namespace")]
406 struct NestedEvent {
407 base: BasicValueEvent,
408 own_namespace_index: u16,
409 complex: ComplexEventField,
410 sub_complex: SubComplexEventField,
411 var: ComplexVariable,
412 #[opcua(ignore)]
413 ignored: i32,
414 #[opcua(rename = "Fancy Name")]
415 renamed: String,
416 #[opcua(placeholder)]
417 extra_fields: PlaceholderEventField<SubComplexEventField>,
418 }
419
420 #[test]
421 fn test_nested_values() {
422 let namespaces = namespace_map();
423 let mut evt = NestedEvent::new_event_now(
424 NestedEvent::event_type_id(&namespaces),
425 ByteString::from_base64("dGVzdA==").unwrap(),
426 "Some message",
427 &namespaces,
428 );
429 let id = NestedEvent::event_type_id(&namespaces);
430 evt.base.float = 2f32;
431 evt.complex.float = 3f32;
432 evt.sub_complex.base.float = 4f32;
433 evt.sub_complex.string = "foo".into();
434 evt.sub_complex.data = 15;
435 evt.ignored = 16;
436 evt.renamed = "bar".to_owned();
437 evt.sub_complex.node_id = NodeId::new(0, 15);
438 evt.var.node_id = NodeId::new(0, 16);
439 evt.var.value = 20;
440
441 assert_eq!(get(&id, &evt, "Float"), Variant::from(2f32));
443 assert_eq!(
445 get(&id, &evt, "Message"),
446 Variant::from(LocalizedText::from("Some message"))
447 );
448 assert_eq!(get(&id, &evt, "Ignored"), Variant::Empty);
450 assert_eq!(
451 get_nested(&id, &evt, &["SubComplex", "Data"]),
452 Variant::Empty
453 );
454 assert_eq!(get(&id, &evt, "Fancy Name"), Variant::from("bar"));
456 assert_eq!(
457 get_nested(&id, &evt, &["SubComplex", "gnirtS"]),
458 Variant::from("foo")
459 );
460 assert_eq!(
462 get_nested(&id, &evt, &["Complex", "Float"]),
463 Variant::from(3f32)
464 );
465 assert_eq!(
466 get_nested(&id, &evt, &["SubComplex", "Float"]),
467 Variant::from(4f32)
468 );
469
470 assert_eq!(
472 evt.get_field(
473 &id,
474 AttributeId::NodeId,
475 &NumericRange::None,
476 &["SubComplex".into()],
477 ),
478 Variant::from(NodeId::new(0, 15))
479 );
480 assert_eq!(
481 evt.get_field(
482 &id,
483 AttributeId::NodeId,
484 &NumericRange::None,
485 &["Var".into()],
486 ),
487 Variant::from(NodeId::new(0, 16))
488 );
489 assert_eq!(
490 evt.get_field(
491 &id,
492 AttributeId::Value,
493 &NumericRange::None,
494 &["Var".into()],
495 ),
496 Variant::from(20i32)
497 );
498
499 let name = QualifiedName::new(1, "Extra1");
500 evt.extra_fields
502 .insert_field(name.clone(), SubComplexEventField::default());
503 evt.extra_fields.get_field_mut(&name).unwrap().base.float = 20f32;
504 let name = QualifiedName::new(1, "Extra2");
505 evt.extra_fields
506 .insert_field(name.clone(), SubComplexEventField::default());
507 evt.extra_fields.get_field_mut(&name).unwrap().base.float = 21f32;
508
509 assert_eq!(
510 evt.get_field(
511 &id,
512 AttributeId::Value,
513 &NumericRange::None,
514 &[QualifiedName::new(1, "Extra1"), "Float".into()],
515 ),
516 Variant::from(20f32)
517 );
518 assert_eq!(
519 evt.get_field(
520 &id,
521 AttributeId::Value,
522 &NumericRange::None,
523 &[QualifiedName::new(1, "Extra2"), "Float".into()],
524 ),
525 Variant::from(21f32)
526 );
527 assert_eq!(
528 evt.get_field(
529 &id,
530 AttributeId::Value,
531 &NumericRange::None,
532 &[QualifiedName::new(1, "Extra3"), "Float".into()],
533 ),
534 Variant::Empty
535 );
536
537 evt.var.extra.insert_field("Magic".into(), 15);
538 assert_eq!(
539 evt.get_field(
540 &id,
541 AttributeId::Value,
542 &NumericRange::None,
543 &["Var".into(), "Magic".into()],
544 ),
545 Variant::from(15)
546 );
547 }
548}