diff --git a/Cargo.toml b/Cargo.toml index 9789221..61520a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,10 @@ config = "0.13.3" ctrlc = "3.4.0" env_logger = "0.10.0" helios-dac = { version = "0.1", default-features = false, features = ["native"] } + log = "0.4.18" redis = "0.23.0" ron = "0.8.0" serde = { version = "1.0.163", features = ["derive"] } toml = "0.7.4" +nalgebra = "0.32.2" diff --git a/copyme.settings.toml b/copyme.settings.toml index f440bc0..5f9eaff 100644 --- a/copyme.settings.toml +++ b/copyme.settings.toml @@ -15,6 +15,9 @@ redis_url = "redis://127.0.0.1:6379/" [dac.helios] id = 0 +# For dummy dac: +# [dac.dummy] + # For Etherdream. IP of the DAC # [dac.etherdream] # url = "192.168.1.68" diff --git a/src/conf.rs b/src/conf.rs index 01827c3..815211c 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -19,6 +19,8 @@ pub enum DacFamily { Helios(HeliosConf), #[serde(rename = "etherdream")] Etherdream(EtherDreamConf), + #[serde(rename = "dummy")] + Dummy, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -44,8 +46,9 @@ pub enum TransformConf { #[serde(rename = "flip_vertical")] FlipV(transformer::FlipVertical), #[serde(rename = "grid")] - Grid(transformer::Grid) - + Grid(transformer::Grid), + #[serde(rename = "homography")] + Homography(transformer::Homography) } @@ -68,7 +71,8 @@ impl Conf { TransformConf::Rotate(r) => Box::new(*r), TransformConf::FlipH(r) => Box::new(*r), TransformConf::FlipV(r) => Box::new(*r), - TransformConf::Grid(r) => Box::new(*r) + TransformConf::Grid(r) => Box::new(*r), + TransformConf::Homography(r) => Box::new(*r), }; v.push(t); } diff --git a/src/device.rs b/src/device.rs index 0947bf2..c97276c 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,9 +1,11 @@ mod helios; +mod dummy; use std::fmt; use crate::conf::{Conf, DacFamily /*EtherDreamConf, HeliosConf*/}; use crate::device::helios::HeliosDevice; +use crate::device::dummy::DummyDevice; use crate::errors::LJResult; use crate::point::Point; use serde::Serialize; @@ -57,9 +59,10 @@ pub trait Device { } pub fn device_factory(config: &Conf) -> LJResult> { - let device = match &config.dac { - DacFamily::Helios(conf) => Box::new(HeliosDevice::new(conf)?), - DacFamily::Etherdream(_conf) => todo!(), + let device : Box = match &config.dac { + DacFamily::Helios(conf) => Box::new(HeliosDevice::new(conf)?), + DacFamily::Etherdream(_conf) => todo!(), + DacFamily::Dummy => Box::new(DummyDevice::new()?) }; Ok(device) } diff --git a/src/device/dummy.rs b/src/device/dummy.rs new file mode 100644 index 0000000..e4c9e42 --- /dev/null +++ b/src/device/dummy.rs @@ -0,0 +1,38 @@ +use crate::device::{Device, Status, PlaybackState}; +use crate::errors::LJResult; +use crate::point::Point; +use log::debug; + +pub struct DummyDevice { + state: PlaybackState +} + +impl DummyDevice { + pub fn new() -> LJResult { + Ok(Self { state: PlaybackState::IDLE }) + } +} + +impl Device for DummyDevice { + fn status(&self) -> Status { + Status { + last_traced_at: "never".to_string(), + properties: vec!["foo".to_string()], + playback_state: self.state, + capacity: 0, + lack: "lack".to_string() + } + } + + fn draw(&mut self, + line: Vec, + speed: u32, + ) -> LJResult<()> { + debug!("Draw Line at speed {speed} : {:?}", line); + Ok(()) + } + + fn stop(&mut self) -> LJResult<()> { + Ok(()) + } +} diff --git a/src/errors.rs b/src/errors.rs index ff40f33..1173dbe 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -9,7 +9,8 @@ pub type LJResult = Result>; pub enum LJError { Config(ConfigError), RedisConnect(RedisError), - HeliosDeviceMissing + HeliosDeviceMissing, + BadEDH } impl fmt::Display for LJError { @@ -26,6 +27,9 @@ impl fmt::Display for LJError { }, HeliosDeviceMissing => { write!(f, "helios device not found") + }, + BadEDH => { + write!(f, "EDH matrix is not a 3x3 matrix") } } } diff --git a/src/lib.rs b/src/lib.rs index b183c49..9048f40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,3 +4,4 @@ pub mod errors; pub mod device; pub mod point; pub mod transformer; +pub mod worldstate; diff --git a/src/main.rs b/src/main.rs index 2cf7953..6a1103b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ mod errors; mod point; mod transformer; mod device; +mod worldstate; use device::device_factory; use std::sync::atomic::{AtomicBool, Ordering}; @@ -19,6 +20,7 @@ use point::Point; use transformer::Transformers; use log::{LevelFilter, info, /* warn, */ error}; use env_logger::Builder; +use worldstate::WorldState; const DEFAULT_CONF_FILE: &str = "settings.toml"; @@ -31,6 +33,7 @@ pub fn main() { } } + fn run_all() -> LJResult<()> { // Setup configuration file and set up logs let filename = std::env::args().nth(1).unwrap_or_else(|| { @@ -47,6 +50,8 @@ fn run_all() -> LJResult<()> { // Setup Redis Service let mut rs = RedisCtrl::new(&config.redis_url, &config.laser_id)?; + let mut world_state = rs.init_world_state()?; + // Setup handler for interrupt Signals let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -61,20 +66,49 @@ fn run_all() -> LJResult<()> { //dbg!(tracer); // Setup geometry transformers on points lists - let transformers = config.get_transformers(); + let transformers = config.get_transformers(); // Dispatch based on redis requests while running.load(Ordering::SeqCst) { - rs.set_status( tracer.status())?; - let order = rs.get_order(config.laser_id)?; - if order != Order::Draw { - info!("Order: {:?}", order); - } + rs.set_status(tracer.status())?; - let frame = get_next_frame(&config, &transformers, - &mut rs, order == Order::Black)?; - // For now, draw all the time - tracer.draw(frame, 2_000)?; + let order = rs.get_order(config.laser_id)?; + // 0 : Draw Normal point list + // 2 : Draw BLACK point list + // 3 : Draw GRID point list + + // /worldstate.rs + // /edh.rs + + // 1 : Get the new EDH = reread redis key /EDH/lasernumber + // 4 : Resampler Change (longs and shorts lsteps) + // 5 : Client Key Change = reread redis key /clientkey + // 6 : Max Intensity Change = reread redis key /intensity + // 7 : kpps change = reread redis key /kpps + // 8 : color balance change = reread redis keys /red /green /blue + + match order { + Order::Draw => { + let frame = get_next_frame( + &config, + &transformers, + &mut rs, + // order == Order::Black, + &world_state + )?; + // For now, draw all the time + tracer.draw(frame, 2_000)?; + } + Order::Edh => { + world_state.edh = rs.get_edh()?; + } + + // Order::ClientKey => rs.client_key(), + // Order::ColorBalance => {}, + _ => { + info!("Order: {:?}", order); + } + } } info!("Exiting, stoping device."); @@ -101,17 +135,18 @@ fn get_next_frame( config: &Conf, transformers: &[Box], rs: &mut RedisCtrl, - _black: bool, + world_state : &WorldState ) -> LJResult> { let line = rs.get(&format!("/pl/{}/0", config.laser_id))?; let mut line: Vec = line.into_iter() - .map(|tpl| tpl.into()) - .collect(); + .map(|tpl| tpl.into()) + .collect(); for transformer in transformers { - line = transformer.apply(&line); + line = transformer.apply(&line, world_state); } //info!("Line: {:?}", line); Ok(line) } + diff --git a/src/point.rs b/src/point.rs index 1d43bfe..5eb6d42 100644 --- a/src/point.rs +++ b/src/point.rs @@ -1,11 +1,11 @@ -#[derive(Debug,Clone,Copy)] +#[derive(Debug,Clone,Copy,Default,PartialEq)] pub struct Point { pub x: f32, pub y: f32, pub color: Color } -#[derive(Debug,Clone,Copy)] +#[derive(Debug,Clone,Copy,Default,PartialEq)] pub struct Color { r: u8, g: u8, diff --git a/src/redis_ctrl.rs b/src/redis_ctrl.rs index 448ed43..5f6eea0 100644 --- a/src/redis_ctrl.rs +++ b/src/redis_ctrl.rs @@ -2,6 +2,8 @@ use redis::{Client, Commands, Connection}; use ron::de::from_str; use crate::device::Status; use crate::errors::{LJError, LJResult}; +use crate::worldstate::{WorldState,EDH}; +use log::info; #[repr(u8)] #[derive(Debug, PartialEq)] @@ -96,4 +98,23 @@ impl RedisCtrl { self.set(lack_key, status.lack.to_string())?; Ok(()) } + + pub fn init_world_state( &mut self) -> LJResult{ + let edh = self.get_edh()?; + info!("EDH: {:?}", edh); + + Ok(WorldState { + edh, + ..WorldState::default() + }) + } + + pub fn get_edh( &mut self ) -> LJResult { + // Get new EDH + let edh_key = format!("/EDH/{}", self.laser_id); + let edh : String = self.connection.get(edh_key)?; + let edh : Vec> = from_str(&edh)?; + let edh = EDH::new(edh)?; + Ok(edh) + } } diff --git a/src/transformer.rs b/src/transformer.rs index 19d4445..aa25291 100644 --- a/src/transformer.rs +++ b/src/transformer.rs @@ -5,8 +5,10 @@ mod rotate; mod flip_horizontal; mod flip_vertical; mod grid; +mod homography; use crate::point::Point; +use crate::worldstate::WorldState; // re-export transformers to be abe to use it directly from transformer:: pub use translate::Translate; @@ -15,7 +17,12 @@ pub use rotate::Rotate; pub use flip_horizontal::FlipHorizontal; pub use flip_vertical::FlipVertical; pub use grid::Grid; +pub use self::homography::Homography; pub trait Transformers { - fn apply(&self, point_list: &[Point]) -> Vec; + fn apply( + &self, + point_list: &[Point], + world_state: &WorldState + ) -> Vec; } diff --git a/src/transformer/flip_horizontal.rs b/src/transformer/flip_horizontal.rs index d156739..ed9cec3 100644 --- a/src/transformer/flip_horizontal.rs +++ b/src/transformer/flip_horizontal.rs @@ -1,5 +1,7 @@ use crate::transformer::Transformers; use crate::point::Point; +use crate::worldstate::WorldState; + use serde::{Serialize,Deserialize}; /// Flip Horizontal @@ -10,7 +12,7 @@ pub struct FlipHorizontal { } impl Transformers for FlipHorizontal { - fn apply(&self, point_list: &[Point]) -> Vec { + fn apply(&self, point_list: &[Point], _ws: &WorldState) -> Vec { point_list.iter() .map(| pt | { let dx = pt.x - self.x; diff --git a/src/transformer/flip_vertical.rs b/src/transformer/flip_vertical.rs index fc741c7..19529e8 100644 --- a/src/transformer/flip_vertical.rs +++ b/src/transformer/flip_vertical.rs @@ -1,5 +1,7 @@ use crate::transformer::Transformers; use crate::point::Point; +use crate::worldstate::WorldState; + use serde::{Serialize,Deserialize}; /// Flip Vertical @@ -10,7 +12,7 @@ pub struct FlipVertical { } impl Transformers for FlipVertical { - fn apply(&self, point_list: &[Point]) -> Vec { + fn apply(&self, point_list: &[Point], _ws: &WorldState) -> Vec { point_list.iter() .map(| pt | { let dy = pt.y - self.y; diff --git a/src/transformer/grid.rs b/src/transformer/grid.rs index 25258ff..7a9d37b 100644 --- a/src/transformer/grid.rs +++ b/src/transformer/grid.rs @@ -1,5 +1,7 @@ use crate::transformer::Transformers; use crate::point::Point; +use crate::worldstate::WorldState; + use serde::{Serialize,Deserialize}; /// Translate @@ -44,7 +46,7 @@ fn square_box(size: f32, color: u32) -> Vec<(f32, f32, u32)> { } impl Transformers for Grid { - fn apply(&self, _point_list: &[Point]) -> Vec { + fn apply(&self, _point_list: &[Point], _ws: &WorldState) -> Vec { let mut sq1 = square_box(1000.0, 255 << 8); let mut line = square_box(2000.0, 255); line.append(&mut sq1); diff --git a/src/transformer/homography.rs b/src/transformer/homography.rs new file mode 100644 index 0000000..8f1091f --- /dev/null +++ b/src/transformer/homography.rs @@ -0,0 +1,64 @@ +use crate::transformer::Transformers; +use crate::point::Point; +use crate::worldstate::{WorldState,EDH}; +use serde::{Serialize,Deserialize}; + +/// Homography + +#[derive(Serialize,Deserialize,Debug,Clone,Copy)] +pub struct Homography {} + +impl Transformers for Homography { + fn apply(&self, point_list: &[Point], ws: &WorldState) -> Vec { + let edh : &EDH = &ws.edh; + + point_list.iter() + .map(| point | edh.apply(point)) + .collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn identity_matrix_let_point_unchanged() { + let p0 = Point { x: -1500.0, y: 1500.0, ..Point::default() }; + + let edh = EDH::new(vec![ + vec![ 1.0, 0.0, 0.0 ], + vec![ 0.0, 1.0, 0.0 ], + vec![ 0.0, 0.0, 1.0 ] + ]).unwrap(); + + let ws = WorldState { edh : edh, ..WorldState::default() }; + + let homography = Homography{}; + let result = homography.apply(&[p0], &ws); + + assert_eq!(result, vec![Point { x: -1500.0, + y: 1500.0, + ..Point::default() }]); + } + + #[test] + fn rotation_matrix_rotate_the_point() { + let p0 = Point { x: -1500.0, y: 1500.0, ..Point::default() }; + + let edh = EDH::new(vec![ + vec![ 1.24107321e-03, 1.00500127e-03, 7.15439347e-01], + vec![-9.93223912e-04, 1.22652939e-03,-6.98671238e-01], + vec![ 1.06017142e-17,-4.69459541e-17, 3.32700590e-05] + ]).unwrap(); + + let ws = WorldState { edh : edh, ..WorldState::default() }; + + let homography = Homography{}; + let result = homography.apply(&[p0], &ws); + + assert_eq!(result, vec![Point { x: 10860.557, + y: 79078.87, + ..Point::default() }]); + } +} diff --git a/src/transformer/replicate.rs b/src/transformer/replicate.rs index 99258ae..a5e5ab2 100644 --- a/src/transformer/replicate.rs +++ b/src/transformer/replicate.rs @@ -1,5 +1,7 @@ use crate::transformer::Transformers; use crate::point::Point; +use crate::worldstate::WorldState; + use serde::{Serialize,Deserialize}; /// Replicate @@ -12,7 +14,7 @@ pub enum Replicate { } impl Transformers for Replicate { - fn apply(&self, point_list: &[Point]) -> Vec { + fn apply(&self, point_list: &[Point], _ws: &WorldState) -> Vec { let mut point_list2 = vec![]; match self { Replicate::Until(n) => { diff --git a/src/transformer/rotate.rs b/src/transformer/rotate.rs index 60fc109..0dac920 100644 --- a/src/transformer/rotate.rs +++ b/src/transformer/rotate.rs @@ -1,5 +1,7 @@ use crate::transformer::Transformers; use crate::point::Point; +use crate::worldstate::WorldState; + use serde::{Serialize,Deserialize}; //use std::f32::consts::PI; @@ -14,7 +16,7 @@ pub struct Rotate { } impl Transformers for Rotate { - fn apply(&self, point_list: &[Point]) -> Vec { + fn apply(&self, point_list: &[Point], _ws: &WorldState) -> Vec { point_list.iter() .map(| pt | { let dx = pt.x - self.cx; diff --git a/src/transformer/translate.rs b/src/transformer/translate.rs index c3f4fec..a0262a1 100644 --- a/src/transformer/translate.rs +++ b/src/transformer/translate.rs @@ -1,5 +1,6 @@ use crate::transformer::Transformers; use crate::point::Point; +use crate::worldstate::WorldState; use serde::{Serialize,Deserialize}; /// Translate @@ -11,7 +12,7 @@ pub struct Translate { } impl Transformers for Translate { - fn apply(&self, point_list: &[Point]) -> Vec { + fn apply(&self, point_list: &[Point], _ws: &WorldState) -> Vec { point_list.iter() .map(| pt | { Point { x: pt.x + self.x, diff --git a/src/worldstate.rs b/src/worldstate.rs new file mode 100644 index 0000000..bf5ecc9 --- /dev/null +++ b/src/worldstate.rs @@ -0,0 +1,51 @@ +use crate::point::{Point,Color}; +use nalgebra::base::{Matrix3,Matrix1x3}; +use crate::errors::{LJError,LJResult}; +use log::debug; + +#[derive(Debug, Default)] +pub struct EDH { + pub matrix: Matrix3 +} + +impl EDH { + pub fn new(vec: Vec>) -> LJResult { + if vec.len() != 3 || + vec[0].len() != 3 || + vec[1].len() != 3 || + vec[2].len() != 3 { + return Err(Box::new(LJError::BadEDH)); + } + + // this is the matrix already transposed. + let matrix = Matrix3::new(vec[0][0], vec[1][0], vec[2][0], + vec[0][1], vec[1][1], vec[2][1], + vec[0][2], vec[1][2], vec[2][2]); + + Ok(EDH { matrix }) + } + + pub fn apply(&self, point: &Point) -> Point { + let p = Matrix1x3::new(point.x, point.y, 1.0); + let p = p * self.matrix; + let new_p = Point { x: p[0] / p[2], y: p[1] / p[2], ..*point }; + + debug!("{:?} => {:?}", point, new_p); + + new_p + } +} + +#[derive(Debug, Default)] +pub struct WorldState { + pub edh: EDH, + pub resampler: Vec, + pub client_key: u8, + pub intensity: u8, + pub kpps: u32, + pub color: Color +} + +impl WorldState { + +}