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}