Files
desktop/src-tauri/src/ambient_light/config_manager.rs
Ivan Li 99cbaf3b9f feat: Add RGBW LED support and hardware communication protocol
- Add RGBW LED type support alongside existing RGB LEDs
- Implement 4-channel RGBW data transmission (R,G,B,W bytes)
- Add RGBW visual preview with half-color, half-white gradient display
- Fix RGB color calibration bug in publisher (was not being applied)
- Create comprehensive hardware communication protocol documentation
- Support mixed RGB/RGBW LED strips on same display
- Add W channel color temperature adjustment in white balance page
- Hardware acts as simple UDP-to-WS2812 bridge without type distinction
2025-07-05 02:46:31 +08:00

267 lines
7.8 KiB
Rust

use std::{borrow::BorrowMut, sync::Arc};
use tauri::async_runtime::RwLock;
use tokio::{sync::OnceCell, task::yield_now};
use crate::ambient_light::{config, LedStripConfigGroup};
use super::{Border, SamplePointMapper, ColorCalibration, LedType};
pub struct ConfigManager {
config: Arc<RwLock<LedStripConfigGroup>>,
config_update_sender: tokio::sync::watch::Sender<LedStripConfigGroup>,
}
impl ConfigManager {
pub async fn global() -> &'static Self {
static CONFIG_MANAGER_GLOBAL: OnceCell<ConfigManager> = OnceCell::const_new();
CONFIG_MANAGER_GLOBAL
.get_or_init(|| async {
let configs = LedStripConfigGroup::read_config().await.unwrap();
let (config_update_sender, config_update_receiver) =
tokio::sync::watch::channel(configs.clone());
if let Err(err) = config_update_sender.send(configs.clone()) {
log::error!("Failed to send config update when read config first time: {}", err);
}
drop(config_update_receiver);
ConfigManager {
config: Arc::new(RwLock::new(configs)),
config_update_sender,
}
})
.await
}
pub async fn reload(&self) -> anyhow::Result<()> {
let mut configs = self.config.write().await;
*configs = LedStripConfigGroup::read_config().await?;
Ok(())
}
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))?;
yield_now().await;
log::debug!("config updated: {:?}", configs);
Ok(())
}
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 config = self.config.write().await;
for strip in config.strips.iter_mut() {
if strip.display_id == display_id && strip.border == border {
let target = strip.len as i64 + delta_len as i64;
if target < 0 || target > 1000 {
return Err(anyhow::anyhow!(
"Overflow. range: 0-1000, current: {}",
target
));
}
strip.len = target as usize;
}
}
Self::rebuild_mappers(&mut config);
let cloned_config = config.clone();
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 patch_led_strip_type(
&self,
display_id: u32,
border: Border,
led_type: LedType,
) -> anyhow::Result<()> {
let mut config = self.config.write().await;
for strip in config.strips.iter_mut() {
if strip.display_id == display_id && strip.border == border {
strip.led_type = led_type;
}
}
let cloned_config = config.clone();
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 move_strip_part(
&self,
display_id: u32,
border: Border,
target_start: usize,
) -> anyhow::Result<()> {
let mut config = self.config.write().await;
for (index, strip) in config.clone().strips.iter().enumerate() {
if strip.display_id == display_id && strip.border == border {
let mut mapper = config.mappers[index].borrow_mut();
if target_start == mapper.start {
return Ok(());
}
let target_end = mapper.end + target_start - mapper.start;
if target_start > 1000 || target_end > 1000 {
return Err(anyhow::anyhow!(
"Overflow. range: 0-1000, current: {}-{}",
target_start,
target_end
));
}
mapper.start = target_start as usize;
mapper.end = target_end as usize;
log::info!("mapper: {:?}", mapper);
}
}
let cloned_config = config.clone();
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 reverse_led_strip_part(
&self,
display_id: u32,
border: Border,
) -> anyhow::Result<()> {
let mut config = self.config.write().await;
for (index, strip) in config.clone().strips.iter().enumerate() {
if strip.display_id == display_id && strip.border == border {
let mut mapper = config.mappers[index].borrow_mut();
let start = mapper.start;
mapper.start = mapper.end;
mapper.end = start;
}
}
let cloned_config = config.clone();
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(())
}
fn rebuild_mappers(config: &mut LedStripConfigGroup) {
let mut prev_pos_end = 0;
let mappers: Vec<SamplePointMapper> = config
.strips
.iter()
.enumerate()
.map(|(index, strip)| {
let mapper = &config.mappers[index];
if mapper.start < mapper.end {
let mapper = SamplePointMapper {
start: mapper.start,
end: mapper.start + strip.len,
pos: prev_pos_end,
};
prev_pos_end = prev_pos_end + strip.len;
mapper
} else {
let mapper = SamplePointMapper {
end: mapper.end,
start: mapper.end + strip.len,
pos: prev_pos_end,
};
prev_pos_end = prev_pos_end + strip.len;
mapper
}
})
.collect();
config.mappers = mappers;
}
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?;
self.config_update_sender
.send(cloned_config)
.map_err(|e| anyhow::anyhow!("Failed to send config update: {}", e))?;
Ok(())
}
pub fn clone_config_update_receiver(
&self,
) -> tokio::sync::watch::Receiver<LedStripConfigGroup> {
self.config_update_sender.subscribe()
}
pub async fn set_color_calibration(&self, color_calibration: ColorCalibration) -> anyhow::Result<()> {
let config = self.config.write().await;
let mut cloned_config = config.clone();
cloned_config.color_calibration = color_calibration;
drop(config);
self.update(&cloned_config).await
}
}