Skip to content

Commit

Permalink
Release Candidate
Browse files Browse the repository at this point in the history
- fix scale and offset
- ctrl a for select all
- ctrl c for copy
- RP1210 product, not id
  • Loading branch information
battjt committed Aug 17, 2021
1 parent e939aae commit 0e46ac4
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 67 deletions.
32 changes: 22 additions & 10 deletions src/j1939.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ pub struct J1939DARow {
pub pg_description: Option<String>,

#[serde(alias = "EDP")]
pub edp: Option<u32>,
pub edp: Option<String>,
#[serde(alias = "DP")]
pub dp: Option<u32>,
pub dp: Option<String>,
#[serde(alias = "PF")]
pub pf: Option<u32>,
pub pf: Option<String>,
#[serde(alias = "PS")]
pub ps: Option<u32>,
pub ps: Option<String>,

#[serde(alias = "Multipacket", deserialize_with = "bool_from_string")]
pub multipacket: Option<bool>,
Expand Down Expand Up @@ -76,16 +76,16 @@ pub struct J1939DARow {
// #[serde(alias = "SLOT Name")]
// #[serde(alias = "SP Type")]
// #[serde(alias = "SP Reference")]
#[serde(alias = "Scale Factor (value only)")]
#[serde(alias = "Scale Factor\r\n(value only)")]
pub scale: Option<f64>,
#[serde(alias = "Offset (value only)")]
#[serde(alias = "Offset\r\n(value only)")]
pub offset: Option<f64>,
#[serde(alias = "Range Maximum (value only)")]
#[serde(alias = "Range Maximum\r\n(value only)")]
pub max: Option<f64>,

#[serde(alias = "Length Minimum (bits)")]
#[serde(alias = "Length Minimum\r\n(bits)")]
pub length_min: Option<u16>,
#[serde(alias = "Length Maximum (bits)")]
#[serde(alias = "Length Maximum\r\n(bits)")]
pub length_max: Option<u16>,
// #[serde(alias = "SP Document")]
// #[serde(alias = "PG Document")]
Expand All @@ -100,21 +100,33 @@ pub fn load_j1939da(file: &str) -> Result<HashMap<u32, J1939DARow>> {
let range = excel
.worksheet_range("SPs & PGs")
.ok_or(Error::Msg("Cannot find 'SPs & PGs'"))??;

// skip the first 3 rows
let subrange = range.range((3, 0), range.end().unwrap());
let iter = RangeDeserializerBuilder::new()
.has_headers(true)
.from_range(&subrange)?;
let mut map = HashMap::new();
let mut errors = 0;
let mut count = 0;
for result in iter {
count += 1;
// ignore missing spns
if result.is_ok() {
let data: J1939DARow = result?;
if let Some(spn) = data.spn {
map.insert(spn, data);
}
} else {
errors += 1;
//println!("FAIL {}: {:?}", errors, result);
}
}
println!("J1939DA parsing {} ms", start.elapsed().as_millis());
println!(
"J1939DA parsing {} ms {} errors of {} total rows.",
start.elapsed().as_millis(),
errors,
count
);
Ok(map)
}
107 changes: 58 additions & 49 deletions src/j1939da_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ pub fn create_ui(this: Rc<Mutex<J1939Table>>) -> gtk::Container {
filter_box.pack_start(&pgn_dec, true, true, 0);
let rc = this.clone();
pgn_dec.connect_changed(move |e| {
let mut c = rc.lock().expect("Unable to lock.");
c.pgn_dec = e.buffer().text();
c.refilter();
let mut table = rc.lock().expect("Unable to lock.");
table.pgn_dec = e.buffer().text();
table.refilter();
});

let pgn_hex = gtk::Entry::builder()
Expand All @@ -71,9 +71,9 @@ pub fn create_ui(this: Rc<Mutex<J1939Table>>) -> gtk::Container {
filter_box.pack_start(&pgn_hex, true, true, 0);
let rc = this.clone();
pgn_hex.connect_changed(move |e| {
let mut c = rc.lock().expect("Unable to lock.");
c.pgn_hex = e.buffer().text();
c.refilter();
let mut table = rc.lock().expect("Unable to lock.");
table.pgn_hex = e.buffer().text();
table.refilter();
});
}
{
Expand All @@ -86,9 +86,9 @@ pub fn create_ui(this: Rc<Mutex<J1939Table>>) -> gtk::Container {
.build();
let rc = this.clone();
spn_dec.connect_changed(move |e| {
let mut c = rc.lock().expect("Unable to lock.");
c.spn_dec = e.buffer().text();
c.refilter();
let mut table = rc.lock().expect("Unable to lock.");
table.spn_dec = e.buffer().text();
table.refilter();
});
filter_box.pack_start(&spn_dec, true, true, 0);

Expand All @@ -98,14 +98,14 @@ pub fn create_ui(this: Rc<Mutex<J1939Table>>) -> gtk::Container {
.build();
let rc = this.clone();
spn_hex.connect_changed(move |e| {
let mut c = rc.lock().expect("Unable to lock.");
c.spn_hex = e.buffer().text();
c.refilter();
let mut table = rc.lock().expect("Unable to lock.");
table.spn_hex = e.buffer().text();
table.refilter();
});
filter_box.pack_start(&spn_hex, true, true, 0);
}
{
//filter description}
//filter description
filter_box.add(&gtk::Label::new(Some("Filter")));

let desc = gtk::Entry::builder()
Expand All @@ -114,28 +114,27 @@ pub fn create_ui(this: Rc<Mutex<J1939Table>>) -> gtk::Container {
.build();
let rc = this.clone();
desc.connect_changed(move |e| {
let mut c = rc.lock().expect("Unable to lock.");
c.description = e
let mut table = rc.lock().expect("Unable to lock.");
table.description = e
.buffer()
.text()
.to_ascii_lowercase()
.split_ascii_whitespace()
.map(|s| s.to_string())
.collect();
c.refilter();
table.refilter();
});
filter_box.pack_start(&desc, true, true, 0);
}
view.selection().set_mode(SelectionMode::Multiple);
connect_selectall_copy(&view);

let sw = ScrolledWindow::new(gtk::NONE_ADJUSTMENT, gtk::NONE_ADJUSTMENT);
sw.add(&view);

let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);
vbox.pack_start(&filter_box, false, false, 4);
vbox.pack_start(&sw, true, true, 0);

add_copy_button(&vbox.upcast(), view)
vbox.upcast()
}

pub(crate) fn j1939da_log(bus: &MultiQueue<J1939Packet>) -> gtk::Container {
Expand All @@ -147,56 +146,61 @@ pub(crate) fn j1939da_log(bus: &MultiQueue<J1939Packet>) -> gtk::Container {
]);
let view = TreeView::with_model(&TreeModelSort::new(&list));

view.append_column(&config_col(&"Time", false, 0));
view.append_column(&config_col(&"Time (ms)", false, 0));
view.append_column(&config_col(&"Size", false, 1));
view.append_column(&config_col(&"Head", true, 2));
view.append_column(&config_col(&"Data", true, 3));

let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
let stream = bus.iter_for(std::time::Duration::from_secs(60 * 60 * 24 * 30));
thread::spawn(move || stream.for_each(|p| tx.send(p).unwrap()));
rx.attach(None, move |p| {
thread::spawn(move || stream.for_each(|packet| tx.send(packet).unwrap()));
rx.attach(None, move |packet| {
list.insert_with_values(
None,
&[
(0, &p.time()),
(1, &(p.data().len() as u32)),
(2, &p.header()),
(3, &p.data_str()),
(0, &packet.time()),
(1, &(packet.data().len() as u32)),
(2, &packet.header()),
(3, &packet.data_str()),
],
);
glib::Continue(true)
});

view.selection().set_mode(SelectionMode::Multiple);
connect_selectall_copy(&view);

let sw = ScrolledWindow::new(gtk::NONE_ADJUSTMENT, gtk::NONE_ADJUSTMENT);
sw.add(&view);

add_copy_button(&sw.upcast(), view)
sw.upcast()
}

fn add_copy_button(sw: &Container, view: TreeView) -> Container {
let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);
vbox.pack_start(sw, true, true, 0);
let buttons = gtk::Box::new(gtk::Orientation::Horizontal, 4);
let copy_button = Button::new();
copy_button.set_label("Copy");
copy_button.connect_clicked(move |_f| {
gtk::Clipboard::get(&gdk::SELECTION_CLIPBOARD).set_text(&copy(&view));
fn connect_selectall_copy(view: &TreeView) {
view.selection().set_mode(SelectionMode::Multiple);
view.connect_key_press_event(|view, key| {
if key.state().contains(gdk::ModifierType::CONTROL_MASK)
&& key.keyval() == gdk::keys::Key::from_unicode('a')
{
view.selection().select_all();
Inhibit(true)
} else if key.state().contains(gdk::ModifierType::CONTROL_MASK)
&& key.keyval() == gdk::keys::Key::from_unicode('c')
{
copy_table_to_clipboard(&view);
Inhibit(true)
} else {
Inhibit(false)
}
});
buttons.pack_end(&copy_button, false, false, 0);
vbox.pack_end(&buttons, false, false, 0);
vbox.upcast()
}

fn copy(view: &TreeView) -> String {
fn copy_table_to_clipboard(view: &TreeView) {
let (vec, list) = view.selection().selected_rows();
vec.iter()
.map(|p| {
let as_string = vec
.iter()
.map(|path| {
(0..list.n_columns())
.map(|c| {
let value = list.value(list.iter(p).as_ref().unwrap(), c);
.map(|column| {
let value = list.value(list.iter(path).as_ref().unwrap(), column);
value
.get::<String>()
.map(|s| "\"".to_string() + &s + "\"")
Expand All @@ -211,7 +215,11 @@ fn copy(view: &TreeView) -> String {
|a, b| if a.is_empty() { b } else { a + "\t" + &b },
)
})
.fold(String::new(), |a, b| a + "\n" + &b)
.fold(
String::new(),
|a, b| if a.is_empty() { b } else { a + "\n" + &b },
);
gtk::Clipboard::get(&gdk::SELECTION_CLIPBOARD).set_text(&as_string);
}

pub struct J1939Table {
Expand Down Expand Up @@ -300,16 +308,17 @@ impl J1939Table {
})
.is_some()
{
let empty = "".to_string();
self.list.insert_with_values(
None,
&[
(0, &row.pg.unwrap_or(0)),
(1, &format!("{:04X}", row.pg.unwrap_or(0))),
(2, &row.spn.unwrap_or(0)),
(3, &format!("{:04X}", row.spn.unwrap_or(0))),
(4, &row.pg_description),
(5, &row.sp_label),
(6, &row.unit),
(4, row.pg_description.as_ref().unwrap_or(&empty)),
(5, row.sp_label.as_ref().unwrap_or(&empty)),
(6, row.unit.as_ref().unwrap_or(&empty)),
(7, &row.scale.unwrap_or(1.0)),
(8, &row.offset.unwrap_or(0.0)),
],
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ fn create_rp1210_menu(bus: MultiQueue<J1939Packet>) -> Result<Menu> {
rp1210_menu.append(&device_menu_item);
}
for product in rp1210_parsing::list_all_products()? {
let product_menu_item = MenuItem::with_label(&product.id);
let product_menu_item = MenuItem::with_label(&product.description);
rp1210_menu.append(&product_menu_item);
let product_menu = Menu::new();

Expand Down
26 changes: 19 additions & 7 deletions src/rp1210_parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub struct Rp1210Dev {
#[derive(Debug)]
pub struct Rp1210Prod {
pub id: String,
pub description: String,
pub devices: Vec<Rp1210Dev>,
}

Expand All @@ -18,16 +19,20 @@ pub fn list_all_products() -> Result<Vec<Rp1210Prod>> {
.get_from(Some("RP1210Support"), "APIImplementations")
.unwrap_or("")
.split(",")
.map(|s| Rp1210Prod {
id: s.to_string(),
devices: list_devices_for_prod(s).unwrap(),
.map(|s| {
let (description, devices) = list_devices_for_prod(s).unwrap();
Rp1210Prod {
id: s.to_string(),
description: description.to_string(),
devices,
}
})
.collect());
println!("RP1210 INI parsing in {} ms", start.elapsed().as_millis());
rtn
}

fn list_devices_for_prod(id: &str) -> Result<Vec<Rp1210Dev>> {
fn list_devices_for_prod(id: &str) -> Result<(String, Vec<Rp1210Dev>)> {
let start = std::time::Instant::now();
let ini = ini::Ini::load_from_file(&format!("c:\\Windows\\{}.ini", id))?;

Expand All @@ -48,7 +53,7 @@ fn list_devices_for_prod(id: &str) -> Result<Vec<Rp1210Dev>> {
.collect();

// find the specified devices
let rtn = Ok(ini
let rtn = ini
.iter()
.filter(|(section, properties)| {
section.unwrap().starts_with("DeviceInformation")
Expand All @@ -65,7 +70,14 @@ fn list_devices_for_prod(id: &str) -> Result<Vec<Rp1210Dev>> {
.unwrap_or("Unknown")
.to_string(),
})
.collect());
.collect();
println!(" {}.ini parsing in {} ms", id, start.elapsed().as_millis());
rtn
let description = ini.section(Some("VendorInformation"))
.unwrap()
.get("Name")
.unwrap().to_string();
Ok((
description,
rtn,
))
}

0 comments on commit 0e46ac4

Please sign in to comment.