use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use std::time::Duration;
use opcua::client::{Client, ClientBuilder, IdentityToken, Session};
use opcua::crypto::SecurityPolicy;
use opcua::types::{MessageSecurityMode, StatusCode};
use tokio::task::JoinHandle;
const CONNECT_TIMEOUT: Duration = Duration::from_secs(5);
pub struct Conn {
_client: Client,
pub session: Arc<Session>,
events: JoinHandle<StatusCode>,
}
impl Conn {
pub async fn open(endpoint: &str, app: &str) -> Result<Self, String> {
static N: AtomicU32 = AtomicU32::new(0);
let pki = std::env::temp_dir().join(format!(
"{app}-{}-{}",
std::process::id(),
N.fetch_add(1, Ordering::Relaxed)
));
let client = ClientBuilder::new()
.application_name(app)
.application_uri(format!("urn:taktwerk:{app}"))
.pki_dir(&pki)
.create_sample_keypair(false)
.trust_server_certs(true)
.session_retry_limit(0)
.max_message_size(64 << 20)
.max_chunk_count(1024)
.max_byte_string_length(16 << 20)
.max_string_length(16 << 20)
.client();
let _ = std::fs::remove_dir_all(&pki);
let mut client = client.map_err(|e| format!("client: {e:?}"))?;
let (session, event_loop) = client
.connect_to_matching_endpoint(
(
endpoint,
SecurityPolicy::None.to_str(),
MessageSecurityMode::None,
),
IdentityToken::Anonymous,
)
.await
.map_err(|e| format!("connect {endpoint}: {e}"))?;
let events = event_loop.spawn();
tokio::time::timeout(CONNECT_TIMEOUT, session.wait_for_connection())
.await
.map_err(|_| format!("connect {endpoint}: timed out"))?;
Ok(Self {
_client: client,
session,
events,
})
}
pub fn ended(&self) -> bool {
self.events.is_finished()
}
pub async fn close(self) {
let _ = self.session.disconnect().await;
self.events.abort();
}
}