Skip to main content

tango/
backend.rs

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