diff --git a/TODO b/TODO index a21f4a1..3b9979f 100644 --- a/TODO +++ b/TODO @@ -10,15 +10,15 @@ Necessary: correct protocol numbers Nice to have: -- ir encode/send needs to read xml file -- cir decode irp should read IrpProtocols.xml +- cir decode irp should try all protocols if nothing specified +- cir decode should allow multiple IRPs/keymaps - pcmak leading gap not decoded - encoding toggle_bit_mask not used when popcount > 1 - compare against kernel encoder/decoder Needed for release: - Need ir-ctl and ir-keytable command line parsing -- man pages +- man pages - rc_mapping() kfunc - localization of cli messages - shell completion diff --git a/cir/src/bin/cir.rs b/cir/src/bin/cir.rs index 6b8267a..32d2be6 100644 --- a/cir/src/bin/cir.rs +++ b/cir/src/bin/cir.rs @@ -2,8 +2,14 @@ use clap::{ error::{Error, ErrorKind}, value_parser, ArgAction, ArgMatches, Args, Command, FromArgMatches, Parser, Subcommand, }; +use irp::Protocol; use log::{Level, LevelFilter, Metadata, Record}; -use std::{ffi::OsString, path::PathBuf}; +use std::{ + ffi::OsString, + io, + path::{Path, PathBuf}, + sync::OnceLock, +}; mod commands; @@ -24,6 +30,14 @@ struct App { #[arg(long, short, global = true, conflicts_with = "verbose")] quiet: bool, + /// Location of IrpProtocols.xml + #[arg( + long = "irp-protocols", + global = true, + default_value = "/usr/share/rc_keymaps/IrpProtocols.xml" + )] + irp_protocols: PathBuf, + #[command(subcommand)] command: Commands, } @@ -573,7 +587,7 @@ fn main() { match &args.command { Commands::Decode(decode) => { if let Some(irp) = &decode.irp { - commands::decode::decode_irp(decode, irp) + commands::decode::decode_irp(&args.irp_protocols, decode, irp) } else { let keymap = decode.keymap.as_ref().unwrap(); @@ -584,7 +598,7 @@ fn main() { } } } - Commands::Transmit(args) => commands::transmit::transmit(args), + Commands::Transmit(tx) => commands::transmit::transmit(&args, tx), #[cfg(target_os = "linux")] Commands::List(args) => commands::list::list(args), #[cfg(target_os = "linux")] @@ -594,6 +608,12 @@ fn main() { } } +static IRP_PROTOCOLS: OnceLock>> = OnceLock::new(); + +fn get_irp_protocols(path: &Path) -> &'static io::Result> { + IRP_PROTOCOLS.get_or_init(|| Protocol::parse(path)) +} + static CLI_LOGGER: CliLogger = CliLogger; struct CliLogger; diff --git a/cir/src/bin/commands/decode.rs b/cir/src/bin/commands/decode.rs index 4ea53f1..173b3fe 100644 --- a/cir/src/bin/commands/decode.rs +++ b/cir/src/bin/commands/decode.rs @@ -1,5 +1,6 @@ #[cfg(target_os = "linux")] use super::keymap::{find_devices, Purpose}; +use crate::get_irp_protocols; #[cfg(target_os = "linux")] use cir::lirc::Lirc; use cir::{keymap::Keymap, lircd_conf::parse}; @@ -11,17 +12,38 @@ use std::{ path::{Path, PathBuf}, }; -pub fn decode_irp(decode: &crate::Decode, irp_str: &String) { +pub fn decode_irp(irp_protocols: &Path, decode: &crate::Decode, irp_notation: &String) { + let mut protocols = &Vec::new(); + + match get_irp_protocols(irp_protocols) { + Ok(res) => { + protocols = res; + } + Err(e) => { + log::error!("{}: {e}", irp_protocols.display()); + } + }; + + let irp_notation = match protocols + .iter() + .find(|e| e.decodable && (&e.name == irp_notation || e.alt_name.contains(irp_notation))) + { + Some(e) => &e.irp, + None => irp_notation, + }; + + log::debug!("decoding IRP: {irp_notation}"); + #[allow(unused_mut)] let mut abs_tolerance = decode.options.aeps.unwrap_or(100); let rel_tolerance = decode.options.eps.unwrap_or(3); #[allow(unused_mut)] let mut max_gap = 100000; - let irp = match Irp::parse(irp_str) { + let irp = match Irp::parse(irp_notation) { Ok(m) => m, Err(s) => { - eprintln!("unable to parse irp ‘{irp_str}’: {s}"); + eprintln!("unable to parse irp ‘{irp_notation}’: {s}"); std::process::exit(2); } }; @@ -50,7 +72,7 @@ pub fn decode_irp(decode: &crate::Decode, irp_str: &String) { let dfa = match irp.compile(&options) { Ok(dfa) => dfa, Err(s) => { - eprintln!("unable to compile irp ‘{irp_str}’: {s}"); + eprintln!("unable to compile irp ‘{irp_notation}’: {s}"); std::process::exit(2); } }; diff --git a/cir/src/bin/commands/transmit.rs b/cir/src/bin/commands/transmit.rs index abdd032..f6c0bec 100644 --- a/cir/src/bin/commands/transmit.rs +++ b/cir/src/bin/commands/transmit.rs @@ -1,3 +1,5 @@ +use crate::get_irp_protocols; + #[cfg(target_os = "linux")] use super::keymap::{open_lirc, Purpose}; use cir::{ @@ -9,8 +11,8 @@ use log::{error, info, warn}; use std::{fs, path::Path}; use terminal_size::{terminal_size, Width}; -pub fn transmit(transmit: &crate::Transmit) { - let message = encode_args(transmit); +pub fn transmit(args: &crate::App, transmit: &crate::Transmit) { + let message = encode_args(&args.irp_protocols, transmit); if let Some(carrier) = &message.carrier { if *carrier == 0 { @@ -116,7 +118,7 @@ pub fn transmit(transmit: &crate::Transmit) { } } -fn encode_args(args: &crate::Transmit) -> Message { +fn encode_args(irp_protocols: &Path, args: &crate::Transmit) -> Message { let mut vars = irp::Vartable::new(); for field in &args.arguments { @@ -257,6 +259,26 @@ fn encode_args(args: &crate::Transmit) -> Message { part.push(Part::Raw(m)); } crate::Transmitables::Irp(irp_notation) => { + let mut protocols = &Vec::new(); + + match get_irp_protocols(irp_protocols) { + Ok(res) => { + protocols = res; + } + Err(e) => { + log::error!("{}: {e}", irp_protocols.display()); + } + }; + + let irp_notation = match protocols.iter().find(|e| { + !e.decode_only && (&e.name == irp_notation || e.alt_name.contains(irp_notation)) + }) { + Some(e) => &e.irp, + None => irp_notation, + }; + + log::debug!("transmit IRP: {irp_notation}"); + let irp = match Irp::parse(irp_notation) { Ok(m) => m, Err(s) => { diff --git a/cir/tests/decode_tests.rs b/cir/tests/decode_tests.rs index 843c81f..d4cb6b3 100644 --- a/cir/tests/decode_tests.rs +++ b/cir/tests/decode_tests.rs @@ -207,3 +207,31 @@ debug: scancode 0x100060 "# ); } + +#[test] +fn irp() { + let mut cmd = Command::cargo_bin("cir").unwrap(); + + let assert = cmd + .args([ + "decode", + "-i", + "Blaupunkt", + "--irp-protocols=../irp/tests/IrpTransmogrifier/src/main/resources/IrpProtocols.xml", + "--raw=+512 -2560 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -23040 +512 -2560 +512 -1024 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +1024 -1024 +512 -512 +512 -120832 +512 -2560 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -23040", + ]) + .assert(); + + let output = assert.get_output(); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_eq!(stderr, "info: decoding: +512 -2560 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -23040 +512 -2560 +512 -1024 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +1024 -1024 +512 -512 +512 -120832 +512 -2560 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -23040\n"); + + assert_eq!( + stdout, + r#"decoded: down D=1, F=0 +"# + ); +} diff --git a/cir/tests/encode_tests.rs b/cir/tests/encode_tests.rs index a02432b..bf50bc5 100644 --- a/cir/tests/encode_tests.rs +++ b/cir/tests/encode_tests.rs @@ -63,6 +63,36 @@ info: rawir: +2664 -888 +444 -444 +444 -444 +444 -888 +444 -888 +888 -444 +444 - ); } +#[test] +fn encode_irp_test() { + let mut cmd = Command::cargo_bin("cir").unwrap(); + + let assert = cmd + .args([ + "transmit", + "--dry-run", + "--irp=Blaupunkt", + "--irp-protocols", + "../irp/tests/IrpTransmogrifier/src/main/resources/IrpProtocols.xml", + "-aF=0,D=1", + ]) + .assert(); + + let output = assert.get_output(); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_eq!(stdout, ""); + + assert_eq!( + stderr, + r#"info: carrier: 30300Hz +info: rawir: +512 -2560 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -23040 +512 -2560 +512 -1024 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +1024 -1024 +512 -512 +512 -120832 +512 -2560 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -512 +512 -23040 +"# + ); +} + #[test] fn encode_lircd_aiwa_test() { let mut cmd = Command::cargo_bin("cir").unwrap(); diff --git a/irp/src/protocols.rs b/irp/src/protocols.rs index 60b4b73..63ed00b 100644 --- a/irp/src/protocols.rs +++ b/irp/src/protocols.rs @@ -21,7 +21,7 @@ pub struct Protocol { pub minimum_leadout: u32, pub decode_only: bool, pub decodable: bool, - pub reject_repeatess: bool, + pub reject_repeatless: bool, } enum Element { @@ -122,7 +122,7 @@ impl Protocol { protocol.decode_only = bool::from_str(&data).unwrap(); } Element::RejectRepeatLess => { - protocol.reject_repeatess = bool::from_str(&data).unwrap(); + protocol.reject_repeatless = bool::from_str(&data).unwrap(); } Element::AbsoluteTolerance => { protocol.absolute_tolerance = u32::from_str(&data).unwrap();