use crate::opc_da::errors::{OpcError, OpcResult};
use async_trait::async_trait;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use uuid::Uuid;
#[cfg(feature = "test-support")]
use mockall::automock;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TagValue {
pub tag_id: String,
pub value: String,
pub quality: String,
pub timestamp: String,
}
#[derive(Debug, Clone, PartialEq)]
pub enum OpcValue {
String(String),
Int(i32),
Float(f64),
Bool(bool),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WriteResult {
pub tag_id: String,
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowseNamespace {
Flat,
Hierarchical,
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BrowseCapabilities {
pub namespace: BrowseNamespace,
pub supports_da3: bool,
pub supports_da2: bool,
pub max_page_size: u32,
}
macro_rules! opaque_browse_token {
($name:ident, $doc:literal) => {
#[doc = $doc]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct $name(Uuid);
impl $name {
pub(crate) fn new() -> Self {
Self(Uuid::new_v4())
}
pub fn parse(value: &str) -> Result<Self, uuid::Error> {
value.parse()
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_tuple(stringify!($name))
.field(&self.0)
.finish()
}
}
impl std::fmt::Display for $name {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(formatter)
}
}
impl std::str::FromStr for $name {
type Err = uuid::Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
value.parse().map(Self)
}
}
};
}
opaque_browse_token!(
BrowseSessionToken,
"Opaque identifier for a browse session owned by the COM worker."
);
opaque_browse_token!(
BrowseNodeToken,
"Opaque identifier for a node returned by a browse session."
);
opaque_browse_token!(
BrowsePageToken,
"Opaque continuation token for the next bounded browse page."
);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowseNodeKind {
Branch,
Item,
BranchAndItem,
}
impl BrowseNodeKind {
pub fn has_children(self) -> bool {
matches!(self, Self::Branch | Self::BranchAndItem)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowseNodeFilter {
Branches,
Items,
All,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BrowseNode {
pub token: BrowseNodeToken,
pub name: String,
pub item_id: Option<String>,
pub kind: BrowseNodeKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BrowsePageRequest {
pub parent: Option<BrowseNodeToken>,
pub filter: BrowseNodeFilter,
pub max_elements: u32,
pub continuation: Option<BrowsePageToken>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BrowsePage {
pub nodes: Vec<BrowseNode>,
pub continuation: Option<BrowsePageToken>,
}
#[cfg_attr(feature = "test-support", automock)]
#[async_trait]
pub trait OpcProvider: Send + Sync {
async fn list_servers(&self, host: &str) -> OpcResult<Vec<String>>;
async fn browse_tags(
&self,
server: &str,
max_tags: usize,
progress: Arc<AtomicUsize>,
tags_sink: Arc<std::sync::Mutex<Vec<String>>>,
) -> OpcResult<Vec<String>>;
async fn browse_capabilities(&self, server: &str) -> OpcResult<BrowseCapabilities> {
let _ = server;
Err(OpcError::NotImplemented(
"Native browsing is not implemented by this provider".to_string(),
))
}
async fn open_browse_session(&self, server: &str) -> OpcResult<BrowseSessionToken> {
let _ = server;
Err(OpcError::NotImplemented(
"Native browsing is not implemented by this provider".to_string(),
))
}
async fn browse_page(
&self,
session: &BrowseSessionToken,
request: BrowsePageRequest,
) -> OpcResult<BrowsePage> {
let _ = (session, request);
Err(OpcError::NotImplemented(
"Native browsing is not implemented by this provider".to_string(),
))
}
async fn close_browse_session(&self, session: &BrowseSessionToken) -> OpcResult<()> {
let _ = session;
Err(OpcError::NotImplemented(
"Native browsing is not implemented by this provider".to_string(),
))
}
async fn read_tag_values(&self, server: &str, tag_ids: Vec<String>)
-> OpcResult<Vec<TagValue>>;
async fn write_tag_value(
&self,
server: &str,
tag_id: &str,
value: OpcValue,
) -> OpcResult<WriteResult>;
}