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}