update period send message

This commit is contained in:
Guangzong Chen 2021-10-09 01:57:27 -04:00
parent cc56f48c0a
commit fdda601379
No known key found for this signature in database
GPG Key ID: 46AFD2E1BC3FB8A1
5 changed files with 299 additions and 53 deletions

View File

@ -10,6 +10,7 @@ edition = "2018"
gtk = "0.14.1"
gio = "0.14.6"
glib = "0.14.5"
#pretty-hex = "0.2.1"
#gtk4 = {git = "https://github.com/gtk-rs/gtk4-rs/"}
#gtk4 = "0.3.0"
#gio4_sys ="0."

68
src/hexdump.rs Normal file
View File

@ -0,0 +1,68 @@
pub fn get_hex(prefix_len: u32, data: Vec<u8>) -> String {
let mut header_begin = prefix_len >> 4;
let mut counter = prefix_len % 16;
// println!("{}", header_begin);
// println!("{}", counter);
let mut ret = String::new();
for i in data {
if counter % 16 == 0 {
counter = 0;
// if header_begin != 0 {
// ret.push('\n');
// }
header_begin += 1;
ret += &format!("{:04x}\t", header_begin - 1);
}
counter += 1;
ret = ret + &*format!("{:02x} ", i);
if counter % 16 ==0 {
ret.push('\n');
}
}
return ret;
}
pub fn get_ascii(prefix_len: u32, data: Vec<u8>) -> String {
let mut header_begin = prefix_len >> 4;
let mut counter = prefix_len % 16;
// println!("{}", header_begin);
// println!("{}", counter);
let mut ret = String::new();
for i in data {
if counter % 16 == 0 {
counter = 0;
// if header_begin != 0 { ret.push('\n'); }
header_begin += 1;
ret += &format!("{:04x}\t", header_begin - 1);
}
counter += 1;
if i.is_ascii_alphanumeric() || i.is_ascii_punctuation() || i.is_ascii_graphic() {
// ret += &*(i as char).to_string();
ret.push(char::from(i));
// basic_show.push(i as char);
} else {
ret += ".";
}
if counter % 16 ==0 {
ret.push('\n');
}
}
return ret;
}
#[cfg(test)]
mod tests {
use crate::hexdump::get_hex;
#[test]
fn exploration() {
let data = vec![10, 20, 203];
let tmp = get_hex(14, data);
println!("{}", tmp);
// assert_eq!(2 + 2, 4);
}
}

View File

