use std::borrow::Cow;
use tango::server::{AttrDef, ClassDef, CommandDef, Dev, ServerBuilder};
use tango::error::{Error, ErrorKind, Result};
use tango::dev::DevState;
struct Motor {
position: f64,
target: f64,
limit: f64,
}
impl Motor {
fn class() -> ClassDef<Self> {
ClassDef::new("Motor", Motor::init)
.doc("One rotation axis, which is moved by asking it to go somewhere and watched by reading where it is.")
.doc_url("https://www.tango-controls.org/")
.state(Motor::state)
.status(Motor::status)
.attr(AttrDef::new("position")
.doc("Where the axis is, and where it is being asked to go.")
.unit("deg")
.display_format("%7.3f")
.change_event(0.001, None)
.push_change_event(true)
.read(Motor::position)
.write_exclusive(Motor::set_position))
.command(CommandDef::new("MoveTo")
.in_doc("Where the axis should go, in degrees.")
.out_doc("Where it is now, which is where it was asked to go.")
.exec_exclusive(Motor::move_to))
.command(CommandDef::new("Stop")
.out_doc("Nothing: the axis stops where it is.")
.exec_exclusive(Motor::stop))
}
fn init(dev: &Dev<Self>) -> Result<Self> {
let limit = dev.server().database()
.get_dev_prop::<f64, _>(dev.name(), "Limit")
.unwrap_or(180.0);
if limit <= 0.0 {
return Err(Error::new(ErrorKind::InvalidProp)
.with_description(format!("The Limit property of {} is {limit}, which \
leaves the axis nowhere to go", dev.name())));
}
dev.add_attr(AttrDef::new("limit")
.doc("How far the axis may go either way.")
.unit("deg")
.read(Motor::limit))?;
Ok(Self { position: 0.0, target: 0.0, limit })
}
fn state(&self, _dev: &Dev<Self>) -> Result<DevState> {
match self.position == self.target {
true => Ok(DevState::On),
false => Ok(DevState::Moving),
}
}
fn status(&self, _dev: &Dev<Self>) -> Result<Cow<'static, str>> {
match self.position == self.target {
true => Ok(Cow::Borrowed("The axis is idle")),
false => Ok(Cow::Owned(format!("The axis is moving to {:.3} deg", self.target))),
}
}
fn position(&self, _dev: &Dev<Self>) -> Result<f64> {
Ok(self.position)
}
fn set_position(&mut self, dev: &Dev<Self>, value: f64) -> Result<()> {
self.move_to(dev, value).map(|_| ())
}
fn limit(&self, _dev: &Dev<Self>) -> Result<f64> {
Ok(self.limit)
}
fn move_to(&mut self, dev: &Dev<Self>, target: f64) -> Result<f64> {
if target.abs() > self.limit {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("The device {} cannot go to {target} deg, which \
is past its limit of {} deg", dev.name(), self.limit)));
}
self.target = target;
self.position = target;
dev.push_change_event("position", self.position)?;
Ok(self.position)
}
fn stop(&mut self, _dev: &Dev<Self>, _input: ()) -> Result<()> {
self.target = self.position;
Ok(())
}
}
fn main() -> Result<()> {
ServerBuilder::from_raw_args(Some("Motor"), None)
.class(Motor::class())
.run()
}