opcua_types/
data_value.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5//! Contains the implementation of `DataValue`.
6
7use std::io::{Read, Write};
8
9use crate::{
10    byte_string::ByteString, date_time::*, guid::Guid, localized_text::LocalizedText,
11    node_id::NodeId, qualified_name::QualifiedName, status_code::StatusCode, string::UAString,
12    variant::Variant, BinaryDecodable, BinaryEncodable, Context, EncodingResult,
13    TimestampsToReturn,
14};
15use bitflags::bitflags;
16
17bitflags! {
18    struct DataValueFlags: u8 {
19        /// False if the Value is Null.
20        const HAS_VALUE = 0x1;
21        /// False if the StatusCode is Good.
22        const HAS_STATUS = 0x2;
23        /// False if the Source Timestamp is DateTime.MinValue.
24        const HAS_SOURCE_TIMESTAMP = 0x4;
25        /// False if the Server Timestamp is DateTime.MinValue.
26        const HAS_SERVER_TIMESTAMP = 0x8;
27        /// False if the Source Picoseconds is 0.
28        const HAS_SOURCE_PICOSECONDS = 0x10;
29        /// False if the Server Picoseconds is 0.
30        const HAS_SERVER_PICOSECONDS = 0x20;
31    }
32}
33
34#[allow(unused)]
35mod opcua {
36    pub(super) use crate as types;
37}
38
39/// A data value is a value of a variable in the OPC UA server and contains information about its
40/// value, status and change timestamps.
41#[derive(Debug, Clone, PartialEq, crate::UaNullable)]
42#[cfg_attr(
43    feature = "json",
44    derive(opcua_macros::JsonEncodable, opcua_macros::JsonDecodable)
45)]
46pub struct DataValue {
47    /// The value. BaseDataType
48    /// Not present if the Value bit in the EncodingMask is False.
49    pub value: Option<Variant>,
50    /// The status associated with the value.
51    /// Not present if the StatusCode bit in the EncodingMask is False
52    pub status: Option<StatusCode>,
53    /// The source timestamp associated with the value.
54    /// Not present if the SourceTimestamp bit in the EncodingMask is False.
55    pub source_timestamp: Option<DateTime>,
56    /// The number of 10 picosecond intervals for the SourceTimestamp.
57    /// Not present if the SourcePicoSeconds bit in the EncodingMask is False.
58    /// If the source timestamp is missing the picoseconds are ignored.
59    pub source_picoseconds: Option<u16>,
60    /// The Server timestamp associated with the value.
61    /// Not present if the ServerTimestamp bit in the EncodingMask is False.
62    pub server_timestamp: Option<DateTime>,
63    /// The number of 10 picosecond intervals for the ServerTimestamp.
64    /// Not present if the ServerPicoSeconds bit in the EncodingMask is False.
65    /// If the Server timestamp is missing the picoseconds are ignored.
66    pub server_picoseconds: Option<u16>,
67}
68
69// For some spectacularly dumb reason Status is different in JSON and XML.
70// It is named "StatusCode" in XML (5.3.1.18) and "Status" in JSON (5.4.2.18), the _only_ place
71// where this is the case on a struct. So we have to implement XML manually.
72#[cfg(feature = "xml")]
73mod xml {
74    use super::DataValue;
75
76    impl crate::xml::XmlEncodable for DataValue {
77        fn encode(
78            &self,
79            stream: &mut crate::xml::XmlStreamWriter<&mut dyn std::io::Write>,
80            ctx: &crate::Context<'_>,
81        ) -> crate::EncodingResult<()> {
82            use crate::xml::XmlWriteExt;
83            stream.encode_child("Value", &self.value, ctx)?;
84            stream.encode_child("StatusCode", &self.status, ctx)?;
85            stream.encode_child("SourceTimestamp", &self.source_timestamp, ctx)?;
86            stream.encode_child("SourcePicoseconds", &self.source_picoseconds, ctx)?;
87            stream.encode_child("ServerTimestamp", &self.server_timestamp, ctx)?;
88            stream.encode_child("ServerPicoseconds", &self.server_picoseconds, ctx)?;
89            Ok(())
90        }
91    }
92    impl crate::xml::XmlDecodable for DataValue {
93        fn decode(
94            stream: &mut crate::xml::XmlStreamReader<&mut dyn std::io::Read>,
95            ctx: &crate::Context<'_>,
96        ) -> crate::EncodingResult<Self> {
97            use crate::xml::XmlReadExt;
98            let mut value = None;
99            let mut status = None;
100            let mut source_timestamp = None;
101            let mut source_picoseconds = None;
102            let mut server_timestamp = None;
103            let mut server_picoseconds = None;
104            stream.iter_children(
105                |__key, stream, ctx| {
106                    match __key.as_str() {
107                        "Value" => {
108                            value = Some(crate::xml::XmlDecodable::decode(stream, ctx)?);
109                        }
110                        "StatusCode" => {
111                            status = Some(crate::xml::XmlDecodable::decode(stream, ctx)?);
112                        }
113                        "SourceTimestamp" => {
114                            source_timestamp = Some(crate::xml::XmlDecodable::decode(stream, ctx)?);
115                        }
116                        "SourcePicoseconds" => {
117                            source_picoseconds =
118                                Some(crate::xml::XmlDecodable::decode(stream, ctx)?);
119                        }
120                        "ServerTimestamp" => {
121                            server_timestamp = Some(crate::xml::XmlDecodable::decode(stream, ctx)?);
122                        }
123                        "ServerPicoseconds" => {
124                            server_picoseconds =
125                                Some(crate::xml::XmlDecodable::decode(stream, ctx)?);
126                        }
127                        _ => {
128                            stream.skip_value()?;
129                        }
130                    }
131                    Ok(())
132                },
133                ctx,
134            )?;
135            Ok(Self {
136                value: value.unwrap_or_default(),
137                status: status.unwrap_or_default(),
138                source_timestamp: source_timestamp.unwrap_or_default(),
139                source_picoseconds: source_picoseconds.unwrap_or_default(),
140                server_timestamp: server_timestamp.unwrap_or_default(),
141                server_picoseconds: server_picoseconds.unwrap_or_default(),
142            })
143        }
144    }
145    impl crate::xml::XmlType for DataValue {
146        const TAG: &'static str = "DataValue";
147    }
148}
149
150impl BinaryEncodable for DataValue {
151    fn byte_len(&self, ctx: &opcua::types::Context<'_>) -> usize {
152        let mut size = 1;
153        let encoding_mask = self.encoding_mask();
154        if encoding_mask.contains(DataValueFlags::HAS_VALUE) {
155            size += self.value.as_ref().unwrap().byte_len(ctx);
156        }
157        if encoding_mask.contains(DataValueFlags::HAS_STATUS) {
158            size += self.status.as_ref().unwrap().byte_len(ctx);
159        }
160        if encoding_mask.contains(DataValueFlags::HAS_SOURCE_TIMESTAMP) {
161            size += self.source_timestamp.as_ref().unwrap().byte_len(ctx);
162            if encoding_mask.contains(DataValueFlags::HAS_SOURCE_PICOSECONDS) {
163                size += self.source_picoseconds.as_ref().unwrap().byte_len(ctx);
164            }
165        }
166        if encoding_mask.contains(DataValueFlags::HAS_SERVER_TIMESTAMP) {
167            size += self.server_timestamp.as_ref().unwrap().byte_len(ctx);
168            if encoding_mask.contains(DataValueFlags::HAS_SERVER_PICOSECONDS) {
169                size += self.server_picoseconds.as_ref().unwrap().byte_len(ctx);
170            }
171        }
172        size
173    }
174
175    fn encode<S: Write + ?Sized>(&self, stream: &mut S, ctx: &Context<'_>) -> EncodingResult<()> {
176        let encoding_mask = self.encoding_mask();
177        encoding_mask.bits().encode(stream, ctx)?;
178
179        if encoding_mask.contains(DataValueFlags::HAS_VALUE) {
180            self.value.as_ref().unwrap().encode(stream, ctx)?;
181        }
182        if encoding_mask.contains(DataValueFlags::HAS_STATUS) {
183            self.status.as_ref().unwrap().bits().encode(stream, ctx)?;
184        }
185        if encoding_mask.contains(DataValueFlags::HAS_SOURCE_TIMESTAMP) {
186            self.source_timestamp
187                .as_ref()
188                .unwrap()
189                .encode(stream, ctx)?;
190            if encoding_mask.contains(DataValueFlags::HAS_SOURCE_PICOSECONDS) {
191                self.source_picoseconds
192                    .as_ref()
193                    .unwrap()
194                    .encode(stream, ctx)?;
195            }
196        }
197        if encoding_mask.contains(DataValueFlags::HAS_SERVER_TIMESTAMP) {
198            self.server_timestamp
199                .as_ref()
200                .unwrap()
201                .encode(stream, ctx)?;
202            if encoding_mask.contains(DataValueFlags::HAS_SERVER_PICOSECONDS) {
203                self.server_picoseconds
204                    .as_ref()
205                    .unwrap()
206                    .encode(stream, ctx)?;
207            }
208        }
209        Ok(())
210    }
211}
212
213impl BinaryDecodable for DataValue {
214    fn decode<S: Read + ?Sized>(stream: &mut S, ctx: &Context<'_>) -> EncodingResult<Self> {
215        let encoding_mask = DataValueFlags::from_bits_truncate(u8::decode(stream, ctx)?);
216
217        // Value
218        let value = if encoding_mask.contains(DataValueFlags::HAS_VALUE) {
219            Some(Variant::decode(stream, ctx)?)
220        } else {
221            None
222        };
223        // Status
224        let status = if encoding_mask.contains(DataValueFlags::HAS_STATUS) {
225            let status = StatusCode::from(u32::decode(stream, ctx)?);
226            Some(status)
227        } else {
228            None
229        };
230        // Source timestamp
231        let source_timestamp = if encoding_mask.contains(DataValueFlags::HAS_SOURCE_TIMESTAMP) {
232            // The source timestamp should never be adjusted, not even when ignoring clock skew
233
234            let ctx = ctx.with_zero_offset();
235            Some(DateTime::decode(stream, &ctx)?)
236        } else {
237            None
238        };
239        let source_picoseconds = if encoding_mask.contains(DataValueFlags::HAS_SOURCE_PICOSECONDS) {
240            Some(u16::decode(stream, ctx)?)
241        } else {
242            None
243        };
244        // Server timestamp
245        let server_timestamp = if encoding_mask.contains(DataValueFlags::HAS_SERVER_TIMESTAMP) {
246            Some(DateTime::decode(stream, ctx)?)
247        } else {
248            None
249        };
250        let server_picoseconds = if encoding_mask.contains(DataValueFlags::HAS_SERVER_PICOSECONDS) {
251            Some(u16::decode(stream, ctx)?)
252        } else {
253            None
254        };
255        // Pico second values are discarded if associated timestamp is not supplied
256        Ok(DataValue {
257            value,
258            status,
259            source_picoseconds: if source_timestamp.is_some() {
260                source_picoseconds
261            } else {
262                None
263            },
264            source_timestamp,
265            server_picoseconds: if server_timestamp.is_some() {
266                server_picoseconds
267            } else {
268                None
269            },
270            server_timestamp,
271        })
272    }
273}
274
275// It would be nice if everything from here to the ... below could be condensed into a single
276// trait impl somehow because it's more or less duplicating all the code in Variant.
277
278impl From<bool> for DataValue {
279    fn from(v: bool) -> Self {
280        Self::from(Variant::from(v))
281    }
282}
283
284impl From<u8> for DataValue {
285    fn from(v: u8) -> Self {
286        Self::from(Variant::from(v))
287    }
288}
289
290impl From<i8> for DataValue {
291    fn from(v: i8) -> Self {
292        Self::from(Variant::from(v))
293    }
294}
295
296impl From<i16> for DataValue {
297    fn from(v: i16) -> Self {
298        Self::from(Variant::from(v))
299    }
300}
301
302impl From<u16> for DataValue {
303    fn from(v: u16) -> Self {
304        Self::from(Variant::from(v))
305    }
306}
307
308impl From<i32> for DataValue {
309    fn from(v: i32) -> Self {
310        Self::from(Variant::from(v))
311    }
312}
313
314impl From<u32> for DataValue {
315    fn from(v: u32) -> Self {
316        Self::from(Variant::from(v))
317    }
318}
319
320impl From<i64> for DataValue {
321    fn from(v: i64) -> Self {
322        Self::from(Variant::from(v))
323    }
324}
325
326impl From<u64> for DataValue {
327    fn from(v: u64) -> Self {
328        Self::from(Variant::from(v))
329    }
330}
331
332impl From<f32> for DataValue {
333    fn from(v: f32) -> Self {
334        Self::from(Variant::from(v))
335    }
336}
337
338impl From<f64> for DataValue {
339    fn from(v: f64) -> Self {
340        Self::from(Variant::from(v))
341    }
342}
343
344impl<'a> From<&'a str> for DataValue {
345    fn from(v: &'a str) -> Self {
346        Self::from(Variant::from(v))
347    }
348}
349
350impl From<String> for DataValue {
351    fn from(v: String) -> Self {
352        Self::from(Variant::from(v))
353    }
354}
355
356impl From<UAString> for DataValue {
357    fn from(v: UAString) -> Self {
358        Self::from(Variant::from(v))
359    }
360}
361
362impl From<DateTime> for DataValue {
363    fn from(v: DateTime) -> Self {
364        Self::from(Variant::from(v))
365    }
366}
367
368impl From<Guid> for DataValue {
369    fn from(v: Guid) -> Self {
370        Self::from(Variant::from(v))
371    }
372}
373
374impl From<StatusCode> for DataValue {
375    fn from(v: StatusCode) -> Self {
376        Self::from(Variant::from(v))
377    }
378}
379
380impl From<ByteString> for DataValue {
381    fn from(v: ByteString) -> Self {
382        Self::from(Variant::from(v))
383    }
384}
385
386impl From<QualifiedName> for DataValue {
387    fn from(v: QualifiedName) -> Self {
388        Self::from(Variant::from(v))
389    }
390}
391
392impl From<LocalizedText> for DataValue {
393    fn from(v: LocalizedText) -> Self {
394        Self::from(Variant::from(v))
395    }
396}
397
398impl From<NodeId> for DataValue {
399    fn from(v: NodeId) -> Self {
400        Self::from(Variant::from(v))
401    }
402}
403//... (see above)
404
405impl From<Variant> for DataValue {
406    fn from(v: Variant) -> Self {
407        DataValue::value_only(v)
408    }
409}
410
411impl From<(Variant, StatusCode)> for DataValue {
412    fn from(v: (Variant, StatusCode)) -> Self {
413        DataValue {
414            value: Some(v.0),
415            status: Some(v.1),
416            source_timestamp: None,
417            source_picoseconds: None,
418            server_timestamp: None,
419            server_picoseconds: None,
420        }
421    }
422}
423
424/*
425impl<'a> From<(Variant, &'a DateTime)> for DataValue {
426    fn from(v: (Variant, &'a DateTime)) -> Self {
427        DataValue {
428            value: Some(v.0),
429            status: Some(StatusCode::Good),
430            source_timestamp: Some(v.1.clone()),
431            source_picoseconds: Some(0),
432            server_timestamp: Some(v.1.clone()),
433            server_picoseconds: Some(0),
434        }
435    }
436}
437
438impl<'a> From<(Variant, &'a DateTime, &'a DateTime)> for DataValue {
439    fn from(v: (Variant, &'a DateTime, &'a DateTime)) -> Self {
440        // First date is source time, second is server time
441        DataValue {
442            value: Some(v.0),
443            status: Some(StatusCode::Good),
444            source_timestamp: Some(v.1.clone()),
445            source_picoseconds: Some(0),
446            server_timestamp: Some(v.2.clone()),
447            server_picoseconds: Some(0),
448        }
449    }
450}
451*/
452
453impl Default for DataValue {
454    fn default() -> Self {
455        Self::null()
456    }
457}
458
459impl DataValue {
460    /// Creates a `DataValue` from the supplied value with nothing else.
461    pub fn value_only<V>(value: V) -> DataValue
462    where
463        V: Into<Variant>,
464    {
465        DataValue {
466            value: Some(value.into()),
467            status: None,
468            source_timestamp: None,
469            source_picoseconds: None,
470            server_timestamp: None,
471            server_picoseconds: None,
472        }
473    }
474
475    /// Creates a `DataValue` from the supplied value AND a timestamp for now. If you are passing a value to the Attribute::Write service
476    /// on a server from a server, you may consider this from the specification:
477    ///
478    /// _If the SourceTimestamp or the ServerTimestamp is specified, the Server shall use these values.
479    /// The Server returns a Bad_WriteNotSupported error if it does not support writing of timestamps_
480    ///
481    /// In which case, use the `value_only()` constructor, or make explicit which fields you pass.
482    pub fn new_now<V>(value: V) -> DataValue
483    where
484        V: Into<Variant>,
485    {
486        let now = DateTime::now();
487        DataValue {
488            value: Some(value.into()),
489            status: Some(StatusCode::Good),
490            source_timestamp: Some(now),
491            source_picoseconds: Some(0),
492            server_timestamp: Some(now),
493            server_picoseconds: Some(0),
494        }
495    }
496
497    /// Creates a `DataValue` from the supplied value and timestamp. If you are passing a value to the Attribute::Write service
498    /// on a server from a server, you may consider this from the specification:
499    ///
500    /// _If the SourceTimestamp or the ServerTimestamp is specified, the Server shall use these values.
501    /// The Server returns a Bad_WriteNotSupported error if it does not support writing of timestamps_
502    ///
503    /// In which case, use the `value_only()` constructor, or make explicit which fields you pass.
504    pub fn new_at<V>(value: V, time: DateTime) -> DataValue
505    where
506        V: Into<Variant>,
507    {
508        DataValue {
509            value: Some(value.into()),
510            status: Some(StatusCode::Good),
511            source_timestamp: Some(time),
512            source_picoseconds: Some(0),
513            server_timestamp: Some(time),
514            server_picoseconds: Some(0),
515        }
516    }
517
518    /// Creates a `DataValue` from the supplied value AND a timestamp for now. If you are passing a value to the Attribute::Write service
519    /// on a server from a server, you may consider this from the specification:
520    ///
521    /// _If the SourceTimestamp or the ServerTimestamp is specified, the Server shall use these values.
522    /// The Server returns a Bad_WriteNotSupported error if it does not support writing of timestamps_
523    ///
524    /// In which case, use the `value_only()` constructor, or make explicit which fields you pass.
525    pub fn new_now_status<V>(value: V, status: StatusCode) -> DataValue
526    where
527        V: Into<Variant>,
528    {
529        let now = DateTime::now();
530        DataValue {
531            value: Some(value.into()),
532            status: Some(status),
533            source_timestamp: Some(now),
534            source_picoseconds: Some(0),
535            server_timestamp: Some(now),
536            server_picoseconds: Some(0),
537        }
538    }
539
540    /// Creates a `DataValue` from the supplied value and timestamp. If you are passing a value to the Attribute::Write service
541    /// on a server from a server, you may consider this from the specification:
542    ///
543    /// _If the SourceTimestamp or the ServerTimestamp is specified, the Server shall use these values.
544    /// The Server returns a Bad_WriteNotSupported error if it does not support writing of timestamps_
545    ///
546    /// In which case, use the `value_only()` constructor, or make explicit which fields you pass.
547    pub fn new_at_status<V>(value: V, time: DateTime, status: StatusCode) -> DataValue
548    where
549        V: Into<Variant>,
550    {
551        DataValue {
552            value: Some(value.into()),
553            status: Some(status),
554            source_timestamp: Some(time),
555            source_picoseconds: Some(0),
556            server_timestamp: Some(time),
557            server_picoseconds: Some(0),
558        }
559    }
560
561    /// Creates an empty DataValue
562    pub fn null() -> DataValue {
563        DataValue {
564            value: None,
565            status: None,
566            source_timestamp: None,
567            source_picoseconds: None,
568            server_timestamp: None,
569            server_picoseconds: None,
570        }
571    }
572
573    /// Sets the value of the data value, updating the timestamps at the same point
574    pub fn set_value<V>(
575        &mut self,
576        value: V,
577        source_timestamp: &DateTime,
578        server_timestamp: &DateTime,
579    ) where
580        V: Into<Variant>,
581    {
582        self.value = Some(value.into());
583        self.source_timestamp = Some(*source_timestamp);
584        self.source_picoseconds = Some(0);
585        self.server_timestamp = Some(*server_timestamp);
586        self.server_picoseconds = Some(0);
587    }
588
589    /// Sets the timestamps of the data value based on supplied timestamps to return
590    pub fn set_timestamps(
591        &mut self,
592        timestamps_to_return: TimestampsToReturn,
593        source_timestamp: DateTime,
594        server_timestamp: DateTime,
595    ) {
596        match timestamps_to_return {
597            TimestampsToReturn::Source => {
598                self.source_timestamp = Some(source_timestamp);
599                self.source_picoseconds = Some(0);
600                self.server_timestamp = None;
601                self.server_picoseconds = None;
602            }
603            TimestampsToReturn::Server => {
604                self.source_timestamp = None;
605                self.source_picoseconds = None;
606                self.server_timestamp = Some(server_timestamp);
607                self.server_picoseconds = Some(0);
608            }
609            TimestampsToReturn::Both => {
610                self.source_timestamp = Some(source_timestamp);
611                self.source_picoseconds = Some(0);
612                self.server_timestamp = Some(server_timestamp);
613                self.server_picoseconds = Some(0);
614            }
615            TimestampsToReturn::Neither => {
616                self.source_timestamp = None;
617                self.source_picoseconds = None;
618                self.server_timestamp = None;
619                self.server_picoseconds = None;
620            }
621            _ => {}
622        }
623    }
624
625    /// Returns the status code or Good if there is no code on the value
626    pub fn status(&self) -> StatusCode {
627        self.status.map_or(StatusCode::Good, |s| s)
628    }
629
630    /// Test if the value held by this data value is known to be good
631    /// Anything other than Good is assumed to be invalid.
632    pub fn is_valid(&self) -> bool {
633        self.status().is_good()
634    }
635
636    fn encoding_mask(&self) -> DataValueFlags {
637        let mut encoding_mask = DataValueFlags::empty();
638        if self.value.is_some() {
639            encoding_mask |= DataValueFlags::HAS_VALUE;
640        }
641        if self.status.is_some() {
642            encoding_mask |= DataValueFlags::HAS_STATUS;
643        }
644        if self.source_timestamp.is_some() {
645            encoding_mask |= DataValueFlags::HAS_SOURCE_TIMESTAMP;
646            if self.source_picoseconds.is_some() {
647                encoding_mask |= DataValueFlags::HAS_SOURCE_PICOSECONDS;
648            }
649        }
650        if self.server_timestamp.is_some() {
651            encoding_mask |= DataValueFlags::HAS_SERVER_TIMESTAMP;
652            if self.server_picoseconds.is_some() {
653                encoding_mask |= DataValueFlags::HAS_SERVER_PICOSECONDS;
654            }
655        }
656        encoding_mask
657    }
658}