rust_powered_lego/lego/
communicator.rs

1use std::pin::Pin;
2
3use btleplug::api::{Peripheral as _, Characteristic, Service, WriteType, ValueNotification};
4use btleplug::platform::{Peripheral};
5
6use anyhow::{Result, anyhow, Ok};
7use num_traits::ToPrimitive;
8use tokio_stream::Stream;
9
10
11use super::check_for_lego_error;
12use super::{MessageTypes, message_parameters::Serialized};
13
14pub const MAX_MESSAGE_SIZE: usize = 130;
15
16
17
18pub struct CommonMessageHeader {}
19
20impl CommonMessageHeader {
21    fn get_header(msg_type: MessageTypes) -> Vec<u8> {
22        vec![0x0, 0x0, msg_type as u8]      // [msg_len, 0, mag_type]
23    }
24}
25
26
27pub struct Communicator {
28    peripheral: Peripheral,
29    characteristic: Characteristic,
30}
31
32impl Communicator {
33    pub async fn new(peripheral: Peripheral) -> Result<Self> {
34        peripheral.discover_services().await?;
35        
36        let srvc: Option<Service>;
37        let mut c: Option<Characteristic> = None;
38        for service in peripheral.services() {
39            srvc = Some(service);
40            for characteristic in srvc.unwrap().characteristics {
41                c = Some(characteristic);
42                break;
43            }
44            break;
45        }
46        println!("Characteristics: {:?}", c);
47        Ok(Self { peripheral, characteristic: c.unwrap() })
48    }
49
50    pub async fn send_message<T>(&self, mt: MessageTypes, mp: T) -> Result<()>
51    where
52        T: Serialized,
53    {
54        let write_type = WriteType::WithResponse;
55
56        let mut data = CommonMessageHeader::get_header(mt);
57        data.append(mp.serialize().as_mut());
58        let size = data.len();
59        data[0] = size.to_u8().unwrap();
60        if size > 127 {
61            data.insert(1, 0x01);
62        }
63
64        let res = self.peripheral.write(
65            &self.characteristic, 
66            &data, 
67            write_type).await;
68
69        if res.is_err() {
70            Err(anyhow!("Couldn't send the message"))
71        } else {
72            Ok(())
73        }
74    }
75
76    pub async fn read_message(&self) -> Result<Vec<u8>> {
77        let mut res = self.peripheral.read(&self.characteristic).await?;
78        if res.is_empty() {
79            res = self.peripheral.read(&self.characteristic).await?;
80        }
81        check_for_lego_error(&res)?;
82        Ok(res)
83    }
84
85    pub async fn get_notification_stream(&self) -> Result<Pin<Box<dyn Stream<Item = ValueNotification> + Send>>> {
86        self.peripheral.subscribe(&self.characteristic).await?;
87        Ok(self.peripheral.notifications().await?)
88    }
89
90    // This function is mainly for debugging and testing
91    pub async fn get_message_only<T>(&self, mt: MessageTypes, mp: T) -> Result<Vec<u8>>
92    where
93        T: Serialized,
94    {
95        let mut data = CommonMessageHeader::get_header(mt);
96        data.append(mp.serialize().as_mut());
97        let size = data.len();
98
99        // Yes, it is assumed that the maximal length is ok here. The Docs are unclear about different encoding.
100        data[0] = size.to_u8().unwrap();
101        if size > 127 {
102            data.insert(1, 0x01);
103        }
104
105        Ok(data)
106    }
107}