Skip to main content

tango/
lib.rs

1//! An implementation of the [Tango] distributed control system, written from scratch in
2//! Rust, for writing clients and device servers.
3//!
4//! The protocol actually used to reach a device is a detail of the crate rather than of
5//! the code using it: a [`dev::DevProxy`] talks to whatever backend can reach its target,
6//! and a [`server::Server`] serves its devices on every backend able to serve them.
7//!
8//! Every call is blocking, up to the timeout of the proxy or the deadline of the request:
9//! there is no runtime to install. Each one also has an asynchronous `*_async` form, which
10//! runs on any executor, so that a single thread can wait on many devices at once; either
11//! way, the backend multiplexes the concurrent requests over the connection it shares.
12//!
13//! # Client
14//!
15//! A client asks a device for what it wants and says which type it expects, one name
16//! giving one value and a tuple of names giving as many, in a single request.
17//!
18//! ```no_run
19//! use tango::dev::DevProxy;
20//! use tango::error::Result;
21//!
22//! fn main() -> Result<()> {
23//!
24//!     // The device is looked up in the database given by `TANGO_HOST`, the connection
25//!     // being made on the first request and kept for the following ones.
26//!     let dev = DevProxy::new("sys/tg_test/1");
27//!     println!("{} is {:?}", dev.name()?, dev.state()?);
28//!
29//!     // A reading carries the value, the write value, the quality and the timestamp.
30//!     let read = dev.read_attr::<f64, _>("double_scalar")?;
31//!     println!("double_scalar = {:?} ({:?})", read.value, read.quality);
32//!
33//!     dev.write_attr(("double_scalar", 3.14))?;
34//!
35//!     // A command is typed the same way, on both of its sides, `()` being no data.
36//!     let echo: String = dev.command("DevString", "hello")?;
37//!     println!("DevString -> {echo}");
38//!
39//!     Ok(())
40//! }
41//! ```
42//!
43//! # Server
44//!
45//! A device server is a plain Rust struct, its model, and the functions of it that the
46//! attributes and the commands of the device are defined from. Nothing is implemented on
47//! the model and no macro is involved: a read function is given a shared access to it, a
48//! write or an exclusive command an exclusive one, and the server takes care of the lock.
49//!
50//! ```no_run
51//! use tango::server::{AttrDef, ClassDef, CommandDef, Dev, ServerBuilder};
52//! use tango::error::Result;
53//!
54//! /// The model of one device, owned by the server serving it.
55//! struct Counter {
56//!     count: i32,
57//! }
58//!
59//! impl Counter {
60//!
61//!     /// Build the model of a device, and give it its interface.
62//!     fn init(dev: &Dev<Self>) -> Result<Self> {
63//!
64//!         dev.add_attr(AttrDef::new("count")
65//!             .doc("The current value of the counter.")
66//!             .read(Counter::count))?;
67//!
68//!         dev.add_command(CommandDef::new("Add")
69//!             .in_doc("How much to add to the counter.")
70//!             .out_doc("The value of the counter afterwards.")
71//!             .exec_exclusive(Counter::add))?;
72//!
73//!         Ok(Self { count: 0 })
74//!     }
75//!
76//!     /// Read the attribute, sharing the model with the other readers.
77//!     fn count(&self, _dev: &Dev<Self>) -> Result<i32> {
78//!         Ok(self.count)
79//!     }
80//!
81//!     /// Execute the command, alone on the model for the time it takes.
82//!     fn add(&mut self, _dev: &Dev<Self>, value: i32) -> Result<i32> {
83//!         self.count += value;
84//!         Ok(self.count)
85//!     }
86//!
87//! }
88//!
89//! /// Serve the devices the database lists for this server until it is stopped.
90//! fn main() -> Result<()> {
91//!     ServerBuilder::from_args()
92//!         .class(ClassDef::new("Counter", Counter::init))
93//!         .run()
94//! }
95//! ```
96//!
97//! The server is named after its executable, as cppTango does, and its instance is taken
98//! from the command line, so that a `Counter` executable is launched as the `Counter/test`
99//! server with `Counter test`, or with `Counter test --dev test/counter/1` to serve a
100//! database of its own for testing, with that device in it, see
101//! [`server::ServerArgs`].
102//!
103//! [Tango]: https://www.tango-controls.org/
104
105mod future;
106mod pool;
107pub mod name;
108pub mod buf;
109
110pub mod value;
111pub mod prop;
112
113mod local;
114pub mod dev;
115pub mod error;
116pub mod database;
117pub mod backend;
118
119pub mod server;
120pub mod testing;
121
122pub mod corba;