streamduck_core/core/
manager.rs

1//! Manager of streamduck cores
2
3use std::collections::HashMap;
4use std::sync::{Arc};
5use std::time::Duration;
6use futures::{stream, StreamExt};
7use crate::core::{RawButtonPanel, SDCore};
8use crate::core::methods::CoreHandle;
9use hidapi::HidApi;
10use serde_json::Value;
11use tokio::sync::RwLock;
12use tokio::time::sleep;
13use crate::config::{Config, DeviceConfig};
14use crate::{connect, find_decks, ModuleManager, RenderingManager, SocketManager};
15use crate::util::{make_panel_unique};
16
17/// Core manager struct
18pub struct CoreManager {
19    hid: RwLock<HidApi>,
20
21    /// Config
22    pub config: Arc<Config>,
23
24    devices: RwLock<HashMap<String, DeviceData>>,
25
26    /// Module manager
27    pub module_manager: Arc<ModuleManager>,
28
29    /// Render manager
30    pub render_manager: Arc<RenderingManager>,
31
32    /// Socket manager
33    pub socket_manager: Arc<SocketManager>,
34}
35
36#[allow(dead_code)]
37impl CoreManager {
38    /// Creates new core manager with provided module manager and config
39    pub fn new(module_manager: Arc<ModuleManager>, render_manager: Arc<RenderingManager>, socket_manager: Arc<SocketManager>, config: Arc<Config>) -> Arc<CoreManager> {
40        let hid = HidApi::new().expect("could not connect to hidapi");
41
42        Arc::new(CoreManager {
43            hid: RwLock::new(hid),
44            config,
45            devices: Default::default(),
46            module_manager,
47            render_manager,
48            socket_manager
49        })
50    }
51
52    /// Adds all devices from config to managed devices, used at start of the software
53    pub async fn add_devices_from_config(&self) {
54        for config in self.config.get_all_device_configs().await {
55            let config_handle = config.read().await;
56            self.add_device(config_handle.vid, config_handle.pid, &config_handle.serial).await;
57        }
58    }
59
60    /// Lists detected unmanaged devices
61    pub async fn list_available_devices(&self) -> Vec<(u16, u16, String)> {
62        let mut handle = self.hid.write().await;
63
64        handle.refresh_devices().ok();
65
66        let mut devices = vec![];
67
68        for (vid, pid, serial) in find_decks(&handle) {
69            if let Some(serial) = serial {
70                if !self.is_device_added(&serial).await {
71                    devices.push((vid, pid, serial));
72                }
73            }
74        }
75
76        devices
77    }
78
79    /// Adds device to automatic reconnection
80    pub async fn add_device(&self, vid: u16, pid: u16, serial: &str) {
81        let mut handle = self.devices.write().await;
82
83        if !handle.contains_key(serial) {
84            let data = DeviceData {
85                core: SDCore::blank(self.module_manager.clone(), self.render_manager.clone(), self.socket_manager.clone(), self.config.clone(), Default::default(), Default::default()).await,
86                vid,
87                pid,
88                serial: serial.to_string()
89            };
90
91            self.config.restore_device_config(serial).await;
92
93            handle.insert(serial.to_string(), data.clone());
94        }
95    }
96
97    /// Connects to a device
98    pub async fn connect_device(&self, vid: u16, pid: u16, serial: &str) -> Result<DeviceData, String> {
99        let hid_handle = self.hid.read().await;
100        let collection = self.config.get_image_collection(serial).await;
101
102        let config = if let Some(config) = self.config.get_device_config(serial).await {
103            config
104        } else {
105            self.config.set_device_config(serial, DeviceConfig {
106                vid,
107                pid,
108                serial: serial.to_string(),
109                brightness: 50,
110                layout: RawButtonPanel {
111                    display_name: "Root".to_string(),
112                    data: Value::Null,
113                    buttons: Default::default()
114                },
115                images: Default::default(),
116                plugin_data: Default::default(),
117                commit_time: Default::default(),
118                dirty_state: false,
119            }).await;
120            self.config.save_device_config(serial).await.ok();
121            self.config.get_device_config(serial).await.unwrap()
122        };
123
124        if let Ok(core) = connect(self.module_manager.clone(), self.render_manager.clone(), self.socket_manager.clone(), self.config.clone(), config.clone(), collection,&hid_handle, vid, pid, serial, self.config.frame_rate()).await {
125            let data = DeviceData {
126                core: core.clone(),
127                vid,
128                pid,
129                serial: serial.to_string()
130            };
131
132            let core_handle = CoreHandle::wrap(core.clone());
133
134            let config_handle = config.read().await;
135
136            let brightness = config_handle.brightness;
137            let layout = config_handle.layout.clone();
138
139            drop(config_handle);
140
141            core_handle.set_brightness(brightness).await;
142            core_handle.reset_stack(make_panel_unique(layout)).await;
143
144
145            let mut handle = self.devices.write().await;
146
147            handle.insert(serial.to_string(), data.clone());
148
149            Ok(data)
150        } else {
151            Err("Failed to connect".to_string())
152        }
153    }
154
155    /// Removes device from automatic reconnection and stops current connection to it
156    pub async fn remove_device(&self, serial: &str) {
157        let mut handle = self.devices.write().await;
158        let data = handle.remove(serial);
159
160        if let Some(data) = data {
161            data.core.close().await;
162            self.config.disable_device_config(serial).await;
163            self.config.reload_device_configs().await.ok();
164        }
165    }
166
167    /// Lists managed devices
168    pub async fn list_added_devices(&self) -> HashMap<String, DeviceData> {
169        self.devices.read().await.iter()
170            .map(|(s, d)| (s.clone(), d.clone()))
171            .collect()
172    }
173
174    /// Returns if specific device is in managed list
175    pub async fn is_device_added(&self, serial: &str) -> bool {
176        self.devices.read().await.contains_key(serial)
177    }
178
179    /// Gets device data from managed devices
180    pub async fn get_device(&self, serial: &str) -> Option<DeviceData> {
181        if let Some(device_data) = self.devices.read().await.get(serial) {
182            if !device_data.core.is_closed().await {
183                Some(device_data.clone())
184            } else {
185                None
186            }
187        } else {
188            None
189        }
190    }
191
192    /// Starts running reconnection routine on current thread, probably spawn it out as a separate thread
193    pub async fn reconnect_routine(&self) {
194        loop {
195            sleep(Duration::from_secs_f32(self.config.reconnect_rate())).await;
196
197            let disconnected = self.get_disconnected().await;
198
199            if !disconnected.is_empty() {
200                for (serial, device) in disconnected {
201                    log::warn!("{} is disconnected, attempting to reconnect", serial);
202                    if let Ok(_) = self.connect_device(device.vid, device.pid, &device.serial).await {
203                        log::info!("Reconnected {}", serial);
204                    }
205                }
206            }
207        }
208    }
209
210    /// Retrieves currently disconnected devices from managed devices list
211    async fn get_disconnected(&self) -> HashMap<String, DeviceData> {
212        let handle = self.devices.read().await;
213
214        let map = stream::iter(handle.iter())
215            .filter(|(_, d)| async { d.core.is_closed().await })
216            .map(|(s, d)| (s.clone(), d.clone()))
217            .collect().await;
218
219        drop(handle);
220
221        map
222    }
223}
224
225/// Device data
226#[derive(Clone)]
227pub struct DeviceData {
228    /// Core that holds connection to the device
229    pub core: Arc<SDCore>,
230    /// Vendor ID
231    pub vid: u16,
232    /// Product ID
233    pub pid: u16,
234    /// Serial number
235    pub serial: String,
236}