//! Sample usage program. //! //! I use it to control a "ShowTec Compact par 7-3" on channel 1 //! //! This is just to show how to use the library, and is not intended to be efficient,... //! //! The mini-command language *is* underpowered,... use regex::Regex; use std::error::Error; use std::fs::File; use std::io::BufRead; use std::path::Path; use std::time::Duration; use std::time::{SystemTime, UNIX_EPOCH}; use std::{env, io}; use libvm116::prelude::*; fn get_epoch_ms() -> u128 { SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_millis() } fn read_lines

(filename: P) -> io::Result>> where P: AsRef, { let file = File::open(filename)?; Ok(io::BufReader::new(file).lines()) } /// Commands for the mini-language #[derive(Debug)] enum Command { /// set a label, to which we will be able to return with 'loop' Label { label: String }, /// loop a number of times to a label Loop { label: String, i: u16, start: u16 }, /// wait some time Wait { millis: u32 }, /// sets the light to rgb (on the current channel) Rgb { r: u8, g: u8, b: u8 }, /// change the current channel Channel { channel: u16 }, /// set all channels to 0 Reset {}, /// finish early Exit {}, } fn main() -> Result<(), Box> { env_logger::init(); let mut commands = vec![]; let re_comment = Regex::new(r"(#.*)").unwrap(); let re_rgb = Regex::new(r"^(?i)rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$").unwrap(); let re_wait = Regex::new(r"^(?i)wait\((\d+)\)$").unwrap(); let re_loop = Regex::new(r"^(?i)loop\(([a-z]+),(\d+)\)$").unwrap(); let re_label = Regex::new(r"^(?i)label\(([a-z]+)\)$").unwrap(); let re_exit = Regex::new(r"^(?i)exit\(\)$").unwrap(); let re_reset = Regex::new(r"^(?i)reset\(\)$").unwrap(); let re_channel = Regex::new(r"^(?i)channel\((\d+)\)$").unwrap(); let args: Vec = env::args().collect(); log::debug!("args({}): {:?}", args.len(), args); if args.len() == 2 { if let Ok(lines) = read_lines(&args[1]) { for mut line in lines { if let Ok(line) = &mut line { let line = re_comment.replace(line, ""); for expr in line.split(';') { let expr = expr.trim(); if expr.is_empty() { continue; } // allow multiple instructions per line if let Some(cap) = re_label.captures(expr) { commands.push(Command::Label { label: cap[1].to_string(), }) } else if let Some(cap) = re_loop.captures(expr) { let start = cap[2].parse()?; commands.push(Command::Loop { label: cap[1].to_string(), i: start, start, }) } else if let Some(cap) = re_rgb.captures(expr) { commands.push(Command::Rgb { r: cap[1].parse()?, g: cap[2].parse()?, b: cap[3].parse()?, }) } else if let Some(cap) = re_wait.captures(expr) { commands.push(Command::Wait { millis: cap[1].parse()?, }) } else if let Some(cap) = re_channel.captures(expr) { commands.push(Command::Channel { channel: cap[1].parse()?, }) } else if re_exit.is_match(expr) { commands.push(Command::Exit {}) } else if re_reset.is_match(expr) { commands.push(Command::Reset {}) } else { Err(format!("Invalid command: '{}'", expr))?; } } } } } } else { let e = |n: usize| { args.get(n) .map(|s| s.parse().ok()) .flatten() .unwrap_or(255u8) }; let r = e(1); let g = e(2); let b = e(3); commands.push(Command::Rgb { r, g, b }); commands.push(Command::Wait { millis: 0 }); } log::debug!("Command: {:?}", commands); let vm116 = Vm116::new()?; // vm116 - K8062 log::debug!("Opened ok"); let mut idx = 0; let mut ref_time = get_epoch_ms(); let mut dmx_state = DmxState::new(512)?; let mut base_channel = 1u16; loop { let mut goto_label = None; if let Some(command) = commands.get_mut(idx) { match command { Command::Rgb { r, g, b } => { dmx_state.set(0 + base_channel, *r)?; dmx_state.set(1 + base_channel, *g)?; dmx_state.set(2 + base_channel, *b)?; } Command::Wait { millis } => { // we calculate until when we should wait, so that if we 'lost' time sending data, // we will still be able to by synchronized (ie: 100 times 'wait(100)' will // really wait until 10 sec is elapsed. if *millis > 0 { let wait_for = ref_time + *millis as u128; let now = get_epoch_ms(); if wait_for > now { let wait_millis = wait_for - now; std::thread::sleep(Duration::from_millis(wait_millis as u64)); } ref_time = wait_for; } let n_packets = vm116.send(&dmx_state)?; log::debug!("Sent {n_packets} packets"); } Command::Exit {} => { let n_packets = vm116.send(&dmx_state)?; log::debug!("Sent {n_packets} packets"); break; } Command::Label { label: _ } => { // nothing to do } Command::Loop { label, i, start } => { if *i == 0u16 { // reset the loop counter *i = *start; } else { goto_label = Some(label); *i -= 1; } } Command::Channel { channel } => { base_channel = *channel; } Command::Reset { .. } => { dmx_state.reset(); } } } else { break; } if let Some(goto_label) = goto_label { let gl = goto_label.clone(); if let Some(label_idx) = commands.iter().position(|c| { if let Command::Label { label } = c { label == &gl } else { false } }) { idx = label_idx; }; } idx = idx + 1; } log::debug!("Finished"); Ok(()) }