use std::collections::HashMap; use std::ffi::OsStr; use std::fs; use std::io; use std::option::Option; use std::path::PathBuf; use imgui; use rlua::Lua; use serialport; use crate::rusbtmc; #[derive(Default, PartialEq)] pub struct Test { pub path: PathBuf, text: Option, } impl Test { pub fn new(path: PathBuf) -> Test { Test { path: path, text: None, } } pub fn read(&mut self) -> &str { if self.text.is_none() { self.text = Some(fs::read_to_string(&self.path).expect("Failed to read file")); } return self.text.as_ref().unwrap(); } // pub fn text(&self) -> &str { // return match &self.text { // Some(text) => text, // None => "", // } // } } #[derive(Default)] pub struct Bench { /* tests window */ lua: Lua, tests: Vec, tests_path: PathBuf, /* devices window */ selected_test: Option, selected_usb_dev: Option, usb_devices: HashMap>, selected_serial_dev: Option, // serial_devices: HashMap>, lua_console: Vec, instr_console: Vec, instr_input: imgui::ImString, } impl Bench { pub fn new() -> Bench { /* Prepare lua environment */ let lua = Lua::new(); let lua_setup = lua.context(|context| -> rlua::Result<()> { use rlua::{String, Variadic}; let globals = context.globals(); let ui_table = context.create_table()?; let ui_print = context.create_function(|_, _strings: Variadic| { // TODO // lua_console.push() Ok(()) })?; ui_table.set("print", ui_print)?; globals.set("ui", ui_table)?; Ok(()) }); if let Err(_) = lua_setup { // TODO: handle errors } let mut b = Bench { lua: lua, tests: Vec::new(), tests_path: PathBuf::from("lua"), selected_test: None, selected_usb_dev: None, usb_devices: HashMap::new(), selected_serial_dev: None, lua_console: Vec::new(), instr_console: Vec::new(), instr_input: imgui::ImString::with_capacity(256), // scope: None, }; // TODO: set graphically and use RV let _ = b.load_tests(); return b; } /// Loads lua tests from tests_path pub fn load_tests(&mut self) -> io::Result<()> { let entries: Result, io::Error> = fs::read_dir(&self.tests_path)? .into_iter() .map(|r| r.map(|f| f.path())) .collect(); match entries { Ok(files) => { self.tests = files .into_iter() .filter(|f| f.is_file()) .filter(|f| f.extension().unwrap_or(OsStr::new("")) == "lua") .map(|f| Test::new(f)) .collect(); return Ok(()); } Err(e) => return Err(e), } } fn get_selected_device<'a>( index: Option, list: &'a mut HashMap>, ) -> Option<&'a mut rusbtmc::Instrument> { let selected_index = match index { Some(i) => i, None => return None, }; for (index, (_name, dev)) in list.iter_mut().enumerate() { if matches!(selected_index, i if i == index) { return Some(dev); } } return None; } /// draws the entire interface pub fn draw(&mut self, _: &mut bool, ui: &mut imgui::Ui) { use imgui::*; /* Testbench window */ let tb_win = Window::new(im_str!("Testbench")) .size([400., 500.], Condition::Appearing) .position([20., 20.], Condition::Appearing); tb_win.build(&ui, || { for (index, test) in self.tests.iter().enumerate() { if let Some(test_name) = test.path.to_str() { let test_name: ImString = test_name.to_string().into(); let selected = matches!(self.selected_test, Some(i) if i == index); if Selectable::new(&test_name).selected(selected).build(ui) { self.selected_test = Some(index) } } } if let Some(index) = self.selected_test { ui.separator(); if let Some(test_name) = self.tests[index].path.to_str() { let imstr: ImString = format!("Selected Test: {}", test_name).to_string().into(); ui.text_wrapped(&imstr); ui.same_line(0.); if ui.button(im_str!("Run"), [0., 0.]) { self.run_test(index); } ui.same_line(0.); if ui.button(im_str!("Show"), [0., 0.]) { self.show_test(index); } } } ui.separator(); ChildWindow::new("lua console") .size([0., 0.]) .scrollable(true) .build(&ui, || { for line in &self.lua_console { ui.text(line); } }); }); /* devices window */ let dev_win = Window::new(im_str!("Devices")) .size([400., 500.], Condition::Appearing) .position([520., 20.], Condition::Appearing); dev_win.build(&ui, || { /* usb devices */ // buttons ui.text_wrapped(im_str!("USB Devices")); ui.same_line(0.); if ui.button(im_str!("Refresh"), [0., 0.]) { // TODO: do not remove open devices self.usb_devices.clear(); self.selected_usb_dev = None; if let Ok(instruments) = rusbtmc::instruments() { for instr in instruments { let desc = match instr.device.device_descriptor() { Ok(desc) => desc, Err(_) => { dbg!("failed to get descriptor"); continue; } }; let handle = match instr.device.open() { Ok(handle) => handle, Err(e) => { dbg!("failed to get handle", e); continue; } }; let prodstr = match handle.read_product_string_ascii(&desc) { Ok(s) => s, Err(_) => { dbg!("failed to read product string"); continue; } }; let dev_name: ImString = prodstr.into(); self.usb_devices.insert(dev_name, instr); } } } if let Some(instr) = Bench::get_selected_device( self.selected_usb_dev.clone(), &mut self.usb_devices ) { // TODO: handle errors ui.same_line(0.); if ui.button(im_str!("Open"), [0., 0.]) { let _ = dbg!(instr.open()); } ui.same_line(0.); if ui.button(im_str!("Close"), [0., 0.]) { let _ = dbg!(instr.close()); } ui.same_line(0.); if ui.button(im_str!("Pulse"), [0., 0.]) { let _ = dbg!(instr.pulse()); } } ui.separator(); // device list for (index, (name, dev)) in self.usb_devices.iter().enumerate() { let selected = matches!(self.selected_usb_dev, Some(i) if i == index); let mut label = name.clone(); if dev.is_connected() { label.push_str(" (connected)"); } if Selectable::new(&label).selected(selected).build(ui) { self.selected_usb_dev = Some(index); } } ui.separator(); // serial devices /* ui.text_wrapped(im_str!("Serial Devices")); ui.same_line(0.); if ui.button(im_str!("Refresh"), [0., 0.]) { if let Ok(ports) = serialport::available_ports() { for port in ports { } } } ui.separator(); */ // interactive device console ui.input_text(im_str!("Device"), &mut self.instr_input) .build(); ui.same_line(0.); if let Some(instr) = Bench::get_selected_device( self.selected_usb_dev, &mut self.usb_devices ) { if ui.button(im_str!("Send"), [0., 0.]) { let _ = dbg!(instr.write(self.instr_input.to_str())); self.instr_input.clear(); } } ChildWindow::new("instrument console") .size([0., 0.]) .scrollable(true) .build(&ui, || { for line in &self.instr_console { ui.text(line); } }); }); } fn run_test(&mut self, index: usize) { if let Some(test) = self.tests.get_mut(index) { let text = test.read(); let mut output = imgui::ImString::new(""); self.lua.context( |context| match context.load(text).eval::() { Ok(values) => { output.push_str( &values .iter() .map(|value| format!("{:?}", value)) .collect::>() .join("\t"), ); } Err(e) => { output.push_str(&e.to_string()); } }, ); self.lua_console.push(output); } } fn show_test(&self, _index: usize) { // TODO } }