Skip to main content

tango/
backend.rs

1//! Generic backend interfaces for devices.
2
3use std::sync::{Arc, Mutex};
4use std::time::SystemTime;
5use std::fmt::Debug;
6
7use futures::future::{ready, BoxFuture};
8use futures::stream::BoxStream;
9
10use crate::dev::{AttrConfig, AttrInfo, AttrRead, AttrValueEvent, CommandInfo, DevInfo, DevSource, DevState, Named, NamedResult};
11use crate::value::{Scalar, Spectrum, Value};
12use crate::error::{Error, Result};
13
14
15// =========================== //
16//       Global backend       //
17// =========================== //
18
19/// Backend for connecting to devices.
20pub trait Backend: Debug + Send + Sync {
21
22    /// Use this backend to connect to a device given its ITR (Interoperable Tango
23    /// Reference). If the backend does not support this type of ITR, then it returns
24    /// [`DevError::DevNotHandled`].
25    fn connect(&self, itr: &str) -> BoxFuture<'_, Result<Arc<dyn BackendDev>>>;
26
27    /// Use this backend to connect to a device given its name, and the host/port of the
28    /// service running the device server.
29    fn connect_at(&self, host: &str, port: u16, name: &str) -> BoxFuture<'_, Result<Arc<dyn BackendDev>>>;
30
31    /// Start serving with this protocol, as configured, resolving to the server once it is
32    /// listening: see [`BackendServer::host`] and [`BackendServer::port`] for where it ends
33    /// up.
34    fn serve<'a>(&'a self, config: &'a BackendServerConfig) -> BoxFuture<'a, Result<Arc<dyn BackendServer>>>;
35
36}
37
38// =========================== //
39//       Device backend        //
40// =========================== //
41
42/// The goal of a backend is to abstract away the actual technology and protocol used to
43/// connect to a specific device. A running backend means that we are connected to a
44/// living device.
45///
46/// No function here waits: a request is issued and a future for its answer is given back,
47/// so a caller makes as many requests as it likes before awaiting any of them, and awaits
48/// all of them at once through a [`futures::future::join_all`] or a `select!`. Backends are
49/// expected to multiplex concurrent requests efficiently, pipelining them on a single
50/// connection where the protocol allows it.
51///
52/// The request goes out when the function is **called** and not when the future it gives
53/// back is first polled, which is what makes issuing a batch of them and awaiting the batch
54/// afterwards worth anything: a future that is held and never polled is a request that is
55/// already on its way.
56///
57/// The names and the values of a request are given owned, as buffers sharing their octets,
58/// so that a backend answering in process, a served device above all, keeps them for as
59/// long as it likes, and answers with the very names it was given, rather than copying
60/// them. A backend that sends its arguments somewhere marshals them on the call.
61///
62/// Every future given out here **resolves exactly once**: on the device's answer, on the
63/// failure of the connection carrying it, or on the backend giving up on it under whatever
64/// timeout it was configured with. There is no deadline in this interface — a caller that
65/// will not wait forever bounds its own wait, wrapping the future in the timeout of
66/// whichever executor it runs under, and a caller that has stopped caring drops the future,
67/// which is what tells the backend to cancel the request rather than pay for its answer.
68///
69/// The requests resolve to what the device answered, decoded: every [`Value`] shares the
70/// octets of the message it was decoded out of, so that reading a spectrum of a million
71/// points copies nothing. The requests that answer for several names resolve to one entry
72/// per name, see [`Named`].
73///
74/// A backend answers the requests its protocol or its devices do not have with a
75/// [`crate::error::ErrorKind::NotImplemented`] naming what is missing, and every caller has
76/// to deal with that anyway. The parts of
77/// the Tango interface this crate does not deal with at all — the pipes above all — have no
78/// place here: a backend has nothing to answer about what this crate has no notion of.
79pub trait BackendDev: Debug + Send + Sync {
80
81    /// Get the full domain/family/member name of this device.
82    fn name(&self) -> BoxFuture<'_, Result<Arc<str>>>;
83
84    /// Get the full domain/family/member name of this device's admin device.
85    fn admin_dev_name(&self) -> BoxFuture<'_, Result<Arc<str>>>;
86
87    /// Get the description of the device, by itself.
88    fn description(&self) -> BoxFuture<'_, Result<Arc<str>>>;
89
90    /// Static information about the device: which class it is an instance of, which server
91    /// runs it and where, see [`DevInfo`].
92    ///
93    /// None of this changes while the device runs, and none of it is the device's own: it
94    /// is what the server it belongs to says of it.
95    fn info(&self) -> BoxFuture<'_, Result<DevInfo>>;
96
97    /// Just ping the device.
98    fn ping(&self) -> BoxFuture<'_, Result<()>>;
99
100    /// Get the device's state, this should be equivalent to running `read_attr(Some(&["state"]))`.
101    /// Despite being equivalent, this function is provided to allow backends to have
102    /// specific implementations for this particular function!
103    fn state(&self) -> BoxFuture<'_, Result<DevState>>;
104
105    /// Get the device's status, this should be equivalent to running `read_attr(Some(&["status"]))`.
106    /// Despite being equivalent, this function is provided to allow backends to have
107    /// specific implementations for this particular function!
108    fn status(&self) -> BoxFuture<'_, Result<String>>;
109
110    /// Request the black box of this device, listing the actions that have been run on it.
111    fn black_box(&self, max: Option<usize>) -> BoxFuture<'_, Result<Vec<String>>>;
112
113    /// Execute a command on the device, from its name.
114    fn command(&self, name: Scalar<str>, input: Value, source: DevSource) -> BoxFuture<'_, Result<Value>>;
115
116    /// Read an attribute or multiple attributes. Pass None to read all attributes.
117    ///
118    /// The idea of reading many attributes at once is to ensure that, if any
119    /// serialization is enforced on server side, the serialization will be held for
120    /// reading all attributes, and then released at the end, thus ensuring atomicity
121    /// of the returned values.
122    ///
123    /// There is one entry per attribute read. If the names are specified, thus
124    /// Some(names), then the entries are the attributes in the order they were given, and
125    /// there are as many of them, which the caller might check.
126    fn read_attr(&self, names: Option<Spectrum<str>>, source: DevSource) -> BoxFuture<'_, Result<Vec<NamedResult<AttrRead<Value>>>>>;
127
128    /// Write the given list of attributes, given as (name, value) pairs. In Tango an
129    /// attribute write only carries a value: quality and timestamp cannot be written.
130    ///
131    /// There is one result per given value, in the order they were given: the outer
132    /// result of the call itself is the request failing as a whole, an inner one is that
133    /// one attribute refusing the write. Nothing names the attribute back, the caller
134    /// having the names it just gave, in that very order.
135    fn write_attr(&self, values: Vec<Named<Value>>) -> BoxFuture<'_, Result<Vec<Result<()>>>>;
136
137    /// Same as write but immediately followed by a read on the server side.
138    fn write_read_attr(&self, values: Vec<Named<Value>>) -> BoxFuture<'_, Result<Vec<NamedResult<AttrRead<Value>>>>>;
139
140    /// Get the command info of the given commands. Pass None to get all of them.
141    fn command_info(&self, names: Option<Spectrum<str>>) -> BoxFuture<'_, Result<Vec<NamedResult<CommandInfo>>>>;
142
143    /// Get the attribute info (including attr config) of the given attributes. Pass None to
144    /// get all of them.
145    fn attr_info(&self, names: Option<Spectrum<str>>) -> BoxFuture<'_, Result<Vec<NamedResult<AttrInfo>>>>;
146
147    /// Change the configuration of the given attributes, each given as the whole
148    /// configuration it is to have.
149    ///
150    /// Only the configuration is writable, which is the part of an [`AttrInfo`] that a
151    /// client is allowed to set: what an attribute *is* — its mode, its data kind and its
152    /// format — is what its definition says and is never written from the outside.
153    ///
154    /// A client changes a configuration by reading it and sending it back with what it
155    /// means to change, every field it left alone being the one the attribute has already,
156    /// which changes nothing. A field with no value brings it back to what the attribute
157    /// inherits from its class, if anything.
158    ///
159    /// One result per given attribute, in the order they were given, see
160    /// [`Self::write_attr`].
161    fn set_attr_config(&self, configs: Vec<Named<AttrConfig>>) -> BoxFuture<'_, Result<Vec<Result<()>>>>;
162
163    /// The recorded history of a command, which is what the polling of the device left
164    /// behind, oldest first and at most `max` entries of it.
165    ///
166    /// A device that is not polled has no history to give, which is a failure and not an
167    /// empty history: nothing was recorded because nothing is recording.
168    ///
169    /// The timestamp is the one of the recording, and the result is what the command
170    /// answered then, an entry of a run that failed carrying the failure it failed with.
171    fn command_history<'a>(&'a self, name: &'a str, max: usize) -> BoxFuture<'a, Result<Vec<(SystemTime, Result<Value>)>>>;
172
173    /// The recorded history of an attribute, see [`Self::command_history`], the timestamp
174    /// of every entry being the one the reading itself carries.
175    fn attr_history<'a>(&'a self, name: &'a str, max: usize) -> BoxFuture<'a, Result<Vec<Result<AttrRead<Value>>>>>;
176
177    // ----------------------- //
178    //      Subscriptions      //
179    // ----------------------- //
180
181    // A subscription is the one place in this trait where a request does not resolve once:
182    // the future resolves once the subscription is in place, to a stream that keeps giving.
183    // The stream gives the last event and not the whole history of them, so a subscriber
184    // that did not keep up with a device pushing faster than it reads skips to the latest
185    // one rather than falling behind forever. Dropping the stream is what unsubscribes,
186    // and dropping the future before it resolved cancels the subscription.
187    //
188    // A subscription that could not be made fails through the future. An item of the
189    // stream that is an error is one event that failed, such as the reading the device was
190    // to push. Note that a subscription gives no initial value: the first event is the
191    // next one the device pushes, so a subscriber that needs to know where the attribute
192    // stands reads it, once subscribed.
193    //
194    // A subscription lives as long as the connection it was made over, just like this
195    // device backend does: once it is lost, such as the device becoming unreachable, the
196    // stream gives an [`ErrorKind::DevNotConnected`] and ends, the very error a request
197    // fails with when its connection is lost. The device may have been restarted,
198    // elsewhere even, so making the subscription again is not the backend's to do, but
199    // the one's who finds the device again, see [`crate::dev::DevChannel`].
200    //
201    // The admin device of the device is given, because subscribing goes through it in
202    // Tango, and a backend has no way to reach it on its own: where it is, is something
203    // only the database knows.
204
205    /// Subscribe to the readings of an attribute, of one of the kinds a device pushes them
206    /// under, see [`AttrValueEvent`].
207    fn subscribe_attr_value<'a>(&'a self, admin: Arc<dyn BackendDev>, name: &'a str, event: AttrValueEvent) -> BoxFuture<'a, Result<BoxStream<'static, Result<AttrRead<Value>>>>>;
208
209    /// Subscribe to an attribute being announced as ready to be read, which carries nothing
210    /// of the attribute itself: what a device pushes here is that there is something to go
211    /// and read, and reading it is the subscriber's to do.
212    fn subscribe_attr_ready<'a>(&'a self, admin: Arc<dyn BackendDev>, name: &'a str) -> BoxFuture<'a, Result<BoxStream<'static, Result<()>>>>;
213
214    /// Subscribe to the configuration of an attribute changing, every event carrying the
215    /// configuration as it stands after the change, the same way [`Self::attr_info`] gives
216    /// it.
217    fn subscribe_attr_conf<'a>(&'a self, admin: Arc<dyn BackendDev>, name: &'a str) -> BoxFuture<'a, Result<BoxStream<'static, Result<AttrInfo>>>>;
218
219}
220
221// =========================== //
222//       Server backend        //
223// =========================== //
224
225/// The backend for a server that is served, from the moment [`Backend::serve`] hands it
226/// over until it is stopped, see [`Self::stop`], or dropped.
227///
228/// Nothing here waits but [`Self::wait`]: adding, removing a device and stopping are quick
229/// and never block, so they are called from anywhere, a request of one of the devices of
230/// the backend included. What they ask for happens at some point after, a device that was
231/// removed still answering the requests it was answering, for instance.
232///
233/// A backend holds the devices it serves, strongly, from [`Self::add`], and, once they
234/// were removed or the backend stopped, until their requests in flight are answered, and
235/// **never beyond [`Self::wait`]**: once it resolves, no [`Arc`] to any device is left
236/// anywhere in the backend. This is what lets the owner of a device rely on the device
237/// being dropped with the last reference it has.
238///
239/// # Dropping
240///
241/// Dropping the last [`Arc`] to a backend server drops everything of it at once, **with no
242/// side effect**: every device it serves is let go of, and every request in flight is
243/// cancelled, its future dropped and nothing answered. A server that is to end properly is
244/// therefore stopped and waited for before it is dropped.
245pub trait BackendServer: Debug + Send + Sync {
246
247    /// Add a device to be served, giving back its ITR, which it is reachable at from then
248    /// on or shortly after. The name of the device is given with the device so the server
249    /// doesn't have to call the name() function on it. This is quick and never blocks, see
250    /// the trait documentation.
251    ///
252    /// Device names are not case sensitive: a backend lowercases the name as soon as it is
253    /// given, which every later lookup of the device, from [`Self::remove`] to the requests
254    /// of the clients, then relies on.
255    ///
256    /// The server might return any error, but the error NotHandled should be ignored.
257    fn add(&self, name: &str, dev: Arc<dyn BackendDev>) -> Result<Arc<str>>;
258
259    /// Stop serving the device of that name, giving back whether one was being served. No
260    /// new request reaches it from the call on, or shortly after, and the ones it was
261    /// answering are still answered. This is quick and never blocks, see the trait
262    /// documentation.
263    ///
264    /// The device itself is **not** given back: the backend drops what it was holding of
265    /// it once its requests are answered, which is what makes it stop being reachable.
266    fn remove(&self, name: &str) -> bool;
267
268    /// The host, ip or hostname, where this server is listening.
269    fn host(&self) -> Arc<str>;
270
271    /// The TCP port this server is listening to.
272    fn port(&self) -> u16;
273
274    /// The version of the protocol the devices are served with, which is what a client is
275    /// told of a device in its info and what the database is told when it is exported.
276    fn version(&self) -> i32;
277
278    /// Stop serving, which nothing undoes: a backend that was stopped is one no client
279    /// reaches again, whatever is added to it afterward. Every device is removed, all at
280    /// once, as [`Self::remove`] does: no new request is taken, and the ones being answered
281    /// are.
282    ///
283    /// This does not wait for anything, and can therefore be called from anywhere,
284    /// including from a request of one of its devices; see [`Self::wait`] for that.
285    fn stop(&self);
286
287    /// Resolve once this backend was stopped, see [`Self::stop`], and holds nothing of any
288    /// of its devices anymore, see the trait documentation, after which dropping it is
289    /// dropping nothing of them.
290    ///
291    /// This waits until every request in flight is answered, so it may not be awaited from
292    /// a future the backend is driving, that is from a request of one of its devices: at
293    /// best that request is left hanging, at worst it waits for itself. Waiting is all it
294    /// does, the future it gives back never blocking the thread polling it, as for every
295    /// future of a backend.
296    fn wait(&self) -> BoxFuture<'_, ()>;
297
298    /// Answer the `ZmqEventSubscriptionChange` command of the admin device, which a client
299    /// sends to have an event published and to be told where it is published. The
300    /// argument and the result are those of the command, as a client sends and expects
301    /// them.
302    ///
303    /// A backend that publishes no event this way answers that it is not implemented,
304    /// which is what this does by default.
305    fn zmq_event_subscription_change(&self, _args: Spectrum<str>) -> BoxFuture<'_, Result<(Spectrum<i32>, Spectrum<str>)>> {
306        Box::pin(ready(Err(Error::not_implemented()
307            .with_append_description("This server publishes no event over ZMQ"))))
308    }
309
310    /// Answer the `EventConfirmSubscription` command of the admin device, which a client
311    /// sends periodically to keep the events it subscribed to published. The argument is
312    /// that of the command, a list of device name, attribute name and event name triplets.
313    ///
314    /// A backend that publishes no event this way answers that it is not implemented,
315    /// which is what this does by default.
316    fn event_confirm_subscription(&self, _args: Spectrum<str>) -> BoxFuture<'_, Result<()>> {
317        Box::pin(ready(Err(Error::not_implemented()
318            .with_append_description("This server publishes no event over ZMQ"))))
319    }
320
321}
322
323/// How a backend serves, see [`Backend::serve`], whatever is not given being left to the
324/// backend, which is what the default configuration is.
325#[derive(Debug, Clone, Default)]
326#[non_exhaustive]
327pub struct BackendServerConfig {
328    /// The host to listen at, an address or a name of this machine, every one of its
329    /// interfaces if none is given.
330    pub host: Option<String>,
331    /// The port to listen at, one the system picks if none is given, which is what a Tango
332    /// device server does: its clients find it through the database, where it is exported.
333    /// A fixed port is what a server without a database needs, its clients having nothing
334    /// else to find it by.
335    pub port: Option<u16>,
336    /// The name of the admin device of the server being served, `dserver/exe/inst`, which
337    /// names the event channel of the server: a backend not given one publishes no event.
338    pub admin_name: Option<Arc<str>>,
339    /// The host and port of the database the server is registered in, which prefixes the
340    /// names of its events. A server without a database prefixes them with its own host and
341    /// port instead, marking them as such.
342    pub tango_host: Option<(Arc<str>, u16)>,
343}
344
345impl BackendServerConfig {
346
347    /// Everything left to the backend.
348    pub fn new() -> Self {
349        Self::default()
350    }
351
352    /// Listen at that host, see [`Self::host`].
353    pub fn with_host(mut self, host: &str) -> Self {
354        self.host = Some(host.to_string());
355        self
356    }
357
358    /// Listen at that port, see [`Self::port`].
359    pub fn with_port(mut self, port: u16) -> Self {
360        self.port = Some(port);
361        self
362    }
363
364    /// Serve the server of that admin device, see [`Self::admin_name`].
365    pub fn with_admin_name(mut self, admin_name: impl Into<Arc<str>>) -> Self {
366        self.admin_name = Some(admin_name.into());
367        self
368    }
369
370    /// Serve a server registered in the database at that host and port, see
371    /// [`Self::tango_host`].
372    pub fn with_tango_host(mut self, host: impl Into<Arc<str>>, port: u16) -> Self {
373        self.tango_host = Some((host.into(), port));
374        self
375    }
376
377}
378
379// =========================== //
380//       Default backend       //
381// =========================== //
382
383static DEFAULT_BACKEND: Mutex<Option<Arc<dyn Backend>>> = Mutex::new(None);
384
385pub fn default_backend() -> Arc<dyn Backend> {
386    match &mut *DEFAULT_BACKEND.lock().unwrap() {
387        Some(backend) => Arc::clone(backend),
388        opt @ None => {
389            let backend = crate::corba::CorbaBackend::new();
390            *opt = Some(Arc::clone(&backend) as Arc<dyn Backend>);
391            backend
392        }
393    }
394}
395
396pub fn set_default_backend(backend: Arc<dyn Backend>) {
397    *DEFAULT_BACKEND.lock().unwrap() = Some(backend);
398}