Skip to content

Commit

Permalink
support select pcan devices on windows
Browse files Browse the repository at this point in the history
  • Loading branch information
TuEmb committed Aug 30, 2024
1 parent 0500517 commit 0f1c63c
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 83 deletions.
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ rfd = "0.14.1"
slint = { version = "1.7.1", default-features = false, features = ["backend-winit", "compat-1-2", "renderer-winit-femtovg"] }

[target.'cfg(windows)'.dependencies]
embedded-can = "0.3.0"
winapi = { version = "0.3.9", features = ["winuser"] }
pcan-basic = { git = "https://github.com/TuEmb/pcan-basic-rs.git", branch="master"}
pcan-basic = { git = "https://github.com/TuEmb/pcan-basic.git", branch="main"}

[target.'cfg(unix)'.dependencies]
dialoguer = "0.11.0"
Expand Down
35 changes: 9 additions & 26 deletions src/event_handler/can_handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use can_dbc::DBC;
use pcan_basic::socket::usb::UsbCanSocket;
use slint::{Model, VecModel, Weak};
use slint::{ModelRc, SharedString};
#[cfg(target_os = "linux")]
Expand All @@ -7,6 +8,7 @@ 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;

Expand All @@ -17,35 +19,17 @@ pub struct CanHandler<'a> {
#[cfg(target_os = "linux")]
pub iface: &'a str,
#[cfg(target_os = "windows")]
pub iface: PcanDriver<Interface>,
pub iface: UsbCanSocket,
pub ui_handle: &'a Weak<AppWindow>,
pub mspc_rx: &'a Receiver<DBC>,
pub mspc_rx: &'a Arc<Mutex<Receiver<DBC>>>,
}

static mut NEW_DBC_CHECK: bool = false;

#[cfg(target_os = "windows")]
use embedded_can::Frame;
#[cfg(target_os = "windows")]
use pcan_basic::{self, Interface};

use super::{EVEN_COLOR, ODD_COLOR};
#[cfg(target_os = "windows")]
pub struct PcanDriver<Can>(pub Can);
#[cfg(target_os = "windows")]
impl<Can> PcanDriver<Can>
where
Can: embedded_can::blocking::Can,
Can::Error: core::fmt::Debug,
{
pub fn read_frame(&mut self) -> Result<Can::Frame, Can::Error> {
self.0.try_read()
}
}

