use std::sync::{Arc, Mutex};
use std::time::Duration;
use asyn_rs::interfaces::motor::AsynMotor;
use asyn_rs::user::AsynUser;
use epics_base_rs::error::CaResult;
use epics_base_rs::server::device_support::DeviceSupport;
use epics_base_rs::server::record::{Record, ScanType};
use tokio::sync::mpsc;
use crate::device_state::*;
use crate::flags::*;
use crate::poll_loop::PollCommand;
pub struct MotorDeviceSupport {
motor: Arc<Mutex<dyn AsynMotor>>,
_addr: i32,
_timeout: Duration,
poll_cmd_tx: mpsc::Sender<PollCommand>,
io_intr_tx: mpsc::Sender<()>,
io_intr_rx: Option<mpsc::Receiver<()>>,
device_state: SharedDeviceState,
initialized: bool,
dtyp_name: String,
}
impl MotorDeviceSupport {
pub fn new(
motor: Arc<Mutex<dyn AsynMotor>>,
addr: i32,
timeout: Duration,
poll_cmd_tx: mpsc::Sender<PollCommand>,
device_state: SharedDeviceState,
) -> Self {
let (io_intr_tx, io_intr_rx) = mpsc::channel(16);
Self {
motor,
_addr: addr,
_timeout: timeout,
poll_cmd_tx,
io_intr_tx,
io_intr_rx: Some(io_intr_rx),
device_state,
initialized: false,
dtyp_name: "asynMotor".to_string(),
}
}
pub fn with_dtyp_name(mut self, name: String) -> Self {
self.dtyp_name = name;
self
}
pub fn io_intr_sender(&self) -> mpsc::Sender<()> {
self.io_intr_tx.clone()
}
fn make_user(&self) -> AsynUser {
AsynUser::new(0)
}
fn execute_actions(&self, actions: &DeviceActions) {
let user = self.make_user();
let mut motor = match self.motor.lock() {
Ok(m) => m,
Err(e) => {
tracing::error!("motor lock poisoned: {e}");
return;
}
};
for cmd in &actions.commands {
let result = match cmd {
MotorCommand::MoveAbsolute { position, velocity, acceleration } => {
tracing::info!("motor command: MoveAbsolute pos={position}, vel={velocity}");
motor.move_absolute(&user, *position, *velocity, *acceleration)
}
MotorCommand::MoveVelocity { direction, velocity, acceleration } => {
let target = if *direction { 1e9 } else { -1e9 };
tracing::info!("motor command: MoveVelocity dir={direction}, vel={velocity}");
motor.move_absolute(&user, target, *velocity, *acceleration)
}
MotorCommand::Home { forward, velocity, acceleration: _ } => {
tracing::info!("motor command: Home forward={forward}");
motor.home(&user, *velocity, *forward)
}
MotorCommand::Stop { acceleration } => {
tracing::info!("motor command: Stop");
motor.stop(&user, *acceleration)
}
MotorCommand::SetPosition { position } => {
tracing::info!("motor command: SetPosition pos={position}");
motor.set_position(&user, *position)
}
MotorCommand::SetClosedLoop { enable } => {
tracing::info!("motor command: SetClosedLoop enable={enable}");
motor.set_closed_loop(&user, *enable)
}
MotorCommand::Poll => {
Ok(())
}
};
if let Err(e) = result {
tracing::error!("motor command error: {e}");
}
}
drop(motor);
match actions.poll {
PollDirective::Start => { let _ = self.poll_cmd_tx.try_send(PollCommand::StartPolling); }
PollDirective::Stop => { let _ = self.poll_cmd_tx.try_send(PollCommand::StopPolling); }
PollDirective::None => {}
}
if let Some(ref delay) = actions.schedule_delay {
let _ = self.poll_cmd_tx.try_send(PollCommand::ScheduleDelay(delay.id, delay.duration));
}
}
}
impl DeviceSupport for MotorDeviceSupport {
fn init(&mut self, record: &mut dyn Record) -> CaResult<()> {
if let Some(motor_rec) = record.as_any_mut()
.and_then(|a| a.downcast_mut::<crate::record::MotorRecord>())
{
motor_rec.set_device_state(self.device_state.clone());
}
let user = self.make_user();
let status = {
let mut motor = self.motor.lock().map_err(|e| {
epics_base_rs::error::CaError::InvalidValue(format!("motor lock: {e}"))
})?;
motor.poll(&user).map_err(|e| {
epics_base_rs::error::CaError::InvalidValue(format!("motor poll: {e}"))
})?
};
let mut ds = self.device_state.lock().map_err(|e| {
epics_base_rs::error::CaError::InvalidValue(format!("device state lock: {e}"))
})?;
ds.latest_status = Some(StampedStatus { seq: 1, status });
drop(ds);
self.initialized = true;
Ok(())
}
fn read(&mut self, _record: &mut dyn Record) -> CaResult<()> {
Ok(())
}
fn write(&mut self, _record: &mut dyn Record) -> CaResult<()> {
let actions = {
let mut ds = self.device_state.lock().map_err(|e| {
epics_base_rs::error::CaError::InvalidValue(format!("device state lock: {e}"))
})?;
ds.pending_actions.take()
};
let Some(actions) = actions else { return Ok(()) };
self.execute_actions(&actions);
Ok(())
}
fn dtyp(&self) -> &str {
&self.dtyp_name
}
fn set_record_info(&mut self, _name: &str, _scan: ScanType) {}
fn io_intr_receiver(&mut self) -> Option<mpsc::Receiver<()>> {
self.io_intr_rx.take()
}
}