rf_core/
context.rs

1use crate::export::{Export, Result};
2use crate::path::Path;
3use crate::sensor_id::SensorId;
4use std::any::Any;
5use std::collections::HashMap;
6use std::rc::Rc;
7use std::str::FromStr;
8
9/// This type represents the local sensors of the device.
10pub type LocalSensors = HashMap<SensorId, Rc<Box<dyn Any>>>;
11
12/// This type represents the neighbouring sensors of the device.
13pub type NbrSensors = HashMap<SensorId, HashMap<i32, Rc<Box<dyn Any>>>>;
14
15/// This type represents the neighbouring exports of the device.
16pub type Exports = HashMap<i32, Export>;
17
18/// # Context implementation
19///
20/// * `selfId` The ID of the device that this context is for.
21///
22/// * `local_sensor` The values perceived by the local sensors of the device.
23///
24/// * `nbr_sensor` The values perceived by the sensors for each neighbor of the device.
25///
26/// * `exports` All the export that are available to the device.
27#[derive(Debug, Clone)]
28pub struct Context {
29    self_id: i32,
30    local_sensor: LocalSensors,
31    nbr_sensor: NbrSensors,
32    exports: Exports,
33}
34
35impl Context {
36    /// Create new Context of a device from the given parameters.
37    ///
38    /// # Arguments
39    ///
40    /// * `self_id` - the ID of the device
41    ///
42    /// * `local_sensor` - The values perceived by the local sensors of the device.
43    ///
44    /// * `nbr_sensor` - The values perceived by the sensors for each neighbor of the device.
45    ///
46    /// * `exports` - All the export that are available to the device.
47    ///
48    /// # Returns
49    ///
50    /// The new Context.
51    pub fn new(
52        self_id: i32,
53        local_sensor: LocalSensors,
54        nbr_sensor: NbrSensors,
55        exports: Exports,
56    ) -> Self {
57        Self {
58            self_id,
59            local_sensor,
60            nbr_sensor,
61            exports,
62        }
63    }
64
65    pub fn self_id(&self) -> &i32 {
66        &self.self_id
67    }
68
69    pub fn exports(&self) -> &Exports {
70        &self.exports
71    }
72
73    /// Add an export of a device to the context.
74    ///
75    /// # Arguments
76    ///
77    /// * `id`  the ID of the device
78    /// * `data` the export of the device
79    pub fn put_export(&mut self, id: i32, data: Export) {
80        self.exports.insert(id, data);
81    }
82
83    /// Read the value corresponding to the given path from the export of a device.
84    ///
85    /// # Arguments
86    ///
87    /// * `id` the ID of the device
88    /// * `path` the path to the value
89    ///
90    /// # Generic Parameters
91    ///
92    /// * `A` the type of the value to return. It must have a `'static` lifetime.
93    ///
94    /// # Returns
95    ///
96    /// An `Option` of the value if it exists
97    pub fn read_export_value<A: 'static + FromStr + Clone>(
98        &self,
99        id: &i32,
100        path: &Path,
101    ) -> Result<A> {
102        self.exports
103            .get(id)
104            .ok_or("Export not found".into())
105            .and_then(|export| export.get(path))
106    }
107
108    pub fn local_sensors(&self) -> &LocalSensors {
109        &self.local_sensor
110    }
111
112    /// Get the value of the given sensor.
113    ///
114    /// # Arguments
115    ///
116    /// * `name` the name of the sensor
117    ///
118    /// # Generic Parameters
119    /// * `A` the type of the value to return. It must have a `'static` lifetime.
120    ///
121    /// # Returns
122    ///
123    /// An `Option` of the value if it exists
124    pub fn local_sense<A: 'static>(&self, local_sensor_id: &SensorId) -> Option<&A> {
125        self.local_sensor
126            .get(local_sensor_id)
127            .and_then(|value| value.downcast_ref::<A>())
128    }
129
130    pub fn nbr_sensors(&self) -> &NbrSensors {
131        &self.nbr_sensor
132    }
133
134    /// Get the value of the given sensor for the given neighbor.
135    ///
136    /// # Arguments
137    ///
138    /// * `sensor_id` the neighbor sensor id
139    /// * `nbr_id` the neighbor id
140    ///
141    /// # Generic Parameters
142    ///
143    /// * `A` the type of the value to return. It must have a `'static` lifetime.
144    ///
145    /// # Returns
146    ///
147    /// An `Option` of the value if it exists
148    pub fn nbr_sense<A: 'static>(&self, sensor_id: &SensorId, nbr_id: &i32) -> Option<&A> {
149        self.nbr_sensor
150            .get(sensor_id)
151            .and_then(|value| value.get(nbr_id))
152            .and_then(|value| value.downcast_ref::<A>())
153    }
154}
155
156#[cfg(test)]
157mod test {
158    use super::*;
159    use crate::path::Path;
160    use crate::sensor_id::{sensor, SensorId};
161    use crate::slot::Slot::{Branch, Nbr, Rep};
162    use crate::{export, path};
163    use std::any::Any;
164    use std::collections::HashMap;
165    use std::rc::Rc;
166
167    fn context_builder() -> Context {
168        let local_sensor = HashMap::from([(sensor("test"), Rc::new(Box::new(10) as Box<dyn Any>))]);
169        let nbr_sensor = HashMap::from([(
170            sensor("test"),
171            HashMap::from([(0, Rc::new(Box::new(10) as Box<dyn Any>))]),
172        )]);
173        let export = HashMap::from([(0, export!((path!(Rep(0), Nbr(0)), 10)))]);
174        Context::new(7, local_sensor, nbr_sensor, export)
175    }
176
177    #[test]
178    fn assert_on_fields() {
179        let context = context_builder();
180        assert_eq!(context.self_id, 7);
181        assert_eq!(context.exports.len(), 1);
182        assert_eq!(context.local_sensor.len(), 1);
183        assert_eq!(context.nbr_sensor.len(), 1);
184    }
185
186    #[test]
187    fn test_put_export() {
188        let mut context = context_builder();
189        assert_eq!(context.exports.len(), 1);
190        let add_export = export!((path!(Branch(0), Nbr(0)), 5));
191        context.put_export(1, add_export);
192        assert_eq!(context.exports.len(), 2)
193    }
194
195    #[test]
196    fn test_read_export_value() {
197        let context = context_builder();
198        assert_eq!(
199            context
200                .read_export_value::<i32>(&0, &path!(Rep(0), Nbr(0)))
201                .unwrap(),
202            10
203        );
204        assert!(context.read_export_value::<i32>(&1, &Path::new()).is_err());
205        assert!(context.read_export_value::<i32>(&0, &Path::new()).is_err());
206    }
207
208    #[test]
209    fn test_local_sense() {
210        let context = context_builder();
211        assert_eq!(
212            context
213                .local_sense::<i32>(&SensorId::new("test".to_string()))
214                .unwrap(),
215            &10
216        );
217    }
218
219    #[test]
220    fn test_nbr_sense() {
221        let context = context_builder();
222        assert_eq!(
223            context
224                .nbr_sense::<i32>(&SensorId::new("test".to_string()), &0)
225                .unwrap(),
226            &10
227        );
228    }
229}