Skip to main content

opcua_nodes/
variable.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 `Variable` and `VariableBuilder`.
6
7use std::convert::Into;
8
9use opcua_types::{
10    AttributeId, AttributesMask, DataEncoding, DataTypeId, DataValue, DateTime, NumericRange,
11    StatusCode, TimestampsToReturn, TryFromVariant, VariableAttributes, Variant,
12};
13use tracing::error;
14
15use crate::FromAttributesError;
16
17use super::base::Base;
18use super::{AccessLevel, Node, NodeBase};
19
20// This is a builder object for constructing variable nodes programmatically.
21
22node_builder_impl!(VariableBuilder, Variable);
23node_builder_impl_component_of!(VariableBuilder);
24node_builder_impl_property_of!(VariableBuilder);
25
26impl VariableBuilder {
27    /// Sets the value of the variable.
28    pub fn value(mut self, value: impl Into<Variant>) -> Self {
29        let _ = self.node.set_value(&NumericRange::None, value);
30        self
31    }
32
33    /// Sets the data type of the variable.
34    pub fn data_type(mut self, data_type: impl Into<NodeId>) -> Self {
35        self.node.set_data_type(data_type);
36        self
37    }
38
39    /// Sets the historizing flag for the variable.
40    pub fn historizing(mut self, historizing: bool) -> Self {
41        self.node.set_historizing(historizing);
42        self
43    }
44
45    /// Sets the access level for the variable.
46    pub fn access_level(mut self, access_level: AccessLevel) -> Self {
47        self.node.set_access_level(access_level);
48        self
49    }
50
51    /// Sets the user access level for the variable.
52    pub fn user_access_level(mut self, user_access_level: AccessLevel) -> Self {
53        self.node.set_user_access_level(user_access_level);
54        self
55    }
56
57    /// Sets the value rank for the variable.
58    pub fn value_rank(mut self, value_rank: i32) -> Self {
59        self.node.set_value_rank(value_rank);
60        self
61    }
62
63    /// Sets the array dimensions for the variable.
64    pub fn array_dimensions(mut self, array_dimensions: &[u32]) -> Self {
65        self.node.set_array_dimensions(array_dimensions);
66        self
67    }
68
69    /// Set the write mask for this variable.
70    pub fn write_mask(mut self, write_mask: WriteMask) -> Self {
71        self.node.set_write_mask(write_mask);
72        self
73    }
74
75    /// Makes the variable writable (by default it isn't)
76    pub fn writable(mut self) -> Self {
77        self.node
78            .set_user_access_level(self.node.user_access_level() | AccessLevel::CURRENT_WRITE);
79        self.node
80            .set_access_level(self.node.access_level() | AccessLevel::CURRENT_WRITE);
81        self
82    }
83
84    /// Makes the variable history-readable
85    pub fn history_readable(mut self) -> Self {
86        self.node
87            .set_user_access_level(self.node.user_access_level() | AccessLevel::HISTORY_READ);
88        self.node
89            .set_access_level(self.node.access_level() | AccessLevel::HISTORY_READ);
90        self
91    }
92
93    /// Makes the variable history-updateable
94    pub fn history_updatable(mut self) -> Self {
95        self.node
96            .set_user_access_level(self.node.user_access_level() | AccessLevel::HISTORY_WRITE);
97        self.node
98            .set_access_level(self.node.access_level() | AccessLevel::HISTORY_WRITE);
99        self
100    }
101
102    /// Sets the minimum sampling interval for the variable.
103    pub fn minimum_sampling_interval(mut self, minimum_sampling_interval: f64) -> Self {
104        self.node
105            .set_minimum_sampling_interval(minimum_sampling_interval);
106        self
107    }
108
109    /// Add a reference to the variable indicating it has a type of another node.
110    pub fn has_type_definition<T>(self, type_id: T) -> Self
111    where
112        T: Into<NodeId>,
113    {
114        self.reference(
115            type_id,
116            ReferenceTypeId::HasTypeDefinition,
117            ReferenceDirection::Forward,
118        )
119    }
120
121    /// Add a reference to the variable indicating it has a modelling rule of another node.
122    pub fn has_modelling_rule<T>(self, type_id: T) -> Self
123    where
124        T: Into<NodeId>,
125    {
126        self.reference(
127            type_id,
128            ReferenceTypeId::HasModellingRule,
129            ReferenceDirection::Forward,
130        )
131    }
132}
133
134// Note we use derivative builder macro so we can skip over the value getter / setter
135
136/// A `Variable` is a type of node within the `AddressSpace`.
137#[derive(Debug)]
138pub struct Variable {
139    pub(super) base: Base,
140    pub(super) data_type: NodeId,
141    pub(super) historizing: bool,
142    pub(super) value_rank: i32,
143    pub(super) value: DataValue,
144    pub(super) access_level: u8,
145    pub(super) user_access_level: u8,
146    pub(super) array_dimensions: Option<Vec<u32>>,
147    pub(super) minimum_sampling_interval: Option<f64>,
148}
149
150impl Default for Variable {
151    fn default() -> Self {
152        Self {
153            base: Base::new(NodeClass::Variable, &NodeId::null(), "", ""),
154            data_type: NodeId::null(),
155            historizing: false,
156            value_rank: -1,
157            value: Variant::Empty.into(),
158            access_level: AccessLevel::CURRENT_READ.bits(),
159            user_access_level: AccessLevel::CURRENT_READ.bits(),
160            array_dimensions: None,
161            minimum_sampling_interval: None,
162        }
163    }
164}
165
166node_base_impl!(Variable);
167
168impl Node for Variable {
169    fn get_attribute_max_age(
170        &self,
171        timestamps_to_return: TimestampsToReturn,
172        attribute_id: AttributeId,
173        index_range: &NumericRange,
174        data_encoding: &DataEncoding,
175        max_age: f64,
176    ) -> Option<DataValue> {
177        /* TODO for Variables derived from the Structure data type, the AttributeId::Value should check
178        data encoding and return the value encoded according "Default Binary", "Default XML" or "Default JSON" (OPC UA 1.04).
179        */
180        match attribute_id {
181            // Mandatory attributes
182            AttributeId::Value => {
183                Some(self.value(timestamps_to_return, index_range, data_encoding, max_age))
184            }
185            AttributeId::DataType => Some(self.data_type().into()),
186            AttributeId::Historizing => Some(self.historizing().into()),
187            AttributeId::ValueRank => Some(self.value_rank().into()),
188            AttributeId::AccessLevel => Some(self.access_level().bits().into()),
189            AttributeId::UserAccessLevel => Some(self.user_access_level().bits().into()),
190            // Optional attributes
191            AttributeId::ArrayDimensions => {
192                self.array_dimensions().map(|v| Variant::from(v).into())
193            }
194            AttributeId::MinimumSamplingInterval => {
195                self.minimum_sampling_interval().map(|v| v.into())
196            }
197            _ => self.base.get_attribute_max_age(
198                timestamps_to_return,
199                attribute_id,
200                index_range,
201                data_encoding,
202                max_age,
203            ),
204        }
205    }
206
207    fn set_attribute(
208        &mut self,
209        attribute_id: AttributeId,
210        value: Variant,
211    ) -> Result<(), StatusCode> {
212        match attribute_id {
213            AttributeId::DataType => {
214                if let Variant::NodeId(v) = value {
215                    self.set_data_type(*v);
216                    Ok(())
217                } else {
218                    Err(StatusCode::BadTypeMismatch)
219                }
220            }
221            AttributeId::Historizing => {
222                if let Variant::Boolean(v) = value {
223                    self.set_historizing(v);
224                    Ok(())
225                } else {
226                    Err(StatusCode::BadTypeMismatch)
227                }
228            }
229            AttributeId::ValueRank => {
230                if let Variant::Int32(v) = value {
231                    self.set_value_rank(v);
232                    Ok(())
233                } else {
234                    Err(StatusCode::BadTypeMismatch)
235                }
236            }
237            AttributeId::Value => {
238                // Call set_value directly
239                self.set_value(&NumericRange::None, value)
240            }
241            AttributeId::AccessLevel => {
242                if let Variant::Byte(v) = value {
243                    self.set_access_level(AccessLevel::from_bits_truncate(v));
244                    Ok(())
245                } else {
246                    Err(StatusCode::BadTypeMismatch)
247                }
248            }
249            AttributeId::UserAccessLevel => {
250                if let Variant::Byte(v) = value {
251                    self.set_user_access_level(AccessLevel::from_bits_truncate(v));
252                    Ok(())
253                } else {
254                    Err(StatusCode::BadTypeMismatch)
255                }
256            }
257            AttributeId::ArrayDimensions => {
258                let array_dimensions = <Vec<u32>>::try_from_variant(value);
259                if let Ok(array_dimensions) = array_dimensions {
260                    self.set_array_dimensions(&array_dimensions);
261                    Ok(())
262                } else {
263                    Err(StatusCode::BadTypeMismatch)
264                }
265            }
266            AttributeId::MinimumSamplingInterval => {
267                if let Variant::Double(v) = value {
268                    self.set_minimum_sampling_interval(v);
269                    Ok(())
270                } else {
271                    Err(StatusCode::BadTypeMismatch)
272                }
273            }
274            _ => self.base.set_attribute(attribute_id, value),
275        }
276    }
277}
278
279impl Variable {
280    /// Creates a new variable. Note that data type, value rank and historizing are mandatory
281    /// attributes of the Variable but not required by the constructor. The data type and value rank
282    /// are inferred from the value. Historizing is not supported so is always false. If the
283    /// inferred types for data type or value rank are wrong, they may be explicitly set, or
284    /// call `new_data_value()` instead.
285    pub fn new(
286        node_id: &NodeId,
287        browse_name: impl Into<QualifiedName>,
288        display_name: impl Into<LocalizedText>,
289        value: impl Into<Variant>,
290    ) -> Variable {
291        let value: Variant = value.into();
292        let data_type = value.data_type().or_else(|| value.data_type());
293        if let Some(data_type) = data_type {
294            Variable::new_data_value(
295                node_id,
296                browse_name,
297                display_name,
298                data_type.node_id,
299                None,
300                None,
301                value,
302            )
303        } else {
304            panic!("Data type cannot be inferred from the value, use another constructor such as new_data_value")
305        }
306    }
307
308    /// Create a new variable with all attributes, may change if
309    /// new attributes are added to the OPC-UA standard.
310    ///
311    /// Note: This uses the given value and data type directly, you must ensure that the
312    /// type of the value matches the data type.
313    #[allow(clippy::too_many_arguments)]
314    pub fn new_full(
315        base: Base,
316        data_type: NodeId,
317        historizing: bool,
318        value_rank: i32,
319        value: DataValue,
320        access_level: u8,
321        user_access_level: u8,
322        array_dimensions: Option<Vec<u32>>,
323        minimum_sampling_interval: Option<f64>,
324    ) -> Self {
325        Self {
326            base,
327            data_type,
328            historizing,
329            value_rank,
330            value,
331            access_level,
332            user_access_level,
333            array_dimensions,
334            minimum_sampling_interval,
335        }
336    }
337
338    /// Create a new variable from [VariableAttributes].
339    pub fn from_attributes(
340        node_id: &NodeId,
341        browse_name: impl Into<QualifiedName>,
342        attributes: VariableAttributes,
343    ) -> Result<Self, FromAttributesError> {
344        let mandatory_attributes = AttributesMask::DISPLAY_NAME
345            | AttributesMask::ACCESS_LEVEL
346            | AttributesMask::USER_ACCESS_LEVEL
347            | AttributesMask::DATA_TYPE
348            | AttributesMask::HISTORIZING
349            | AttributesMask::VALUE
350            | AttributesMask::VALUE_RANK;
351        let mask = AttributesMask::from_bits(attributes.specified_attributes)
352            .ok_or(FromAttributesError::InvalidMask)?;
353        if mask.contains(mandatory_attributes) {
354            let mut node = Self::new_data_value(
355                node_id,
356                browse_name,
357                attributes.display_name,
358                attributes.data_type,
359                None,
360                None,
361                attributes.value,
362            );
363            node.set_value_rank(attributes.value_rank);
364            node.set_historizing(attributes.historizing);
365            node.set_access_level(AccessLevel::from_bits_truncate(attributes.access_level));
366            node.set_user_access_level(AccessLevel::from_bits_truncate(
367                attributes.user_access_level,
368            ));
369
370            if mask.contains(AttributesMask::DESCRIPTION) {
371                node.set_description(attributes.description);
372            }
373            if mask.contains(AttributesMask::WRITE_MASK) {
374                node.set_write_mask(WriteMask::from_bits_truncate(attributes.write_mask));
375            }
376            if mask.contains(AttributesMask::USER_WRITE_MASK) {
377                node.set_user_write_mask(WriteMask::from_bits_truncate(attributes.user_write_mask));
378            }
379            if mask.contains(AttributesMask::ARRAY_DIMENSIONS) {
380                node.set_array_dimensions(attributes.array_dimensions.unwrap().as_slice());
381            }
382            if mask.contains(AttributesMask::MINIMUM_SAMPLING_INTERVAL) {
383                node.set_minimum_sampling_interval(attributes.minimum_sampling_interval);
384            }
385            Ok(node)
386        } else {
387            error!("Variable cannot be created from attributes - missing mandatory values");
388            Err(FromAttributesError::MissingMandatoryValues)
389        }
390    }
391
392    /// Constructs a new variable with the specified id, name, type and value
393    pub fn new_data_value<S, R, N, V>(
394        node_id: &NodeId,
395        browse_name: R,
396        display_name: S,
397        data_type: N,
398        value_rank: Option<i32>,
399        array_dimensions: Option<u32>,
400        value: V,
401    ) -> Variable
402    where
403        R: Into<QualifiedName>,
404        S: Into<LocalizedText>,
405        N: Into<NodeId>,
406        V: Into<Variant>,
407    {
408        let value = value.into();
409        let array_dimensions = if let Some(array_dimensions) = array_dimensions {
410            Some(vec![array_dimensions])
411        } else {
412            match value {
413                Variant::Array(ref array) => {
414                    if let Some(ref array_dimensions) = array.dimensions {
415                        // Multidimensional arrays encode/decode dimensions with Int32 in Part 6, but arrayDimensions in Part 3
416                        // wants them as u32. Go figure... So convert Int32 to u32
417                        Some(array_dimensions.to_vec())
418                    } else {
419                        Some(vec![array.values.len() as u32])
420                    }
421                }
422                _ => None,
423            }
424        };
425
426        let value_rank = if let Some(value_rank) = value_rank {
427            value_rank
428        } else if let Some(ref array_dimensions) = array_dimensions {
429            array_dimensions.len() as i32
430        } else {
431            -1
432        };
433
434        let builder = VariableBuilder::new(node_id, browse_name, display_name)
435            .user_access_level(AccessLevel::CURRENT_READ)
436            .access_level(AccessLevel::CURRENT_READ)
437            .data_type(data_type)
438            .historizing(false)
439            .value_rank(value_rank)
440            .value(value);
441
442        // Set the array info
443        let builder = if let Some(ref array_dimensions) = array_dimensions {
444            builder.array_dimensions(array_dimensions.as_slice())
445        } else {
446            builder
447        };
448        builder.build()
449    }
450
451    /// Get whether this is a valid instance of a variable.
452    pub fn is_valid(&self) -> bool {
453        !self.data_type.is_null() && self.base.is_valid()
454    }
455
456    /// Read the value of the variable.
457    pub fn value(
458        &self,
459        timestamps_to_return: TimestampsToReturn,
460        index_range: &NumericRange,
461        _data_encoding: &DataEncoding,
462        _max_age: f64,
463    ) -> DataValue {
464        let data_value = &self.value;
465        let mut result = DataValue {
466            server_picoseconds: data_value.server_picoseconds,
467            server_timestamp: data_value.server_timestamp,
468            source_picoseconds: data_value.source_picoseconds,
469            source_timestamp: data_value.source_timestamp,
470            value: None,
471            status: None,
472        };
473
474        // Get the value
475        if let Some(ref value) = data_value.value {
476            match value.range_of(index_range) {
477                Ok(value) => {
478                    result.value = Some(value);
479                    result.status = data_value.status;
480                }
481                Err(err) => {
482                    result.status = Some(err);
483                }
484            }
485        }
486
487        match timestamps_to_return {
488            TimestampsToReturn::Source => {
489                result.server_timestamp = None;
490                result.server_picoseconds = None;
491            }
492            TimestampsToReturn::Server => {
493                result.source_timestamp = None;
494                result.source_picoseconds = None;
495            }
496            TimestampsToReturn::Neither => {
497                result.server_timestamp = None;
498                result.source_timestamp = None;
499                result.server_picoseconds = None;
500                result.source_picoseconds = None;
501            }
502            _ => (),
503        }
504
505        result
506        //}
507    }
508
509    /// Sets the variable's `Variant` value. The timestamps for the change are updated to now.
510    pub fn set_value<V>(&mut self, index_range: &NumericRange, value: V) -> Result<(), StatusCode>
511    where
512        V: Into<Variant>,
513    {
514        let mut value = value.into();
515
516        // A special case is required here for when the variable is a single dimension
517        // byte array and the value is a ByteString.
518        match self.value_rank {
519            -3 | -2 | 1 => {
520                if self.data_type == DataTypeId::Byte {
521                    if let Variant::ByteString(_) = value {
522                        // Convert the value from a byte string to a byte array
523                        value = value
524                            .to_byte_array()
525                            .map_err(|_| StatusCode::BadUnexpectedError)?;
526                    }
527                }
528            }
529            _ => { /* DO NOTHING */ }
530        };
531
532        let now = DateTime::now();
533        if index_range.has_range() {
534            self.set_value_range(value, index_range, StatusCode::Good, &now, &now)
535        } else {
536            self.set_value_direct(value, StatusCode::Good, &now, &now)
537        }
538        //}
539    }
540
541    /// Set a part of the current value given by `index_range`.
542    pub fn set_value_range(
543        &mut self,
544        value: Variant,
545        index_range: &NumericRange,
546        status_code: StatusCode,
547        server_timestamp: &DateTime,
548        source_timestamp: &DateTime,
549    ) -> Result<(), StatusCode> {
550        if matches!(index_range, NumericRange::None) {
551            self.value.value = Some(value);
552            self.value.status = Some(status_code);
553            self.value.server_timestamp = Some(*server_timestamp);
554            self.value.source_timestamp = Some(*source_timestamp);
555            return Ok(());
556        }
557
558        match self.value.value {
559            Some(ref mut full_value) => {
560                // Overwrite a partial section of the value
561                full_value.set_range_of(index_range, &value)?;
562                self.value.status = Some(status_code);
563                self.value.server_timestamp = Some(*server_timestamp);
564                self.value.source_timestamp = Some(*source_timestamp);
565                Ok(())
566            }
567            None => Err(StatusCode::BadIndexRangeInvalid),
568        }
569    }
570
571    /// Sets the variable's `DataValue`
572    pub fn set_value_direct<V>(
573        &mut self,
574        value: V,
575        status_code: StatusCode,
576        server_timestamp: &DateTime,
577        source_timestamp: &DateTime,
578    ) -> Result<(), StatusCode>
579    where
580        V: Into<Variant>,
581    {
582        self.value.value = Some(value.into());
583        self.value.status = Some(status_code);
584        self.value.server_timestamp = Some(*server_timestamp);
585        self.value.source_timestamp = Some(*source_timestamp);
586        Ok(())
587    }
588
589    /// Sets the variable type's `DataValue`
590    pub fn set_data_value(&mut self, value: DataValue) {
591        self.value = value;
592    }
593
594    /// Gets the minimum sampling interval, if the attribute was set
595    pub fn minimum_sampling_interval(&self) -> Option<f64> {
596        self.minimum_sampling_interval
597    }
598
599    /// Sets the minimum sampling interval
600    ///
601    /// Specifies in milliseconds how fast the server can reasonably sample the value for changes
602    ///
603    /// The value 0 means server is to monitor the value continuously. The value -1 means indeterminate.
604    pub fn set_minimum_sampling_interval(&mut self, minimum_sampling_interval: f64) {
605        self.minimum_sampling_interval = Some(minimum_sampling_interval);
606    }
607
608    /// Test if the variable is readable. This will be called by services before getting the value
609    /// of the node.
610    pub fn is_readable(&self) -> bool {
611        self.access_level().contains(AccessLevel::CURRENT_READ)
612    }
613
614    /// Test if the variable is writable. This will be called by services before setting the value
615    /// on the node.
616    pub fn is_writable(&self) -> bool {
617        self.access_level().contains(AccessLevel::CURRENT_WRITE)
618    }
619
620    /// Sets the variable writable state.
621    pub fn set_writable(&mut self, writable: bool) {
622        let mut access_level = self.access_level();
623        if writable {
624            access_level.insert(AccessLevel::CURRENT_WRITE);
625        } else {
626            access_level.remove(AccessLevel::CURRENT_WRITE);
627        }
628        self.set_access_level(access_level);
629    }
630
631    /// Returns the access level of the variable.
632    pub fn access_level(&self) -> AccessLevel {
633        AccessLevel::from_bits_truncate(self.access_level)
634    }
635
636    /// Sets the access level of the variable.
637    pub fn set_access_level(&mut self, access_level: AccessLevel) {
638        self.access_level = access_level.bits();
639    }
640
641    /// Test if the variable is user readable.
642    pub fn is_user_readable(&self) -> bool {
643        self.user_access_level().contains(AccessLevel::CURRENT_READ)
644    }
645
646    /// Test if the variable is user writable.
647    pub fn is_user_writable(&self) -> bool {
648        self.user_access_level()
649            .contains(AccessLevel::CURRENT_WRITE)
650    }
651
652    /// Returns the user access level of the variable.
653    pub fn user_access_level(&self) -> AccessLevel {
654        AccessLevel::from_bits_truncate(self.user_access_level)
655    }
656
657    /// Set the user access level of the variable.
658    pub fn set_user_access_level(&mut self, user_access_level: AccessLevel) {
659        self.user_access_level = user_access_level.bits();
660    }
661
662    /// Get the variable value rank.
663    pub fn value_rank(&self) -> i32 {
664        self.value_rank
665    }
666
667    /// Set the variable value rank.
668    pub fn set_value_rank(&mut self, value_rank: i32) {
669        self.value_rank = value_rank;
670    }
671
672    /// Get the `Historizing` attribute of the variable,
673    /// whether it stores new values in a historical store.
674    pub fn historizing(&self) -> bool {
675        self.historizing
676    }
677
678    /// Set the `Historizing` attribute of the variable,
679    /// whether it stores new values in a historical store.
680    pub fn set_historizing(&mut self, historizing: bool) {
681        self.historizing = historizing;
682    }
683
684    /// Get the array dimensions of this variable.
685    pub fn array_dimensions(&self) -> Option<Vec<u32>> {
686        self.array_dimensions.clone()
687    }
688
689    /// Set the array dimensions of this variable.
690    pub fn set_array_dimensions(&mut self, array_dimensions: &[u32]) {
691        self.array_dimensions = Some(array_dimensions.to_vec());
692    }
693
694    /// Get the data type of this variable.
695    pub fn data_type(&self) -> NodeId {
696        self.data_type.clone()
697    }
698
699    /// Set the data type of this variable.
700    pub fn set_data_type(&mut self, data_type: impl Into<NodeId>) {
701        self.data_type = data_type.into();
702    }
703}