feat: 支持预览灯条排序效果。

This commit is contained in:
2023-04-01 10:42:46 +08:00
parent 958a422672
commit 4e75aa4307
15 changed files with 820 additions and 84 deletions

View File

@@ -3,7 +3,10 @@ use std::env::current_dir;
use paris::{error, info};
use serde::{Deserialize, Serialize};
use tauri::api::path::config_dir;
use tokio::sync::OnceCell;
use crate::screenshot::{self, LedSamplePoints};
const CONFIG_FILE_NAME: &str = "cc.ivanli.ambient_light/led_strip_config.toml";
#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)]
pub enum Border {
@@ -32,23 +35,16 @@ pub struct LedStripConfig {
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct LedStripConfigGroup {
pub items: Vec<LedStripConfig>,
pub strips: Vec<LedStripConfig>,
pub mappers: Vec<SamplePointMapper>,
}
impl LedStripConfig {
pub async fn global() -> &'static Vec<LedStripConfig> {
static LED_STRIP_CONFIGS_GLOBAL: OnceCell<Vec<LedStripConfig>> = OnceCell::const_new();
LED_STRIP_CONFIGS_GLOBAL
.get_or_init(|| async { Self::read_config().await.unwrap() })
.await
}
pub async fn read_config() -> anyhow::Result<Vec<Self>> {
impl LedStripConfigGroup {
pub async fn read_config() -> anyhow::Result<Self> {
// config path
let path = config_dir()
.unwrap_or(current_dir().unwrap())
.join("led_strip_config.toml");
.join(CONFIG_FILE_NAME);
let exists = tokio::fs::try_exists(path.clone())
.await
@@ -60,42 +56,44 @@ impl LedStripConfig {
let config: LedStripConfigGroup = toml::from_str(&config)
.map_err(|e| anyhow::anyhow!("Failed to parse config file: {}", e))?;
Ok(config.items)
Ok(config)
} else {
info!("config file not exist, fallback to default config");
Ok(Self::get_default_config().await?)
}
}
pub async fn write_config(configs: &Vec<Self>) -> anyhow::Result<()> {
pub async fn write_config(configs: &Self) -> anyhow::Result<()> {
let path = config_dir()
.unwrap_or(current_dir().unwrap())
.join("led_strip_config.toml");
.join(CONFIG_FILE_NAME);
let configs = LedStripConfigGroup { items: configs.clone() };
tokio::fs::create_dir_all(path.parent().unwrap()).await?;
let config = toml::to_string(&configs).map_err(|e| {
let config_text = toml::to_string(&configs).map_err(|e| {
anyhow::anyhow!("Failed to parse config file: {}. configs: {:?}", e, configs)
})?;
tokio::fs::write(&path, config).await.map_err(|e| {
tokio::fs::write (&path, config_text).await.map_err(|e| {
anyhow::anyhow!("Failed to write config file: {}. path: {:?}", e, &path)
})?;
Ok(())
}
pub async fn get_default_config() -> anyhow::Result<Vec<Self>> {
pub async fn get_default_config() -> anyhow::Result<Self> {
let displays = display_info::DisplayInfo::all().map_err(|e| {
error!("can not list display info: {}", e);
anyhow::anyhow!("can not list display info: {}", e)
})?;
let mut configs = Vec::new();
let mut strips = Vec::new();
let mut mappers = Vec::new();
for (i, display) in displays.iter().enumerate() {
let mut configs = Vec::new();
for j in 0..4 {
let config = Self {
index: j + i * 4 * 30,
let item = LedStripConfig {
index: j + i * 4,
display_id: display.id,
border: match j {
0 => Border::Top,
@@ -104,14 +102,18 @@ impl LedStripConfig {
3 => Border::Right,
_ => unreachable!(),
},
start_pos: 0,
start_pos: j + i * 4 * 30,
len: 30,
};
configs.push(config);
configs.push(item);
strips.push(item);
mappers.push(SamplePointMapper {
start: (j + i * 4) * 30,
end: (j + i * 4 + 1) * 30,
})
}
}
Ok(configs)
Ok(Self { strips, mappers })
}
}
@@ -220,7 +222,7 @@ impl LedStripConfigOfDisplays {
start_pos: i * 4 * 30 + 90,
len: 30,
}),
}
},
};
configs.push(config);
}
@@ -228,3 +230,15 @@ impl LedStripConfigOfDisplays {
Ok(configs[0])
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SamplePointMapper {
pub start: usize,
pub end: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SamplePointConfig {
pub display_id: u32,
pub points: Vec<LedSamplePoints>,
}

View File

@@ -1,16 +1,16 @@
use std::{sync::Arc, borrow::Borrow};
use std::sync::Arc;
use tauri::async_runtime::RwLock;
use tokio::sync::OnceCell;
use crate::ambient_light::{config, LedStripConfig};
use crate::ambient_light::{config, LedStripConfigGroup};
use super::Border;
pub struct ConfigManager {
configs: Arc<RwLock<Vec<LedStripConfig>>>,
config_update_receiver: tokio::sync::watch::Receiver<Vec<LedStripConfig>>,
config_update_sender: tokio::sync::watch::Sender<Vec<LedStripConfig>>,
config: Arc<RwLock<LedStripConfigGroup>>,
config_update_receiver: tokio::sync::watch::Receiver<LedStripConfigGroup>,
config_update_sender: tokio::sync::watch::Sender<LedStripConfigGroup>,
}
impl ConfigManager {
@@ -18,11 +18,11 @@ impl ConfigManager {
static CONFIG_MANAGER_GLOBAL: OnceCell<ConfigManager> = OnceCell::const_new();
CONFIG_MANAGER_GLOBAL
.get_or_init(|| async {
let configs = LedStripConfig::read_config().await.unwrap();
let configs = LedStripConfigGroup::read_config().await.unwrap();
let (config_update_sender, config_update_receiver) =
tokio::sync::watch::channel(configs.clone());
ConfigManager {
configs: Arc::new(RwLock::new(configs)),
config: Arc::new(RwLock::new(configs)),
config_update_receiver,
config_update_sender,
}
@@ -31,45 +31,71 @@ impl ConfigManager {
}
pub async fn reload(&self) -> anyhow::Result<()> {
let mut configs = self.configs.write().await;
*configs = LedStripConfig::read_config().await?;
let mut configs = self.config.write().await;
*configs = LedStripConfigGroup::read_config().await?;
Ok(())
}
pub async fn update(&self, configs: &Vec<LedStripConfig>) -> anyhow::Result<()> {
LedStripConfig::write_config(configs).await?;
pub async fn update(&self, configs: &LedStripConfigGroup) -> anyhow::Result<()> {
LedStripConfigGroup::write_config(configs).await?;
self.reload().await?;
self.config_update_sender.send(configs.clone()).map_err(|e| {
anyhow::anyhow!("Failed to send config update: {}", e)
})?;
self.config_update_sender
.send(configs.clone())
.map_err(|e| anyhow::anyhow!("Failed to send config update: {}", e))?;
log::info!("config updated: {:?}", configs);
Ok(())
}
pub async fn configs(&self) -> Vec<LedStripConfig> {
self.configs.read().await.clone()
pub async fn configs(&self) -> LedStripConfigGroup {
self.config.read().await.clone()
}
pub async fn patch_led_strip_len(&self, display_id: u32, border: Border, delta_len: i8) -> anyhow::Result<()> {
let mut configs = self.configs.write().await;
pub async fn patch_led_strip_len(
&self,
display_id: u32,
border: Border,
delta_len: i8,
) -> anyhow::Result<()> {
let mut config = self.config.write().await;
for config in configs.iter_mut() {
for config in config.strips.iter_mut() {
if config.display_id == display_id && config.border == border {
let target = config.len as i64 + delta_len as i64;
if target < 0 || target > 1000 {
return Err(anyhow::anyhow!("Overflow. range: 0-1000, current: {}", target));
return Err(anyhow::anyhow!(
"Overflow. range: 0-1000, current: {}",
target
));
}
config.len = target as usize;
}
}
let cloned_config = configs.clone();
let cloned_config = config.clone();
drop(configs);
drop(config);
self.update(&cloned_config).await?;
self.config_update_sender
.send(cloned_config)
.map_err(|e| anyhow::anyhow!("Failed to send config update: {}", e))?;
Ok(())
}
pub async fn set_items(&self, items: Vec<config::LedStripConfig>) -> anyhow::Result<()> {
let mut config = self.config.write().await;
config.strips = items;
let cloned_config = config.clone();
drop(config);
self.update(&cloned_config).await?;
@@ -82,7 +108,7 @@ impl ConfigManager {
pub fn clone_config_update_receiver(
&self,
) -> tokio::sync::watch::Receiver<Vec<LedStripConfig>> {
) -> tokio::sync::watch::Receiver<LedStripConfigGroup> {
self.config_update_receiver.clone()
}
}

View File

@@ -1,5 +1,7 @@
mod config;
mod config_manager;
mod publisher;
pub use config::*;
pub use config_manager::*;
pub use publisher::*;

View File

@@ -0,0 +1,105 @@
use std::sync::Arc;
use paris::warn;
use tauri::async_runtime::RwLock;
use tokio::sync::watch;
use crate::{
ambient_light::{config, ConfigManager},
rpc::MqttRpc,
screenshot_manager::ScreenshotManager,
};
pub struct LedColorsPublisher {
rx: Arc<RwLock<watch::Receiver<Vec<u8>>>>,
tx: Arc<RwLock<watch::Sender<Vec<u8>>>>,
}
impl LedColorsPublisher {
pub async fn global() -> &'static Self {
static LED_COLORS_PUBLISHER_GLOBAL: tokio::sync::OnceCell<LedColorsPublisher> =
tokio::sync::OnceCell::const_new();
let (tx, rx) = watch::channel(Vec::new());
LED_COLORS_PUBLISHER_GLOBAL
.get_or_init(|| async {
LedColorsPublisher {
rx: Arc::new(RwLock::new(rx)),
tx: Arc::new(RwLock::new(tx)),
}
})
.await
}
pub fn start(&self) -> anyhow::Result<()> {
let tx = self.tx.clone();
tokio::spawn(async move {
let tx = tx.write().await;
let screenshot_manager = ScreenshotManager::global().await;
let config_manager = ConfigManager::global().await;
loop {
let configs = config_manager.configs().await;
let channels = screenshot_manager.channels.read().await;
let mut colors_configs = Vec::new();
for (display_id, rx) in channels.iter() {
let led_strip_configs: Vec<_> = configs
.strips
.iter()
.filter(|c| c.display_id == *display_id)
.collect();
if led_strip_configs.len() == 0 {
warn!("no led strip config for display_id: {}", display_id);
continue;
}
let mut rx = rx.clone();
if rx.changed().await.is_ok() {
let screenshot = rx.borrow().clone();
// log::info!("screenshot updated: {:?}", display_id);
let points: Vec<_> = led_strip_configs
.iter()
.map(|config| screenshot.get_sample_points(&config))
.flatten()
.collect();
let colors_config = config::SamplePointConfig {
display_id: *display_id,
points,
};
colors_configs.push(colors_config);
}
}
let colors = screenshot_manager.get_all_colors(&colors_configs, &configs.mappers, &channels).await;
match tx.send(colors) {
Ok(_) => {
// log::info!("colors updated");
}
Err(_) => {
warn!("colors update failed");
}
}
}
});
Ok(())
}
pub async fn send_colors(payload: Vec<u8>) -> anyhow::Result<()> {
let mqtt = MqttRpc::global().await;
mqtt.publish_led_sub_pixels(payload).await
}
pub async fn clone_receiver(&self) -> watch::Receiver<Vec<u8>> {
self.rx.read().await.clone()
}
}