feat: 支持 MQTT 上报灯条颜色数据

This commit is contained in:
2022-11-20 20:09:25 +08:00
parent d4709b9404
commit c56304a6ba
12 changed files with 535 additions and 185 deletions

View File

@@ -0,0 +1,50 @@
use paris::info;
use serde::Serialize;
#[derive(Clone, Copy)]
pub struct LedColor {
bits: [u8; 3],
}
impl LedColor {
pub fn default() -> Self {
Self { bits: [0, 0, 0] }
}
pub fn new(r: u8, g: u8, b: u8) -> Self {
Self { bits: [r, g, b] }
}
pub fn get_rgb(&self) -> [u8; 3] {
self.bits
}
pub fn is_empty(&self) -> bool {
self.bits.iter().any(|bit| *bit == 0)
}
pub fn set_rgb(&mut self, r: u8, g: u8, b: u8) -> &Self {
self.bits = [r, g, b];
self
}
pub fn merge(&mut self, r: u8, g: u8, b: u8) -> &Self {
self.bits = [
(self.bits[0] / 2 + r / 2),
(self.bits[1] / 2 + g / 2),
(self.bits[2] / 2 + b / 2),
];
self
}
}
impl Serialize for LedColor {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
info!("{:?}", self.bits);
let hex = format!("#{}", hex::encode(self.bits));
serializer.serialize_str(hex.as_str())
}
}

View File

@@ -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<Mutex<Vec<Screen>>>,
pub screenshots: Arc<Mutex<Vec<Screenshot>>>,
}
impl Picker {
pub fn global() -> &'static Picker {
static SCREEN_COLOR_PICKER: OnceCell<Picker> = 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<Vec<Screenshot>> {
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<Vec<LedColor>> {
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)
}
}

View File

@@ -0,0 +1,4 @@
pub mod led_color;
pub mod screen;
pub mod manager;
pub mod screenshot;

View File

@@ -0,0 +1,43 @@
use scrap::Capturer;
pub struct Screen {
capturer: Option<Capturer>,
init_error: Option<anyhow::Error>,
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<Vec<u8>> {
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 {}

View File

@@ -0,0 +1,125 @@
use std::sync::Arc;
use tokio::sync::Mutex;
use super::led_color::LedColor;
#[derive(Clone)]
pub struct Screenshot {
bitmap: Arc<Mutex<Option<Vec<u8>>>>,
width: usize,
height: usize,
led_number_of_x: usize,
led_number_of_y: usize,
}
impl Screenshot {
pub fn new(width: usize, height: usize) -> Self {
Self {
bitmap: Arc::new(Mutex::new(None)),
led_number_of_x: 0,
led_number_of_y: 0,
width,
height,
}
}
pub fn get_size(&self) -> (usize, usize) {
(self.width, self.height)
}
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;
}
pub async fn get_top_colors(&self) -> anyhow::Result<Vec<LedColor>> {
self.get_x_colors(XPosition::Top).await
}
pub async fn get_bottom_colors(&self) -> anyhow::Result<Vec<LedColor>> {
self.get_x_colors(XPosition::Bottom).await
}
async fn get_x_colors(&self, position: XPosition) -> anyhow::Result<Vec<LedColor>> {
if self.led_number_of_x == 0 {
return Ok(vec![]);
}
let bitmap = self.bitmap.lock().await;
match bitmap.as_ref() {
Some(bitmap) => {
let cell_size_x = self.width / self.led_number_of_x;
let cell_size_y = self.height / 5;
let cell_size = cell_size_x * cell_size_y;
let y_range = match position {
XPosition::Top => 0..cell_size_y,
XPosition::Bottom => self.height - cell_size_y..self.height,
};
let mut colors = vec![LedColor::default(); self.led_number_of_x];
let stride = bitmap.len() / self.height;
for pos in 0..self.led_number_of_x {
let mut r = 0u32;
let mut g = 0u32;
let mut b = 0u32;
for x in pos * cell_size_x..(pos + 1) * cell_size_x {
for y in y_range.to_owned() {
let i = stride * y + 4 * x;
r += bitmap[i + 2] as u32;
g += bitmap[i + 1] as u32;
b += bitmap[i] as u32;
}
}
colors[pos] = LedColor::new(
(r / cell_size as u32) as u8,
(g / cell_size as u32) as u8,
(b / cell_size as u32) as u8,
);
}
return Ok(colors);
}
None => Ok(vec![]),
}
}
pub async fn set_bitmap(&mut self, bitmap: Vec<u8>) {
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,
Bottom,
}