use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;
use super::InterfaceType;
use crate::interrupt::{InterruptFilter, InterruptValue, SyncCallbackSubscription};
use crate::param::ParamValue;
use crate::port_handle::PortHandle;
use crate::trace::TraceIoMask;
const WAKEUP_DEPTH: usize = 1;
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum IoIntrSample {
Octet(String),
Int32(i32),
UInt32(u32),
Float64(f64),
}
impl IoIntrSample {
fn from_value(iface: InterfaceType, value: &ParamValue) -> Option<Self> {
match (iface, value) {
(InterfaceType::Octet, ParamValue::Octet(s)) => Some(Self::Octet(
crate::escape::escaped_from_raw(s.as_bytes(), super::TINP_SIZE),
)),
(InterfaceType::Int32, ParamValue::Int32(v)) => Some(Self::Int32(*v)),
(InterfaceType::UInt32Digital, ParamValue::UInt32Digital(v)) => Some(Self::UInt32(*v)),
(InterfaceType::Float64, ParamValue::Float64(v)) => Some(Self::Float64(*v)),
_ => None,
}
}
}
#[derive(Clone)]
pub(crate) struct IoIntrBinding {
pub handle: PortHandle,
pub iface: InterfaceType,
pub addr: i32,
pub reason: usize,
pub ui32mask: u32,
}
#[derive(Default)]
struct Registration {
active: bool,
binding: Option<IoIntrBinding>,
sub: Option<SyncCallbackSubscription>,
started: bool,
}
pub(crate) struct IoIntrScan {
reg: Mutex<Registration>,
sample: Arc<Mutex<Option<IoIntrSample>>>,
tx: mpsc::Sender<()>,
rx: Mutex<Option<mpsc::Receiver<()>>>,
}
impl IoIntrScan {
pub(crate) fn new() -> Self {
let (tx, rx) = mpsc::channel(WAKEUP_DEPTH);
Self {
reg: Mutex::new(Registration::default()),
sample: Arc::new(Mutex::new(None)),
tx,
rx: Mutex::new(Some(rx)),
}
}
pub(crate) fn take_receiver(&self) -> Option<mpsc::Receiver<()>> {
let rx = self.rx.lock().unwrap().take()?;
{
let mut reg = self.reg.lock().unwrap();
reg.started = true;
}
let _ = self.refresh();
Some(rx)
}
pub(crate) fn set_active(&self, active: bool) -> Result<(), String> {
{
let mut reg = self.reg.lock().unwrap();
if reg.active == active {
return Ok(());
}
reg.active = active;
}
self.refresh()
}
pub(crate) fn rebind(&self, binding: Option<IoIntrBinding>) -> Result<(), String> {
{
let mut reg = self.reg.lock().unwrap();
reg.binding = binding;
}
self.refresh()
}
pub(crate) fn is_active(&self) -> bool {
self.reg.lock().unwrap().active
}
pub(crate) fn take_sample(&self) -> Option<IoIntrSample> {
self.sample.lock().unwrap().take()
}
pub(crate) fn clear_sample(&self) {
*self.sample.lock().unwrap() = None;
}
fn refresh(&self) -> Result<(), String> {
let mut reg = self.reg.lock().unwrap();
*self.sample.lock().unwrap() = None;
let binding = match (reg.active && reg.started, reg.binding.clone()) {
(true, Some(b)) => b,
_ => {
reg.sub = None;
return Ok(());
}
};
if !binding.handle.has_interface(binding.iface.registry_type()) {
reg.sub = None;
return Err(format!("No {} interface", binding.iface.c_asyn_name()));
}
let filter = InterruptFilter {
reason: Some(binding.reason),
addr: Some(binding.addr),
uint32_mask: match binding.iface {
InterfaceType::UInt32Digital => Some(binding.ui32mask),
_ => None,
},
iface: Some(binding.iface.registry_type()),
};
let iface = binding.iface;
let tx = self.tx.clone();
let sample_cell = self.sample.clone();
let sub = binding.handle.interrupts().register_sync_callback(
filter,
move |iv: &InterruptValue| {
let Some(sample) = IoIntrSample::from_value(iface, &iv.value) else {
return;
};
{
let mut slot = sample_cell.lock().unwrap();
if slot.is_some() {
return;
}
*slot = Some(sample);
}
let _ = tx.try_send(());
},
);
reg.sub = Some(sub);
Ok(())
}
}