diff --git a/Cargo.lock b/Cargo.lock index f61b6a0..b91d292 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -302,6 +302,12 @@ dependencies = [ "log", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "heck" version = "0.4.1" @@ -612,12 +618,13 @@ dependencies = [ [[package]] name = "seymour_life" -version = "2.3.2" +version = "2.3.3" dependencies = [ "chrono", "clap", "derivative", "fern", + "glob", "log", "once_cell", "rppal", diff --git a/Cargo.toml b/Cargo.toml index 81c36cf..02a5089 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "seymour_life" -version = "2.3.2" +version = "2.3.3" edition = "2021" #This feature is currently limited to nightly versions of cargo. #forced-target="aarch64-unknown-linux-musl" @@ -20,6 +20,7 @@ once_cell = "1.17.1" derivative = "2.2.0" time = "0.2.23" clap = { version = "4.3.2", features = ["derive"] } +glob = "0.3.1" [dev-dependencies] time = "0.2.23" diff --git a/src/device.rs b/src/device.rs index 975f4f8..0f48f61 100755 --- a/src/device.rs +++ b/src/device.rs @@ -16,7 +16,8 @@ pub enum State{ LoginPrompt, DebugMenu, LifecycleMenu, - BrightnessMenu + BrightnessMenu, + ShellPrompt } #[derive(Debug)] @@ -112,6 +113,11 @@ impl Device{ usb_port.write_to_device(Command::Newline); _ = usb_port.read_from_device(None); initial_state = State::LoginPrompt; + }, + Response::UBoot=>{ + usb_port.write_to_device(Command::Boot); + while usb_port.read_from_device(None) != Response::LoginPrompt {} + initial_state = State::LoginPrompt; }, //Response::Empty parsing here is potentially in bad faith Response::Other | Response::Empty | Response::ShellPrompt | Response::FailedDebugMenu | Response::DebugInit | @@ -126,18 +132,34 @@ impl Device{ initial_state = State::LoginPrompt; }, Response::ShellPrompt => { - usb_port.write_to_device(Command::Shutdown); - while usb_port.read_from_device(None) != Response::LoginPrompt {} - initial_state = State::LoginPrompt; + initial_state = State::ShellPrompt; + }, + Response::DebugMenu => { + usb_port.write_to_device(Command::Newline); + match usb_port.read_from_device(None) { + Response::DebugMenu | Response::ShellPrompt => { + initial_state = State::ShellPrompt; + }, + _ => { + log::error!("Unknown state for TTY {:?}!!! Consult logs immediately.",usb_port); + log::debug!("Last known state: DebugMenu."); + log::debug!("Assumed but incorrect current state: successfully exited debug menu"); + return Err("Failed TTY init. Unknown state, cannot trust.".to_string()); + } + }; }, _ => { log::error!("Unknown state for TTY {:?}!!! Consult logs immediately.",usb_port); + log::debug!("Last known state: DebugMenu."); + log::debug!("Assumed but incorrect current state: attempted to exit debug menu"); return Err("Failed TTY init. Unknown state, cannot trust.".to_string()); } }; }, + //Serial response shouldn't exist, emptynewline is already filtered in main Response::Serial(_) | Response::EmptyNewline => { log::error!("Unknown state for TTY {:?}!!! Consult logs immediately.",usb_port); + log::debug!("How did I get here???"); return Err("Failed TTY init. Unknown state, cannot trust.".to_string()); }, }; @@ -196,16 +218,18 @@ impl Device{ Response::PreShellPrompt | Response::Empty | Response::ShuttingDown | Response::DebugInit | Response::EmptyNewline | Response::Rebooting => {}, Response::PasswordPrompt => {self.usb_tty.write_to_device(Command::Newline);}, - Response::ShellPrompt => break, + Response::FailedDebugMenu | Response::ShellPrompt => break, _ => { log::error!("Unexpected response from device {}!",self.serial); - log::debug!("brightness menu, catch-all, first loop, {}, {:?}",self.serial,self.usb_tty); + log::debug!("brightness menu, catch-all, login loop, {}, {:?}",self.serial,self.usb_tty); log::error!("Unsure how to continue. Expect data from device {} to be erratic until next cycle.",self.serial); //break; }, }; }; - //_ = self.usb_tty.read_from_device(None); + self.current_state = State::ShellPrompt; + }, + State::ShellPrompt => { self.usb_tty.write_to_device(Command::DebugMenu); loop { match self.usb_tty.read_from_device(None) { @@ -219,14 +243,11 @@ impl Device{ Response::DebugMenu => break, Response::FailedDebugMenu => { - while self.usb_tty.read_from_device(None) != Response::LoginPrompt {}; - self.usb_tty.write_to_device(Command::Login); - while self.usb_tty.read_from_device(None) != Response::ShellPrompt {}; self.usb_tty.write_to_device(Command::DebugMenu); }, _ => { log::error!("Unexpected response from device {}!", self.serial); - log::debug!("brightness menu, catch-all, second loop, {}, {:?}",self.serial,self.usb_tty); + log::debug!("brightness menu, catch-all, shell prompt loop, {}, {:?}",self.serial,self.usb_tty); log::error!("Unsure how to continue. Expect data from device {} to be erratic until next cycle.",self.serial); //break; }, @@ -267,7 +288,7 @@ impl Device{ Response::PreShellPrompt | Response::Empty | Response::ShuttingDown | Response::DebugInit | Response::EmptyNewline | Response::Rebooting => {}, Response::PasswordPrompt => {self.usb_tty.write_to_device(Command::Newline);}, - Response::ShellPrompt => break, + Response::FailedDebugMenu | Response::ShellPrompt => break, _ => { log::error!("Unexpected response from device {}!",self.serial); log::debug!("lifecycle menu, catch-all, first loop, {}, {:?}",self.serial,self.usb_tty); @@ -276,6 +297,9 @@ impl Device{ }, }; }; + self.current_state = State::ShellPrompt; + }, + State::ShellPrompt => { self.usb_tty.write_to_device(Command::DebugMenu); loop { let read_in = self.usb_tty.read_from_device(None); @@ -283,16 +307,11 @@ impl Device{ Response::PreShellPrompt | Response::Empty | Response::ShuttingDown | Response::DebugInit | Response::EmptyNewline | Response::Rebooting => {}, Response::LoginPrompt => { - self.usb_tty.write_to_device(Command::Login); - while self.usb_tty.read_from_device(None) != Response::ShellPrompt {}; self.usb_tty.write_to_device(Command::DebugMenu); }, Response::DebugMenu => break, Response::FailedDebugMenu => { - while self.usb_tty.read_from_device(None) != Response::LoginPrompt {}; - self.usb_tty.write_to_device(Command::Login); - while self.usb_tty.read_from_device(None) != Response::ShellPrompt {}; self.usb_tty.write_to_device(Command::DebugMenu); }, _ => { @@ -353,39 +372,56 @@ impl Device{ return true } pub fn auto_set_serial(&mut self) -> bool{ - self.reboot(); - self.usb_tty.write_to_device(Command::Login); - while self.usb_tty.read_from_device(None) != Response::ShellPrompt {} - self.usb_tty.write_to_device(Command::GetSerial); - loop{ - let return_value = self.usb_tty.read_from_device(None); - match return_value{ - Response::Serial(Some(contains_serial)) =>{ - for line in contains_serial.split("\n").collect::>(){ - if !line.contains(':') { continue; } - let (section,value) = line.split_once(':').unwrap(); - if section.contains(SERIAL_HEADER){ - self.serial = value.trim().replace("\"",""); + loop { + match self.current_state { + State::LoginPrompt => { + self.usb_tty.write_to_device(Command::Login); + while self.usb_tty.read_from_device(None) != Response::ShellPrompt {}; + self.current_state = State::ShellPrompt; + }, + State::Shutdown => { + while self.usb_tty.read_from_device(None) != Response::LoginPrompt{}; + self.current_state = State::LoginPrompt; + }, + State::DebugMenu | State::LifecycleMenu | State::BrightnessMenu => { + self.usb_tty.write_to_device(Command::Quit); + _ = self.usb_tty.read_from_device(None); + self.current_state = State::ShellPrompt; + }, + State::ShellPrompt => { + self.usb_tty.write_to_device(Command::GetSerial); + loop{ + let return_value = self.usb_tty.read_from_device(None); + match return_value{ + Response::Serial(Some(contains_serial)) =>{ + for line in contains_serial.split("\n").collect::>(){ + if !line.contains(':') { continue; } + let (section,value) = line.split_once(':').unwrap(); + if section.contains(SERIAL_HEADER){ + self.serial = value.trim().replace("\"",""); + } + } + log::info!("Serial found for device {}",self.serial); + break; + }, + Response::DebugInit | Response::Empty | Response::EmptyNewline => { continue; } + _ => { + log::error!("Bad value: {:?}",return_value); + return false + }, } } - log::info!("Serial found for device {}",self.serial); - break; - }, - Response::DebugInit | Response::Empty | Response::EmptyNewline => { continue; } - _ => { - log::error!("Bad value: {:?}",return_value); - return false + self.usb_tty.write_to_device(Command::DebugMenu); + while self.usb_tty.read_from_device(None) != Response::DebugMenu {} + self.current_state = State::DebugMenu; + self.load_values(); + self.save_values(); + return true }, } } - self.usb_tty.write_to_device(Command::DebugMenu); - while self.usb_tty.read_from_device(None) != Response::DebugMenu {} - self.current_state = State::DebugMenu; - self.reboot(); - self.load_values(); - self.save_values(); - return true } + pub fn manual_set_serial(&mut self, serial:&str) -> &mut Self{ self.serial = serial.to_string(); self.load_values(); @@ -529,6 +565,7 @@ impl Device{ } pub fn reboot(&mut self) -> () { self.usb_tty.write_to_device(Command::Quit); + self.usb_tty.write_to_device(Command::Reboot); let mut successful_reboot:bool = false; //let mut exited_menu:bool = false; loop{ diff --git a/src/main.rs b/src/main.rs index c307afc..2b5522a 100755 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ struct Args{ } -const VERSION:&str="2.3.2"; +const VERSION:&str="2.3.3"; const DEBUG_ITERATION_COUNT:u64=50000; fn int_input_filtering(prompt:Option<&str>) -> u64{ @@ -58,7 +58,7 @@ fn input_filtering(prompt:Option<&str>) -> String{ log::debug!("{}:{}",internal_prompt,user_input); return user_input; } - +//Path::new(&&str).is_dir() -> bool fn main(){ let args = Args::parse(); setup_logs(&args.debug); @@ -80,106 +80,130 @@ fn main(){ log::info!("Testing all available USB ports for connected devices. This may take several minutes, and devices may reboot several times."); let gpio = &mut GpioPins::new(); - match std::fs::read_dir("/dev/serial/by-path"){ - Ok(available_ttys)=>{ - let mut possible_devices:Vec> = Vec::new(); - let mut tty_test_threads:Vec>> = Vec::new(); - for possible_tty in available_ttys.into_iter(){ - tty_test_threads.push( - thread::spawn(move ||{ - let tty_ref = possible_tty.as_ref(); - match tty_ref{ - Ok(tty_real_ref)=>{ - let tty_path = tty_real_ref.path(); - let tty_name = tty_path.to_string_lossy(); - log::debug!("Testing port {}",&tty_name); - let possible_port = TTY::new(&tty_name); - match possible_port{ - Some(mut port) =>{ - port.write_to_device(tty::Command::Newline); - let response = port.read_from_device(Some(":")); - if response != Response::Empty{ - log::debug!("{} is valid port!",tty_name); - let new_device = Device::new(port,Some(response)); - match new_device{ - Ok(mut device) => { - device.darken_screen(); - if !args.manual { - device.auto_set_serial(); - } - Some(device) - }, - Err(_) => None - } - } - else { None } - }, - None=>{None} - } - }, - Err(error)=>{ - log::debug!("{}",error); - None + let mut available_ttys:Vec> = Vec::new(); + for entry in glob::glob("/dev/serial/*").expect("Failed to read glob pattern"){ + match entry{ + Ok(real_path) =>{ + match fs::read_dir::<&Path>(real_path.as_ref()){ + Ok(possible_ttys) =>{ + possible_ttys.into_iter().for_each(|tty| { + if let Ok(single_tty) = tty { + available_ttys.push(single_tty.path().into()); + } + }); + break; + } + Err(error) =>{ + log::error!("Invalid permissions to /dev directory... did you run with sudo?"); + log::error!("{}",error); + return; + } + } + } + Err(error) =>{ + log::error!("{}",error); + } + } + } + if available_ttys.is_empty(){ + for entry in glob::glob("/dev/ttyUSB*").expect("Unable to read glob"){ + match entry{ + Ok(possible_tty) => available_ttys.push(Path::new(&possible_tty).into()), + Err(error) => { + log::error!("Invalid permissions to /dev directory... did you run with sudo?"); + log::error!("{}",error); + return; + } + }; + } + } + + if available_ttys.is_empty(){ + log::error!("No serial devices detected! Please ensure all connections."); + return; + } + let mut possible_devices:Vec> = Vec::new(); + let mut tty_test_threads:Vec>> = Vec::new(); + for possible_tty in available_ttys.into_iter(){ + tty_test_threads.push( + thread::spawn(move ||{ + let tty_name = possible_tty.to_string_lossy(); + log::debug!("Testing port {}",&tty_name); + let possible_port = TTY::new(&tty_name); + match possible_port{ + Some(mut port) =>{ + port.write_to_device(tty::Command::Newline); + let response = port.read_from_device(Some(":")); + if response != Response::Empty{ + log::debug!("{} is valid port!",tty_name); + let new_device = Device::new(port,Some(response)); + match new_device{ + Ok(mut device) => { + device.darken_screen(); + if !args.manual { + device.auto_set_serial(); + } + Some(device) + }, + Err(_) => None } } - })); - } - for thread in tty_test_threads{ - let output = thread.join().unwrap_or_else(|x|{log::trace!("{:?}",x); None}); - possible_devices.push(output); - } - - let mut serials_set:bool = true; - let mut devices:Vec = Vec::new(); - for possible_device in possible_devices.into_iter(){ - if let Some(device) = possible_device{ - if device.get_serial().eq("uninitialised"){ - serials_set = false; - } - devices.push(device); + else { None } + }, + None=>{None} } - } + })); + } + for thread in tty_test_threads{ + let output = thread.join().unwrap_or_else(|x|{log::trace!("{:?}",x); None}); + possible_devices.push(output); + } - log::info!("--------------------------------------"); - log::info!("Number of devices detected: {}",devices.len()); - log::info!("--------------------------------------\n\n"); - - log::info!("Setting up probe wells for all devices. This may take several minutes..."); - for device in devices.iter_mut(){ - if !serials_set || args.manual { - device.brighten_screen(); - device.manual_set_serial(&input_filtering(Some("Enter the serial of the device with the bright screen: ")).to_string()); - device.darken_screen(); - } - log::info!("Checking probe well of device {}",device.get_serial()); - log::debug!("Number of unassigned addresses: {}",gpio.get_unassigned_addresses().len()); - if !find_gpio(device, gpio){ - device.set_pin_address(21); - log::error!("Unable to find probe-well for device {}. Please ensure that the probe well is installed properly, and the calibration key is plugged in.",device.get_serial()); - device.brighten_screen(); - panic!(); - } - } - - let mut iteration_threads = Vec::new(); - while let Some(mut device) = devices.pop(){ - iteration_threads.push(thread::spawn(move||{ - device.init_temp_count(); - for i in 1..=iteration_count{ - log::info!("Starting iteration {} of {} for device {}...", - i,iteration_count,device.get_serial()); - device.test_cycle(None); - } - })); - } - for thread in iteration_threads{ - thread.join().unwrap(); + let mut serials_set:bool = true; + let mut devices:Vec = Vec::new(); + for possible_device in possible_devices.into_iter(){ + if let Some(device) = possible_device{ + if device.get_serial().eq("uninitialised"){ + serials_set = false; } + devices.push(device); } - Err(_)=>{ - log::error!("Invalid serial location! Please make sure that /dev/serial/by-path exists."); - break; + } + + log::info!("--------------------------------------"); + log::info!("Number of devices detected: {}",devices.len()); + log::info!("--------------------------------------\n\n"); + + log::info!("Setting up probe wells for all devices. This may take several minutes..."); + for device in devices.iter_mut(){ + if !serials_set || args.manual { + device.brighten_screen(); + device.manual_set_serial(&input_filtering(Some("Enter the serial of the device with the bright screen: ")).to_string()); + device.darken_screen(); } + log::info!("Checking probe well of device {}",device.get_serial()); + log::debug!("Number of unassigned addresses: {}",gpio.get_unassigned_addresses().len()); + if !find_gpio(device, gpio){ + device.set_pin_address(21); + log::error!("Unable to find probe-well for device {}. Please ensure that the probe well is installed properly, and the calibration key is plugged in.",device.get_serial()); + device.brighten_screen(); + panic!(); + } + } + + let mut iteration_threads = Vec::new(); + while let Some(mut device) = devices.pop(){ + iteration_threads.push(thread::spawn(move||{ + device.init_temp_count(); + for i in 1..=iteration_count{ + log::info!("Starting iteration {} of {} for device {}...", + i,iteration_count,device.get_serial()); + device.test_cycle(None); + } + })); + } + for thread in iteration_threads{ + thread.join().unwrap(); } if input_filtering(Some("Would you like to run the tests again? (y/N): ")).to_string().contains("y") {} else { break; } diff --git a/src/tty.rs b/src/tty.rs index c1e8afe..876532b 100755 --- a/src/tty.rs +++ b/src/tty.rs @@ -26,8 +26,9 @@ pub enum Command{ Login, DebugMenu, Newline, - Shutdown, + Reboot, GetSerial, + Boot, } #[derive(Clone,Eq,Derivative,Debug)] @@ -49,6 +50,7 @@ pub enum Response{ EmptyNewline, DebugInit, Serial(Option), + UBoot, } @@ -64,26 +66,34 @@ const COMMAND_MAP:Lazy> = Lazy::new(||HashMap::from([ (Command::UpMenuLevel, "\\"), (Command::Login,"root\n"), (Command::RedrawMenu,"?"), - (Command::DebugMenu," python3 -m debugmenu; shutdown -r now\n"), + (Command::DebugMenu,"python3 -m debugmenu\n"), (Command::Newline,"\n"), - (Command::Shutdown,"shutdown -r now\n"), + (Command::Reboot,"shutdown -r now\n"), + (Command::Boot,"boot\n"), (Command::GetSerial,"echo 'y1q' | python3 -m debugmenu\n"), ])); -const RESPONSES:[(&str,Response);13] = [ +const COMMAND_RESPONSES: [&str;3] = [ + "python3 -m debugmenu", + "q", + "root", +]; + +const RESPONSES:[(&str,Response);14] = [ + ("uboot=>",Response::UBoot), ("Last login:",Response::PreShellPrompt), ("reboot: Restarting",Response::Rebooting), ("command not found",Response::FailedDebugMenu), ("login:",Response::LoginPrompt), ("Password:",Response::PasswordPrompt), ("DtCtrlCfgDeviceSerialNum",Response::Serial(None)), - ("root@",Response::ShellPrompt), - ("EXIT Debug menu",Response::ShuttingDown), ("Check NIBP In Progress: True",Response::BPOn), ("Check NIBP In Progress: False",Response::BPOff), ("SureTemp Probe Pulls:",Response::TempCount(None)), (">",Response::DebugMenu), ("Loading App-Framework",Response::DebugInit), + ("root@",Response::ShellPrompt), + ("EXIT Debug menu",Response::ShuttingDown), ]; pub struct TTY{ @@ -141,7 +151,12 @@ impl TTY{ let read_line:String = String::from_utf8_lossy(read_buffer.as_slice()).to_string(); if read_line.eq("\r\n") { return Response::EmptyNewline; - } + } + for command in COMMAND_RESPONSES{ + if read_line.trim().eq(command.trim()){ + return self.read_from_device(None); + } + }; for (string,enum_value) in RESPONSES{ if read_line.contains(string){ if(enum_value == Response::BPOn) || (enum_value == Response::BPOff) {