summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNao Pross <np@0hm.ch>2021-04-18 17:12:20 +0200
committerNao Pross <np@0hm.ch>2021-04-18 17:12:20 +0200
commit9bc323efb9b37887e1c43f0d3ac1fe70230eefac (patch)
treea6d2298f16fa2fa0f662524045d0ee53295dd1a8
parentDelete demo lua console, start usbtmc driver (diff)
downloadtestbench-ui-9bc323efb9b37887e1c43f0d3ac1fe70230eefac.tar.gz
testbench-ui-9bc323efb9b37887e1c43f0d3ac1fe70230eefac.zip
Finish usbtmc writeHEADmaster
-rw-r--r--src/rusbtmc.rs81
-rw-r--r--src/testbench.rs114
2 files changed, 157 insertions, 38 deletions
diff --git a/src/rusbtmc.rs b/src/rusbtmc.rs
index 994ae31..124b9ff 100644
--- a/src/rusbtmc.rs
+++ b/src/rusbtmc.rs
@@ -80,6 +80,10 @@ pub enum Error {
Request,
#[error("device does not support the request type")]
NotSupported,
+ #[error("decoding error (utf-8)")]
+ Decoding(#[from] std::string::FromUtf8Error),
+ #[error("not implemented")]
+ NotImplemented,
}
/// Get a list of connected instruments
@@ -89,6 +93,7 @@ pub fn instruments() -> Result<Vec<Instrument<rusb::GlobalContext>>, Error> {
Err(e) => return Err(Error::Rusb(e)),
};
+
let mut instruments = Vec::<Instrument<rusb::GlobalContext>>::new();
for dev in devices.iter() {
@@ -103,16 +108,22 @@ pub fn instruments() -> Result<Vec<Instrument<rusb::GlobalContext>>, Error> {
/// 'High level' Instrument wrapper around rusb::Device
pub struct Instrument<C: rusb::UsbContext> {
connected: bool,
+ // rusb objects
pub device: rusb::Device<C>,
pub handle: Option<rusb::DeviceHandle<C>>,
+ // usbtmc capabilites
capabilities: Option<Capabilities>,
+ // for linux kernel
has_kernel_driver: bool,
+ // addresses in the usb device
config_num: Option<u8>,
iface_num: Option<u8>,
ep_bulk_in: Option<u8>,
ep_bulk_out: Option<u8>,
ep_interrupt_in: Option<u8>,
+ // btag number to keep track of packet parts
btag: Wrapping<u8>,
+ // default timeout
timeout: Duration,
}
@@ -161,6 +172,10 @@ impl<C: rusb::UsbContext> Instrument<C> {
})
}
+ pub fn is_connected(&self) -> bool {
+ return self.connected;
+ }
+
/// Opens (searches for) the USBTMC interface of the device
///
/// It loops through the available usb interfaces and uses the first that
@@ -197,6 +212,7 @@ impl<C: rusb::UsbContext> Instrument<C> {
// check if it is has USB488
if iface_desc.protocol_code() == USB488_BINTERFACE_PROTOCOL {
// TODO
+ return Err(Error::NotImplemented);
}
self.config_num = Some(cfg_desc.number());
@@ -439,6 +455,7 @@ impl<C: rusb::UsbContext> Instrument<C> {
Ok(capabilities)
}
+ ///
pub fn pulse(&mut self) -> Result<(), Error> {
if !self.connected {
return Err(Error::NotConnected);
@@ -480,6 +497,11 @@ impl<C: rusb::UsbContext> Instrument<C> {
Ok(())
}
+ /// Write a string to the instrument
+ pub fn write(&mut self, message: &str) -> Result<usize, Error> {
+ return self.write_raw(message.as_bytes());
+ }
+
/// Write binary data to the instrument
pub fn write_raw(&mut self, data: &[u8]) -> Result<usize, Error> {
if !self.connected {
@@ -512,8 +534,8 @@ impl<C: rusb::UsbContext> Instrument<C> {
);
let mut packet: [u8; PACKET_SIZE] = [0; PACKET_SIZE];
- packet[..12].clone_from_slice(&header);
- packet[13..].clone_from_slice(chunk);
+ packet[..HEADER_SIZE].clone_from_slice(&header);
+ packet[(HEADER_SIZE +1)..].clone_from_slice(chunk);
sent_bytes += match handle.write_bulk(endpoint, &packet, self.timeout) {
Ok(sent) => sent,
@@ -533,8 +555,8 @@ impl<C: rusb::UsbContext> Instrument<C> {
}
// send remainder
- let padding = (4 - (last_data.len() % 4)) % 4;
- let last_data_size: usize = last_data.len() + padding;
+ let pad_size = (4 - (last_data.len() % 4)) % 4;
+ let last_data_size: usize = last_data.len() + pad_size;
let header = make_bulk_header(
MsgId::DeviceDependent,
@@ -544,11 +566,29 @@ impl<C: rusb::UsbContext> Instrument<C> {
true,
);
- // TODO: pad and send last byte
+ let mut packet: [u8; PACKET_SIZE] = [0; PACKET_SIZE];
+ packet[..HEADER_SIZE].clone_from_slice(&header);
+ for i in 0..last_data.len() {
+ packet[HEADER_SIZE + 1 + i] = last_data[i];
+ }
+
+ sent_bytes += match handle.write_bulk(
+ endpoint,
+ &packet[..(HEADER_SIZE + last_data_size)],
+ self.timeout
+ ) {
+ Ok(sent) => sent,
+ Err(e) => {
+ dbg!("failed to send chunk during bulk out");
+ self.abort_bulk_out();
+ return Err(Error::Rusb(e));
+ }
+ };
Ok(sent_bytes)
}
+ /// Abort a bulk-out operation
fn abort_bulk_out(&mut self) -> Result<(), Error> {
if !self.connected {
return Err(Error::NotConnected);
@@ -576,6 +616,20 @@ impl<C: rusb::UsbContext> Instrument<C> {
Ok(())
}
+
+ /// Read binary data from the device and decode into an utf-8 string
+ pub fn read(&mut self) -> Result<String, Error> {
+ let data = self.read_raw()?;
+ return match String::from_utf8(data) {
+ Ok(s) => Ok(s),
+ Err(e) => Err(Error::Decoding(e)),
+ };
+ }
+
+ /// Read binary data from the device
+ pub fn read_raw(&mut self) -> Result<Vec<u8>, Error> {
+ return Err(Error::NotImplemented);
+ }
}
/// helper function to create bulk headers
@@ -610,7 +664,7 @@ fn make_bulk_header(
ts_bytes[0],
ts_bytes[1],
ts_bytes[2],
- ts_bytes[4],
+ ts_bytes[3],
// whether this is the last chunk
match is_last {
true => 0x01,
@@ -624,3 +678,18 @@ fn make_bulk_header(
header
}
+
+impl<C: rusb::UsbContext> std::fmt::Debug for Instrument<C> {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ // TODO: complete
+ f.debug_struct("Instrument")
+ .field("connected", &self.connected)
+ .field("capabilities", &self.capabilities)
+ .field("has_kernel_driver", &self.has_kernel_driver)
+ .field("config_num", &self.config_num)
+ .field("iface_num", &self.iface_num)
+ .finish()
+
+ }
+}
+
diff --git a/src/testbench.rs b/src/testbench.rs
index 5dd18fd..52f5287 100644
--- a/src/testbench.rs
+++ b/src/testbench.rs
@@ -7,7 +7,7 @@ use std::path::PathBuf;
use imgui;
use rlua::Lua;
-// use serialport;
+use serialport;
use crate::rusbtmc;
@@ -43,15 +43,16 @@ impl Test {
#[derive(Default)]
pub struct Bench {
- // tests window
+ /* tests window */
lua: Lua,
tests: Vec<Test>,
tests_path: PathBuf,
+ /* devices window */
selected_test: Option<usize>,
selected_usb_dev: Option<usize>,
usb_devices: HashMap<imgui::ImString, rusbtmc::Instrument<rusb::GlobalContext>>,
selected_serial_dev: Option<usize>,
- // serial_devices: Vec<?>
+ // serial_devices: HashMap<imgui::ImString, (serialport::SerialPortBuilder, Option<serialport::SerialPort>>,
lua_console: Vec<imgui::ImString>,
instr_console: Vec<imgui::ImString>,
instr_input: imgui::ImString,
@@ -103,6 +104,7 @@ impl Bench {
return b;
}
+ /// Loads lua tests from tests_path
pub fn load_tests(&mut self) -> io::Result<()> {
let entries: Result<Vec<PathBuf>, io::Error> = fs::read_dir(&self.tests_path)?
.into_iter()
@@ -124,6 +126,25 @@ impl Bench {
}
}
+ fn get_selected_device<'a>(
+ index: Option<usize>,
+ list: &'a mut HashMap<imgui::ImString, rusbtmc::Instrument<rusb::GlobalContext>>,
+ ) -> Option<&'a mut rusbtmc::Instrument<rusb::GlobalContext>> {
+ 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::*;
@@ -179,7 +200,8 @@ impl Bench {
.position([520., 20.], Condition::Appearing);
dev_win.build(&ui, || {
- // usb devices
+ /* usb devices */
+ // buttons
ui.text_wrapped(im_str!("USB Devices"));
ui.same_line(0.);
if ui.button(im_str!("Refresh"), [0., 0.]) {
@@ -199,8 +221,8 @@ impl Bench {
let handle = match instr.device.open() {
Ok(handle) => handle,
- Err(_) => {
- // dbg!("failed to get handle");
+ Err(e) => {
+ dbg!("failed to get handle", e);
continue;
}
};
@@ -219,46 +241,74 @@ impl Bench {
}
}
- 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) = 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());
}
- 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!("Close"), [0., 0.]) {
+ let _ = dbg!(instr.close());
+ }
- ui.same_line(0.);
- if ui.button(im_str!("Pulse"), [0., 0.]) {
- let _ = dbg!(instr.pulse());
- }
+ 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() {
+ 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(&name).selected(selected).build(ui) {
+ if Selectable::new(&label).selected(selected).build(ui) {
self.selected_usb_dev = Some(index);
}
+ }
- ui.same_line(0.);
+ 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();
- ui.input_text(im_str!("Device"), &mut self.instr_input).build();
+ */
+
+ // 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)