use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use epics_base_rs::server::iocsh::registry::*;
use crate::builder::MotorBuilder;
use crate::device_support::MotorDeviceSupport;
use crate::sim_motor::SimMotor;
pub struct SimMotorHolder {
motors: Mutex<HashMap<String, Option<MotorDeviceSupport>>>,
}
impl SimMotorHolder {
pub fn new() -> Arc<Self> {
Arc::new(Self {
motors: Mutex::new(HashMap::new()),
})
}
pub fn sim_motor_create_command(self: &Arc<Self>) -> CommandDef {
let holder = self.clone();
CommandDef::new(
"simMotorCreate",
vec![
ArgDesc { name: "port", arg_type: ArgType::String, optional: false },
ArgDesc { name: "lowLimit", arg_type: ArgType::Double, optional: false },
ArgDesc { name: "highLimit", arg_type: ArgType::Double, optional: false },
ArgDesc { name: "pollMs", arg_type: ArgType::Int, optional: true },
],
"simMotorCreate(port, lowLimit, highLimit, [pollMs]) - Create a simulated motor",
move |args: &[ArgValue], ctx: &CommandContext| {
let port = match &args[0] {
ArgValue::String(s) => s.clone(),
_ => return Err("port must be a string".into()),
};
let low_limit = match &args[1] {
ArgValue::Double(v) => *v,
_ => return Err("lowLimit must be a number".into()),
};
let high_limit = match &args[2] {
ArgValue::Double(v) => *v,
_ => return Err("highLimit must be a number".into()),
};
let poll_ms = match &args[3] {
ArgValue::Int(v) => *v as u64,
ArgValue::Missing => 100,
_ => return Err("pollMs must be an integer".into()),
};
let dtyp_key = format!("simMotor_{port}");
let motor: Arc<Mutex<dyn asyn_rs::interfaces::motor::AsynMotor>> =
Arc::new(Mutex::new(SimMotor::new().with_limits(low_limit, high_limit)));
let setup = MotorBuilder::new(motor)
.poll_interval(Duration::from_millis(poll_ms))
.build();
let crate::builder::MotorSetup {
record: _,
device_support,
poll_loop,
poll_cmd_tx: _,
} = setup;
let device_support = device_support.with_dtyp_name(dtyp_key.clone());
ctx.runtime_handle().spawn(poll_loop.run());
holder.motors.lock().unwrap().insert(dtyp_key.clone(), Some(device_support));
println!("simMotorCreate: port={port} limits=[{low_limit}, {high_limit}] poll={poll_ms}ms (DTYP={dtyp_key})");
Ok(CommandOutcome::Continue)
},
)
}
pub fn device_support_factory(
self: &Arc<Self>,
) -> impl Fn(&epics_base_rs::server::ioc_app::DeviceSupportContext) -> Option<Box<dyn epics_base_rs::server::device_support::DeviceSupport>>
+ Send
+ Sync {
let holder = self.clone();
move |ctx: &epics_base_rs::server::ioc_app::DeviceSupportContext| {
let mut motors = holder.motors.lock().unwrap();
if let Some(slot) = motors.get_mut(ctx.dtyp) {
if let Some(ds) = slot.take() {
return Some(
Box::new(ds)
as Box<dyn epics_base_rs::server::device_support::DeviceSupport>,
);
}
}
None
}
}
}