mabi_opcua/codec/
data_value.rs1use bytes::{BufMut, Bytes, BytesMut};
8use chrono::{DateTime, Utc};
9
10use crate::codec::decoder::BinaryDecodable;
11use crate::codec::encoder::BinaryEncodable;
12use crate::error::OpcUaResult;
13use crate::types::{DataValue, StatusCode, Variant};
14
15const HAS_VALUE: u8 = 0x01;
17const HAS_STATUS: u8 = 0x02;
18const HAS_SOURCE_TIMESTAMP: u8 = 0x04;
19const HAS_SERVER_TIMESTAMP: u8 = 0x08;
20const HAS_SOURCE_PICOSECONDS: u8 = 0x10;
21const HAS_SERVER_PICOSECONDS: u8 = 0x20;
22
23impl BinaryEncodable for DataValue {
24 fn encode(&self, buf: &mut BytesMut) -> OpcUaResult<()> {
25 let mut mask: u8 = 0;
26 if self.value().is_some() {
27 mask |= HAS_VALUE;
28 }
29 if self.status().raw() != 0 {
30 mask |= HAS_STATUS;
31 }
32 if self.source_timestamp().is_some() {
33 mask |= HAS_SOURCE_TIMESTAMP;
34 }
35 if self.server_timestamp().is_some() {
36 mask |= HAS_SERVER_TIMESTAMP;
37 }
38 if self.source_picoseconds() != 0 {
39 mask |= HAS_SOURCE_PICOSECONDS;
40 }
41 if self.server_picoseconds() != 0 {
42 mask |= HAS_SERVER_PICOSECONDS;
43 }
44
45 buf.put_u8(mask);
46
47 if let Some(v) = self.value() {
48 v.encode(buf)?;
49 }
50 if (mask & HAS_STATUS) != 0 {
51 buf.put_u32_le(self.status().raw());
52 }
53 if let Some(ts) = self.source_timestamp() {
54 ts.encode(buf)?;
55 }
56 if (mask & HAS_SOURCE_PICOSECONDS) != 0 {
57 buf.put_u16_le(self.source_picoseconds());
58 }
59 if let Some(ts) = self.server_timestamp() {
60 ts.encode(buf)?;
61 }
62 if (mask & HAS_SERVER_PICOSECONDS) != 0 {
63 buf.put_u16_le(self.server_picoseconds());
64 }
65
66 Ok(())
67 }
68
69 fn encoded_size(&self) -> usize {
70 let mut size = 1; if let Some(v) = self.value() {
72 size += v.encoded_size();
73 }
74 if self.status().raw() != 0 {
75 size += 4;
76 }
77 if self.source_timestamp().is_some() {
78 size += 8;
79 }
80 if self.source_picoseconds() != 0 {
81 size += 2;
82 }
83 if self.server_timestamp().is_some() {
84 size += 8;
85 }
86 if self.server_picoseconds() != 0 {
87 size += 2;
88 }
89 size
90 }
91}
92
93impl BinaryDecodable for DataValue {
94 fn decode(buf: &mut Bytes) -> OpcUaResult<Self> {
95 let mask = u8::decode(buf)?;
96
97 let value = if (mask & HAS_VALUE) != 0 {
98 Some(Variant::decode(buf)?)
99 } else {
100 None
101 };
102
103 let status = if (mask & HAS_STATUS) != 0 {
104 StatusCode::from_raw(u32::decode(buf)?)
105 } else {
106 StatusCode::GOOD
107 };
108
109 let source_timestamp = if (mask & HAS_SOURCE_TIMESTAMP) != 0 {
110 Some(DateTime::<Utc>::decode(buf)?)
111 } else {
112 None
113 };
114
115 let source_picoseconds = if (mask & HAS_SOURCE_PICOSECONDS) != 0 {
116 u16::decode(buf)?
117 } else {
118 0
119 };
120
121 let server_timestamp = if (mask & HAS_SERVER_TIMESTAMP) != 0 {
122 Some(DateTime::<Utc>::decode(buf)?)
123 } else {
124 None
125 };
126
127 let server_picoseconds = if (mask & HAS_SERVER_PICOSECONDS) != 0 {
128 u16::decode(buf)?
129 } else {
130 0
131 };
132
133 let mut dv = if let Some(value) = value {
134 DataValue::with_status(value, status)
135 } else {
136 let mut dv = DataValue::null();
137 dv.set_status(status);
138 dv
139 };
140
141 if let Some(ts) = source_timestamp {
143 dv.set_source_timestamp(ts);
144 }
145 if source_picoseconds != 0 {
146 dv.set_source_picoseconds(source_picoseconds);
147 }
148 if let Some(ts) = server_timestamp {
149 dv.set_server_timestamp(ts);
150 }
151 if server_picoseconds != 0 {
152 dv.set_server_picoseconds(server_picoseconds);
153 }
154
155 Ok(dv)
156 }
157}
158
159use crate::nodes::{LocalizedText, QualifiedName};
164
165impl BinaryEncodable for QualifiedName {
166 fn encode(&self, buf: &mut BytesMut) -> OpcUaResult<()> {
167 self.namespace_index.encode(buf)?;
168 self.name.encode(buf)?;
169 Ok(())
170 }
171 fn encoded_size(&self) -> usize {
172 2 + 4 + self.name.len()
173 }
174}
175
176impl BinaryDecodable for QualifiedName {
177 fn decode(buf: &mut Bytes) -> OpcUaResult<Self> {
178 let namespace_index = u16::decode(buf)?;
179 let name = String::decode(buf)?;
180 Ok(QualifiedName::new(namespace_index, name))
181 }
182}
183
184impl BinaryEncodable for LocalizedText {
185 fn encode(&self, buf: &mut BytesMut) -> OpcUaResult<()> {
186 let mut mask: u8 = 0;
187 if !self.locale.is_empty() {
188 mask |= 0x01;
189 }
190 if !self.text.is_empty() {
191 mask |= 0x02;
192 }
193 buf.put_u8(mask);
194 if (mask & 0x01) != 0 {
195 self.locale.encode(buf)?;
196 }
197 if (mask & 0x02) != 0 {
198 self.text.encode(buf)?;
199 }
200 Ok(())
201 }
202 fn encoded_size(&self) -> usize {
203 let mut size = 1; if !self.locale.is_empty() {
205 size += 4 + self.locale.len();
206 }
207 if !self.text.is_empty() {
208 size += 4 + self.text.len();
209 }
210 size
211 }
212}
213
214impl BinaryDecodable for LocalizedText {
215 fn decode(buf: &mut Bytes) -> OpcUaResult<Self> {
216 let mask = u8::decode(buf)?;
217 let locale = if (mask & 0x01) != 0 {
218 String::decode(buf)?
219 } else {
220 String::new()
221 };
222 let text = if (mask & 0x02) != 0 {
223 String::decode(buf)?
224 } else {
225 String::new()
226 };
227 Ok(LocalizedText::new(locale, text))
228 }
229}
230
231impl BinaryEncodable for StatusCode {
236 fn encode(&self, buf: &mut BytesMut) -> OpcUaResult<()> {
237 buf.put_u32_le(self.raw());
238 Ok(())
239 }
240 fn encoded_size(&self) -> usize {
241 4
242 }
243}
244
245impl BinaryDecodable for StatusCode {
246 fn decode(buf: &mut Bytes) -> OpcUaResult<Self> {
247 Ok(StatusCode::from_raw(u32::decode(buf)?))
248 }
249}
250
251#[derive(Debug, Clone)]
262pub struct ExtensionObject {
263 pub type_id: crate::types::NodeId,
265 pub body: Option<Vec<u8>>,
267}
268
269impl BinaryEncodable for ExtensionObject {
270 fn encode(&self, buf: &mut BytesMut) -> OpcUaResult<()> {
271 self.type_id.encode(buf)?;
272 match &self.body {
273 Some(body) => {
274 buf.put_u8(0x01); buf.put_i32_le(body.len() as i32);
276 buf.put_slice(body);
277 }
278 None => {
279 buf.put_u8(0x00); }
281 }
282 Ok(())
283 }
284 fn encoded_size(&self) -> usize {
285 self.type_id.encoded_size()
286 + 1
287 + match &self.body {
288 Some(body) => 4 + body.len(),
289 None => 0,
290 }
291 }
292}
293
294impl BinaryDecodable for ExtensionObject {
295 fn decode(buf: &mut Bytes) -> OpcUaResult<Self> {
296 let type_id = crate::types::NodeId::decode(buf)?;
297 let encoding = u8::decode(buf)?;
298 let body = match encoding {
299 0x00 => None,
300 0x01 => {
301 let body_bytes = Vec::<u8>::decode(buf)?;
302 Some(body_bytes)
303 }
304 0x02 => {
305 let xml = String::decode(buf)?;
307 Some(xml.into_bytes())
308 }
309 _ => {
310 return Err(crate::error::OpcUaError::Codec(format!(
311 "Unknown ExtensionObject encoding: 0x{:02X}",
312 encoding
313 )));
314 }
315 };
316 Ok(ExtensionObject { type_id, body })
317 }
318}
319
320#[derive(Debug, Clone, Default)]
326pub struct DiagnosticInfo {
327 pub symbolic_id: Option<i32>,
328 pub namespace_uri: Option<i32>,
329 pub locale: Option<i32>,
330 pub localized_text: Option<i32>,
331 pub additional_info: Option<String>,
332 pub inner_status_code: Option<StatusCode>,
333 pub inner_diagnostic_info: Option<Box<DiagnosticInfo>>,
334}
335
336impl BinaryEncodable for DiagnosticInfo {
337 fn encode(&self, buf: &mut BytesMut) -> OpcUaResult<()> {
338 let mut mask: u8 = 0;
339 if self.symbolic_id.is_some() {
340 mask |= 0x01;
341 }
342 if self.namespace_uri.is_some() {
343 mask |= 0x02;
344 }
345 if self.localized_text.is_some() {
346 mask |= 0x04;
347 }
348 if self.locale.is_some() {
349 mask |= 0x08;
350 }
351 if self.additional_info.is_some() {
352 mask |= 0x10;
353 }
354 if self.inner_status_code.is_some() {
355 mask |= 0x20;
356 }
357 if self.inner_diagnostic_info.is_some() {
358 mask |= 0x40;
359 }
360
361 buf.put_u8(mask);
362 if let Some(v) = self.symbolic_id {
363 v.encode(buf)?;
364 }
365 if let Some(v) = self.namespace_uri {
366 v.encode(buf)?;
367 }
368 if let Some(v) = self.localized_text {
369 v.encode(buf)?;
370 }
371 if let Some(v) = self.locale {
372 v.encode(buf)?;
373 }
374 if let Some(v) = &self.additional_info {
375 v.encode(buf)?;
376 }
377 if let Some(v) = &self.inner_status_code {
378 v.encode(buf)?;
379 }
380 if let Some(v) = &self.inner_diagnostic_info {
381 v.encode(buf)?;
382 }
383 Ok(())
384 }
385 fn encoded_size(&self) -> usize {
386 let mut size = 1;
387 if self.symbolic_id.is_some() {
388 size += 4;
389 }
390 if self.namespace_uri.is_some() {
391 size += 4;
392 }
393 if self.localized_text.is_some() {
394 size += 4;
395 }
396 if self.locale.is_some() {
397 size += 4;
398 }
399 if let Some(s) = &self.additional_info {
400 size += 4 + s.len();
401 }
402 if self.inner_status_code.is_some() {
403 size += 4;
404 }
405 if let Some(d) = &self.inner_diagnostic_info {
406 size += d.encoded_size();
407 }
408 size
409 }
410}
411
412impl BinaryDecodable for DiagnosticInfo {
413 fn decode(buf: &mut Bytes) -> OpcUaResult<Self> {
414 let mask = u8::decode(buf)?;
415 Ok(DiagnosticInfo {
416 symbolic_id: if (mask & 0x01) != 0 {
417 Some(i32::decode(buf)?)
418 } else {
419 None
420 },
421 namespace_uri: if (mask & 0x02) != 0 {
422 Some(i32::decode(buf)?)
423 } else {
424 None
425 },
426 localized_text: if (mask & 0x04) != 0 {
427 Some(i32::decode(buf)?)
428 } else {
429 None
430 },
431 locale: if (mask & 0x08) != 0 {
432 Some(i32::decode(buf)?)
433 } else {
434 None
435 },
436 additional_info: if (mask & 0x10) != 0 {
437 Some(String::decode(buf)?)
438 } else {
439 None
440 },
441 inner_status_code: if (mask & 0x20) != 0 {
442 Some(StatusCode::decode(buf)?)
443 } else {
444 None
445 },
446 inner_diagnostic_info: if (mask & 0x40) != 0 {
447 Some(Box::new(DiagnosticInfo::decode(buf)?))
448 } else {
449 None
450 },
451 })
452 }
453}
454
455#[cfg(test)]
456mod tests {
457 use super::*;
458
459 fn roundtrip_dv(dv: &DataValue) -> DataValue {
460 let mut buf = BytesMut::new();
461 dv.encode(&mut buf).unwrap();
462 let mut data = buf.freeze();
463 DataValue::decode(&mut data).unwrap()
464 }
465
466 #[test]
467 fn test_null_datavalue() {
468 let dv = DataValue::null();
469 let result = roundtrip_dv(&dv);
470 assert!(result.value().is_none());
471 }
472
473 #[test]
474 fn test_datavalue_with_value() {
475 let dv = DataValue::new(Variant::Double(42.0));
476 let result = roundtrip_dv(&dv);
477 assert_eq!(result.value(), Some(&Variant::Double(42.0)));
478 }
479
480 #[test]
481 fn test_extension_object_roundtrip() {
482 let eo = ExtensionObject {
483 type_id: crate::types::NodeId::numeric(0, 461),
484 body: Some(vec![1, 2, 3, 4]),
485 };
486 let mut buf = BytesMut::new();
487 eo.encode(&mut buf).unwrap();
488 let mut data = buf.freeze();
489 let result = ExtensionObject::decode(&mut data).unwrap();
490 assert_eq!(result.type_id, eo.type_id);
491 assert_eq!(result.body, eo.body);
492 }
493
494 #[test]
495 fn test_qualified_name_roundtrip() {
496 let qn = QualifiedName::new(2, "Temperature");
497 let mut buf = BytesMut::new();
498 qn.encode(&mut buf).unwrap();
499 let mut data = buf.freeze();
500 let result = QualifiedName::decode(&mut data).unwrap();
501 assert_eq!(result.namespace_index, 2);
502 assert_eq!(result.name, "Temperature");
503 }
504
505 #[test]
506 fn test_localized_text_roundtrip() {
507 let lt = LocalizedText::new("en", "Hello");
508 let mut buf = BytesMut::new();
509 lt.encode(&mut buf).unwrap();
510 let mut data = buf.freeze();
511 let result = LocalizedText::decode(&mut data).unwrap();
512 assert_eq!(result.locale, "en");
513 assert_eq!(result.text, "Hello");
514 }
515
516 #[test]
517 fn test_diagnostic_info_roundtrip() {
518 let di = DiagnosticInfo {
519 symbolic_id: Some(1),
520 additional_info: Some("test".into()),
521 ..Default::default()
522 };
523 let mut buf = BytesMut::new();
524 di.encode(&mut buf).unwrap();
525 let mut data = buf.freeze();
526 let result = DiagnosticInfo::decode(&mut data).unwrap();
527 assert_eq!(result.symbolic_id, Some(1));
528 assert_eq!(result.additional_info.as_deref(), Some("test"));
529 }
530}