diff --git a/Cargo.lock b/Cargo.lock index ee99d32..e36d8f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,33 @@ dependencies = [ "nb 1.1.0", ] +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "clipboard-win" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" +dependencies = [ + "error-code", +] + [[package]] name = "embedded-hal" version = "0.2.7" @@ -38,12 +65,72 @@ dependencies = [ "nb 1.1.0", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "error-code" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" + +[[package]] +name = "fd-lock" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + [[package]] name = "nb" version = "0.1.3" @@ -59,6 +146,37 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rpitest" version = "0.1.0" @@ -67,6 +185,7 @@ dependencies = [ "libc", "nb 1.1.0", "rppal", + "rustyline", ] [[package]] @@ -84,6 +203,47 @@ dependencies = [ "void", ] +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustyline" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63" +dependencies = [ + "bitflags", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "windows-sys", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "spin_sleep" version = "1.2.0" @@ -93,6 +253,24 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "void" version = "1.0.2" diff --git a/Cargo.toml b/Cargo.toml index 65e877a..d12101d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,4 @@ rppal = { version = "0.17", features = ["hal"] } libc = "0.2" ads1x1x = "0.2" nb = "1.1.0" +rustyline = "14.0.0" \ No newline at end of file diff --git a/src/cli_mode.rs b/src/cli_mode.rs index 6df3c97..9cf98b1 100644 --- a/src/cli_mode.rs +++ b/src/cli_mode.rs @@ -1,23 +1,106 @@ -use crate::error::GenericResult; +use rustyline::{config::Configurer, error::ReadlineError, history::FileHistory}; + +use crate::{ + error::GenericResult, + io::{self, get_input_voltage}, +}; struct LoopFlags { exit: bool, } -fn process_input(input: String) -> GenericResult { - todo!() +fn process_input(input: String, program_state: &mut ProgramState) -> GenericResult { + let args = input.split(' ').collect::>(); + let main_command = *args.first().ok_or("No main command found.")?; + match main_command { + "ana" => command_ana(&args)?, + "rel" => command_rel(&args, program_state)?, + "exit" => return Ok(LoopFlags { exit: true }), + _ => return Err("Unknown main command".into()), + }; + + Ok(LoopFlags { exit: false }) +} + +fn command_rel(args: &[&str], program_state: &mut ProgramState) -> GenericResult<()> { + let pin = args + .get(1) + .ok_or("Must specify pin number.")? + .parse::() + .map_err(|_| "Not a valid pin number")?; + + let switch_state = args.get(2).map(|arg| match *arg { + "1" => Ok(io::RelaySwitchState::On), + "on" => Ok(io::RelaySwitchState::On), + "true" => Ok(io::RelaySwitchState::On), + "0" => Ok(io::RelaySwitchState::Off), + "off" => Ok(io::RelaySwitchState::Off), + "false" => Ok(io::RelaySwitchState::Off), + _ => Err("Not a valid switch state"), + }); + + match switch_state { + Some(state) => { + println!("Switching relay"); + program_state.relay.switch(pin, state?)? + } + None => { + println!("Toggling relay"); + program_state.relay.toggle(pin)? + } + }; + + Ok(()) } -fn cli_loop() -> GenericResult { - print!("Enter command: "); - let mut input_string = String::new(); - std::io::stdin().read_line(&mut input_string)?; - process_input(input_string) +fn command_ana(args: &[&str]) -> GenericResult<()> { + let pin = args + .get(1) + .ok_or("Must specify pin number.")? + .parse::() + .map_err(|_| "Not a valid pin number")?; + + let voltage = get_input_voltage(pin)?; + println!("Voltage read: {}", voltage); + + Ok(()) +} + +fn cli_loop(rl: &mut CLIEditor, program_state: &mut ProgramState) -> GenericResult { + let readline = rl.readline("growpi>> "); + + match readline { + Ok(line) => { + rl.add_history_entry(line.as_str())?; + process_input(line, program_state) + } + Err(ReadlineError::Eof) => Ok(LoopFlags { exit: true }), + Err(_) => Err("No input".into()), + } +} + +type CLIEditor = rustyline::Editor<(), FileHistory>; +fn init_readline() -> GenericResult { + let mut rl = rustyline::DefaultEditor::new()?; + rl.set_max_history_size(10)?; + Ok(rl) +} +struct ProgramState { + relay: io::Relay, +} + +fn init_state() -> GenericResult { + Ok(ProgramState { + relay: io::Relay::new()?, + }) } pub fn run_cli() { + let mut rl = init_readline().unwrap(); + let mut program_state = init_state().unwrap(); + 'cli_loop: loop { - match cli_loop() { + match cli_loop(&mut rl, &mut program_state) { Ok(loop_flags) => { if loop_flags.exit { println!("Leaving CLI"); @@ -28,77 +111,3 @@ pub fn run_cli() { } } } - -// fn process_command(command: String, program_state: &mut ProgramState) -> GenericResult { -// let command = command -// .strip_suffix('\n') -// .ok_or("Couldn't strip whitespace")?; -// let args = command.split(' ').collect::>(); - -// let main_command = *args.first().ok_or("No command found")?; -// match main_command { -// "water" => toggle_water(&args, program_state)?, -// "fan" => toggle_fan(program_state)?, -// "light" => toggle_light(program_state)?, -// "ana" => analogue_command(&args, program_state)?, -// "exit" => return Ok(true), -// _ => return Err(format!("Main command '{}' invalid", main_command).into()), -// } - -// Ok(false) -// } - -// fn analogue_command(args: &[&str], program_state: &mut ProgramState) -> GenericResult<()> { -// let pin = args.get(1).ok_or("No pin given")?; -// let pin: u8 = pin.parse()?; - -// let adc_target = rppal::i2c::I2c::new()?; -// let mut adc_target = -// Ads1x1x::new_ads1115(adc_target, ads1x1x::SlaveAddr::Alternative(false, false)); -// let channel: ChannelSelection = match pin { -// 0 => ChannelSelection::SingleA0, -// 1 => ChannelSelection::SingleA1, -// 2 => ChannelSelection::SingleA2, -// 3 => ChannelSelection::SingleA3, -// _ => ChannelSelection::SingleA0, -// }; -// loop { -// let value = block!(adc_target.read(channel)).map_err(|e| format!("{:?}", e))?; -// let value = value as f32; -// let voltage = value / i16::MAX as f32 * 2.048; -// let resistance = (3.3 / voltage - 1.) * 9_700.; -// println!("Value: {}", value); -// println!("resistance: {}", resistance); -// let temp = 1. / ((1. / 298.15) + (1. / 3950. * f32::ln(resistance / 10_000.))) - 273.15; -// println!("Temp: {}", temp); -// //println!("Value of pin {} is: {}", pin, value); -// thread::sleep(Duration::from_millis(2000)); -// } -// Ok(()) -// } - -// fn toggle_water(args: &[&str], program_state: &mut ProgramState) -> GenericResult<()> { -// if let Some(duration) = args.get(1) { -// let duration: u64 = duration.parse()?; -// println!("Turning on water for {} milliseconds", duration); -// program_state.water_pin.set_low(); -// thread::sleep(Duration::from_millis(duration)); -// program_state.water_pin.set_high(); -// } else { -// program_state.water_pin.toggle(); -// println!("Toggling water output"); -// } -// Ok(()) -// } - -// fn toggle_light(program_state: &mut ProgramState) -> GenericResult<()> { -// program_state.light_pin.toggle(); -// println!("Toggling light output"); -// Ok(()) -// } - -// fn toggle_fan(program_state: &mut ProgramState) -> GenericResult<()> { -// program_state.fan_pin.toggle(); -// println!("Toggling fan output"); -// Ok(()) -// }