use ws281x_rpi::Ws2812Rpi; use crate::{DBPool, animations::Animation}; use std::error::Error; use smart_leds::{RGB8, SmartLedsWrite, RGB}; use std::time::{SystemTime, UNIX_EPOCH}; #[derive(Clone, Debug)] struct LocalLedConfiguration { tag: Option, start: usize, end: usize } #[derive(Clone)] pub struct LigthSetting { start: Option, end: Option, tags: Option, color: RGB8, } #[derive(Clone)] pub struct KeyFrame { duration: u128, settings: Vec } #[derive(Clone)] pub struct RenderAnimation { key_frames: Vec, priority: u32, name: String, current_frame: usize, start_time: u128, reset: bool, } pub struct Render { conn: DBPool, num_leds: u32, last_render: u128, ws: Option, last: Vec>, animations: Vec, local_led_config: Vec, } fn try_into_usize (input: Option) -> Option { if input.is_none() {return None;} let t: Result = input.unwrap().try_into(); if t.is_err() {return None;} return t.ok(); } impl Render { pub fn new(conn: DBPool) -> Result> { let mut render = Render { conn, num_leds: 0, last_render: 0, ws: None, animations: vec![], local_led_config: vec![], last: vec![], }; render.load_config()?; return Ok(render); } pub fn load_config(&mut self) -> Result<(), Box> { println!("Load config"); // GPIO Pin 10 is SPI // Other modes and PINs are available depending on the Raspberry Pi revision // Additional OS configuration might be needed for any mode. // Check https://github.com/jgarff/rpi_ws281x for more information. const PIN: i32 = 18; let conn = self.conn.get()?; let stmt = conn.prepare("SELECT ledcount, tags from configuration;"); if stmt.is_err() || stmt.as_ref().ok().is_none() { Err(format!("Something went wrong while reading the database {:?}", stmt.as_ref().err()))?; } let mut stmt = stmt?; struct Data { ledcount: u32, tag: String } let version_iter = stmt.query_map([], |row| { Ok(Data { ledcount: row.get(0)?, tag: row.get(1)? }) }); if version_iter.is_err() || version_iter.as_ref().ok().is_none() { Err(format!("Something went wrong while reading the database {:?}", version_iter.as_ref().err()))?; } let version_iter = version_iter.ok().unwrap(); let mut current_pos = 0; let mut local_led_config = Vec::new(); for data in version_iter { let data = data?; let tag = if data.tag.is_empty() { None } else { Some(data.tag) }; let start = current_pos; let end = current_pos + data.ledcount; current_pos = end; let start: Result = start.try_into(); let end: Result = end.try_into(); if start.is_err() || start.ok().is_none() || end.is_err() || end.ok().is_none() { println!("Skiping loading configuration"); continue; } let start = start.ok().unwrap(); let end = end.ok().unwrap(); local_led_config.push(LocalLedConfiguration { tag, start, end }); } self.num_leds = current_pos; println!("Loaded: {} leds, with config: {:?}", self.num_leds, local_led_config); self.local_led_config = local_led_config; self.ws = Some(Ws2812Rpi::new(current_pos as i32, PIN).unwrap()); println!("Here"); Ok(()) } pub fn add_animation(&mut self, animation: Animation) { println!("Added animation"); let mut key_frames = Vec::new(); for frame in animation.get_frames().iter() { let mut light_settings = Vec::new(); for setting in frame.get_settings() { light_settings.push(LigthSetting { tags: setting.get_tags(), start: try_into_usize(setting.get_start()), end: try_into_usize(setting.get_end()), color: RGB8::new(setting.get_r(), setting.get_g(), setting.get_b()), }); } key_frames.push(KeyFrame { duration: (frame.get_duration() as u128) * (1000 as u128), settings: light_settings }) } let new_animation = RenderAnimation { key_frames, priority: animation.get_priority(), name: animation.get_name(), current_frame: 0, start_time: 0, reset: animation.get_repeat(), }; self.animations.push(new_animation); self.animations.sort_by(|a,b| a.priority.partial_cmp(&b.priority).unwrap()) } pub fn blank(&mut self) { self.animations = Vec::new(); } pub fn remove_animation(&mut self, animation_name: String) { self.animations = self.animations.clone().into_iter().filter(|ani| !ani.name.eq(&animation_name)).collect::>(); } pub fn proccess_settings (&mut self, data: &mut Vec>, settings: &Vec) { fn load_led_into_light (data: &mut Vec>, color: RGB8, start: usize, end: usize) { for i in start..=end { data[i] = RGB::new( color.r as f64, color.g as f64, color.b as f64 ); } } for setting in settings { if setting.tags.is_some() { let tags = setting.tags.as_ref().unwrap().to_string(); let tags = tags.split(","); let tags_fn = |t: String| tags.clone().into_iter().any(|tag| tag.eq(&t)); for config in self.local_led_config.clone() { if config.tag.is_some() && tags_fn(config.tag.unwrap()) { load_led_into_light(data, setting.color, config.start, config.end); } } } if setting.start.is_some() && setting.end.is_some() { load_led_into_light(data, setting.color, setting.start.unwrap(), setting.end.unwrap()); } } } pub fn render(&mut self) { if self.ws.is_none() { return; } let mut data: Vec> = vec![RGB::default(); self.num_leds.try_into().unwrap()]; let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_millis(); if self.last_render == 0 { self.last_render = time; } let delta = time - self.last_render; self.last_render = time; let mut to_clean: Vec = Vec::new(); let animations = self.animations.clone().iter_mut().map(|animation| { let mut animation = animation.to_owned(); let mut start_time = animation.start_time; if start_time == 0 { animation.start_time = time; animation.current_frame = 0; self.proccess_settings(&mut data, &animation.key_frames[0].settings); return animation; } while start_time + animation.key_frames[animation.current_frame].duration < time { start_time += animation.key_frames[animation.current_frame].duration; animation.start_time = start_time; if animation.current_frame + 1 >= animation.key_frames.len() { if animation.reset { animation.start_time = time; animation.current_frame = 0; self.proccess_settings(&mut data, &animation.key_frames[0].settings); } else { to_clean.push(animation.name.to_string()); } return animation; } animation.current_frame += 1; } let cf = animation.current_frame; self.proccess_settings(&mut data, &animation.key_frames[cf].settings); return animation; }).collect(); self.animations = animations; to_clean.iter().for_each(|i| self.remove_animation(i.to_string())); if self.last.is_empty() || !self.last.clone().iter().eq(data.clone().iter()) { if self.last.len() == 0 { self.last = data.clone(); } let lerp_data: Vec> = data.iter().zip(&self.last).map(|(d, l)| l.lerp(d, delta as f64)).collect(); //println!("d:{:?}\n l:{:?}", data, lerp_data); let to_set_data: Vec> = rgbs_f64_to_u8(&lerp_data); let err = self.ws.as_mut().unwrap().write(to_set_data.into_iter()); if err.is_err() || err.ok().is_none() { println!("Failed to write data"); } self.last = lerp_data; } } } pub fn rgbs_f64_to_u8(data: &Vec>) -> Vec>{ data.clone().iter().map(|a| { RGB::new( a.r as u8, a.g as u8, a.b as u8 ) }).collect() } pub trait Lerp { fn lerp(self, other: &Self, diff: f64) -> Self; } impl Lerp for f64 { fn lerp(self, other: &Self, diff: f64) -> Self { let delta = diff / 1000.0; let change = (*other - self) * 0.8 * delta; let lerped = self + change; if *other != self && diff != 0.0 { println!("c: {} target:{} lerp: {} move: {}", self, other, lerped, change); } if (*other - self).abs() < 2.0 && diff != 0.0 { return *other; } lerped } } impl Lerp for RGB { fn lerp(self, other: &Self, diff: f64) -> Self { RGB::new(self.r.lerp(&other.r, diff), self.g.lerp(&other.g, diff), self.b.lerp(&other.b, diff)) } }