Skip to main content

tango/server/
class.rs

1//! Implementation of the classes of a server.
2
3use std::sync::{Arc, Mutex, Weak};
4use std::collections::HashMap;
5use std::fmt::{self, Debug};
6use std::future::Future;
7use std::any::Any;
8
9use crate::dev::{All, Named, ToNames, ToNamesNotAll};
10use crate::database::{GetPropSpec, SetPropSpec};
11use crate::error::{Error, ErrorKind, Result};
12use crate::name::{self, ArcName, Name};
13use crate::value::Spectrum;
14
15use super::{reserved_name, Model, RESERVED_ATTRS};
16use super::dev::{AnyDev, Dev};
17use super::server::Server;
18use super::def::ClassDef;
19
20
21/// A class of any model, which is how the server holds its classes.
22pub(super) trait AnyClass: Debug + Send + Sync {
23
24    /// The name of that class, shared so that it is cheaply copied out.
25    fn name(&self) -> &Arc<str>;
26
27    /// The rank of that class among the classes of its server, which is the order they
28    /// were made in and the order their devices are initialized in, see [`Server::sync_async`].
29    fn index(&self) -> usize;
30
31    /// Add a device of that class to the server, see [`Class::add_dev`].
32    fn add_dev(&self, name: &str) -> Result<Arc<dyn AnyDev>>;
33
34    /// That class as [`Any`], for it to be downcast to the [`Class`] it is.
35    fn into_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
36
37}
38
39/// A class of a server, through which its devices reach its definition and the
40/// properties the database holds for it.
41pub struct Class<M: Model> {
42    /// The name of that class, as its definition gave it, shared with the key it is
43    /// registered under in the server.
44    name: Arc<str>,
45    /// See [`AnyClass::index`].
46    index: usize,
47    /// Self reference, for what a class hands its devices of itself.
48    this: Weak<Self>,
49    server: Arc<Server>,
50    def: Arc<ClassDef<M>>,
51    /// The properties the database holds for the attributes of this class, by attribute
52    /// name, which every device of the class is configured from, see [`Self::attr_props`].
53    attr_props: Mutex<HashMap<ArcName, Arc<[Named<Spectrum<str>>]>>>,
54}
55
56impl<M: Model> Class<M> {
57
58    /// A new class of that server, from its definition, at that rank among the classes of
59    /// the server, see [`AnyClass::index`].
60    pub(super) fn new(server: Arc<Server>, def: Arc<ClassDef<M>>, index: usize) -> Arc<Self> {
61        Arc::new_cyclic(|this| Self {
62            name: Arc::from(&*def.name),
63            index,
64            this: this.clone(),
65            server,
66            def,
67            attr_props: Mutex::new(HashMap::new()),
68        })
69    }
70
71    /// The name of that class.
72    #[inline]
73    pub fn name(&self) -> &str {
74        &self.name
75    }
76
77    /// The server this class is defined in.
78    #[inline]
79    pub fn server(&self) -> &Server {
80        &self.server
81    }
82
83    // The properties of a class are the database's: everything below asks the database 
84    // of the server for what it holds of this class, see the very same methods on
85    // [`crate::database::DatabaseProxy`]. The blocking forms are for synchronous handlers, 
86    // which run on the pool, and must never run in an asynchronous one.
87
88    /// The names of the properties of this class that match the given filter.
89    pub async fn list_prop_async(&self, filter: &str) -> Result<Spectrum<str>> {
90        self.server().database().list_class_prop_async(&self.name, filter).await
91    }
92
93    /// The names of the properties of this class, see [`Self::list_prop_async`].
94    pub fn list_prop(&self, filter: &str) -> Result<Spectrum<str>> {
95        self.server().database().list_class_prop(&self.name, filter)
96    }
97
98    /// The properties of this class, parsed into the shape the given names ask for, see
99    /// [`crate::database::DatabaseProxy::get_class_prop`].
100    pub fn get_prop_async<Spec, Names: ToNames>(&self, names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
101    where
102        Spec: GetPropSpec<Names>,
103    {
104        self.server().database().get_class_prop_async::<Spec, Names>(&self.name, names)
105    }
106
107    /// The properties of this class, see [`Self::get_prop_async`].
108    pub fn get_prop<Spec, Names: ToNames>(&self, names: Names) -> Result<Spec::Output>
109    where
110        Spec: GetPropSpec<Names>,
111    {
112        self.server().database().get_class_prop::<Spec, Names>(&self.name, names)
113    }
114
115    /// Write the given properties of this class.
116    pub fn set_prop_async<'a, Spec>(&'a self, spec: Spec) -> impl Future<Output = Result<()>> + Send
117    where
118        Spec: SetPropSpec<'a>,
119    {
120        self.server().database().set_class_prop_async(&self.name, spec)
121    }
122
123    /// Write the given properties of this class, see [`Self::set_prop_async`].
124    pub fn set_prop<'a, Spec>(&'a self, spec: Spec) -> Result<()>
125    where
126        Spec: SetPropSpec<'a>,
127    {
128        self.server().database().set_class_prop(&self.name, spec)
129    }
130
131    /// Delete the given properties of this class.
132    pub fn delete_prop_async<Names: ToNamesNotAll>(&self, names: Names) -> impl Future<Output = Result<()>> + Send {
133        self.server().database().delete_class_prop_async(&self.name, names)
134    }
135
136    /// Delete the given properties of this class, see [`Self::delete_prop_async`].
137    pub fn delete_prop<Names: ToNamesNotAll>(&self, names: Names) -> Result<()> {
138        self.server().database().delete_class_prop(&self.name, names)
139    }
140
141    /// The properties the database holds for the attributes of this class of those names,
142    /// in the same order, an attribute it holds nothing for having none.
143    /// 
144    /// They are the same for every device of the class, so the database is only asked the
145    /// first time a device has an attribute of that name, and never again: what a device
146    /// is configured with only changes at the level of the device itself, which is the
147    /// one a client changes.
148    ///
149    /// The State and the Status have none, whatever the database holds: they are the
150    /// server's own, which only the device configures, the way cppTango does.
151    pub(super) async fn attr_props(&self, names: &[&str]) -> Result<Vec<Arc<[Named<Spectrum<str>>]>>> {
152
153        // Only refetch the missing attribute names.
154        let missing = {
155            let attr_props = self.attr_props.lock().unwrap();
156            names.iter()
157                .copied()
158                .filter(|name| !attr_props.contains_key(Name::new(name)))
159                .filter(|name| reserved_name(name, RESERVED_ATTRS).is_none())
160                .collect::<Vec<_>>()
161        };
162
163        if !missing.is_empty() {
164            
165            let fetched = self.server()
166                .database()
167                .get_class_attr_prop_async::<Spectrum<str>, _, _>(&self.name, missing.as_slice(), All).await?;
168
169            let mut attr_props = self.attr_props.lock().unwrap();
170            for (name, props) in missing.into_iter().zip(fetched) {
171                attr_props.insert(ArcName::from(name), Arc::from(props?));
172            }
173
174        }
175
176        let attr_props = self.attr_props.lock().unwrap();
177        Ok(names
178            .iter()
179            .map(|name| attr_props.get(Name::new(name))
180                .map_or_else(|| Arc::from([]), Arc::clone))
181            .collect())
182
183    }
184
185    /// The definition of that class.
186    #[inline]
187    pub fn def(&self) -> &ClassDef<M> {
188        &self.def
189    }
190
191    /// Add a device of that class to the server, whose name has to be a valid
192    /// `domain/family/member` one, see [`name::check_tango_dev_name`], and unique
193    /// server-wide.
194    /// 
195    /// The device is only registered: it is initialized, served and exported by the next
196    /// [`Server::sync_async`].
197    pub fn add_dev(&self, name: &str) -> Result<Arc<Dev<M>>> {
198
199        if !name::check_tango_dev_name(name) {
200            return Err(Error::new(ErrorKind::DevNameInvalid)
201                .with_description(format!("Device {name:?} has an invalid name, it should be \
202                    domain/family/member, each part made of letters, digits, '_' and '-', at most 85 long")));
203        }
204
205        let core = self.server.core_checked()?;
206        let mut server_devs = core.devs.lock().unwrap();
207        if server_devs.contains_key(Name::new(name)) {
208            return Err(Error::new(ErrorKind::DevAlreadyDefined)
209                .with_description(format!("Device {name:?} is already defined on the server")));
210        }
211
212        let name = Arc::<str>::from(name);
213        // A class is only ever reached through a reference to it, so it is alive.
214        let dev = Dev::new(self.this.upgrade().unwrap(), Arc::clone(&name));
215        server_devs.insert(ArcName::from(name), Arc::clone(&dev) as Arc<dyn AnyDev>);
216        drop(server_devs);
217
218        core.pending.lock().unwrap().added.push(Arc::clone(&dev) as Arc<dyn AnyDev>);
219        Ok(dev)
220
221    }
222
223}
224
225impl<M: Model> AnyClass for Class<M> {
226
227    #[inline]
228    fn name(&self) -> &Arc<str> {
229        &self.name
230    }
231
232    #[inline]
233    fn index(&self) -> usize {
234        self.index
235    }
236
237    fn add_dev(&self, name: &str) -> Result<Arc<dyn AnyDev>> {
238        Class::add_dev(self, name).map(|dev| dev as Arc<dyn AnyDev>)
239    }
240
241    #[inline]
242    fn into_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
243        self
244    }
245
246}
247
248impl<M: Model> Debug for Class<M> {
249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        f.debug_struct("Class").field("name", &self.name).finish_non_exhaustive()
251    }
252}