summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs69
-rw-r--r--src/rusbtmc.rs626
-rw-r--r--src/support/mod.rs36
-rw-r--r--src/testbench.rs162
4 files changed, 785 insertions, 108 deletions
diff --git a/src/main.rs b/src/main.rs
index 9e7d545..f997c06 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,80 +1,13 @@
-use imgui::*;
-use rlua::{Lua, MultiValue};
-
+mod rusbtmc;
mod support;
mod testbench;
-#[derive(Default)]
-struct State {
- lua: Lua,
- console: Vec<ImString>,
- repl_input: ImString,
-}
-
-impl State {
- fn new() -> State {
- State {
- lua: Lua::new(),
- console: Vec::new(),
- repl_input: ImString::with_capacity(256),
- }
- }
-}
-
-fn draw_console(_run: &mut bool, ui: &mut Ui, state: &mut State) {
- let win = Window::new(im_str!("Lua Console"))
- .size([400., 500.], Condition::Appearing)
- .position([440., 20.], Condition::Appearing);
-
- win.build(&ui, || {
- ChildWindow::new("console")
- .size([0., 400.])
- .scrollable(true)
- .build(&ui, || {
- for line in &state.console {
- ui.text(line);
- }
- });
-
- ui.separator();
- ui.input_text(im_str!("Lua"), &mut state.repl_input).build();
- ui.same_line(0.);
- if ui.button(im_str!("Eval"), [0., 0.]) {
- let input = state.repl_input.to_str().clone();
- let mut new_text = String::new();
-
- state.lua.context(|ctx| {
- match ctx.load(input).eval::<MultiValue>() {
- Ok(values) => {
- new_text.push_str(
- &values
- .iter()
- .map(|value| format!("{:?}", value))
- .collect::<Vec<_>>()
- .join("\t"),
- );
- }
- Err(e) => {
- new_text.push_str(&e.to_string());
- }
- };
- });
-
- state.console.push(ImString::new(format!("> {}", input)));
- state.console.push(ImString::new(new_text));
- state.repl_input.clear();
- }
- });
-}
-
fn main() {
let system = support::init(file!());
- let mut state = State::new();
let mut bench = testbench::Bench::new();
system.main_loop(move |run, ui| {
// ui.show_demo_window(run);
bench.draw(run, ui);
- draw_console(run, ui, &mut state);
});
}
diff --git a/src/rusbtmc.rs b/src/rusbtmc.rs
new file mode 100644
index 0000000..994ae31
--- /dev/null
+++ b/src/rusbtmc.rs
@@ -0,0 +1,626 @@
+use rusb;
+
+use std::num::Wrapping;
+use std::time::Duration;
+use thiserror::Error;
+
+const USBTMC_BINTERFACE_CLASS: u8 = 0xfe;
+const USBTMC_BINTERFACE_SUBCLASS: u8 = 3;
+// const USBTMC_BINTERFACE_PROTOCOL: u8 = 0;
+const USB488_BINTERFACE_PROTOCOL: u8 = 1;
+
+/* control values */
+
+/// USBTMC bRequest Values
+#[repr(u8)]
+#[allow(dead_code)]
+enum RequestType {
+ InitiateAbortBulkOut = 1,
+ CheckAbortBulkOutStatus = 2,
+ InitiateAbortBunkIn = 3,
+ CheckAbortBulkInStatus = 4,
+ InitiateClear = 5,
+ CheckClearStatus = 6,
+ GetCapabilities = 7,
+ IndicatorPulse = 64,
+}
+
+/// USBTMC Status values
+#[repr(u8)]
+#[allow(dead_code)]
+enum Status {
+ Success = 0x01,
+ Pending = 0x02,
+ Failed = 0x80,
+ TransferNotInProgress = 0x81,
+ SplitNotInProgress = 0x82,
+ SplitInProgress = 0x83,
+}
+
+/* bulk values */
+
+enum Direction {
+ In,
+ Out,
+}
+
+enum MsgId {
+ DeviceDependent,
+ VendorSpecific,
+}
+
+/* instruments */
+
+/// USBTMC Capabilities
+#[derive(Clone, Debug)]
+pub struct Capabilities {
+ // version number (in BCD)
+ bcd_usbtmc: u16,
+ // interface capabilities
+ pulse: bool,
+ talk_only: bool,
+ listen_only: bool,
+ // device capabilities
+ term_char: bool,
+}
+
+#[derive(Error, Debug)]
+pub enum Error {
+ #[error("error on low level USB")]
+ Rusb(#[from] rusb::Error),
+ #[error("device not found")]
+ DeviceNotFound,
+ #[error("not a usbtmc device")]
+ NotUsbtmcDevice,
+ #[error("no usb handle")]
+ NoHandle,
+ #[error("not connected")]
+ NotConnected,
+ #[error("request failed")]
+ Request,
+ #[error("device does not support the request type")]
+ NotSupported,
+}
+
+/// Get a list of connected instruments
+pub fn instruments() -> Result<Vec<Instrument<rusb::GlobalContext>>, Error> {
+ let devices = match rusb::devices() {
+ Ok(devices) => devices,
+ Err(e) => return Err(Error::Rusb(e)),
+ };
+
+ let mut instruments = Vec::<Instrument<rusb::GlobalContext>>::new();
+
+ for dev in devices.iter() {
+ if let Ok(instr) = Instrument::new(dev) {
+ instruments.push(instr);
+ }
+ }
+
+ Ok(instruments)
+}
+
+/// 'High level' Instrument wrapper around rusb::Device
+pub struct Instrument<C: rusb::UsbContext> {
+ connected: bool,
+ pub device: rusb::Device<C>,
+ pub handle: Option<rusb::DeviceHandle<C>>,
+ capabilities: Option<Capabilities>,
+ has_kernel_driver: bool,
+ config_num: Option<u8>,
+ iface_num: Option<u8>,
+ ep_bulk_in: Option<u8>,
+ ep_bulk_out: Option<u8>,
+ ep_interrupt_in: Option<u8>,
+ btag: Wrapping<u8>,
+ timeout: Duration,
+}
+
+impl<C: rusb::UsbContext> Instrument<C> {
+ /// Creates and Instrument from a rusb Device
+ pub fn new(device: rusb::Device<C>) -> Result<Instrument<C>, Error> {
+ Ok(Instrument {
+ connected: false,
+ device: device,
+ handle: None,
+ capabilities: None,
+ has_kernel_driver: false,
+ config_num: None,
+ iface_num: None,
+ ep_bulk_in: None,
+ ep_bulk_out: None,
+ ep_interrupt_in: None,
+ btag: Wrapping(0_u8),
+ timeout: Duration::from_millis(20),
+ })
+ }
+
+ /// Creates an Instrument from the idVendor and idProduct numbers
+ pub fn from_vid_pid(
+ id_vendor: u16,
+ id_product: u16,
+ ) -> Result<Instrument<rusb::GlobalContext>, Error> {
+ let handle = match rusb::open_device_with_vid_pid(id_vendor, id_product) {
+ Some(handle) => handle,
+ None => return Err(Error::DeviceNotFound),
+ };
+
+ Ok(Instrument {
+ connected: false,
+ device: handle.device(),
+ handle: Some(handle),
+ capabilities: None,
+ has_kernel_driver: false,
+ config_num: None,
+ iface_num: None,
+ ep_bulk_in: None,
+ ep_bulk_out: None,
+ ep_interrupt_in: None,
+ btag: Wrapping(0_u8),
+ timeout: Duration::from_millis(20),
+ })
+ }
+
+ /// Opens (searches for) the USBTMC interface of the device
+ ///
+ /// It loops through the available usb interfaces and uses the first that
+ /// matches the usbtmc spec class and subclass
+ pub fn open(&mut self) -> Result<bool, Error> {
+ if self.connected {
+ dbg!("device already connected");
+ return Ok(self.connected);
+ }
+
+ self.handle = match self.device.open() {
+ Ok(handle) => Some(handle),
+ Err(e) => {
+ dbg!("failed to get device handle");
+ return Err(Error::Rusb(e));
+ }
+ };
+
+ let desc = self.device.device_descriptor()?;
+
+ 'outer: for cfg_desc in (0..desc.num_configurations())
+ .map(|num| self.device.config_descriptor(num))
+ .filter_map(|cfg_desc| cfg_desc.ok())
+ {
+ for iface_desc in cfg_desc
+ .interfaces()
+ .map(|iface| iface.descriptors())
+ .flatten()
+ {
+ // check if it is an USBTMC device
+ if iface_desc.class_code() == USBTMC_BINTERFACE_CLASS
+ && iface_desc.sub_class_code() == USBTMC_BINTERFACE_SUBCLASS
+ {
+ // check if it is has USB488
+ if iface_desc.protocol_code() == USB488_BINTERFACE_PROTOCOL {
+ // TODO
+ }
+
+ self.config_num = Some(cfg_desc.number());
+ self.iface_num = Some(iface_desc.interface_number());
+
+ // find endpoints
+ for ep_desc in iface_desc.endpoint_descriptors() {
+ match ep_desc.transfer_type() {
+ rusb::TransferType::Bulk => match ep_desc.direction() {
+ rusb::Direction::Out => {
+ self.ep_bulk_out = Some(ep_desc.address());
+ }
+ rusb::Direction::In => {
+ self.ep_bulk_in = Some(ep_desc.address());
+ }
+ },
+ rusb::TransferType::Interrupt => {
+ if ep_desc.direction() == rusb::Direction::In {
+ self.ep_interrupt_in = Some(ep_desc.address());
+ }
+ }
+ // not interested in other cases
+ _ => {}
+ }
+ }
+
+ // found first interface = happy
+ break 'outer;
+ }
+ }
+ }
+
+ // check for valid addresse
+ if self.ep_bulk_out.is_none() || self.ep_bulk_in.is_none() || self.ep_interrupt_in.is_none()
+ {
+ return Err(Error::NotUsbtmcDevice);
+ }
+
+ let handle = self.handle.as_mut().unwrap(); // is this safe?
+
+ // detach kernel driver if necessary
+ let iface_num = match self.iface_num {
+ Some(num) => num,
+ None => {
+ dbg!("no interface number");
+ return Err(Error::NotUsbtmcDevice);
+ }
+ };
+ self.has_kernel_driver = match handle.kernel_driver_active(iface_num) {
+ Ok(true) => {
+ if let Err(e) = handle.detach_kernel_driver(iface_num) {
+ dbg!("failed to detach kernel driver");
+ return Err(Error::Rusb(e));
+ }
+ true
+ }
+ _ => false,
+ };
+
+ // set configuration if not correct
+ let config_num = match self.config_num {
+ Some(num) => num,
+ None => {
+ dbg!("no configuration number");
+ return Err(Error::NotUsbtmcDevice);
+ }
+ };
+
+ let active_conf = handle.active_configuration();
+ if active_conf != Ok(config_num) {
+ if let Err(e) = handle.set_active_configuration(config_num) {
+ dbg!("failed to set configuration");
+ return Err(Error::Rusb(e));
+ }
+ dbg!(format!("set configuration to {}", config_num));
+ }
+
+ // claim the interface
+ if let Err(e) = handle.claim_interface(iface_num) {
+ dbg!("failed to claim interface");
+ return Err(Error::Rusb(e));
+ }
+
+ self.connected = true;
+
+ if let Err(e) = self.clear() {
+ return Err(e);
+ }
+
+ Ok(self.connected)
+ }
+
+ /// Closes the devices
+ pub fn close(&mut self) {
+ if !self.connected {
+ return;
+ }
+
+ if let Some(handle) = &mut self.handle {
+ if let Some(iface_num) = self.iface_num {
+ if let Err(e) = handle.release_interface(iface_num) {
+ dbg!(e);
+ dbg!("failed to release interface");
+ }
+
+ if self.has_kernel_driver {
+ if let Err(e) = handle.attach_kernel_driver(iface_num) {
+ dbg!(e);
+ dbg!("failed to attach kernel driver");
+ }
+ }
+ }
+ }
+
+ // TODO: reset configuration
+
+ self.connected = false;
+
+ self.ep_bulk_out = None;
+ self.ep_bulk_in = None;
+ self.ep_interrupt_in = None;
+ }
+
+ /// Sends a clear request and waits for it to complete
+ pub fn clear(&mut self) -> Result<(), Error> {
+ if !self.connected {
+ return Err(Error::NotConnected);
+ }
+
+ let handle = self.handle.as_mut().unwrap();
+ let index = self.iface_num.unwrap();
+
+ // response buffer
+ let buf: &mut [u8] = &mut [0];
+
+ // send clear request
+ if let Err(e) = handle.read_control(
+ rusb::request_type(
+ rusb::Direction::In,
+ rusb::RequestType::Class,
+ rusb::Recipient::Interface,
+ ),
+ RequestType::InitiateClear as u8,
+ 0x0000,
+ index.into(),
+ buf,
+ self.timeout,
+ ) {
+ dbg!("failed to send clear request");
+ return Err(Error::Rusb(e));
+ }
+
+ if buf[0] != Status::Success as u8 {
+ return Err(Error::Request);
+ }
+
+ // wait for completion of clear
+ loop {
+ // response buffer
+ let mut buf: &mut [u8] = &mut [0, 0];
+
+ // send check status
+ if let Err(e) = handle.read_control(
+ rusb::request_type(
+ rusb::Direction::In,
+ rusb::RequestType::Class,
+ rusb::Recipient::Interface,
+ ),
+ RequestType::CheckClearStatus as u8,
+ 0x0000,
+ index.into(),
+ &mut buf,
+ self.timeout,
+ ) {
+ return Err(Error::Rusb(e));
+ }
+
+ if buf[0] != Status::Pending as u8 {
+ break;
+ }
+
+ std::thread::sleep(Duration::from_millis(100));
+ }
+
+ // clear halt condition
+ let bulk_out_ep = self.ep_bulk_out.unwrap();
+ if let Err(e) = handle.clear_halt(bulk_out_ep) {
+ dbg!("failed to clear halt");
+ return Err(Error::Rusb(e));
+ }
+
+ Ok(())
+ }
+
+ /// Ask to the device with features are supported
+ pub fn get_capabilities(&mut self) -> Result<Capabilities, Error> {
+ if !self.connected {
+ return Err(Error::NotConnected);
+ }
+
+ let handle = self.handle.as_mut().unwrap();
+ let index = self.iface_num.unwrap();
+
+ // response buffer
+ let buf: &mut [u8; 0x18] = &mut [0; 0x18];
+
+ // send request
+ if let Err(e) = handle.read_control(
+ rusb::request_type(
+ rusb::Direction::In,
+ rusb::RequestType::Class,
+ rusb::Recipient::Interface,
+ ),
+ RequestType::GetCapabilities as u8,
+ 0x0000,
+ index.into(),
+ buf,
+ self.timeout,
+ ) {
+ dbg!("failed to send get capabilities request");
+ return Err(Error::Rusb(e));
+ }
+
+ if buf[0] != Status::Success as u8 {
+ return Err(Error::Request);
+ }
+
+ // TODO: USB448 subclass
+
+ let bcd_usbtmc: u16 = (u16::from(buf[3]) << 8) + u16::from(buf[2]);
+ let capabilities = Capabilities {
+ bcd_usbtmc: bcd_usbtmc,
+ pulse: (buf[4] & 4) != 0,
+ talk_only: (buf[4] & 2) != 0,
+ listen_only: (buf[4] & 1) != 0,
+ term_char: (buf[5] & 1) != 0,
+ };
+
+ self.capabilities = Some(capabilities.clone());
+ Ok(capabilities)
+ }
+
+ pub fn pulse(&mut self) -> Result<(), Error> {
+ if !self.connected {
+ return Err(Error::NotConnected);
+ }
+
+ let can_pulse = match &self.capabilities {
+ Some(c) => c.pulse,
+ None => {
+ let c = self.get_capabilities()?;
+ c.pulse
+ }
+ };
+
+ if !can_pulse {
+ return Err(Error::NotSupported);
+ }
+
+ let handle = self.handle.as_ref().unwrap();
+ let index = self.iface_num.unwrap();
+
+ let buf: &mut [u8] = &mut [0];
+
+ // send request
+ if let Err(_) = handle.read_control(
+ rusb::request_type(
+ rusb::Direction::In,
+ rusb::RequestType::Class,
+ rusb::Recipient::Interface,
+ ),
+ RequestType::GetCapabilities as u8,
+ 0x0000,
+ index.into(),
+ buf,
+ self.timeout,
+ ) {
+ return Err(Error::NotSupported);
+ }
+
+ Ok(())
+ }
+
+ /// Write binary data to the instrument
+ pub fn write_raw(&mut self, data: &[u8]) -> Result<usize, Error> {
+ if !self.connected {
+ return Err(Error::NotConnected);
+ }
+
+ let mut sent_bytes = 0;
+
+ let handle = self.handle.as_ref().unwrap();
+ let endpoint = self.ep_bulk_out.unwrap();
+
+ const HEADER_SIZE: usize = 12;
+ const TRANSFER_SIZE: usize = 1024 * 1024;
+ const PACKET_SIZE: usize = HEADER_SIZE + TRANSFER_SIZE;
+
+ // reset btag counter
+ self.btag = Wrapping(1_u8);
+
+ // send chunks
+ let iter = data.chunks_exact(TRANSFER_SIZE);
+ let last_data = iter.remainder();
+
+ for chunk in iter {
+ let header = make_bulk_header(
+ MsgId::DeviceDependent,
+ Direction::Out,
+ self.btag.0,
+ TRANSFER_SIZE as u32,
+ false,
+ );
+
+ let mut packet: [u8; PACKET_SIZE] = [0; PACKET_SIZE];
+ packet[..12].clone_from_slice(&header);
+ packet[13..].clone_from_slice(chunk);
+
+ sent_bytes += match handle.write_bulk(endpoint, &packet, self.timeout) {
+ Ok(sent) => sent,
+ Err(e) => {
+ dbg!("failed to send chunk during bulk out");
+ self.abort_bulk_out();
+ return Err(Error::Rusb(e));
+ }
+ };
+
+ // increment btag
+ if self.btag.0 == 0 {
+ self.btag = Wrapping(1_u8);
+ } else {
+ self.btag += Wrapping(1_u8);
+ }
+ }
+
+ // send remainder
+ let padding = (4 - (last_data.len() % 4)) % 4;
+ let last_data_size: usize = last_data.len() + padding;
+
+ let header = make_bulk_header(
+ MsgId::DeviceDependent,
+ Direction::Out,
+ self.btag.0,
+ last_data_size as u32,
+ true,
+ );
+
+ // TODO: pad and send last byte
+
+ Ok(sent_bytes)
+ }
+
+ fn abort_bulk_out(&mut self) -> Result<(), Error> {
+ if !self.connected {
+ return Err(Error::NotConnected);
+ }
+
+ let handle = self.handle.as_ref().unwrap();
+ let endpoint = self.ep_bulk_out.unwrap();
+
+ let buf: &[u8; 2] = &[0, 0];
+ if let Err(e) = handle.write_control(
+ rusb::request_type(
+ rusb::Direction::In,
+ rusb::RequestType::Class,
+ rusb::Recipient::Endpoint,
+ ),
+ RequestType::InitiateAbortBulkOut as u8,
+ u16::from(self.btag.0),
+ endpoint.into(),
+ buf,
+ self.timeout,
+ ) {
+ dbg!("failed to initiate abort bulk out");
+ return Err(Error::Rusb(e));
+ }
+
+ Ok(())
+ }
+}
+
+/// helper function to create bulk headers
+fn make_bulk_header(
+ msgid: MsgId,
+ direction: Direction,
+ btag: u8,
+ transfer_size: u32,
+ is_last: bool,
+) -> [u8; 12] {
+ // table 2 in spec
+ let msgid_nr: u8 = match msgid {
+ MsgId::DeviceDependent => match direction {
+ Direction::Out => 1,
+ Direction::In => 2,
+ },
+ MsgId::VendorSpecific => match direction {
+ Direction::Out => 126,
+ Direction::In => 127,
+ },
+ };
+
+ let ts_bytes = transfer_size.to_le_bytes();
+ let header: [u8; 12] = [
+ // table 1 in spec
+ msgid_nr,
+ btag,
+ !btag,
+ 0x00,
+ // table 3 in spec
+ // size of the transfer, without header
+ ts_bytes[0],
+ ts_bytes[1],
+ ts_bytes[2],
+ ts_bytes[4],
+ // whether this is the last chunk
+ match is_last {
+ true => 0x01,
+ false => 0x00,
+ },
+ // reserved, must be zeroes
+ 0x00,
+ 0x00,
+ 0x00,
+ ];
+
+ header
+}
diff --git a/src/support/mod.rs b/src/support/mod.rs
index af894ba..aa27f78 100644
--- a/src/support/mod.rs
+++ b/src/support/mod.rs
@@ -50,25 +50,25 @@ pub fn init(title: &str) -> System {
}
let hidpi_factor = platform.hidpi_factor();
- let font_size = (13.0 * hidpi_factor) as f32;
+ let font_size = (15.0 * hidpi_factor) as f32;
- // imgui.fonts().add_font(&[
- // FontSource::DefaultFontData {
- // config: Some(FontConfig {
- // size_pixels: font_size,
- // ..FontConfig::default()
- // }),
- // },
- // FontSource::TtfData {
- // data: include_bytes!("../../../resources/mplus-1p-regular.ttf"),
- // size_pixels: font_size,
- // config: Some(FontConfig {
- // rasterizer_multiply: 1.75,
- // glyph_ranges: FontGlyphRanges::japanese(),
- // ..FontConfig::default()
- // }),
- // },
- // ]);
+ imgui.fonts().add_font(&[
+ // FontSource::DefaultFontData {
+ // config: Some(FontConfig {
+ // size_pixels: font_size,
+ // ..FontConfig::default()
+ // }),
+ // },
+ FontSource::TtfData {
+ data: include_bytes!("../../res/Hack-Regular.ttf"),
+ size_pixels: font_size,
+ config: Some(FontConfig {
+ rasterizer_multiply: 1.75,
+ glyph_ranges: FontGlyphRanges::default(),
+ ..FontConfig::default()
+ }),
+ },
+ ]);
imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32;
diff --git a/src/testbench.rs b/src/testbench.rs
index 6d9ea16..5dd18fd 100644
--- a/src/testbench.rs
+++ b/src/testbench.rs
@@ -1,11 +1,15 @@
+use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::option::Option;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
use imgui;
use rlua::Lua;
+// use serialport;
+
+use crate::rusbtmc;
#[derive(Default, PartialEq)]
pub struct Test {
@@ -29,21 +33,28 @@ impl Test {
return self.text.as_ref().unwrap();
}
- pub fn text(&self) -> &str {
- return match &self.text {
- Some(text) => text,
- None => "",
- }
- }
+ // pub fn text(&self) -> &str {
+ // return match &self.text {
+ // Some(text) => text,
+ // None => "",
+ // }
+ // }
}
#[derive(Default)]
pub struct Bench {
+ // tests window
lua: Lua,
tests: Vec<Test>,
tests_path: PathBuf,
selected_test: Option<usize>,
- console: Vec<imgui::ImString>,
+ selected_usb_dev: Option<usize>,
+ usb_devices: HashMap<imgui::ImString, rusbtmc::Instrument<rusb::GlobalContext>>,
+ selected_serial_dev: Option<usize>,
+ // serial_devices: Vec<?>
+ lua_console: Vec<imgui::ImString>,
+ instr_console: Vec<imgui::ImString>,
+ instr_input: imgui::ImString,
}
impl Bench {
@@ -56,7 +67,9 @@ impl Bench {
let globals = context.globals();
let ui_table = context.create_table()?;
- let ui_print = context.create_function(|_, strings: Variadic<String>| {
+ let ui_print = context.create_function(|_, _strings: Variadic<String>| {
+ // TODO
+ // lua_console.push()
Ok(())
})?;
@@ -66,25 +79,31 @@ impl Bench {
Ok(())
});
- // TODO: handle errors
- // if let Err(e) = lua_setup {}
+ 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,
- console: Vec::new(),
+ 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
- b.load_tests();
+ let _ = b.load_tests();
return b;
}
- pub fn load_tests(&mut self) -> io::Result<()>
- {
+ pub fn load_tests(&mut self) -> io::Result<()> {
let entries: Result<Vec<PathBuf>, io::Error> = fs::read_dir(&self.tests_path)?
.into_iter()
.map(|r| r.map(|f| f.path()))
@@ -108,11 +127,12 @@ impl Bench {
pub fn draw(&mut self, _: &mut bool, ui: &mut imgui::Ui) {
use imgui::*;
- let win = Window::new(im_str!("Testbench"))
+ /* Testbench window */
+ let tb_win = Window::new(im_str!("Testbench"))
.size([400., 500.], Condition::Appearing)
.position([20., 20.], Condition::Appearing);
- win.build(&ui, || {
+ 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();
@@ -124,8 +144,8 @@ impl Bench {
}
}
- ui.separator();
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();
@@ -143,11 +163,107 @@ impl Bench {
}
ui.separator();
- ChildWindow::new("console")
+ 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
+ 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(_) => {
+ // dbg!("failed to get handle");
+ 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 self.selected_usb_dev.is_some() {
+ // search dev
+ let mut instr = None;
+ for (index, (_name, dev)) in self.usb_devices.iter_mut().enumerate() {
+ if matches!(self.selected_usb_dev, Some(i) if i == index) {
+ instr = Some(dev);
+ break;
+ }
+ }
+
+ if let Some(instr) = instr {
+ 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());
+ }
+ }
+ }
+
+ for (index, (name, _dev)) in self.usb_devices.iter().enumerate() {
+ let selected = matches!(self.selected_usb_dev, Some(i) if i == index);
+
+ if Selectable::new(&name).selected(selected).build(ui) {
+ self.selected_usb_dev = Some(index);
+ }
+
+ ui.same_line(0.);
+ }
+
+ ui.separator();
+ ui.input_text(im_str!("Device"), &mut self.instr_input).build();
+ ChildWindow::new("instrument console")
.size([0., 0.])
.scrollable(true)
.build(&ui, || {
- for line in &self.console {
+ for line in &self.instr_console {
ui.text(line);
}
});
@@ -176,9 +292,11 @@ impl Bench {
},
);
- self.console.push(output);
+ self.lua_console.push(output);
}
}
- fn show_test(&self, index: usize) {}
+ fn show_test(&self, _index: usize) {
+ // TODO
+ }
}