Expand description
An implementation of the Tango distributed control system, written from scratch in Rust, for writing clients and device servers.
The protocol actually used to reach a device is a detail of the crate rather than of
the code using it: a dev::DevProxy talks to whatever backend can reach its target,
and a server::Server serves its devices on every backend able to serve them.
Every call is blocking, up to the timeout of the proxy or the deadline of the request:
there is no runtime to install. Each one also has an asynchronous *_async form, which
runs on any executor, so that a single thread can wait on many devices at once; either
way, the backend multiplexes the concurrent requests over the connection it shares.
§Client
A client asks a device for what it wants and says which type it expects, one name giving one value and a tuple of names giving as many, in a single request.
use tango::dev::DevProxy;
use tango::error::Result;
fn main() -> Result<()> {
// The device is looked up in the database given by `TANGO_HOST`, the connection
// being made on the first request and kept for the following ones.
let dev = DevProxy::new("sys/tg_test/1");
println!("{} is {:?}", dev.name()?, dev.state()?);
// A reading carries the value, the write value, the quality and the timestamp.
let read = dev.read_attr::<f64, _>("double_scalar")?;
println!("double_scalar = {:?} ({:?})", read.value, read.quality);
dev.write_attr(("double_scalar", 3.14))?;
// A command is typed the same way, on both of its sides, `()` being no data.
let echo: String = dev.command("DevString", "hello")?;
println!("DevString -> {echo}");
Ok(())
}§Server
A device server is a plain Rust struct, its model, and the functions of it that the attributes and the commands of the device are defined from. Nothing is implemented on the model and no macro is involved: a read function is given a shared access to it, a write or an exclusive command an exclusive one, and the server takes care of the lock.
use tango::server::{AttrDef, ClassDef, CommandDef, Dev, ServerBuilder};
use tango::error::Result;
/// The model of one device, owned by the server serving it.
struct Counter {
count: i32,
}
impl Counter {
/// Build the model of a device, and give it its interface.
fn init(dev: &Dev<Self>) -> Result<Self> {
dev.add_attr(AttrDef::new("count")
.doc("The current value of the counter.")
.read(Counter::count))?;
dev.add_command(CommandDef::new("Add")
.in_doc("How much to add to the counter.")
.out_doc("The value of the counter afterwards.")
.exec_exclusive(Counter::add))?;
Ok(Self { count: 0 })
}
/// Read the attribute, sharing the model with the other readers.
fn count(&self, _dev: &Dev<Self>) -> Result<i32> {
Ok(self.count)
}
/// Execute the command, alone on the model for the time it takes.
fn add(&mut self, _dev: &Dev<Self>, value: i32) -> Result<i32> {
self.count += value;
Ok(self.count)
}
}
/// Serve the devices the database lists for this server until it is stopped.
fn main() -> Result<()> {
ServerBuilder::from_args()
.class(ClassDef::new("Counter", Counter::init))?
.run()
}The server is named after its executable, as cppTango does, and its instance is taken
from the command line, so that a Counter executable is launched as the Counter/test
server with Counter test, or with Counter test --dev test/counter/1 to serve a
database of its own for testing, with that device in it, see
server::ServerArgs.
Modules§
- backend
- Generic backend interfaces for devices.
- buf
- Typed buffers, octets being a
Buf<u8, 1>, which is what a backend receives a message into. - corba
- Version 1 of the backend, the one currently implemented by every Tango implementation.
- database
- Proxy to database devices.
- dev
- All proxy class to devices, classes and servers.
- error
- The standard Tango error type.
- name
- Types for the names Tango gives to things: case-ignored names, and type safe Tango Resource Locators.
- prop
- Code for parsing and serializing device or global property values.
- server
- This module provides a high-level access to the server-side of Tango.
- testing
- What testing a client or a server of this crate takes: a database that does not need to
be there, and servers run for the time of a test, see
TestContext. - value
- The values Tango devices exchange: a
Valueis aBufof any element type and dimensions Tango has, and what the arguments and results of handlers convert from and into.