Skip to content

Commit

Permalink
support more information for CAN bus (#6)
Browse files Browse the repository at this point in the history
* busload, bus state, circle time, bitrate support

* remove setting bitrate - because of PRIVILEGE

* also add bus load and circle time for windows

* support CAN bus bitrate configuration

* also support bitrate configuration for Windows
  • Loading branch information
TuEmb committed Sep 16, 2024
1 parent 32fd4ae commit fc7be68
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 55 deletions.
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
[package]
name = "can-viewer"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
description = "view real-time CAN packages"

[dependencies]
chrono = "0.4.38"
sudo = "0.6"
can-dbc = { git="https://github.com/TuEmb/can-dbc.git", branch="dev" }
rfd = "0.14.1"
slint = { version = "1.7.1", default-features = false, features = ["backend-winit", "compat-1-2", "renderer-winit-femtovg"] }
Expand All @@ -15,6 +17,7 @@ winapi = { version = "0.3.9", features = ["winuser"] }
pcan-basic = { git = "https://github.com/TuEmb/pcan-basic.git", branch="main"}

[target.'cfg(unix)'.dependencies]
privilege-rs = "0.1.0"
socketcan = { git = "https://github.com/socketcan-rs/socketcan-rs.git", rev="e0d7760eca8085b247f37ea22f0aa41e00fa25fa", features = ["enumerate"] }

[build-dependencies]
Expand All @@ -24,7 +27,7 @@ winresource = "0.1.17"
[package.metadata.bundle]
name = "can-viewer"
icon = ["ui/images/can_viewer_32px.png", "ui/images/can_viewer_128px.png", "ui/images/can_viewer_256px.png"]
version = "1.0.0"
version = "0.2.1"
copyright = "Copyright (c) Tu Nguyen 2024. All rights reserved."
category = "Developer Tool"
short_description = "view real-time CAN packages"
Expand All @@ -34,5 +37,5 @@ can-view can records real-time can packages and parse data with DBC input.
"""

[package.metadata.winresource]
OriginalFilename = "can-viewer.exe"
OriginalFilename = "can-viewer_0.2.1.exe"
LegalCopyright = "Copyright © 2024"
144 changes: 115 additions & 29 deletions src/event_handler/can_handler.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
use can_dbc::DBC;
use chrono::Utc;
#[cfg(target_os = "windows")]
use pcan_basic::socket::usb::UsbCanSocket;
use slint::{Model, VecModel, Weak};
use slint::{ModelRc, SharedString};
#[cfg(target_os = "linux")]
use socketcan::{CanSocket, EmbeddedFrame, Frame, Socket};
use socketcan::{CanInterface, CanSocket, EmbeddedFrame, Frame, Socket};
use std::collections::HashMap;
use std::fmt::Write;
use std::rc::Rc;
use std::sync::mpsc::Receiver;
use std::sync::{Arc, Mutex};
use std::thread::sleep;
use std::time::Duration;
use std::time::Instant;

use crate::slint_generatedAppWindow::AppWindow;
use crate::slint_generatedAppWindow::CanData;
Expand All @@ -23,6 +25,7 @@ pub struct CanHandler<'a> {
pub iface: UsbCanSocket,
pub ui_handle: &'a Weak<AppWindow>,
pub mspc_rx: &'a Arc<Mutex<Receiver<DBC>>>,
pub bitrate: String,
}

static mut NEW_DBC_CHECK: bool = false;
Expand All @@ -33,8 +36,12 @@ impl<'a> CanHandler<'a> {
if let Ok(dbc) = self.mspc_rx.lock().unwrap().try_recv() {
#[cfg(target_os = "linux")]
{
let can_if = CanInterface::open(self.iface).unwrap();
let _ = can_if.bring_down();
let _ = can_if.set_bitrate(self.bitrate().unwrap(), None);
let _ = can_if.bring_up();
let can_socket = self.open_can_socket();
self.process_ui_events(dbc, can_socket);
self.process_ui_events(dbc, can_socket, can_if);
}
#[cfg(target_os = "windows")]
self.process_ui_events(dbc);
Expand All @@ -60,8 +67,27 @@ impl<'a> CanHandler<'a> {
}
}
#[cfg(target_os = "linux")]
fn process_ui_events(&self, dbc: DBC, can_socket: CanSocket) {
fn process_ui_events(&self, dbc: DBC, can_socket: CanSocket, can_if: CanInterface) {
let mut start_bus_load = Instant::now();
let mut total_bits = 0;
loop {
let bus_state = match can_if.state().unwrap().unwrap() {
socketcan::nl::CanState::ErrorActive => "ERR_ACTIVE",
socketcan::nl::CanState::ErrorWarning => "ERR_WARNING",
socketcan::nl::CanState::ErrorPassive => "ERR_PASSIVE",
socketcan::nl::CanState::BusOff => "BUSOFF",
socketcan::nl::CanState::Stopped => "STOPPED",
socketcan::nl::CanState::Sleeping => "SLEEPING",
};
let bitrate = can_if.bit_rate().unwrap().unwrap();
let busload = if start_bus_load.elapsed() >= Duration::from_millis(1000) {
start_bus_load = Instant::now();
let bus_load = (total_bits as f64 / bitrate as f64) * 100.0;
total_bits = 0;
bus_load
} else {
0.0
};
let _ = self.ui_handle.upgrade_in_event_loop(move |ui| unsafe {
if ui.get_is_new_dbc() {
if ui.get_is_first_open() {
Expand All @@ -71,6 +97,11 @@ impl<'a> CanHandler<'a> {
}
ui.set_is_new_dbc(false);
}
ui.set_state(bus_state.into());
ui.set_bitrate(bitrate as i32);
if busload > 0.0 {
ui.set_bus_load(busload as i32);
}
});
unsafe {
if NEW_DBC_CHECK {
Expand All @@ -79,6 +110,7 @@ impl<'a> CanHandler<'a> {
}
}
if let Ok(frame) = can_socket.read_frame() {
total_bits += (frame.len() + 6) * 8; // Data length + overhead (approximation)
let frame_id = frame.raw_id() & !0x80000000;
for message in dbc.messages() {
if frame_id == (message.message_id().raw() & !0x80000000) {
Expand Down Expand Up @@ -106,9 +138,19 @@ impl<'a> CanHandler<'a> {
}
#[cfg(target_os = "windows")]
fn process_ui_events(&mut self, dbc: DBC) {
use pcan_basic::socket::RecvCan;

use pcan_basic::{error::PcanError, socket::RecvCan};
let mut start_bus_load = Instant::now();
let mut total_bits = 0;
loop {
let bitrate = self.bitrate().unwrap();
let busload = if start_bus_load.elapsed() >= Duration::from_millis(1000) {
start_bus_load = Instant::now();
let bus_load = (total_bits as f64 / bitrate as f64) * 100.0;
total_bits = 0;
bus_load
} else {
0.0
};
let _ = self.ui_handle.upgrade_in_event_loop(move |ui| unsafe {
if ui.get_is_new_dbc() {
if ui.get_is_first_open() {
Expand All @@ -118,39 +160,54 @@ impl<'a> CanHandler<'a> {
}
ui.set_is_new_dbc(false);
}
ui.set_bitrate(bitrate as i32);
if busload > 0.0 {
ui.set_bus_load(busload as i32);
}
});
unsafe {
if NEW_DBC_CHECK {
NEW_DBC_CHECK = false;
break;
}
}
if let Ok(frame) = self.iface.recv_frame() {
let id = frame.can_id();
let frame_id = id & !0x80000000;
for message in dbc.messages() {
if frame_id == (message.message_id().raw() & !0x80000000) {
let padding_data = Self::pad_to_8_bytes(frame.data());
let hex_string = Self::array_to_hex_string(frame.data());
let signal_data = message.parse_from_can(&padding_data);
let _ = self.ui_handle.upgrade_in_event_loop(move |ui| {
let is_filter = ui.get_is_filter();
let messages: ModelRc<CanData> = if !is_filter {
ui.get_messages()
} else {
ui.get_filter_messages()
};
Self::update_ui_with_signals(
&messages,
frame_id,
signal_data,
hex_string,
);
});
match self.iface.recv_frame() {
Ok(frame) => {
let _ = self.ui_handle.upgrade_in_event_loop(move |ui| {
ui.set_state("OK".into());
});
total_bits += (frame.dlc() as u32 + 6) * 8; // Data length + overhead (approximation)
let id = frame.can_id();
let frame_id = id & !0x80000000;
for message in dbc.messages() {
if frame_id == (message.message_id().raw() & !0x80000000) {
let padding_data = Self::pad_to_8_bytes(frame.data());
let hex_string = Self::array_to_hex_string(frame.data());
let signal_data = message.parse_from_can(&padding_data);
let _ = self.ui_handle.upgrade_in_event_loop(move |ui| {
let is_filter = ui.get_is_filter();
let messages: ModelRc<CanData> = if !is_filter {
ui.get_messages()
} else {
ui.get_filter_messages()
};
Self::update_ui_with_signals(
&messages,
frame_id,
signal_data,
hex_string,
);
});
}
}
}
} else {
sleep(Duration::from_millis(50));
Err(e) => {
let _ = self.ui_handle.upgrade_in_event_loop(move |ui| {
if e != PcanError::QrcvEmpty {
ui.set_state(format!("{:?}", e).into());
}
});
}
}
}
}
Expand All @@ -163,7 +220,10 @@ impl<'a> CanHandler<'a> {
) {
for (message_count, message) in messages.iter().enumerate() {
if message.can_id == format!("{:08X}", frame_id) {
let now = Utc::now().timestamp_micros();
let can_signals = Self::create_can_signals(&message, &signal_data);
let circle_time =
(now - (message.time_stamp).parse::<i64>().unwrap()) as f32 / 1000.0;
messages.set_row_data(
message_count,
CanData {
Expand All @@ -177,6 +237,8 @@ impl<'a> CanHandler<'a> {
} else {
ODD_COLOR
},
circle_time: format!("{:.02} ms", circle_time).into(),
time_stamp: now.to_string().into(),
},
);
break;
Expand Down Expand Up @@ -240,4 +302,28 @@ impl<'a> CanHandler<'a> {
hex_string.pop(); // Remove the trailing space
hex_string
}

fn bitrate(&self) -> Option<u32> {
let bitrate_map: HashMap<&str, u32> = [
("1 Mbit/s", 1_000_000),
("800 kbit/s", 800_000),
("500 kbit/s", 500_000),
("250 kbit/s", 250_000),
("125 kbit/s", 125_000),
("100 kbit/s", 100_000),
("95.238 kbit/s", 95_238),
("83.333 kbit/s", 83_333),
("50 kbit/s", 50_000),
("47.619 kbit/s", 47_619),
("33.333 kbit/s", 33_333),
("20 kbit/s", 20_000),
("10 kbit/s", 10_000),
("5 kbit/s", 5_000),
]
.iter()
.cloned()
.collect();

bitrate_map.get(self.bitrate.as_str()).copied()
}
}
4 changes: 4 additions & 0 deletions src/event_handler/dbc_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ impl<'a> DBCFile<'a> {
counter: 0,
raw_can: SharedString::from("default"),
color: ODD_COLOR,
circle_time: "0.0".into(),
time_stamp: "0".into(),
}]
.to_vec(),
));
Expand Down Expand Up @@ -83,6 +85,8 @@ impl<'a> DBCFile<'a> {
} else {
ODD_COLOR
},
circle_time: "0.0".into(),
time_stamp: "0".into(),
};

if message_count == 0 {
Expand Down
23 changes: 23 additions & 0 deletions src/event_handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,30 @@ pub(crate) mod packet_filter;
pub use can_handler::CanHandler;
pub use dbc_file::DBCFile;
pub use packet_filter::PacketFilter;
#[cfg(target_os = "windows")]
use pcan_basic::socket::Baudrate;
use slint::Color;

const ODD_COLOR: Color = Color::from_rgb_u8(0x18, 0x1c, 0x27);
const EVEN_COLOR: Color = Color::from_rgb_u8(0x13, 0x16, 0x1f);

#[cfg(target_os = "windows")]
pub fn p_can_bitrate(bitrate: &str) -> Option<Baudrate> {
match bitrate {
"1 Mbit/s" => Some(Baudrate::Baud1M),
"800 kbit/s" => Some(Baudrate::Baud800K),
"500 kbit/s" => Some(Baudrate::Baud500K),
"250 kbit/s" => Some(Baudrate::Baud250K),
"125 kbit/s" => Some(Baudrate::Baud125K),
"100 kbit/s" => Some(Baudrate::Baud100K),
"95.238 kbit/s" => Some(Baudrate::Baud95K),
"83.333 kbit/s" => Some(Baudrate::Baud83),
"50 kbit/s" => Some(Baudrate::Baud50K),
"47.619 kbit/s" => Some(Baudrate::Baud47K),
"33.333 kbit/s" => Some(Baudrate::Baud33K),
"20 kbit/s" => Some(Baudrate::Baud20K),
"10 kbit/s" => Some(Baudrate::Baud10K),
"5 kbit/s" => Some(Baudrate::Baud5K),
_ => None,
}
}
2 changes: 2 additions & 0 deletions src/event_handler/packet_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ impl<'a> PacketFilter<'a> {
packet_name: self.filter.packet_name,
raw_can: self.filter.raw_can,
signal_value: self.filter.signal_value,
circle_time: "0.0".into(),
time_stamp: "0".into(),
});
} else {
// Remove filter ID
Expand Down
Loading

0 comments on commit fc7be68

Please sign in to comment.