NOSHP_Client/
client.rs

1use std::sync::Arc;
2
3use anyhow::anyhow;
4use fxhash::FxHashMap;
5use rsa::{pss::BlindedSigningKey, sha2::Sha256};
6
7use crate::{
8    client_config::ParsedConfig,
9    client_polling::{self},
10    client_registration,
11    client_types::types::DeviceCapabilityStatus,
12};
13
14//todo: make these optional parameters user can change
15const RSA_KEY_SIZE: usize = 2048;
16const POLLING_INTERVAL: u64 = 500;
17
18pub type ThreadSafeMutable<T> = Arc<std::sync::Mutex<T>>;
19
20pub struct NoshpClient<S: UserDefinedState> {
21    callbacks: FxHashMap<String, Box<Callback<S>>>,
22    client_state: ClientState<S>,
23    server_ip: Option<String>,
24}
25//state should return an object which has:
26//  - user defined state
27//  - all capabilities with their properties
28
29impl<S> NoshpClient<S>
30where
31    S: UserDefinedState + 'static,
32{
33    pub fn new() -> Self {
34        let has_update = ThreadSafeMutable::new(std::sync::Mutex::new(false));
35        Self {
36            callbacks: FxHashMap::default(),
37            client_state: ClientState::new(has_update),
38            server_ip: None,
39        }
40    }
41    ///Replaces current callback function with supplied one
42    pub fn add_callback(mut self, capabillity: &str, callback: Box<Callback<S>>) -> Self {
43        self.callbacks.insert(String::from(capabillity), callback);
44        self
45    }
46    ///Sets the state that is passed as a parameter to callback functions
47    pub fn set_state(mut self, state: S) -> Self {
48        self.client_state.user_state = state;
49        self
50    }
51    ///Overrides any server_ip set in the config, should be in format http://serverip:port
52    pub fn set_server_ip(mut self, server_ip: String) -> Self {
53        self.server_ip = Some(server_ip);
54        self
55    }
56    ///Consumes self
57    pub async fn run(mut self, config: ParsedConfig) -> anyhow::Result<()> {
58        //this is not pretty, probably refactor in the future
59        if let (None, None) = (self.server_ip.clone(), config.server_ip.clone()) {
60            return Err(anyhow!("Did not receive a server ip from the config or using set_server_ip, one of them needs to be set"));
61        }
62
63        //unwrap is guaranteed to succeed due to above check
64        let server_ip = match self.server_ip {
65            Some(v) => v,
66            None => config.server_ip.unwrap(),
67        };
68
69        let capabilities = config.capabilities;
70
71        let capabilities: ThreadSafeMutable<Vec<DeviceCapabilityStatus>> =
72            Arc::new(std::sync::Mutex::new(capabilities));
73
74        self.client_state.capabilities = capabilities.clone();
75        let (private_key, signing_key) = {
76            let mut rng = rand::thread_rng();
77            let private_key = rsa::RsaPrivateKey::new(&mut rng, RSA_KEY_SIZE).unwrap();
78            let signing_key = BlindedSigningKey::<Sha256>::new(private_key.clone());
79            (private_key, signing_key)
80        };
81
82        let client_connection_details = client_registration::repeated_register_self(
83            &private_key.to_public_key(),
84            capabilities.clone(),
85            config.device_name,
86            server_ip.clone(),
87        )
88        .await;
89
90        {
91            let certificate = client_connection_details.security_certificate.clone();
92            let uuid = client_connection_details.uuid.clone();
93
94            let mut interval =
95                tokio::time::interval(std::time::Duration::from_millis(POLLING_INTERVAL));
96
97            let update_client = client_polling::polling::request_update_service_client
98                ::RequestUpdateServiceClient::connect(server_ip).await?;
99
100            let mut polling_service = client_polling::PollingService::new(
101                update_client,
102                capabilities.clone(),
103                self.client_state.has_update.clone(),
104                client_connection_details,
105                signing_key,
106            );
107
108            loop {
109                interval.tick().await;
110                let updates = polling_service
111                    .get_updates(certificate.clone(), uuid.clone())
112                    .await;
113                let updates = match updates {
114                    Some(v) => v,
115                    None => {
116                        // println!("No updates available");
117                        continue;
118                    }
119                };
120                for update in updates.into_iter() {
121                    let callback = self.callbacks.get(&update.capability);
122                    //todo: the requests aren't really implemented yet
123                    let empty_request = Request::new(update.value);
124                    match callback {
125                        Some(callback) => callback(&mut self.client_state, empty_request),
126                        None => println!("Received signal to {}", update.capability),
127                    }
128                }
129            }
130        }
131    }
132}
133
134pub trait UserDefinedState: Default {}
135//todo: add capabilities to the capabilities struct here
136pub struct ClientState<S: UserDefinedState> {
137    pub user_state: S,
138    capabilities: Arc<std::sync::Mutex<Vec<DeviceCapabilityStatus>>>,
139    has_update: Arc<std::sync::Mutex<bool>>,
140}
141impl<S: UserDefinedState> ClientState<S> {
142    fn new(has_update: Arc<std::sync::Mutex<bool>>) -> Self {
143        return Self {
144            capabilities: Arc::default(),
145            user_state: S::default(),
146            has_update,
147        };
148    }
149    pub fn update_capability_availabillity(
150        &self,
151        capability_name: &str,
152        available: bool,
153    ) -> anyhow::Result<()> {
154        {
155            let has_update = self.has_update.lock();
156            let mut has_update = match has_update {
157                Ok(v) => v,
158                Err(e) => return Err(anyhow!("Unable to get lock, error: {}", e)),
159            };
160
161            *has_update = true;
162        }
163        {
164            let capabilities = self.capabilities.lock();
165            let mut capabilities = match capabilities {
166                Ok(v) => v,
167                Err(e) => return Err(anyhow!("Unable to get lock, error: {}", e)),
168            };
169            for capability in capabilities.iter_mut() {
170                if capability.capability == capability_name {
171                    capability.available = available;
172                    return Ok(());
173                }
174            }
175        }
176        Err(anyhow!(
177            "Unable to find specified capability with name: {}",
178            capability_name
179        ))
180    }
181}
182
183pub type Callback<S> = fn(&mut ClientState<S>, Request);
184
185pub struct Request {
186    pub value: Option<f32>,
187}
188impl Request {
189    pub fn new(value: Option<f32>) -> Self {
190        Self { value }
191    }
192}