impl<'a> CanHandler<'a> {
pub fn process_can_messages(&mut self) {
if let Ok(dbc) = self.mspc_rx.try_recv() {
if let Ok(dbc) = self.mspc_rx.lock().unwrap().try_recv() {
#[cfg(target_os = "linux")]
{
let can_socket = self.open_can_socket();
Expand Down Expand Up @@ -123,6 +107,8 @@ impl<'a> CanHandler<'a> {
}
#[cfg(target_os = "windows")]
fn process_ui_events(&mut self, dbc: DBC) {
use pcan_basic::socket::RecvCan;

loop {
let _ = self.ui_handle.upgrade_in_event_loop(move |ui| unsafe {
if ui.get_is_new_dbc() {
Expand All @@ -140,11 +126,8 @@ impl<'a> CanHandler<'a> {
break;
}
}
if let Ok(frame) = self.iface.read_frame() {
let id = match frame.id() {
pcan_basic::Id::Standard(std_id) => std_id.as_raw() as u32,
pcan_basic::Id::Extended(ext_id) => ext_id.as_raw(),
};
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) {
Expand Down
170 changes: 115 additions & 55 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,153 @@
use std::io;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};

mod event_handler;
use can_dbc::DBC;
#[cfg(target_os = "linux")]
use dialoguer::{theme::ColorfulTheme, Select};
#[cfg(target_os = "windows")]
use event_handler::can_handler::PcanDriver;
use event_handler::{CanHandler, DBCFile, PacketFilter};
use pcan_basic::hw::attached_channels;
use pcan_basic::{
bus::UsbBus,
socket::{usb::UsbCanSocket, Baudrate},
};
use slint::{Model, ModelRc, SharedString, VecModel};
#[cfg(target_os = "linux")]
use socketcan::available_interfaces;
#[cfg(target_os = "windows")]
use winapi::um::wincon::FreeConsole;

slint::include_modules!();

fn main() -> io::Result<()> {
let ui = AppWindow::new().unwrap();
let (tx, rx) = mpsc::channel::<DBC>();
// Wrap `rx` in an Arc<Mutex<>> so it can be shared safely across threads
let rx = Arc::new(Mutex::new(rx));
unsafe {
FreeConsole(); // This detaches the console from the application
}

// Find available socket CAN
#[cfg(target_os = "linux")]
let socket_if = match available_interfaces() {
match available_interfaces() {
Ok(interface) => {
if interface.is_empty() {
println!("ERR: Can't find any socket can interfaces: length is 0");
ui.set_init_string(SharedString::from("No CAN device found !"));
return Ok(());
} else {
interface
ui.set_init_string(SharedString::from(format!(
"Found {} CAN devices\n Please select your device ",
interface.len()
)));
for channel in interface {
let socket_name = SharedString::from(format!(
"{}(0x{:02X})",
channel.device_name(),
channel.channel_information.device_id
));
can_socket_names.push(socket_name);
can_socket_index.push(channel.channel_information.channel_handle as i32);
}
let socket_info = socket_info {
index: ModelRc::new(VecModel::from(can_socket_index)),
name: ModelRc::new(VecModel::from(can_socket_names)),
};
ui.set_can_sockets(socket_info);
}
}
Err(e) => {
println!("ERR: Can't find any socket can interfaces: {}", e);
ui.set_init_string(SharedString::from(format!("Can't get device list: {}", e)));
return Ok(());
}
};

// Create and selectable list for socket CAN
#[cfg(target_os = "linux")]
let selection = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Choose an socket CAN interface:")
.items(&socket_if[..])
.default(0)
.interact()
.unwrap();
// // Create and selectable list for socket CAN
// #[cfg(target_os = "linux")]
// let selection = Select::with_theme(&ColorfulTheme::default())
// .with_prompt("Choose an socket CAN interface:")
// .items(&socket_if[..])
// .default(0)
// .interact()
// .unwrap();
#[cfg(target_os = "windows")]
{
unsafe {
FreeConsole(); // This detaches the console from the application
}
let can_interface = pcan_basic::Interface::init(0x011C);
match can_interface {
Ok(can) => {
drop(can);
// get channel_handle
let mut can_socket_names = Vec::default();
let mut can_socket_index = Vec::default();
match attached_channels() {
Ok(channels) => {
if channels.is_empty() {
ui.set_init_string(SharedString::from("No CAN device found !"));
} else {
ui.set_init_string(SharedString::from(format!(
"Found {} CAN devices\n Please select your device ",
channels.len()
)));
for channel in channels {
let socket_name = SharedString::from(format!(
"{}(0x{:02X})",
channel.device_name(),
channel.channel_information.device_id
));
can_socket_names.push(socket_name);
can_socket_index.push(channel.channel_information.channel_handle as i32);
}
let socket_info = socket_info {
index: ModelRc::new(VecModel::from(can_socket_index)),
name: ModelRc::new(VecModel::from(can_socket_names)),
};
ui.set_can_sockets(socket_info);
}
}
Err(e) => {
println!("ERROR: Can't find any PCAN interface input: {}", e);
return Ok(());
ui.set_init_string(SharedString::from(format!(
"Can't get device list: {:?}",
e
)));
}
}
}

let ui = AppWindow::new().unwrap();
let (tx, rx) = mpsc::channel();
// Handle start event
let ui_handle = ui.as_weak();
ui.on_start(move |index| {
let ui = ui_handle.unwrap();
let get_device_handle = match ui.get_can_sockets().index.row_data(index as usize) {
Some(device) => device,
None => {
ui.set_init_string(SharedString::from("No device found!!!"));
return;
}
};
let usb_can = UsbBus::try_from(get_device_handle as u16).unwrap();
let ui_handle = ui.as_weak();
match UsbCanSocket::open(usb_can, Baudrate::Baud250K) {
Ok(socket) => {
ui_handle.unwrap().set_is_init(true);
let rx = Arc::clone(&rx);
std::thread::spawn(move || {
let mut can_handler = CanHandler {
#[cfg(target_os = "linux")]
iface: &socket_if[selection],
#[cfg(target_os = "windows")]
iface: socket,
ui_handle: &ui_handle,
mspc_rx: &rx,
};
loop {
can_handler.process_can_messages();
}
});
}
Err(e) => {
ui_handle
.unwrap()
.set_init_string(SharedString::from(format!("Failed to start: {:?}", e)));
}
}
});

// Handle open file event
let ui_handle = ui.as_weak();
Expand All @@ -70,36 +160,6 @@ fn main() -> io::Result<()> {
dbc_handle.process_dbc_file();
});

// Create thread to handle CAN packet comming and
// update to UI the signal - value
let ui_handle = ui.as_weak();
std::thread::spawn(move || {
#[cfg(target_os = "windows")]
let can_interface = loop {
std::thread::sleep(std::time::Duration::from_secs(1));
match pcan_basic::Interface::init(0x011C) {
Ok(can_if) => break PcanDriver(can_if),
Err(e) => {
println!("ERR: Can't find any PCAN Interface: {}", e);
println!("Trying to reconnect ...");
continue;
}
}
};

let mut can_handler = CanHandler {
#[cfg(target_os = "linux")]
iface: &socket_if[selection],
#[cfg(target_os = "windows")]
iface: can_interface,
ui_handle: &ui_handle,
mspc_rx: &rx,
};
loop {
can_handler.process_can_messages();
}
});

// Handle filter page
let ui_handle = ui.as_weak();
ui.on_filter_id(move |filter, is_check| {
Expand Down
15 changes: 15 additions & 0 deletions ui/app.slint
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,23 @@ import { CanData, CanMessage, CanSignal } from "messages.slint";
import { viewPage } from "view_page.slint";
import { filterPage } from "filter_page.slint";
import { selectPage } from "page_selection.slint";
import { initPage, socket_info } from "init_page.slint";

export component AppWindow inherits Window {
in property <bool> is_filter: false;
in property <bool> is_new_dbc: false;
in property <bool> is_first_open: true;
in property <bool> is_init: false;
in property <string> init_string: "Please select CAN device to start";
in property <socket_info> can_sockets;
in property <[CanData]> messages;
in property <[CanData]> filter_messages;

in-out property <int> active-page: 0;

callback open_dbc_file();
callback filter_id(CanData, bool);
callback start(int);
title: @tr("CAN VIEWER (version 0.1.0)");
icon: @image-url("images/can_viewer_128px.png");
background: #1a1f2b;
Expand All @@ -26,6 +31,16 @@ export component AppWindow inherits Window {
preferred-height: 600px;
preferred-width: 800px;

if !is_init:
initPage {
out: init_string;
can_sockets: can_sockets;
start(index) => {
start(index)
}
}

if is_init:
VerticalLayout {
HorizontalLayout {
view_selection := selectPage {
Expand Down
Binary file added ui/images/can_viewer.ico
Binary file not shown.
Loading

0 comments on commit 0f1c63c

Please sign in to comment.