use std::any::Any;
use std::time::{Duration, SystemTime};
use crate::port::QueuePriority;
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(1);
pub fn timeout_from_secs(secs: f64) -> Option<Duration> {
if secs.is_finite() && secs < 0.0 {
return None;
}
Some(Duration::try_from_secs_f64(secs).unwrap_or(DEFAULT_TIMEOUT))
}
pub const ASYN_REASON_QUEUE_EVEN_IF_NOT_CONNECTED: usize = 0x7000_0000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectCheck {
Required,
Waived,
}
pub struct AsynUser {
pub reason: usize,
pub addr: i32,
pub timeout: Option<Duration>,
pub queue_timeout: Option<Duration>,
pub priority: QueuePriority,
pub timestamp: Option<SystemTime>,
pub alarm_status: u16,
pub alarm_severity: u16,
pub user_data: Option<Box<dyn Any + Send>>,
pub block_token: Option<u64>,
pub trace: Option<UserTrace>,
pub error_message: String,
}
#[derive(Clone)]
pub struct UserTrace {
pub manager: std::sync::Arc<crate::trace::TraceManager>,
pub port: std::sync::Arc<str>,
}
impl UserTrace {
pub fn print(
&self,
addr: i32,
reason: i32,
mask: crate::trace::TraceMask,
file: &str,
line: u32,
args: std::fmt::Arguments<'_>,
) {
if self.manager.is_enabled_device(&self.port, addr, mask) {
self.manager.output_device_with_source(
&self.port,
Some(addr),
reason,
mask,
file,
line,
&args.to_string(),
);
}
}
}
impl Default for AsynUser {
fn default() -> Self {
Self {
reason: 0,
addr: -1,
timeout: Some(DEFAULT_TIMEOUT),
queue_timeout: None,
priority: QueuePriority::default(),
timestamp: None,
alarm_status: 0,
alarm_severity: 0,
user_data: None,
block_token: None,
trace: None,
error_message: String::new(),
}
}
}
impl AsynUser {
pub fn new(reason: usize) -> Self {
Self {
reason,
..Default::default()
}
}
pub fn with_addr(mut self, addr: i32) -> Self {
self.addr = addr;
self
}
pub fn with_priority(mut self, priority: QueuePriority) -> Self {
self.priority = priority;
self
}
pub fn queue_even_if_not_connected(mut self) -> Self {
self.reason = ASYN_REASON_QUEUE_EVEN_IF_NOT_CONNECTED;
self.priority = QueuePriority::Connect;
self
}
pub fn connect_check(&self) -> ConnectCheck {
if self.priority == QueuePriority::Connect {
self.connect_check_at_connect_priority()
} else {
ConnectCheck::Required
}
}
pub fn connect_check_at_connect_priority(&self) -> ConnectCheck {
if self.addr < 0 || self.reason == ASYN_REASON_QUEUE_EVEN_IF_NOT_CONNECTED {
ConnectCheck::Waived
} else {
ConnectCheck::Required
}
}
pub fn with_timeout(self, timeout: Duration) -> Self {
self.with_timeout_opt(Some(timeout))
}
pub fn with_timeout_opt(mut self, timeout: Option<Duration>) -> Self {
self.timeout = timeout;
self
}
pub fn waiting_forever(mut self) -> Self {
self.timeout = None;
self
}
pub fn with_queue_timeout(mut self, queue_timeout: Duration) -> Self {
self.queue_timeout = Some(queue_timeout);
self
}
pub fn print(
&self,
mask: crate::trace::TraceMask,
file: &str,
line: u32,
args: std::fmt::Arguments<'_>,
) {
if let Some(t) = &self.trace {
t.print(self.addr, self.reason as i32, mask, file, line, args);
}
}
pub fn print_io(
&self,
mask: crate::trace::TraceMask,
data: &[u8],
label: &str,
file: &str,
line: u32,
) {
let Some(t) = &self.trace else { return };
if t.manager.is_enabled(&t.port, mask) {
t.manager.output_device_io(
&t.port,
Some(self.addr),
self.reason as i32,
mask,
data,
label,
file,
line,
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_user_default() {
let u = AsynUser::default();
assert_eq!(u.reason, 0);
assert_eq!(u.addr, -1);
assert_eq!(u.timeout, Some(Duration::from_secs(1)));
}
#[test]
fn test_user_builder() {
let u = AsynUser::new(42)
.with_addr(3)
.with_timeout(Duration::from_millis(500));
assert_eq!(u.reason, 42);
assert_eq!(u.addr, 3);
assert_eq!(u.timeout, Some(Duration::from_millis(500)));
}
}