pub trait BackendDev:
Debug
+ Send
+ Sync {
Show 20 methods
// Required methods
fn name(&self) -> BoxFuture<'_, Result<Arc<str>>>;
fn admin_dev_name(&self) -> BoxFuture<'_, Result<Arc<str>>>;
fn description(&self) -> BoxFuture<'_, Result<Arc<str>>>;
fn info(&self) -> BoxFuture<'_, Result<DevInfo>>;
fn ping(&self) -> BoxFuture<'_, Result<()>>;
fn state(&self) -> BoxFuture<'_, Result<DevState>>;
fn status(&self) -> BoxFuture<'_, Result<String>>;
fn black_box(
&self,
max: Option<usize>,
) -> BoxFuture<'_, Result<Vec<String>>>;
fn command(
&self,
name: Scalar<str>,
input: Value,
source: DevSource,
) -> BoxFuture<'_, Result<Value>>;
fn read_attr(
&self,
names: Option<Spectrum<str>>,
source: DevSource,
) -> BoxFuture<'_, Result<Vec<NamedResult<AttrRead<Value>>>>>;
fn write_attr(
&self,
values: Vec<Named<Value>>,
) -> BoxFuture<'_, Result<Vec<Result<()>>>>;
fn write_read_attr(
&self,
values: Vec<Named<Value>>,
) -> BoxFuture<'_, Result<Vec<NamedResult<AttrRead<Value>>>>>;
fn command_info(
&self,
names: Option<Spectrum<str>>,
) -> BoxFuture<'_, Result<Vec<NamedResult<CommandInfo>>>>;
fn attr_info(
&self,
names: Option<Spectrum<str>>,
) -> BoxFuture<'_, Result<Vec<NamedResult<AttrInfo>>>>;
fn set_attr_config(
&self,
configs: Vec<Named<AttrConfig>>,
) -> BoxFuture<'_, Result<Vec<Result<()>>>>;
fn command_history<'a>(
&'a self,
name: &'a str,
max: usize,
) -> BoxFuture<'a, Result<Vec<(SystemTime, Result<Value>)>>>;
fn attr_history<'a>(
&'a self,
name: &'a str,
max: usize,
) -> BoxFuture<'a, Result<Vec<Result<AttrRead<Value>>>>>;
fn subscribe_attr_value<'a>(
&'a self,
admin: Arc<dyn BackendDev>,
name: &'a str,
event: AttrValueEvent,
) -> BoxFuture<'a, Result<BoxStream<'static, Result<AttrRead<Value>>>>>;
fn subscribe_attr_ready<'a>(
&'a self,
admin: Arc<dyn BackendDev>,
name: &'a str,
) -> BoxFuture<'a, Result<BoxStream<'static, Result<()>>>>;
fn subscribe_attr_conf<'a>(
&'a self,
admin: Arc<dyn BackendDev>,
name: &'a str,
) -> BoxFuture<'a, Result<BoxStream<'static, Result<AttrInfo>>>>;
}Expand description
The goal of a backend is to abstract away the actual technology and protocol used to connect to a specific device. A running backend means that we are connected to a living device.
No function here waits: a request is issued and a future for its answer is given back,
so a caller makes as many requests as it likes before awaiting any of them, and awaits
all of them at once through a futures::future::join_all or a select!. Backends are
expected to multiplex concurrent requests efficiently, pipelining them on a single
connection where the protocol allows it.
The request goes out when the function is called and not when the future it gives back is first polled, which is what makes issuing a batch of them and awaiting the batch afterwards worth anything: a future that is held and never polled is a request that is already on its way.
The names and the values of a request are given owned, as buffers sharing their octets, so that a backend answering in process, a served device above all, keeps them for as long as it likes, and answers with the very names it was given, rather than copying them. A backend that sends its arguments somewhere marshals them on the call.
Every future given out here resolves exactly once: on the device’s answer, on the failure of the connection carrying it, or on the backend giving up on it under whatever timeout it was configured with. There is no deadline in this interface — a caller that will not wait forever bounds its own wait, wrapping the future in the timeout of whichever executor it runs under, and a caller that has stopped caring drops the future, which is what tells the backend to cancel the request rather than pay for its answer.
The requests resolve to what the device answered, decoded: every Value shares the
octets of the message it was decoded out of, so that reading a spectrum of a million
points copies nothing. The requests that answer for several names resolve to one entry
per name, see Named.
A backend answers the requests its protocol or its devices do not have with a
crate::error::ErrorKind::NotImplemented naming what is missing, and every caller has
to deal with that anyway. The parts of
the Tango interface this crate does not deal with at all — the pipes above all — have no
place here: a backend has nothing to answer about what this crate has no notion of.
Required Methods§
Sourcefn name(&self) -> BoxFuture<'_, Result<Arc<str>>>
fn name(&self) -> BoxFuture<'_, Result<Arc<str>>>
Get the full domain/family/member name of this device.
Sourcefn admin_dev_name(&self) -> BoxFuture<'_, Result<Arc<str>>>
fn admin_dev_name(&self) -> BoxFuture<'_, Result<Arc<str>>>
Get the full domain/family/member name of this device’s admin device.
Sourcefn description(&self) -> BoxFuture<'_, Result<Arc<str>>>
fn description(&self) -> BoxFuture<'_, Result<Arc<str>>>
Get the description of the device, by itself.
Sourcefn info(&self) -> BoxFuture<'_, Result<DevInfo>>
fn info(&self) -> BoxFuture<'_, Result<DevInfo>>
Static information about the device: which class it is an instance of, which server
runs it and where, see DevInfo.
None of this changes while the device runs, and none of it is the device’s own: it is what the server it belongs to says of it.
Sourcefn state(&self) -> BoxFuture<'_, Result<DevState>>
fn state(&self) -> BoxFuture<'_, Result<DevState>>
Get the device’s state, this should be equivalent to running read_attr(Some(&["state"])).
Despite being equivalent, this function is provided to allow backends to have
specific implementations for this particular function!
Sourcefn status(&self) -> BoxFuture<'_, Result<String>>
fn status(&self) -> BoxFuture<'_, Result<String>>
Get the device’s status, this should be equivalent to running read_attr(Some(&["status"])).
Despite being equivalent, this function is provided to allow backends to have
specific implementations for this particular function!
Sourcefn black_box(&self, max: Option<usize>) -> BoxFuture<'_, Result<Vec<String>>>
fn black_box(&self, max: Option<usize>) -> BoxFuture<'_, Result<Vec<String>>>
Request the black box of this device, listing the actions that have been run on it.
Sourcefn command(
&self,
name: Scalar<str>,
input: Value,
source: DevSource,
) -> BoxFuture<'_, Result<Value>>
fn command( &self, name: Scalar<str>, input: Value, source: DevSource, ) -> BoxFuture<'_, Result<Value>>
Execute a command on the device, from its name.
Sourcefn read_attr(
&self,
names: Option<Spectrum<str>>,
source: DevSource,
) -> BoxFuture<'_, Result<Vec<NamedResult<AttrRead<Value>>>>>
fn read_attr( &self, names: Option<Spectrum<str>>, source: DevSource, ) -> BoxFuture<'_, Result<Vec<NamedResult<AttrRead<Value>>>>>
Read an attribute or multiple attributes. Pass None to read all attributes.
The idea of reading many attributes at once is to ensure that, if any serialization is enforced on server side, the serialization will be held for reading all attributes, and then released at the end, thus ensuring atomicity of the returned values.
There is one entry per attribute read. If the names are specified, thus Some(names), then the entries are the attributes in the order they were given, and there are as many of them, which the caller might check.
Sourcefn write_attr(
&self,
values: Vec<Named<Value>>,
) -> BoxFuture<'_, Result<Vec<Result<()>>>>
fn write_attr( &self, values: Vec<Named<Value>>, ) -> BoxFuture<'_, Result<Vec<Result<()>>>>
Write the given list of attributes, given as (name, value) pairs. In Tango an attribute write only carries a value: quality and timestamp cannot be written.
There is one result per given value, in the order they were given: the outer result of the call itself is the request failing as a whole, an inner one is that one attribute refusing the write. Nothing names the attribute back, the caller having the names it just gave, in that very order.
Sourcefn write_read_attr(
&self,
values: Vec<Named<Value>>,
) -> BoxFuture<'_, Result<Vec<NamedResult<AttrRead<Value>>>>>
fn write_read_attr( &self, values: Vec<Named<Value>>, ) -> BoxFuture<'_, Result<Vec<NamedResult<AttrRead<Value>>>>>
Same as write but immediately followed by a read on the server side.
Sourcefn command_info(
&self,
names: Option<Spectrum<str>>,
) -> BoxFuture<'_, Result<Vec<NamedResult<CommandInfo>>>>
fn command_info( &self, names: Option<Spectrum<str>>, ) -> BoxFuture<'_, Result<Vec<NamedResult<CommandInfo>>>>
Get the command info of the given commands. Pass None to get all of them.
Sourcefn attr_info(
&self,
names: Option<Spectrum<str>>,
) -> BoxFuture<'_, Result<Vec<NamedResult<AttrInfo>>>>
fn attr_info( &self, names: Option<Spectrum<str>>, ) -> BoxFuture<'_, Result<Vec<NamedResult<AttrInfo>>>>
Get the attribute info (including attr config) of the given attributes. Pass None to get all of them.
Sourcefn set_attr_config(
&self,
configs: Vec<Named<AttrConfig>>,
) -> BoxFuture<'_, Result<Vec<Result<()>>>>
fn set_attr_config( &self, configs: Vec<Named<AttrConfig>>, ) -> BoxFuture<'_, Result<Vec<Result<()>>>>
Change the configuration of the given attributes, each given as the whole configuration it is to have.
Only the configuration is writable, which is the part of an AttrInfo that a
client is allowed to set: what an attribute is — its mode, its data kind and its
format — is what its definition says and is never written from the outside.
A client changes a configuration by reading it and sending it back with what it means to change, every field it left alone being the one the attribute has already, which changes nothing. A field with no value brings it back to what the attribute inherits from its class, if anything.
One result per given attribute, in the order they were given, see
Self::write_attr.
Sourcefn command_history<'a>(
&'a self,
name: &'a str,
max: usize,
) -> BoxFuture<'a, Result<Vec<(SystemTime, Result<Value>)>>>
fn command_history<'a>( &'a self, name: &'a str, max: usize, ) -> BoxFuture<'a, Result<Vec<(SystemTime, Result<Value>)>>>
The recorded history of a command, which is what the polling of the device left
behind, oldest first and at most max entries of it.
A device that is not polled has no history to give, which is a failure and not an empty history: nothing was recorded because nothing is recording.
The timestamp is the one of the recording, and the result is what the command answered then, an entry of a run that failed carrying the failure it failed with.
Sourcefn attr_history<'a>(
&'a self,
name: &'a str,
max: usize,
) -> BoxFuture<'a, Result<Vec<Result<AttrRead<Value>>>>>
fn attr_history<'a>( &'a self, name: &'a str, max: usize, ) -> BoxFuture<'a, Result<Vec<Result<AttrRead<Value>>>>>
The recorded history of an attribute, see Self::command_history, the timestamp
of every entry being the one the reading itself carries.
Sourcefn subscribe_attr_value<'a>(
&'a self,
admin: Arc<dyn BackendDev>,
name: &'a str,
event: AttrValueEvent,
) -> BoxFuture<'a, Result<BoxStream<'static, Result<AttrRead<Value>>>>>
fn subscribe_attr_value<'a>( &'a self, admin: Arc<dyn BackendDev>, name: &'a str, event: AttrValueEvent, ) -> BoxFuture<'a, Result<BoxStream<'static, Result<AttrRead<Value>>>>>
Subscribe to the readings of an attribute, of one of the kinds a device pushes them
under, see AttrValueEvent.
Sourcefn subscribe_attr_ready<'a>(
&'a self,
admin: Arc<dyn BackendDev>,
name: &'a str,
) -> BoxFuture<'a, Result<BoxStream<'static, Result<()>>>>
fn subscribe_attr_ready<'a>( &'a self, admin: Arc<dyn BackendDev>, name: &'a str, ) -> BoxFuture<'a, Result<BoxStream<'static, Result<()>>>>
Subscribe to an attribute being announced as ready to be read, which carries nothing of the attribute itself: what a device pushes here is that there is something to go and read, and reading it is the subscriber’s to do.
Sourcefn subscribe_attr_conf<'a>(
&'a self,
admin: Arc<dyn BackendDev>,
name: &'a str,
) -> BoxFuture<'a, Result<BoxStream<'static, Result<AttrInfo>>>>
fn subscribe_attr_conf<'a>( &'a self, admin: Arc<dyn BackendDev>, name: &'a str, ) -> BoxFuture<'a, Result<BoxStream<'static, Result<AttrInfo>>>>
Subscribe to the configuration of an attribute changing, every event carrying the
configuration as it stands after the change, the same way Self::attr_info gives
it.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".