Skip to main content

proto_lab/device/
wireless_modem.rs

1use std::{
2    collections::VecDeque,
3    sync::{Arc, Mutex},
4};
5
6use super::IODriverSimulator;
7
8enum AntennaState {
9    Transmit(u8),
10    Receive(u8),
11    Idle,
12}
13
14//  Diagram of a half-duplex device, probably radio driver, or single wired transceiver
15//  Is made to picture the idea of internal quques connectivities.
16//
17//```
18//                (Network side)
19//                       \|/
20//                        |  - Antenna
21//                        |
22//   +--------------------|-----------------+
23//   | Wireless Device  +-+-+               |
24//   |                  \   \               |
25//   |        +------>--+   +-->--+         |
26//   |        |                   |         |
27//   |        +-<-  [to_ether] -<---+       |
28//   |                            | |       |
29//   |                            | |       |
30//   |     ---<--- [from_ether] <-+ |       |
31//   |     |                        |       |
32//   |     |                        |       |
33//   |     |                        |       |
34//   |   TX pin                 RX pin      |
35//   +--------------------------------------+
36//        |                     |
37//        |      (Pins side)    |
38//        |                     |
39//        o                     o
40//```
41//
42enum TickState {
43    InTick,
44    OffTick,
45}
46
47struct InternalState {
48    tick_state: TickState,
49    from_antenna_buffer: VecDeque<u8>,
50    to_antenna_buffer: VecDeque<u8>,
51    antennta_state: AntennaState,
52}
53
54impl embedded_io::ErrorType for WirelessModemFake {
55    type Error = core::convert::Infallible;
56}
57
58impl embedded_io::ReadReady for WirelessModemFake {
59    fn read_ready(&mut self) -> Result<bool, Self::Error> {
60        Ok(self.readable())
61    }
62}
63
64impl embedded_io::Read for WirelessModemFake {
65    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
66        WirelessModemFake::read(&self, buf)
67    }
68}
69
70impl embedded_io::Write for WirelessModemFake {
71    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
72        WirelessModemFake::write(&self, buf)
73    }
74
75    fn flush(&mut self) -> Result<(), Self::Error> {
76        WirelessModemFake::flush(&self)
77    }
78}
79
80pub struct WirelessModemFake {
81    arc_mutexed_internal_state: Arc<Mutex<InternalState>>,
82    name: String,
83}
84
85impl WirelessModemFake {
86    pub fn new(name: &str) -> Self {
87        WirelessModemFake {
88            arc_mutexed_internal_state: Arc::new(Mutex::new(InternalState {
89                tick_state: TickState::OffTick,
90                from_antenna_buffer: VecDeque::new(),
91                to_antenna_buffer: VecDeque::new(),
92                antennta_state: AntennaState::Idle,
93            })),
94            name: String::from(name),
95        }
96    }
97
98    pub fn read(&self, buf: &mut [u8]) -> Result<usize, core::convert::Infallible> {
99        let mut count_red: usize = 0;
100        for buf_vancant_place in buf.iter_mut() {
101            if let Some(byte) = self.get_from_tx_pin() {
102                *buf_vancant_place = byte;
103                count_red += 1;
104            }
105        }
106        Ok(count_red)
107    }
108
109    pub fn write(&self, buf: &[u8]) -> Result<usize, core::convert::Infallible> {
110        let mut count_written: usize = 0;
111        for b in buf {
112            self.put_to_rx_pin(*b);
113            count_written += 1;
114        }
115        Ok(count_written)
116    }
117
118    pub fn flush(&self) -> Result<(), core::convert::Infallible> {
119        Ok(())
120    }
121
122    /// While clonning - method internally shares data for all clonned
123    /// instances of the modem. So all of them can be used in different
124    /// parts of the program, and even in different threads.
125    pub fn clone(&self) -> Self {
126        WirelessModemFake {
127            arc_mutexed_internal_state: Arc::clone(&self.arc_mutexed_internal_state),
128            name: self.name.clone(),
129        }
130    }
131}
132
133impl IODriverSimulator for WirelessModemFake {
134    /// Simulates that the modem emits a byte towards the ether
135    /// ```
136    /// use proto_lab::WirelessModemFake;
137    /// use proto_lab::IODriverSimulator;
138    ///
139    /// let device = WirelessModemFake::new("my_modem");
140    /// device.start_tick();
141    /// assert_eq!(device.get_from_device_network_side(), None);
142    /// device.end_tick();
143    ///
144    /// device.put_to_rx_pin(1);
145    ///
146    /// device.start_tick();
147    /// assert_eq!(device.get_from_device_network_side(), Some(1));
148    /// device.end_tick();
149    /// ```
150
151    fn get_from_device_network_side(&self) -> Option<u8> {
152        let locked_internal_state = self
153            .arc_mutexed_internal_state
154            .lock()
155            .expect(format!("Fail to lock mutex for modem :{}", self.name).as_str());
156
157        match locked_internal_state.tick_state {
158            TickState::OffTick => panic!("Impossible to put_to_device_network_side. Device not in simulation mode. Simulation is within the tick. You shall start tick first."),
159            TickState::InTick => match locked_internal_state.antennta_state {
160                AntennaState::Transmit(byte) => Some(byte),
161                _ => None,
162            },
163        }
164    }
165
166    /// Simulates that the modem caught a byte from the ether
167    /// ```
168    /// use proto_lab::WirelessModemFake;
169    /// use proto_lab::IODriverSimulator;
170    ///
171    /// let device = WirelessModemFake::new("my_modem");
172    /// assert_eq!(device.get_from_tx_pin(), None);
173    /// device.start_tick();
174    /// device.put_to_device_network_side(1);
175    /// device.end_tick();
176    /// assert_eq!(device.get_from_tx_pin(), Some(1));
177    /// ```
178    fn put_to_device_network_side(&self, byte: u8) {
179        let mut locked_internal_state = self
180            .arc_mutexed_internal_state
181            .lock()
182            .expect(format!("Fail to lock mutex for modem :{}", self.name).as_str());
183
184        match locked_internal_state.tick_state {
185            TickState::OffTick => panic!("Impossible to put_to_device_network_side. Device not in simulation mode. Simulation is within the tick. You shall start tick first."),
186            TickState::InTick => match locked_internal_state.antennta_state {
187                AntennaState::Transmit(_) => (),
188                AntennaState::Idle | AntennaState::Receive(_) => {
189                    locked_internal_state.antennta_state = AntennaState::Receive(byte)
190                }
191            },
192        }
193    }
194
195    /// Reads a byte on the TX pin
196    /// ```
197    /// use proto_lab::WirelessModemFake;
198    /// use proto_lab::IODriverSimulator;
199    ///
200    /// let device = WirelessModemFake::new("my_modem");
201    /// assert_eq!(device.get_from_tx_pin(), None);
202    /// device.start_tick();
203    /// device.put_to_device_network_side(1);
204    /// device.end_tick();
205    /// assert_eq!(device.get_from_tx_pin(), Some(1));
206    /// ```
207    fn get_from_tx_pin(&self) -> Option<u8> {
208        let mut locked_internal_state = self
209            .arc_mutexed_internal_state
210            .lock()
211            .expect(format!("Fail to lock mutex for modem :{}", self.name).as_str());
212
213        locked_internal_state.from_antenna_buffer.pop_front()
214    }
215
216    /// Writes a byte on the RX pin
217    /// ```
218    /// use proto_lab::WirelessModemFake;
219    /// use proto_lab::IODriverSimulator;
220    ///
221    /// let device = WirelessModemFake::new("my_modem");
222    /// device.put_to_rx_pin(1);
223    /// device.start_tick();
224    /// assert_eq!(device.get_from_device_network_side(), Some(1));
225    /// device.end_tick();
226    fn put_to_rx_pin(&self, byte: u8) {
227        let mut locked_internal_state = self
228            .arc_mutexed_internal_state
229            .lock()
230            .expect(format!("Fail to lock mutex for modem :{}", self.name).as_str());
231
232        locked_internal_state.to_antenna_buffer.push_back(byte);
233    }
234
235    /// Tick is needed only for simulating time during which ineraction with the ether is going.
236    /// Other operations like put to pin or get from pin can be done not in tick.
237    fn start_tick(&self) {
238        let mut locked_internal_state = self
239            .arc_mutexed_internal_state
240            .lock()
241            .expect(format!("Fail to lock mutex for modem :{}", self.name).as_str());
242
243        match locked_internal_state.tick_state {
244            TickState::OffTick => {
245                locked_internal_state.antennta_state =
246                    match locked_internal_state.to_antenna_buffer.pop_front() {
247                        Some(byte) => AntennaState::Transmit(byte),
248                        _ => AntennaState::Idle,
249                    };
250
251                locked_internal_state.tick_state = TickState::InTick;
252            }
253            TickState::InTick => (),
254        }
255    }
256
257    /// Tick is needed only for simulating time during which ineraction with the ether is going.
258    /// Other operations like put to pin or get from pin can be done not in tick.
259    fn end_tick(&self) {
260        let mut locked_internal_state = self
261            .arc_mutexed_internal_state
262            .lock()
263            .expect(format!("Fail to lock mutex for modem :{}", self.name).as_str());
264
265        match locked_internal_state.tick_state {
266            TickState::OffTick => (),
267            TickState::InTick => {
268                match locked_internal_state.antennta_state {
269                    AntennaState::Receive(byte) => {
270                        locked_internal_state.from_antenna_buffer.push_back(byte);
271                    }
272                    _ => (),
273                }
274
275                locked_internal_state.antennta_state = AntennaState::Idle;
276
277                locked_internal_state.tick_state = TickState::OffTick;
278            }
279        }
280    }
281
282    /// Tells if the device has some bytes to be red from pin
283    /// ```
284    /// use proto_lab::WirelessModemFake;
285    /// use proto_lab::IODriverSimulator;
286    /// let mut device = WirelessModemFake::new("");
287    /// assert!(
288    /// !device.readable());
289    /// device.start_tick();
290    /// device.put_to_device_network_side(1);
291    /// device.end_tick();
292    /// assert!(device.readable());
293    /// ```
294    fn readable(&self) -> bool {
295        let locked_internal_state = self
296            .arc_mutexed_internal_state
297            .lock()
298            .expect(format!("Fail to lock mutex for modem :{}", self.name).as_str());
299
300        !locked_internal_state.from_antenna_buffer.is_empty()
301    }
302
303    /// Tells if the device is ready to be written in
304    /// ```
305    /// use proto_lab::WirelessModemFake;
306    /// use proto_lab::IODriverSimulator;
307    /// assert!(WirelessModemFake::new("").writable());
308    /// ```
309    fn writable(&self) -> bool {
310        true
311    }
312
313    /// Returns the name of the device
314    /// ```
315    /// use proto_lab::WirelessModemFake;
316    /// use proto_lab::IODriverSimulator;
317    /// let device = WirelessModemFake::new("my_modem");
318    /// assert_eq!(device.get_name(), "my_modem");
319    /// ```
320    fn get_name(&self) -> &str {
321        &self.name
322    }
323}
324
325#[cfg(test)]
326mod radio_modem_device_tests {
327    use super::*;
328
329    #[test]
330    fn test_half_duplex_send_per_tick() {
331        let modem_device = WirelessModemFake::new("");
332        modem_device.start_tick();
333        modem_device.put_to_device_network_side(b'a');
334        modem_device.put_to_rx_pin(b'b');
335        modem_device.end_tick();
336
337        let byte_on_tx_pin = modem_device.get_from_tx_pin();
338
339        modem_device.start_tick();
340        assert_eq!(modem_device.get_from_device_network_side(), Some(b'b'));
341        modem_device.end_tick();
342        assert_eq!(byte_on_tx_pin, Some(b'a'));
343    }
344
345    // Test data collision with overwriting data per same tick
346    #[test]
347    fn test_data_collision_per_tick() {
348        let modem_device = WirelessModemFake::new("");
349        modem_device.start_tick();
350        modem_device.put_to_device_network_side(b'a');
351        modem_device.put_to_device_network_side(b'b');
352        modem_device.put_to_device_network_side(b'c');
353        modem_device.end_tick();
354        assert_eq!(modem_device.get_from_tx_pin(), Some(b'c'));
355    }
356}