diff --git a/Cargo.toml b/Cargo.toml index 1b4a46d..a9ba871 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "lazer" +name = "lj_rust" version = "0.1.0" edition = "2021" diff --git a/copyme.Settings.toml b/copyme.Settings.toml index 5296d56..21944b6 100644 --- a/copyme.Settings.toml +++ b/copyme.Settings.toml @@ -1,6 +1,20 @@ +# file: Settings.toml +# Rename me ! + +# The main key of your laser in LJ laser_id = 0 + +# Activate for more debug debug = "true" + +# Redis URL as IP:port redis_url = "127.0.0.1" + +# Either Helios or Etherdream dac_family = "Helios" -dac_id = 1 + +# For Helios. USB Device Id of the DAC +dac_id = 0 + +# For Etherdream. IP of the DAC dac_url = "192.168.1.68" diff --git a/src/conf.rs b/src/conf.rs index f7768de..2b449ec 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -1,31 +1,26 @@ - use config::Config; use serde::Deserialize; - -#[derive(Deserialize,Debug)] +#[derive(Deserialize, Debug)] pub enum DacFamily { Helios, - Etherdream - } - -#[derive(Deserialize,Debug)] -pub struct Conf { - laser_id : u8, - debug : bool, - redis_url : String, - dac_family: DacFamily, - dac_id : Option, - dac_url : Option + Etherdream, } - -pub fn load_config( path : &str )-> Result> { +#[derive(Deserialize, Debug)] +pub struct Conf { + pub laser_id: u8, + pub debug: bool, + pub redis_url: String, + pub dac_family: DacFamily, + pub dac_id: Option, + pub dac_url: Option, +} +pub fn load_config(path: &str) -> Result> { let settings = Config::builder() .add_source(config::File::with_name(path)) .build()?; let conf : Conf = settings.try_deserialize()?; - Ok(conf) } diff --git a/src/errors.rs b/src/errors.rs index b3a4a3a..9a9c179 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,12 +1,11 @@ use std::error::Error; use std::fmt; - -use config::ConfigError; +use redis::RedisError; #[derive(Debug)] pub enum LJError { ConfigFileMissing, - + RedisConnect(RedisError) } impl fmt::Display for LJError { @@ -18,6 +17,9 @@ impl fmt::Display for LJError { write!(f, "first argument must be a TOML config file \ (see copyme.Settings.toml)") }, + RedisConnect(err) => { + write!(f, "unable to connect to redis server: {err}") + } } } } @@ -28,6 +30,7 @@ impl Error for LJError { match self { ConfigFileMissing => None, + RedisConnect(err) => Some(err) } } } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f3347b9 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +pub mod conf; +pub mod redis_ctrl; +pub mod errors; diff --git a/src/main.rs b/src/main.rs index c386386..6b5fa5e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,20 +2,20 @@ /// Configure udev: /// https://github.com/Grix/helios_dac/blob/master/docs/udev_rules_for_linux.md /// - mod redis_ctrl; mod conf; mod errors; -use helios_dac::{Frame, - Point, - DeviceStatus, - // Coordinate, - Color}; use helios_dac::NativeHeliosDacController; +use helios_dac::{ + // Coordinate, + Color, + DeviceStatus, + Frame, + Point, +}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; - use redis_ctrl::{RedisCtrl,Order}; use conf::{load_config,Conf}; use errors::LJError; @@ -34,10 +34,8 @@ pub fn main() { fn run_all() -> Result<(), Box> { let Some(filename) = std::env::args().nth(1) else { return Err(Box::new(LJError::ConfigFileMissing)); - }; - - let config = load_config(&filename)?; - + }; + let config = load_config(&filename)?; let rs = RedisCtrl::new()?; run_dac(config, rs) @@ -56,7 +54,7 @@ fn run_dac( let controller = NativeHeliosDacController::new()?; let devices = controller.list_devices()?; - + let Some(device) = devices.into_iter().next() else { println!("Unable to find an helios device"); return Ok(()); @@ -65,12 +63,13 @@ fn run_dac( let mut device = device.open()?; while running.load(Ordering::SeqCst) { - let order = rs.get_order(0)?; + let order = rs.get_order(config.laser_id)?; if order != Order::Draw { println!("{:?}", order); } - let frame = get_next_frame(1000, &mut rs, order == Order::Black)?; + let frame = get_next_frame(&config, 1000, + &mut rs, order == Order::Black)?; while let Ok(DeviceStatus::NotReady) = device.status() { } @@ -83,27 +82,28 @@ fn run_dac( } fn get_next_frame( + config: &Conf, speed: u32, rs: &mut RedisCtrl, - black: bool + _black: bool ) -> Result> { - let line = rs.get("/pl/0/0")?; - let line : Vec = line.iter() - .map(tuple_to_point) - .collect(); - + let line = rs.get(&format!("/pl/{}/0", config.laser_id))?; + let line: Vec = line.iter().map(tuple_to_point).collect(); + let mut line2 = vec![]; while line2.len() < 48 { - for p in &line { - line2.push(*p); - } + for p in &line { + line2.push(*p); + } } + println!("{:?}", line2); + Ok(Frame::new(speed, line2)) } -fn tuple_to_point(tpl: &(f32,f32,u32)) -> Point { +fn tuple_to_point(tpl: &(f32, f32, u32)) -> Point { let (x, y, col) = tpl; let r = (col >> 16) as u8 ; @@ -114,14 +114,14 @@ fn tuple_to_point(tpl: &(f32,f32,u32)) -> Point { let y = CENTER.1 + *y as u16 * 2; if x >= 4096 || y >= 4096 { - println!("WARN: coordinate out of range: {} {}", x, y); + println!("WARN: coordinate out of range: {} {}", x, y); } let x = x.clamp(0, 4095); let y = y.clamp(0, 4095); - + Point { coordinate: (x, y).into(), color: Color::new(r, g, b), - intensity: 0xFF + intensity: 0xFF, } } diff --git a/src/redis_ctrl.rs b/src/redis_ctrl.rs index d8a27dd..5653a0d 100644 --- a/src/redis_ctrl.rs +++ b/src/redis_ctrl.rs @@ -1,12 +1,9 @@ -use redis::{ - Client, - Connection, - Commands -}; +use redis::{Client, Commands, Connection}; use ron::de::from_str; +use crate::errors::LJError; #[repr(u8)] -#[derive(Debug,PartialEq)] +#[derive(Debug, PartialEq)] pub enum Order { Draw = 0, Edh, //homography @@ -16,14 +13,14 @@ pub enum Order { ClientKey, Intensity, Kpps, - ColorBalance + ColorBalance, } impl TryFrom for Order { type Error = String; - + fn try_from(value: u8) -> Result { - use Order::*; + use Order::*; if value > 8 { return Err("order out of range".to_string()); @@ -42,45 +39,38 @@ impl TryFrom for Order { _ => unreachable!() }) } -} +} -pub type Line = Vec<(f32,f32,u32)>; +pub type Line = Vec<(f32, f32, u32)>; pub struct RedisCtrl { pub client: Client, - pub connection: Connection + pub connection: Connection, } impl RedisCtrl { - pub fn new() -> Result> { - let client = Client::open("redis://127.0.0.1/")?; - let connection = client.get_connection()?; - Ok(RedisCtrl { client, connection }) + let client = Client::open("redis://127.0.0.1/") + .map_err(| err | LJError::RedisConnect(err))?; + let connection = client.get_connection() + .map_err(| err | LJError::RedisConnect(err))?; + Ok(RedisCtrl { client, connection }) } - pub fn get( - &mut self, - key: &str - ) -> Result> { - let val : String = self.connection.get(key)?; - let line : Line = from_str(&val)?; - Ok(line) - } - - pub fn get_order( - &mut self, - id: u8 - ) -> Result> { - let path = format!("/order/{id}"); - let val : u8 = self.connection.get(path.clone())?; - - if val == 1 || val >= 4 { - self.connection.set(path, 0)?; - } - - Ok(val.try_into()?) + pub fn get(&mut self, key: &str) -> Result> { + let val: String = self.connection.get(key)?; + let line: Line = from_str(&val)?; + Ok(line) } + pub fn get_order(&mut self, id: u8) -> Result> { + let path = format!("/order/{id}"); + let val: u8 = self.connection.get(path.clone())?; + + if val == 1 || val >= 4 { + self.connection.set(path, 0)?; + } + + Ok(val.try_into()?) + } } - diff --git a/tests/settings/empty.toml b/tests/settings/empty.toml new file mode 100644 index 0000000..e69de29 diff --git a/tests/settings/valid.toml b/tests/settings/valid.toml new file mode 100644 index 0000000..1d9689c --- /dev/null +++ b/tests/settings/valid.toml @@ -0,0 +1,19 @@ +# file: valid.toml + +# The main key of your laser in LJ +laser_id = 0 + +# Activate for more debug +debug = "true" + +# Redis URL as IP:port +redis_url = "127.0.0.1" + +# Either Helios or Etherdream +dac_family = "Helios" + +# For Helios. USB Device Id of the DAC +dac_id = 0 + +# For Etherdream. IP of the DAC +dac_url = "192.168.1.68" diff --git a/tests/test_conf.rs b/tests/test_conf.rs new file mode 100644 index 0000000..a366a11 --- /dev/null +++ b/tests/test_conf.rs @@ -0,0 +1,32 @@ +use lj_rust::conf::{load_config, DacFamily}; + +#[test] +fn it_loads_a_valid_conf() { + let result = load_config("tests/settings/valid"); + assert!(result.is_ok()); +} + +#[test] +fn it_fails_invalid_conf() { + let result = load_config("tests/settings/empty"); + assert!(result.is_err()); +} + +#[test] +fn it_finds_struct_fields() { + let config = match load_config("tests/settings/valid") { + Ok(c) => c, + Err(err) => { + panic!("Unable to load config file: {:?}", err) + } + }; + assert_eq!(config.laser_id, u8::from(0)); + assert_eq!(config.debug, true); + assert_eq!(config.redis_url, String::from("127.0.0.1")); + assert!(match config.dac_family { + DacFamily::Helios => true, + _ => false, + }); + assert_eq!(config.dac_id, Some(0)); + assert_eq!(config.dac_url, Some(String::from("192.168.1.68"))); +}