use crate::session::{continuation_points::ContinuationPoint, instance::Session};
use opcua_crypto::random;
use opcua_types::{
match_extension_object_owned, ByteString, DeleteAtTimeDetails, DeleteEventDetails,
DeleteRawModifiedDetails, DynEncodable, ExtensionObject, HistoryData, HistoryEvent,
HistoryModifiedData, HistoryReadResult, HistoryReadValueId, HistoryUpdateResult, NodeId,
NumericRange, ObjectId, QualifiedName, ReadAnnotationDataDetails, ReadAtTimeDetails,
ReadEventDetails, ReadProcessedDetails, ReadRawModifiedDetails, StatusCode, UpdateDataDetails,
UpdateEventDetails, UpdateStructureDataDetails,
};
pub struct HistoryNode {
node_id: NodeId,
index_range: NumericRange,
data_encoding: QualifiedName,
input_continuation_point: Option<ContinuationPoint>,
next_continuation_point: Option<ContinuationPoint>,
result: Option<ExtensionObject>,
status: StatusCode,
}
pub(crate) enum HistoryReadDetails {
RawModified(ReadRawModifiedDetails),
AtTime(ReadAtTimeDetails),
Processed(ReadProcessedDetails),
Events(ReadEventDetails),
Annotations(ReadAnnotationDataDetails),
}
impl HistoryReadDetails {
pub(crate) fn from_extension_object(obj: ExtensionObject) -> Result<Self, StatusCode> {
match_extension_object_owned!(obj,
v: ReadRawModifiedDetails => Ok(Self::RawModified(v)),
v: ReadAtTimeDetails => Ok(Self::AtTime(v)),
v: ReadProcessedDetails => Ok(Self::Processed(v)),
v: ReadEventDetails => Ok(Self::Events(v)),
v: ReadAnnotationDataDetails => Ok(Self::Annotations(v)),
_ => Err(StatusCode::BadHistoryOperationInvalid)
)
}
}
#[derive(Debug, Clone)]
pub enum HistoryUpdateDetails {
UpdateData(UpdateDataDetails),
UpdateStructureData(UpdateStructureDataDetails),
UpdateEvent(UpdateEventDetails),
DeleteRawModified(DeleteRawModifiedDetails),
DeleteAtTime(DeleteAtTimeDetails),
DeleteEvent(DeleteEventDetails),
}
impl HistoryUpdateDetails {
pub fn from_extension_object(obj: ExtensionObject) -> Result<Self, StatusCode> {
match_extension_object_owned!(obj,
v: UpdateDataDetails => Ok(Self::UpdateData(v)),
v: UpdateStructureDataDetails => Ok(Self::UpdateStructureData(v)),
v: UpdateEventDetails => Ok(Self::UpdateEvent(v)),
v: DeleteRawModifiedDetails => Ok(Self::DeleteRawModified(v)),
v: DeleteAtTimeDetails => Ok(Self::DeleteAtTime(v)),
v: DeleteEventDetails => Ok(Self::DeleteEvent(v)),
_ => Err(StatusCode::BadHistoryOperationInvalid)
)
}
pub fn node_id(&self) -> &NodeId {
match self {
HistoryUpdateDetails::UpdateData(d) => &d.node_id,
HistoryUpdateDetails::UpdateStructureData(d) => &d.node_id,
HistoryUpdateDetails::UpdateEvent(d) => &d.node_id,
HistoryUpdateDetails::DeleteRawModified(d) => &d.node_id,
HistoryUpdateDetails::DeleteAtTime(d) => &d.node_id,
HistoryUpdateDetails::DeleteEvent(d) => &d.node_id,
}
}
}
pub trait HistoryResult: DynEncodable + Sized {
fn into_extension_object(self) -> ExtensionObject {
ExtensionObject::from_message(self)
}
}
impl HistoryResult for HistoryData {}
impl HistoryResult for HistoryModifiedData {}
impl HistoryResult for HistoryEvent {}
impl HistoryNode {
pub(crate) fn new(
node: HistoryReadValueId,
is_events: bool,
cp: Option<ContinuationPoint>,
) -> Self {
let mut status = StatusCode::BadNodeIdUnknown;
if !matches!(node.index_range, NumericRange::None) && is_events {
status = StatusCode::BadIndexRangeDataMismatch;
}
Self {
node_id: node.node_id,
index_range: node.index_range,
data_encoding: node.data_encoding,
input_continuation_point: cp,
next_continuation_point: None,
result: None,
status,
}
}
pub fn node_id(&self) -> &NodeId {
&self.node_id
}
pub fn index_range(&self) -> &NumericRange {
&self.index_range
}
pub fn data_encoding(&self) -> &QualifiedName {
&self.data_encoding
}
pub fn continuation_point(&self) -> Option<&ContinuationPoint> {
self.input_continuation_point.as_ref()
}
pub fn next_continuation_point(&self) -> Option<&ContinuationPoint> {
self.next_continuation_point.as_ref()
}
pub fn set_next_continuation_point(&mut self, continuation_point: Option<ContinuationPoint>) {
self.next_continuation_point = continuation_point;
}
pub fn set_result<T: HistoryResult>(&mut self, result: T) {
self.result = Some(result.into_extension_object());
}
pub fn set_status(&mut self, status: StatusCode) {
self.status = status;
}
pub fn status(&self) -> StatusCode {
self.status
}
pub(crate) fn into_result(mut self, session: &mut Session) -> HistoryReadResult {
let cp = match self.next_continuation_point {
Some(p) => {
let id = random::byte_string(6);
if session.add_history_continuation_point(&id, p).is_err() {
self.status = StatusCode::BadNoContinuationPoints;
ByteString::null()
} else {
id
}
}
None => ByteString::null(),
};
HistoryReadResult {
status_code: self.status,
continuation_point: cp,
history_data: self.result.unwrap_or_else(ExtensionObject::null),
}
}
}
pub struct HistoryUpdateNode {
details: HistoryUpdateDetails,
status: StatusCode,
operation_results: Option<Vec<StatusCode>>,
}
impl HistoryUpdateNode {
pub(crate) fn new(details: HistoryUpdateDetails) -> Self {
Self {
details,
status: StatusCode::BadNodeIdUnknown,
operation_results: None,
}
}
pub fn set_status(&mut self, status: StatusCode) {
self.status = status;
}
pub fn status(&self) -> StatusCode {
self.status
}
pub fn set_operation_results(&mut self, operation_results: Option<Vec<StatusCode>>) {
self.operation_results = operation_results;
}
pub(crate) fn into_result(mut self) -> HistoryUpdateResult {
if self.details.node_id() == &ObjectId::Server
&& matches!(self.status, StatusCode::BadNodeIdUnknown)
{
self.status = StatusCode::BadHistoryOperationUnsupported;
}
HistoryUpdateResult {
diagnostic_infos: None,
status_code: self.status,
operation_results: self.operation_results,
}
}
pub fn details(&self) -> &HistoryUpdateDetails {
&self.details
}
}