@ -1,11 +1,13 @@
#![windows_subsystem = "windows"]
mod port;
mod hexdump;
use closure::closure;
use gtk::{Inhibit, TextView, Statusbar, Label};
use gtk::{gio, glib};
use gtk::prelude::{GtkWindowExt, BuilderExtManual, GtkMenuItemExt, WidgetExt, EntryExt, TextBufferExt};
use gtk::prelude::{GtkWindowExt, BuilderExtManual, GtkMenuItemExt, WidgetExt, EntryExt, TextBufferExt, ScrolledWindowExt, AdjustmentExt};
use gio::prelude::{ApplicationExt, ApplicationExtManual};
use std::sync::mpsc::{Sender, Receiver, channel};
use serialport::{StopBits, FlowControl, Parity, DataBits};
@ -13,6 +15,10 @@ use crate::port::*;
use gtk::prelude::TextViewExt;
use gtk::prelude::LabelExt;
use std::str;
use std::time::Duration;
use std::num::ParseIntError;
fn main() {
let application = gtk::Application::new(
Some("com.github.gtk-rs.examples.menu_bar_system"),
@ -27,8 +33,8 @@ fn main() {
fn build_ui(application: &gtk::Application) {
let builder = gtk::Builder::from_string(include_str!("test.ui"));
let main_window: gtk::ApplicationWindow = builder.object("main_window").expect("Can not get window!");
main_window.set_application(Some(application));
let ui_main_window: gtk::ApplicationWindow = builder.object("main_window").expect("Can not get window!");
ui_main_window.set_application(Some(application));
let menu_view_command: gtk::MenuItem = builder.object("view_command").expect("Can not get command item");
let command_entry: gtk::Entry = builder.object("command_entry").expect("Can not get command entry");
let tmp = command_entry.clone();
@ -40,23 +46,38 @@ fn build_ui(application: &gtk::Application) {
tmp.set_has_focus(true);
}
});
let log_window: gtk::Window = builder.object("log_window").expect("Can not get log view");
let ui_log_window: gtk::Window = builder.object("log_window").expect("Can not get log view");
let menu_log_command: gtk::MenuItem = builder.object("log_command").expect("Can not get log command");
let menu_hex_command: gtk::MenuItem = builder.object("hex_command").expect("Can not get log command");
let ui_receive_view: TextView = builder.object("receive_view").expect("can not get receive view");
let ui_baudrate: Label = builder.object("ui_baudrate").unwrap();
let ui_databit: Label = builder.object("ui_databit").unwrap();
let ui_parity: Label = builder.object("ui_parity").unwrap();
let ui_stopbit: Label = builder.object("ui_stopbit").unwrap();
let ui_period: Label = builder.object("ui_period").unwrap();
let ui_com: Label = builder.object("ui_com").unwrap();
let ui_statusbar: Statusbar = builder.object("statusbar").unwrap();
let ui_log_view: TextView = builder.object("log_view").unwrap();
let ui_hex_view: TextView = builder.object("hex_view").unwrap();
let ui_hex_window: gtk::Window = builder.object("hex_window").unwrap();
let ui_ascii_view: TextView = builder.object("ascii_view").unwrap();
let ui_hex_scroll: gtk::ScrolledWindow = builder.object("ui_hex_scroll").unwrap();
log_window.show();
log_window.connect_delete_event(|a, _| {
// ui_viewport.set_vscroll_policy(Sc)
ui_log_window.show();
// ui_hex_window.show();
ui_log_window.connect_delete_event(|a, _| {
a.hide();
Inhibit(true)
});
let tmp = log_window.clone();
ui_hex_window.connect_delete_event(|a, _| {
a.hide();
Inhibit(true)
});
let tmp = ui_log_window.clone();
menu_log_command.connect_activate(move |_| {
if tmp.is_visible() {
tmp.hide();
@ -64,6 +85,16 @@ fn build_ui(application: &gtk::Application) {
tmp.show();
}
});
let tmp = ui_hex_window.clone();
menu_hex_command.connect_activate(move |_| {
if tmp.is_visible() {
tmp.hide();
} else {
tmp.show();
}
});
let mut receive_char_counter = 0;
let (sender_ui_upd, receiver_ui_upd) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
receiver_ui_upd.attach(None, move |msg| {
match msg {
@ -71,19 +102,41 @@ fn build_ui(application: &gtk::Application) {
let buf = ui_log_view.buffer().unwrap();
buf.insert(&mut buf.end_iter(), text.as_str());
}
Message::UiUpdateReceiveMsg(text) => {
Message::UiUpdateReceiveMsg(data) => {
let mut basic_show = String::new();
for i in data.clone() {
if i.is_ascii_alphanumeric() || i.is_ascii_whitespace() || i.is_ascii_punctuation() || i.is_ascii_graphic() {
basic_show.push(i as char);
}
}
let buf = ui_hex_view.buffer().unwrap();
let hex = hexdump::get_hex(receive_char_counter, data.clone());
buf.insert(&mut buf.end_iter(), hex.as_str());
// println!("{:?}",std::mem::size_of_val(&buf));
let buf = ui_receive_view.buffer().unwrap();
buf.insert(&mut buf.end_iter(), text.as_str());
ui_receive_view.scroll_to_mark(&buf.get_insert().unwrap(), 0.0, true, 0.5, 0.5);
buf.insert(&mut buf.end_iter(), basic_show.as_str());
let buf = ui_ascii_view.buffer().unwrap();
let ascii = hexdump::get_ascii(receive_char_counter, data.clone());
buf.insert(&mut buf.end_iter(), ascii.as_str());
receive_char_counter += data.len() as u32;
let tmp = ui_hex_scroll.vadjustment();
tmp.set_value(tmp.upper());
}
Message::UiCleanReceiveMsg => ui_receive_view.buffer().unwrap().set_text(""),
Message::UiCleanLog => ui_log_view.buffer().unwrap().set_text(""),
Message::UiShowLog => log_window.show(),
Message::UiHideLog => log_window.hide(),
Message::UiShowLog => ui_log_window.show(),
Message::UiHideLog => ui_log_window.hide(),
Message::UiShowHex => ui_hex_window.show(),
Message::UiHideHex => ui_hex_window.hide(),
Message::UiUpdateStatusBarBaudrate(value) => ui_baudrate.set_text(value.to_string().as_str()),
Message::UiUpdateStatusBarDatabit(value) => ui_databit.set_text(value.to_string().as_str()),
Message::UiUpdateStatusBarParity(value) => ui_parity.set_text(value.to_string().as_str()),
Message::UiUpdateStatusBarStopbit(value) => ui_stopbit.set_text(value.to_string().as_str()),
Message::UiUpdateStatusBarPeriod(value) => ui_period.set_text(&*(value.to_string() + " ms")),
Message::UiUpdateStatusBarCom(value) => ui_com.set_text(value.to_string().as_str()),
_ => {}
}
@ -102,7 +155,7 @@ fn build_ui(application: &gtk::Application) {
process_cmd(command, to_ui.clone(), gui2main_tx.clone());
});
main_window.show();
ui_main_window.show();
}
@ -135,7 +188,21 @@ fn process_cmd(command: String, to_ui: glib::Sender<Message>, gui2main_tx: Sende
to_ui.send(Message::UiUpdateLog(list_available_ports().0)).unwrap();
}
"send" => {
gui2main_tx.send(Message::MainCmdSend(command[5..].parse().unwrap())).unwrap();
if command.len() <= 5 {
to_ui.send(Message::UiUpdateLog("no data!\n".parse().unwrap())).unwrap();
} else {
gui2main_tx.send(Message::MainCmdSend(command[5..].parse().unwrap())).unwrap();
}
}
"loop" => {
if command.len() <= 5 {
to_ui.send(Message::UiUpdateLog("no data!\n".parse().unwrap())).unwrap();
} else {
gui2main_tx.send(Message::MainCmdSendPeriod(command[5..].parse().unwrap())).unwrap();
}
}
"loopend" => {
gui2main_tx.send(Message::MainCmdStopPeriod).unwrap();
}
"clean" => {
to_ui.send(Message::UiCleanReceiveMsg).unwrap();
@ -146,7 +213,8 @@ fn process_cmd(command: String, to_ui: glib::Sender<Message>, gui2main_tx: Sende
to_ui.send(Message::UiUpdateLog("No enough argument\n".to_string())).unwrap();
} else {
match command_split[1] {
"log" => { to_ui.send(Message::UiShowLog).unwrap(); }
"log" => to_ui.send(Message::UiShowLog).unwrap(),
"hex" => to_ui.send(Message::UiShowHex).unwrap(),
_ => {}
}
}
@ -156,7 +224,8 @@ fn process_cmd(command: String, to_ui: glib::Sender<Message>, gui2main_tx: Sende
to_ui.send(Message::UiUpdateLog("No enough argument\n".to_string())).unwrap();
} else {
match command_split[1] {
"log" => { to_ui.send(Message::UiHideLog).unwrap(); }
"log" => to_ui.send(Message::UiHideLog).unwrap(),
"hex" => to_ui.send(Message::UiHideHex).unwrap(),
_ => {}
}
}
@ -168,7 +237,6 @@ fn process_cmd(command: String, to_ui: glib::Sender<Message>, gui2main_tx: Sende
fn main_proc(to_main_tx: Sender<Message>, rx: Receiver<Message>, ui_upd: glib::Sender<Message>) {
let (mut main2port_tx, _rx_port_receive) = channel::<Message>();
let mut default_com_port = "USB0".to_string();
let mut port_default_config = PortConfig {
baud_rate: 115_200,
data_bits: DataBits::Eight,
@ -178,8 +246,8 @@ fn main_proc(to_main_tx: Sender<Message>, rx: Receiver<Message>, ui_upd: glib::S
timeout: 10,
};
let mut port_status_open = false;
default_com_port = list_available_ports().1;
ui_upd.send(Message::UiUpdateStatusBarCom(default_com_port.clone()));
let mut default_com_port = list_available_ports().1;
ui_upd.send(Message::UiUpdateStatusBarCom(default_com_port.clone())).unwrap();
loop {
if let Ok(msg) = rx.try_recv() {
match msg {
@ -187,19 +255,19 @@ fn main_proc(to_main_tx: Sender<Message>, rx: Receiver<Message>, ui_upd: glib::S
Message::MainCmdOpen(arg) => {
if port_status_open {
ui_upd.send(Message::UiUpdateLog("Port Already Open\n".to_string())).unwrap();
continue;
continue;
}
let (tmp_to_port, main2port_rx) = channel::<Message>();
main2port_tx = tmp_to_port;
let port_name = if arg.is_empty() {default_com_port.clone()} else {arg};
let port_name = if arg.is_empty() { default_com_port.clone() } else { arg };
default_com_port = port_name.clone();
ui_upd.send(Message::UiUpdateStatusBarCom(default_com_port.clone()));
ui_upd.send(Message::UiUpdateStatusBarCom(default_com_port.clone())).unwrap();
match port_open(port_name, port_default_config) {
Ok(port) => {
port_status_open = true;
std::thread::spawn(closure!(clone to_main_tx, || port_proc(port, to_main_tx, main2port_rx)));
ui_upd.send(Message::UiUpdateLog("open Port succeed\n".to_string())).unwrap();
ui_upd.send(Message::UiUpdateLog("open port succeed\n".to_string())).unwrap();
}
Err(e) => {
match e {
@ -211,16 +279,15 @@ fn main_proc(to_main_tx: Sender<Message>, rx: Receiver<Message>, ui_upd: glib::S
}
}
Message::MainCmdClose => {
if port_status_open{
if port_status_open {
main2port_tx.send(Message::PortCmdClose).unwrap();
}
else {
} else {
ui_upd.send(Message::UiUpdateLog("No port opened\n".to_string())).unwrap();
}
}
Message::MainCmdSend(msg) => {
main2port_tx.send(Message::PortCmdSend(msg)).unwrap();
}
Message::MainCmdSend(msg) => main2port_tx.send(Message::PortCmdSend(msg)).unwrap(),
Message::MainCmdSendPeriod(msg) => { main2port_tx.send(Message::PortPeriodSend(msg)).unwrap() }
Message::MainCmdStopPeriod=> { main2port_tx.send(Message::PortPeriodStop).unwrap() }
Message::MainCmdSet(args) => {
match args[0].as_str() {
"baudrate" => {
@ -287,7 +354,7 @@ fn main_proc(to_main_tx: Sender<Message>, rx: Receiver<Message>, ui_upd: glib::S
match value {
1 => port_default_config.stopbit = StopBits::One,
2 => port_default_config.stopbit = StopBits::Two,
_ => { ui_upd.send(Message::UiUpdateLog("Invalid command!\n".to_string())).unwrap(); }
_ => ui_upd.send(Message::UiUpdateLog("Invalid command!\n".to_string())).unwrap(),
}
} else {
ui_upd.send(Message::UiUpdateLog("Invalid command!\n".to_string())).unwrap();
@ -306,31 +373,44 @@ fn main_proc(to_main_tx: Sender<Message>, rx: Receiver<Message>, ui_upd: glib::S
}
}
}
"period" => {
if args.len() != 2 {
ui_upd.send(Message::UiUpdateLog("Invalid command!\n".to_string())).unwrap();
} else {
match args[1].parse::<u64>() {
Ok(value) => {
if port_status_open {
main2port_tx.send(Message::PortPeriodDuration(value)).unwrap();
ui_upd.send(Message::UiUpdateStatusBarPeriod(value)).unwrap();
}
else{
ui_upd.send(Message::UiUpdateLog("No port open\n".parse().unwrap())).unwrap();
}
}
Err(e) => ui_upd.send(Message::UiUpdateLog(format!("{:?}\n", e))).unwrap(),
}
}
}
_ => { ui_upd.send(Message::UiUpdateLog("command does not support!\n".to_string())).unwrap(); }
}
}
Message::PortHaveData(data) => {
let mut message_show = String::new();
for i in data {
// let char = i as char;
// // let char = 'i';
if i.is_ascii_alphanumeric() || i.is_ascii_whitespace() || i.is_ascii_punctuation() || i.is_ascii_graphic() {
message_show.push(i as char);
}
}
// ui_upd.send(Message::UiUpdateReceiveMsg(String::from_utf8_lossy(&*data).parse().unwrap())).unwrap();
ui_upd.send(Message::UiUpdateReceiveMsg(message_show)).unwrap();
ui_upd.send(Message::UiUpdateReceiveMsg(data)).unwrap();
}
Message::PortCloseSucceed => {
ui_upd.send(Message::UiUpdateLog("Close port succeed\n".to_string())).unwrap();
port_status_open = false;
port_status_open = false;
}
Message::PortError(msg) => {
ui_upd.send(Message::UiUpdateLog(format!("port closed by error: {}", msg))).unwrap();
ui_upd.send(Message::UiUpdateLog(format!("port closed by error: {}\n", msg))).unwrap();
port_status_open = false;
}
Message::PortPeriodDuration(value) => {
ui_upd.send(Message::UiUpdateStatusBarPeriod(value)).unwrap();
}
_ => {}
}
}
std::thread::sleep(Duration::from_millis(20));
}
}

View File

@ -6,26 +6,35 @@ use std::time::Duration;
pub enum Message {
UiUpdateLog(String),
UiUpdateReceiveMsg(String),
UiUpdateReceiveMsg(Vec<u8>),
UiCleanReceiveMsg,
UiCleanLog,
UiShowLog,
UiHideLog,
UiShowHex,
UiHideHex,
UiUpdateStatusBarCom(String),
UiUpdateStatusBarBaudrate(u32),
UiUpdateStatusBarParity(String),
UiUpdateStatusBarStopbit(u32),
UiUpdateStatusBarPeriod(u64),
UiUpdateStatusBarDatabit(u32),
MainCmdOpen(String),
MainCmdClose,
MainCmdSend(String),
MainCmdSet(Vec<String>),
MainCmdSendPeriod(String),
MainCmdStopPeriod,
PortCmdClose,
PortCmdSend(String),
PortPeriodSend(String),
PortPeriodStop,
PortPeriodDuration(u64),
PortCloseSucceed,
PortHaveData(Vec<u8>),
PortError(String),
@ -46,14 +55,14 @@ pub struct PortConfig {
}
pub fn port_open(port: String, config: PortConfig) -> Result<Box<dyn SerialPort>, Error> {
let mut prelude = ""; if env::consts::OS == "linux" { prelude = "/dev/tty"; }
else if env::consts::OS == "windows" { prelude = "" }
let mut prelude = "";
if env::consts::OS == "linux" { prelude = "/dev/tty"; } else if env::consts::OS == "windows" { prelude = "" }
let port_builder = serialport::new(prelude.to_owned() + &port, config.baud_rate)
.data_bits(config.data_bits)
.parity(config.parity)
.flow_control(config.flow_control)
.stop_bits(config.stopbit)
.timeout(Duration::from_millis(config.timeout as u64));
.flow_control(config.flow_control)
.stop_bits(config.stopbit)
.timeout(Duration::from_millis(config.timeout as u64));
match port_builder.open() {
Ok(port) => { return Ok(port); }
Err(e) => {
@ -65,6 +74,11 @@ pub fn port_open(port: String, config: PortConfig) -> Result<Box<dyn SerialPort>
pub fn port_proc(mut port: Box<dyn SerialPort>, to_main: Sender<Message>, from_main: Receiver<Message>) {
let mut buf: Vec<u8> = vec![0; 128];
let mut period_send = false;
let mut period_timeout = std::time::Duration::from_millis(100);
let mut period_send_msg = String::new();
let mut last_send = std::time::Instant::now();
to_main.send(Message::PortPeriodDuration(100));
loop {
if let Ok(msg) = from_main.try_recv() {
match msg {
@ -80,6 +94,16 @@ pub fn port_proc(mut port: Box<dyn SerialPort>, to_main: Sender<Message>, from_m
_ => {}
}
}
Message::PortPeriodSend(msg) => {
period_send = true;
period_send_msg = msg;
}
Message::PortPeriodStop=> {
period_send = false;
}
Message::PortPeriodDuration(value) => {
period_timeout = std::time::Duration::from_millis(value);
}
_ => {}
}
}
@ -92,10 +116,17 @@ pub fn port_proc(mut port: Box<dyn SerialPort>, to_main: Sender<Message>, from_m
}
}
}
if period_send && (last_send.elapsed() > period_timeout){
last_send = std::time::Instant::now();
match port.write(period_send_msg.as_bytes()) {
Err(e) => to_main.send(Message::PortError(format!("{}", e).to_string())).unwrap(),
_ => {}
}
}
}
}
pub(crate) fn list_available_ports() -> (String, String){
pub(crate) fn list_available_ports() -> (String, String) {
let mut ret = String::new();
let mut ret_portname = String::new();
match serialport::available_ports() {
@ -111,5 +142,5 @@ pub(crate) fn list_available_ports() -> (String, String){
ret = format!("{}", err);
}
}
(ret,ret_portname)
(ret, ret_portname)
}

View File

@ -2,8 +2,60 @@
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkWindow" id="hex_window">
<property name="can-focus">False</property>
<property name="default-width">900</property>
<property name="default-height">400</property>
<child>
<object class="GtkScrolledWindow" id="ui_hex_scroll">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="shadow-type">out</property>
<child>
<object class="GtkPaned">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
<child>
<object class="GtkTextView" id="hex_view">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="editable">False</property>
<property name="monospace">True</property>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<object class="GtkTextView" id="ascii_view">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="editable">False</property>
<property name="monospace">True</property>
</object>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<object class="GtkWindow" id="log_window">
<property name="can-focus">False</property>
<property name="default-width">300</property>
<property name="default-height">400</property>
<property name="icon-name">network-server</property>
<child>
<object class="GtkScrolledWindow">
@ -22,8 +74,8 @@
</object>
<object class="GtkApplicationWindow" id="main_window">
<property name="can-focus">False</property>
<property name="default-width">300</property>
<property name="default-height">400</property>
<property name="default-width">600</property>
<property name="default-height">800</property>
<property name="icon-name">network-server</property>
<child>
<object class="GtkBox">
@ -176,7 +228,7 @@
</object>
</child>
<child>
<object class="GtkMenuItem">
<object class="GtkMenuItem" id="hex_command">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">hex</property>
@ -238,6 +290,7 @@
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="editable">False</property>
<property name="monospace">True</property>
</object>
</child>
</object>
@ -318,6 +371,19 @@
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ui_period">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">100 ms</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">6</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>