From c56304a6bad5cfdc46bc84362d32ecb91acacbc5 Mon Sep 17 00:00:00 2001 From: Ivan Li Date: Sun, 20 Nov 2022 20:09:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20MQTT=20=E4=B8=8A?= =?UTF-8?q?=E6=8A=A5=E7=81=AF=E6=9D=A1=E9=A2=9C=E8=89=B2=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/Cargo.lock | 206 ++++++++++++++++++ src-tauri/Cargo.toml | 2 + src-tauri/src/main.rs | 84 +++---- src-tauri/src/{ => picker}/led_color.rs | 0 src-tauri/src/picker/manager.rs | 82 +++++++ src-tauri/src/picker/mod.rs | 4 + src-tauri/src/picker/screen.rs | 43 ++++ .../src/{screen.rs => picker/screenshot.rs} | 121 +++++----- src-tauri/src/rpc/manager.rs | 41 ++++ src-tauri/src/rpc/mod.rs | 2 + src-tauri/src/rpc/mqtt.rs | 66 ++++++ src-tauri/src/screen_color_picker.rs | 69 ------ 12 files changed, 535 insertions(+), 185 deletions(-) rename src-tauri/src/{ => picker}/led_color.rs (100%) create mode 100644 src-tauri/src/picker/manager.rs create mode 100644 src-tauri/src/picker/mod.rs create mode 100644 src-tauri/src/picker/screen.rs rename src-tauri/src/{screen.rs => picker/screenshot.rs} (51%) create mode 100644 src-tauri/src/rpc/manager.rs create mode 100644 src-tauri/src/rpc/mod.rs create mode 100644 src-tauri/src/rpc/mqtt.rs delete mode 100644 src-tauri/src/screen_color_picker.rs diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 20bef9a..1d7c27f 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -636,6 +636,19 @@ dependencies = [ "miniz_oxide 0.5.4", ] +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin 0.9.4", +] + [[package]] name = "fnv" version = "1.0.7" @@ -676,6 +689,21 @@ dependencies = [ "new_debug_unreachable", ] +[[package]] +name = "futures" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.25" @@ -683,6 +711,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -719,6 +748,12 @@ dependencies = [ "syn", ] +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + [[package]] name = "futures-task" version = "0.3.25" @@ -731,9 +766,13 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -861,8 +900,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1457,6 +1498,15 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.8", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -1915,6 +1965,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -1971,6 +2041,12 @@ dependencies = [ "miniz_oxide 0.6.2", ] +[[package]] +name = "pollster" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2220,6 +2296,40 @@ dependencies = [ "windows 0.37.0", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted", + "web-sys", + "winapi 0.3.9", +] + +[[package]] +name = "rumqttc" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "499b7ab08ffa5a722958b6ce1b7c0270bea30909f589d12c5ec3a051afe423fc" +dependencies = [ + "bytes", + "flume", + "futures", + "http", + "log", + "pollster", + "rustls-native-certs", + "rustls-pemfile 0.3.0", + "thiserror", + "tokio", + "tokio-rustls", +] + [[package]] name = "rustc_version" version = "0.3.3" @@ -2238,6 +2348,48 @@ dependencies = [ "semver 1.0.14", ] +[[package]] +name = "rustls" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.1", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +dependencies = [ + "base64", +] + [[package]] name = "rustversion" version = "1.0.9" @@ -2309,17 +2461,29 @@ dependencies = [ "hex", "once_cell", "paris", + "rumqttc", "scrap", "serde", "serde_json", "tauri", "tauri-build", + "time", "tokio", "tracing", "tracing-subscriber", "webp", ] +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "security-framework" version = "2.7.0" @@ -2596,6 +2760,21 @@ dependencies = [ "system-deps 5.0.0", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" +dependencies = [ + "lock_api", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -3079,6 +3258,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + [[package]] name = "toml" version = "0.5.9" @@ -3198,6 +3388,12 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.3.1" @@ -3417,6 +3613,16 @@ dependencies = [ "libwebp-sys", ] +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "webview2-com" version = "0.19.1" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 64fd6fb..52b4fde 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -28,6 +28,8 @@ tokio = { version = "1.22.0", features = ["full"] } tracing = "0.1.37" tracing-subscriber = "0.3.16" hex = "0.4.3" +rumqttc = "0.17.0" +time = { version = "0.3.17", features = ["formatting"] } [features] # by default Tauri runs in production mode diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 15d03f7..4992cd1 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -3,21 +3,19 @@ windows_subsystem = "windows" )] -mod led_color; -mod screen; -mod screen_color_picker; +mod picker; +mod rpc; -use led_color::LedColor; use paris::*; -use screen_color_picker::ScreenColorPicker; +use picker::led_color::LedColor; +use picker::manager::Picker; use std::{ - sync::{Arc, Mutex}, time::Instant, }; #[tauri::command] -fn refresh_displays() { - match ScreenColorPicker::global().refresh_displays() { +async fn refresh_displays() { + match Picker::global().refresh_displays().await { Ok(_) => {} Err(error) => { error!("{}", error) @@ -26,67 +24,39 @@ fn refresh_displays() { } #[tauri::command] -async fn take_snapshot() -> Arc>> { +async fn take_snapshot() -> Vec { let start = Instant::now(); + let manager = Picker::global(); - let screenshot_futures = match ScreenColorPicker::global().take_screenshots_for_all() { - Ok(bitmaps) => { - info!("bitmaps len: {}", bitmaps.len()); - match ScreenColorPicker::global().screens.lock() { - Ok(screens) => Some(Vec::from_iter(screens.iter().enumerate().map( - |(index, screen)| { - bitmap_to_webp_base64(screen.width, screen.height, bitmaps[index].to_vec()) - }, - ))), - Err(error) => { - error!("can not lock screens. {}", error); - None - } + match manager.take_screenshots_for_all().await { + Ok(screenshots) => { + info!("screenshots len: {}", screenshots.len()); + let mut futures = Vec::new(); + for screenshot in screenshots { + let future = screenshot.to_webp_base64().await; + futures.push(future); } + futures } Err(error) => { error!("can not take screenshots for all. {}", error); - None + vec![] } - }; - match screenshot_futures { - Some(futures) => { - let mut handles = Vec::with_capacity(futures.len()); - - for fut in futures { - handles.push(tokio::spawn(fut)); - } - - let mut results = Vec::with_capacity(handles.len()); - for handle in handles { - results.push(handle.await.unwrap()); - } - println!("运行耗时: {:?}", start.elapsed()); - Arc::new(Mutex::new(results)) - } - None => Arc::new(Mutex::new(Vec::new())), } } #[tauri::command] async fn get_led_strip_colors() -> Result, String> { - let screens = ScreenColorPicker::global() - .screens - .lock() - .map_err(|error| { - error!("can not lock ScreenColorPick.screens. {}", error); - "failed to lock." - })?; - let mut colors = Vec::new(); - for screen in screens.iter() { - let result = screen.get_top_colors(); - if let Ok(result) = result { - colors.extend_from_slice(&result); - } else if let Err(result) = result { - return Err(format!("can not get led strip colors. {}", result)); + let colors = Picker::global().get_led_strip_colors().await; + match colors { + Ok(colors) => { + rpc::manager::Manager::global() + .publish_led_colors(&colors) + .await; + Ok(colors) } + Err(error) => Err(format!("{}", error)), } - Ok(colors) } async fn bitmap_to_webp_base64(width: usize, height: usize, bitmap: Vec) -> String { @@ -111,7 +81,9 @@ async fn bitmap_to_webp_base64(width: usize, height: usize, bitmap: Vec) -> return base64::encode(&*webp_memory); } -fn main() { +#[tokio::main] +async fn main() { + rpc::manager::Manager::global(); tauri::Builder::default() .invoke_handler(tauri::generate_handler![ take_snapshot, diff --git a/src-tauri/src/led_color.rs b/src-tauri/src/picker/led_color.rs similarity index 100% rename from src-tauri/src/led_color.rs rename to src-tauri/src/picker/led_color.rs diff --git a/src-tauri/src/picker/manager.rs b/src-tauri/src/picker/manager.rs new file mode 100644 index 0000000..8416a8a --- /dev/null +++ b/src-tauri/src/picker/manager.rs @@ -0,0 +1,82 @@ +use once_cell::sync::OnceCell; +use paris::*; +use scrap::{Capturer, Display}; +use std::sync::Arc; +use tokio::sync::Mutex; + +use crate::picker::screen::Screen; + +use super::{ + led_color::LedColor, + screenshot::{Screenshot}, +}; + +pub struct Picker { + pub screens: Arc>>, + pub screenshots: Arc>>, +} + +impl Picker { + pub fn global() -> &'static Picker { + static SCREEN_COLOR_PICKER: OnceCell = OnceCell::new(); + + SCREEN_COLOR_PICKER.get_or_init(|| Picker { + screens: Arc::new(Mutex::new(vec![])), + screenshots: Arc::new(Mutex::new(vec![])), + }) + } + + pub async fn refresh_displays(&self) -> anyhow::Result<()> { + let displays = Display::all() + .map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?; + let mut screens = self.screens.lock().await; + let mut screenshots = self.screenshots.lock().await; + screens.clear(); + info!("number of displays: {}", displays.len()); + for display in displays { + let height = display.height(); + let width = display.width(); + match Capturer::new(display) { + Ok(capturer) => screens.push(Screen::new(capturer, width, height)), + Err(error) => screens.push(Screen::new_failed( + anyhow::anyhow!("{}", error), + width, + height, + )), + }; + screenshots.push(Screenshot::new(width, height)); + } + + screens.reverse(); + screenshots.reverse(); + screenshots[0].set_number_of_leds(22, 0); + screenshots[1].set_number_of_leds(38, 0); + + Ok(()) + } + + pub async fn take_screenshots_for_all(&self) -> anyhow::Result> { + let mut screens = self.screens.lock().await; + let mut screenshots = self.screenshots.lock().await; + for (index, screen) in screens.iter_mut().enumerate() { + let bitmap = screen.take().map_err(|error| { + anyhow::anyhow!("take screenshot for display failed. {}", error) + })?; + screenshots[index].set_bitmap(bitmap).await + } + Ok(screenshots.to_vec()) + } + + pub async fn get_led_strip_colors(&self) -> anyhow::Result> { + let screenshots = self.screenshots.lock().await; + let mut colors = Vec::new(); + for screenshot in screenshots.iter() { + let result = screenshot + .get_top_colors() + .await + .map_err(|error| anyhow::anyhow!("get top colors failed. {}", error))?; + colors.extend_from_slice(&result); + } + Ok(colors) + } +} diff --git a/src-tauri/src/picker/mod.rs b/src-tauri/src/picker/mod.rs new file mode 100644 index 0000000..d76db0e --- /dev/null +++ b/src-tauri/src/picker/mod.rs @@ -0,0 +1,4 @@ +pub mod led_color; +pub mod screen; +pub mod manager; +pub mod screenshot; \ No newline at end of file diff --git a/src-tauri/src/picker/screen.rs b/src-tauri/src/picker/screen.rs new file mode 100644 index 0000000..3950c61 --- /dev/null +++ b/src-tauri/src/picker/screen.rs @@ -0,0 +1,43 @@ + +use scrap::Capturer; + +pub struct Screen { + capturer: Option, + init_error: Option, + pub width: usize, + pub height: usize, +} + +impl Screen { + pub fn new(capturer: Capturer, width: usize, height: usize) -> Self { + Self { + capturer: Some(capturer), + init_error: None, + width, + height, + } + } + + pub fn new_failed(init_error: anyhow::Error, width: usize, height: usize) -> Self { + Self { + capturer: None, + init_error: Some(init_error), + width, + height, + } + } + + pub fn take(&mut self) -> anyhow::Result> { + match self.capturer.as_mut() { + Some(capturer) => { + let buffer = capturer + .frame() + .map_err(|error| anyhow::anyhow!("failed to frame of display. {}", error))?; + anyhow::Ok(buffer.to_vec()) + } + None => anyhow::bail!("Do not initialized"), + } + } +} + +unsafe impl Send for Screen {} \ No newline at end of file diff --git a/src-tauri/src/screen.rs b/src-tauri/src/picker/screenshot.rs similarity index 51% rename from src-tauri/src/screen.rs rename to src-tauri/src/picker/screenshot.rs index 1d8c024..c60ce1c 100644 --- a/src-tauri/src/screen.rs +++ b/src-tauri/src/picker/screenshot.rs @@ -1,81 +1,53 @@ -use std::sync::{Arc, Mutex}; +use std::sync::Arc; +use tokio::sync::Mutex; -use anyhow::Ok; -use scrap::Capturer; +use super::led_color::LedColor; -use crate::led_color::LedColor; - -pub struct Screen { +#[derive(Clone)] +pub struct Screenshot { bitmap: Arc>>>, - capturer: Option, - init_error: Option, - pub width: usize, - pub height: usize, - pub led_number_of_x: usize, - pub led_number_of_y: usize, + width: usize, + height: usize, + led_number_of_x: usize, + led_number_of_y: usize, } -impl Screen { - pub fn new(capturer: Capturer, width: usize, height: usize) -> Self { +impl Screenshot { + pub fn new(width: usize, height: usize) -> Self { Self { bitmap: Arc::new(Mutex::new(None)), - capturer: Some(capturer), - init_error: None, - width, - height, led_number_of_x: 0, led_number_of_y: 0, + width, + height, } } - pub fn new_failed(init_error: anyhow::Error, width: usize, height: usize) -> Self { - Self { - bitmap: Arc::new(Mutex::new(None)), - capturer: None, - init_error: Some(init_error), - width, - height, - led_number_of_x: 0, - led_number_of_y: 0, - } + pub fn get_size(&self) -> (usize, usize) { + (self.width, self.height) } - pub fn set_number_of_leds(&mut self, led_number_of_x: usize, led_number_of_y: usize) -> &Self { + pub fn get_number_of_leds(&self) -> (usize, usize) { + (self.led_number_of_x, self.led_number_of_y) + } + + pub fn set_number_of_leds(&mut self, led_number_of_x: usize, led_number_of_y: usize) { self.led_number_of_x = led_number_of_x; self.led_number_of_y = led_number_of_y; - self + } + pub async fn get_top_colors(&self) -> anyhow::Result> { + self.get_x_colors(XPosition::Top).await + } + pub async fn get_bottom_colors(&self) -> anyhow::Result> { + self.get_x_colors(XPosition::Bottom).await } - pub fn take(&mut self) -> anyhow::Result> { - match self.capturer.as_mut() { - Some(capturer) => { - let buffer = capturer - .frame() - .map_err(|error| anyhow::anyhow!("failed to frame of display. {}", error))?; - - self.bitmap = Arc::new(Mutex::new(Some(buffer.to_vec()))); - anyhow::Ok(buffer.to_vec()) - } - None => anyhow::bail!("Do not initialized"), - } - } - - pub fn get_top_colors(&self) -> anyhow::Result> { - self.get_x_colors(XPosition::Top) - } - pub fn get_bottom_colors(&self) -> anyhow::Result> { - self.get_x_colors(XPosition::Bottom) - } - - fn get_x_colors(&self, position: XPosition) -> anyhow::Result> { + async fn get_x_colors(&self, position: XPosition) -> anyhow::Result> { if self.led_number_of_x == 0 { return Ok(vec![]); } - let bitmap = self - .bitmap - .lock() - .map_err(|error| anyhow::anyhow!("can not lock Screen#bitmap. {}", error))?; + let bitmap = self.bitmap.lock().await; match bitmap.as_ref() { Some(bitmap) => { let cell_size_x = self.width / self.led_number_of_x; @@ -90,8 +62,6 @@ impl Screen { let stride = bitmap.len() / self.height; for pos in 0..self.led_number_of_x { - let mut y_range = y_range.to_owned(); - let mut r = 0u32; let mut g = 0u32; let mut b = 0u32; @@ -114,9 +84,40 @@ impl Screen { None => Ok(vec![]), } } -} -unsafe impl Send for Screen {} + pub async fn set_bitmap(&mut self, bitmap: Vec) { + let mut self_bitmap = self.bitmap.lock().await; + *self_bitmap = Some(bitmap); + } + + pub async fn to_webp_base64(&self) -> String { + let bitmap = self.bitmap.lock().await; + match bitmap.to_owned() { + Some(bitmap) => { + let mut bitflipped = Vec::with_capacity(self.width * self.height * 3); + let stride = bitmap.len() / self.height; + + for y in 0..self.height { + for x in 0..self.width { + let i = stride * y + 4 * x; + bitflipped.extend_from_slice(&[bitmap[i + 2], bitmap[i + 1], bitmap[i]]); + } + } + + let webp_memory = webp::Encoder::from_rgb( + bitflipped.as_slice(), + self.width as u32, + self.height as u32, + ) + .encode(100.0); + return base64::encode(&*webp_memory); + }, + None => { + "".to_owned() + } + } + } +} enum XPosition { Top, diff --git a/src-tauri/src/rpc/manager.rs b/src-tauri/src/rpc/manager.rs new file mode 100644 index 0000000..977e41e --- /dev/null +++ b/src-tauri/src/rpc/manager.rs @@ -0,0 +1,41 @@ +use crate::picker::led_color::LedColor; + +use super::mqtt::MqttConnection; +use once_cell::sync::OnceCell; + +pub struct Manager { + mqtt: MqttConnection, +} + +impl Manager { + pub fn global() -> &'static Self { + static RPC_MANAGER: OnceCell = OnceCell::new(); + + RPC_MANAGER.get_or_init(|| Manager::new()) + } + + pub fn new() -> Self { + let mut mqtt = MqttConnection::new(); + mqtt.initialize(); + Self { mqtt } + } + + pub async fn publish_led_colors(&self, colors: &Vec) -> anyhow::Result<()> { + let payload = colors + .iter() + .map(|c| c.get_rgb().clone()) + .flatten() + .collect::>(); + + self.mqtt + .client + .publish( + "screen-bg-light/desktop/colors", + rumqttc::QoS::AtMostOnce, + false, + payload, + ) + .await + .map_err(|error| anyhow::anyhow!("mqtt publish failed. {}", error)) + } +} diff --git a/src-tauri/src/rpc/mod.rs b/src-tauri/src/rpc/mod.rs new file mode 100644 index 0000000..54a385b --- /dev/null +++ b/src-tauri/src/rpc/mod.rs @@ -0,0 +1,2 @@ +pub mod manager; +pub mod mqtt; \ No newline at end of file diff --git a/src-tauri/src/rpc/mqtt.rs b/src-tauri/src/rpc/mqtt.rs new file mode 100644 index 0000000..28b8da4 --- /dev/null +++ b/src-tauri/src/rpc/mqtt.rs @@ -0,0 +1,66 @@ +use rumqttc::{AsyncClient, MqttOptions, QoS}; +use std::{time::Duration}; +use time::{format_description, OffsetDateTime}; +use tokio::task; +use tracing::warn; + +pub struct MqttConnection { + pub client: AsyncClient, +} + +impl MqttConnection { + pub fn new() -> Self { + let mut options = MqttOptions::new("rumqtt-async", "192.168.31.11", 1883); + options.set_keep_alive(Duration::from_secs(5)); + + let (mut client, mut eventloop) = AsyncClient::new(options, 10); + task::spawn(async move { + while let Ok(notification) = eventloop.poll().await { + println!("Received = {:?}", notification); + } + }); + Self { client } + } + + pub fn initialize(&mut self) { + self.subscribe_board(); + self.broadcast_desktop_online(); + } + + fn subscribe_board(&self) { + self.client + .subscribe("screen-bg-light/board/#", QoS::AtMostOnce); + } + + fn broadcast_desktop_online(&mut self) { + let client = self.client.to_owned(); + task::spawn(async move { + loop { + match OffsetDateTime::now_utc() + .format(&format_description::well_known::Iso8601::DEFAULT) + { + Ok(now_str) => { + match client + .publish( + "screen-bg-light/desktop/last-online-at", + QoS::AtLeastOnce, + false, + now_str.as_bytes(), + ) + .await + { + Ok(_) => {} + Err(error) => { + warn!("can not publish last online time. {}", error) + } + } + } + Err(error) => { + warn!("can not get time for now. {}", error); + } + } + tokio::time::sleep(Duration::from_millis(1000)).await; + } + }); + } +} diff --git a/src-tauri/src/screen_color_picker.rs b/src-tauri/src/screen_color_picker.rs deleted file mode 100644 index f988623..0000000 --- a/src-tauri/src/screen_color_picker.rs +++ /dev/null @@ -1,69 +0,0 @@ -use once_cell::sync::OnceCell; -use paris::*; -use scrap::{Capturer, Display}; -use tracing::field::display; -use std::{ - borrow::BorrowMut, - sync::{Arc, Mutex}, -}; - -use crate::screen::Screen; - -pub struct ScreenColorPicker { - pub screens: Arc>>, -} - -impl ScreenColorPicker { - pub fn global() -> &'static ScreenColorPicker { - static SCREEN_COLOR_PICKER: OnceCell = OnceCell::new(); - - SCREEN_COLOR_PICKER.get_or_init(|| ScreenColorPicker { - screens: Arc::new(Mutex::new(Vec::::new())), - }) - } - - pub fn refresh_displays(&self) -> anyhow::Result<()> { - let displays = Display::all() - .map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?; - let mut screens = self - .screens - .lock() - .map_err(|error| anyhow::anyhow!("lock screens failed. {}", error))?; - screens.clear(); - info!("number of displays: {}", displays.len()); - for display in displays { - let height = display.height(); - let width = display.width(); - match Capturer::new(display) { - Ok(capturer) => screens.push(Screen::new(capturer, width, height)), - Err(error) => screens.push(Screen::new_failed( - anyhow::anyhow!("{}", error), - width, - height, - )), - } - } - - screens.reverse(); - screens[0].set_number_of_leds(22, 0); - screens[1].set_number_of_leds(38, 0); - - Ok(()) - } - - pub fn take_screenshots_for_all(&self) -> anyhow::Result>> { - let mut screens = self - .screens - .lock() - .map_err(|error| anyhow::anyhow!("lock screens failed. {}", error))?; - let mut screenshots = Vec::>::new(); - for screen in screens.iter_mut() { - let screenshot = screen.take().map_err(|error| { - anyhow::anyhow!("take screenshot for display failed. {}", error) - })?; - screenshots.push(screenshot); - } - - anyhow::Ok(screenshots) - } -}