Skip to main content

opcua_server/node_manager/
history.rs

1use crate::session::{continuation_points::ContinuationPoint, instance::Session};
2use opcua_crypto::random;
3use opcua_types::{
4    match_extension_object_owned, ByteString, DeleteAtTimeDetails, DeleteEventDetails,
5    DeleteRawModifiedDetails, DynEncodable, ExtensionObject, HistoryData, HistoryEvent,
6    HistoryModifiedData, HistoryReadResult, HistoryReadValueId, HistoryUpdateResult, NodeId,
7    NumericRange, ObjectId, QualifiedName, ReadAnnotationDataDetails, ReadAtTimeDetails,
8    ReadEventDetails, ReadProcessedDetails, ReadRawModifiedDetails, StatusCode, UpdateDataDetails,
9    UpdateEventDetails, UpdateStructureDataDetails,
10};
11
12/// Container for a single node in a history read request.
13pub struct HistoryNode {
14    node_id: NodeId,
15    index_range: NumericRange,
16    data_encoding: QualifiedName,
17    input_continuation_point: Option<ContinuationPoint>,
18    next_continuation_point: Option<ContinuationPoint>,
19    result: Option<ExtensionObject>,
20    status: StatusCode,
21}
22
23pub(crate) enum HistoryReadDetails {
24    RawModified(ReadRawModifiedDetails),
25    AtTime(ReadAtTimeDetails),
26    Processed(ReadProcessedDetails),
27    Events(ReadEventDetails),
28    Annotations(ReadAnnotationDataDetails),
29}
30
31impl HistoryReadDetails {
32    pub(crate) fn from_extension_object(obj: ExtensionObject) -> Result<Self, StatusCode> {
33        match_extension_object_owned!(obj,
34            v: ReadRawModifiedDetails => Ok(Self::RawModified(v)),
35            v: ReadAtTimeDetails => Ok(Self::AtTime(v)),
36            v: ReadProcessedDetails => Ok(Self::Processed(v)),
37            v: ReadEventDetails => Ok(Self::Events(v)),
38            v: ReadAnnotationDataDetails => Ok(Self::Annotations(v)),
39            _ => Err(StatusCode::BadHistoryOperationInvalid)
40        )
41    }
42}
43
44/// Details object for history updates.
45#[derive(Debug, Clone)]
46pub enum HistoryUpdateDetails {
47    /// Update data values.
48    UpdateData(UpdateDataDetails),
49    /// Update historical structure data.
50    UpdateStructureData(UpdateStructureDataDetails),
51    /// Update historical events.
52    UpdateEvent(UpdateEventDetails),
53    /// Delete raw/modified data values.
54    DeleteRawModified(DeleteRawModifiedDetails),
55    /// Delete at a specific list of timestamps.
56    DeleteAtTime(DeleteAtTimeDetails),
57    /// Delete historical events.
58    DeleteEvent(DeleteEventDetails),
59}
60
61impl HistoryUpdateDetails {
62    /// Try to create a `HistoryUpdateDetails` object from an extension object.
63    pub fn from_extension_object(obj: ExtensionObject) -> Result<Self, StatusCode> {
64        match_extension_object_owned!(obj,
65            v: UpdateDataDetails => Ok(Self::UpdateData(v)),
66            v: UpdateStructureDataDetails => Ok(Self::UpdateStructureData(v)),
67            v: UpdateEventDetails => Ok(Self::UpdateEvent(v)),
68            v: DeleteRawModifiedDetails => Ok(Self::DeleteRawModified(v)),
69            v: DeleteAtTimeDetails => Ok(Self::DeleteAtTime(v)),
70            v: DeleteEventDetails => Ok(Self::DeleteEvent(v)),
71            _ => Err(StatusCode::BadHistoryOperationInvalid)
72        )
73    }
74
75    /// Get the node ID of the details object, independent of type.
76    pub fn node_id(&self) -> &NodeId {
77        match self {
78            HistoryUpdateDetails::UpdateData(d) => &d.node_id,
79            HistoryUpdateDetails::UpdateStructureData(d) => &d.node_id,
80            HistoryUpdateDetails::UpdateEvent(d) => &d.node_id,
81            HistoryUpdateDetails::DeleteRawModified(d) => &d.node_id,
82            HistoryUpdateDetails::DeleteAtTime(d) => &d.node_id,
83            HistoryUpdateDetails::DeleteEvent(d) => &d.node_id,
84        }
85    }
86}
87
88/// Trait for values storable as history data.
89pub trait HistoryResult: DynEncodable + Sized {
90    /// Return an extension object containing the encoded data for the current object.
91    fn into_extension_object(self) -> ExtensionObject {
92        ExtensionObject::from_message(self)
93    }
94}
95
96impl HistoryResult for HistoryData {}
97impl HistoryResult for HistoryModifiedData {}
98impl HistoryResult for HistoryEvent {}
99// impl HistoryResult for HistoryModifiedEvent {}
100
101impl HistoryNode {
102    pub(crate) fn new(
103        node: HistoryReadValueId,
104        is_events: bool,
105        cp: Option<ContinuationPoint>,
106    ) -> Self {
107        let mut status = StatusCode::BadNodeIdUnknown;
108
109        if !matches!(node.index_range, NumericRange::None) && is_events {
110            status = StatusCode::BadIndexRangeDataMismatch;
111        }
112
113        Self {
114            node_id: node.node_id,
115            index_range: node.index_range,
116            data_encoding: node.data_encoding,
117            input_continuation_point: cp,
118            next_continuation_point: None,
119            result: None,
120            status,
121        }
122    }
123
124    /// Get the node ID to read history from.
125    pub fn node_id(&self) -> &NodeId {
126        &self.node_id
127    }
128
129    /// Get the index range to read.
130    pub fn index_range(&self) -> &NumericRange {
131        &self.index_range
132    }
133
134    /// Get the specified data encoding to read.
135    pub fn data_encoding(&self) -> &QualifiedName {
136        &self.data_encoding
137    }
138
139    /// Get the current continuation point.
140    pub fn continuation_point(&self) -> Option<&ContinuationPoint> {
141        self.input_continuation_point.as_ref()
142    }
143
144    /// Get the next continuation point.
145    pub fn next_continuation_point(&self) -> Option<&ContinuationPoint> {
146        self.next_continuation_point.as_ref()
147    }
148
149    /// Set the next continuation point.
150    pub fn set_next_continuation_point(&mut self, continuation_point: Option<ContinuationPoint>) {
151        self.next_continuation_point = continuation_point;
152    }
153
154    /// Set the result to some history data object.
155    pub fn set_result<T: HistoryResult>(&mut self, result: T) {
156        self.result = Some(result.into_extension_object());
157    }
158
159    /// Set the result status.
160    pub fn set_status(&mut self, status: StatusCode) {
161        self.status = status;
162    }
163
164    /// Get the current result status.
165    pub fn status(&self) -> StatusCode {
166        self.status
167    }
168
169    pub(crate) fn into_result(mut self, session: &mut Session) -> HistoryReadResult {
170        let cp = match self.next_continuation_point {
171            Some(p) => {
172                let id = random::byte_string(6);
173                if session.add_history_continuation_point(&id, p).is_err() {
174                    self.status = StatusCode::BadNoContinuationPoints;
175                    ByteString::null()
176                } else {
177                    id
178                }
179            }
180            None => ByteString::null(),
181        };
182
183        HistoryReadResult {
184            status_code: self.status,
185            continuation_point: cp,
186            history_data: self.result.unwrap_or_else(ExtensionObject::null),
187        }
188    }
189}
190
191/// History update details for one node.
192pub struct HistoryUpdateNode {
193    details: HistoryUpdateDetails,
194    status: StatusCode,
195    operation_results: Option<Vec<StatusCode>>,
196}
197
198impl HistoryUpdateNode {
199    pub(crate) fn new(details: HistoryUpdateDetails) -> Self {
200        Self {
201            details,
202            status: StatusCode::BadNodeIdUnknown,
203            operation_results: None,
204        }
205    }
206
207    /// Set the result status of this history operation.
208    pub fn set_status(&mut self, status: StatusCode) {
209        self.status = status;
210    }
211
212    /// Get the current status.
213    pub fn status(&self) -> StatusCode {
214        self.status
215    }
216
217    /// Set the operation results. If present the length must match
218    /// the length of the entries in the history update details.
219    pub fn set_operation_results(&mut self, operation_results: Option<Vec<StatusCode>>) {
220        self.operation_results = operation_results;
221    }
222
223    pub(crate) fn into_result(mut self) -> HistoryUpdateResult {
224        // Special case reads on the server node, to return a proper error if no node manager supports it...
225        if self.details.node_id() == &ObjectId::Server
226            && matches!(self.status, StatusCode::BadNodeIdUnknown)
227        {
228            self.status = StatusCode::BadHistoryOperationUnsupported;
229        }
230        HistoryUpdateResult {
231            diagnostic_infos: None,
232            status_code: self.status,
233            operation_results: self.operation_results,
234        }
235    }
236
237    /// Get a reference to the history update details describing the history update
238    /// to execute.
239    pub fn details(&self) -> &HistoryUpdateDetails {
240        &self.details
241    }
242}