Skip to content

Commit 0071490

Browse files
committed
v0.8.0: refactor + new features
- Bug fix: SerialState op_lock concurrency race condition - Bug fix: CSS dead code and undefined variable - Refactor: split serial_cmd.rs into encoding_utils + multi_string + serial_cmd - Refactor: split handle_mcp into dedicated handler functions - Refactor: ChecksumAlgo enum replaces string-based algorithm selection - Refactor: unified hex parsing, extracted SerialState::to_port_info() - Rename: McpBuffer -> ReceiveBuffer, window_helper url -> path - Feature: multi-string items support per-entry name/note - Feature: LSB/MSB byte-order switch for CRC16/CRC32 checksum
1 parent 59c51a0 commit 0071490

19 files changed

Lines changed: 725 additions & 467 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "zcom",
33
"private": true,
4-
"version": "0.7.1",
4+
"version": "0.8.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "zcom"
3-
version = "0.7.1"
3+
version = "0.8.0"
44
description = "High performance serial debug assistant"
55
authors = ["zt"]
66
edition = "2021"

src-tauri/src/checksum.rs

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
use crc::{Crc, Algorithm};
22
use serde::Serialize;
3+
use std::str::FromStr;
4+
5+
#[derive(Debug, Clone, Copy, PartialEq)]
6+
pub enum ChecksumAlgo {
7+
Crc16,
8+
Crc32,
9+
Add8,
10+
Xor8,
11+
}
12+
13+
impl FromStr for ChecksumAlgo {
14+
type Err = String;
15+
fn from_str(s: &str) -> Result<Self, Self::Err> {
16+
match s {
17+
"crc16" => Ok(Self::Crc16),
18+
"crc32" => Ok(Self::Crc32),
19+
"add8" => Ok(Self::Add8),
20+
"xor8" => Ok(Self::Xor8),
21+
_ => Err(format!("Unknown checksum algorithm: {}", s)),
22+
}
23+
}
24+
}
325

426
#[derive(Debug, Serialize)]
527
pub struct ChecksumResult {
@@ -29,46 +51,48 @@ const CRC32: Crc<u32> = Crc::<u32>::new(&Algorithm {
2951
residue: 0xDEBB20E3,
3052
});
3153

32-
pub fn calc_checksum(data: &[u8], algo: &str) -> ChecksumResult {
54+
pub fn calc_checksum(data: &[u8], algo: ChecksumAlgo) -> ChecksumResult {
3355
match algo {
34-
"crc16" => {
56+
ChecksumAlgo::Crc16 => {
3557
let digest = CRC16_MODBUS.checksum(data);
3658
ChecksumResult {
3759
value: digest.to_string(),
3860
hex: format!("{:04X}", digest),
3961
}
4062
}
41-
"crc32" => {
63+
ChecksumAlgo::Crc32 => {
4264
let digest = CRC32.checksum(data);
4365
ChecksumResult {
4466
value: digest.to_string(),
4567
hex: format!("{:08X}", digest),
4668
}
4769
}
48-
"add8" => {
70+
ChecksumAlgo::Add8 => {
4971
let sum: u8 = data.iter().fold(0u8, |a, b| a.wrapping_add(*b));
5072
ChecksumResult {
5173
value: sum.to_string(),
5274
hex: format!("{:02X}", sum),
5375
}
5476
}
55-
"xor8" => {
77+
ChecksumAlgo::Xor8 => {
5678
let xor = data.iter().fold(0u8, |a, b| a ^ b);
5779
ChecksumResult {
5880
value: xor.to_string(),
5981
hex: format!("{:02X}", xor),
6082
}
6183
}
62-
_ => ChecksumResult {
63-
value: "0".into(),
64-
hex: "00".into(),
65-
},
6684
}
6785
}
6886

69-
pub fn apply_checksum(data: &[u8], algo: &str, position: i32) -> Vec<u8> {
87+
pub fn apply_checksum(data: &[u8], algo: ChecksumAlgo, position: i32, lsb: bool) -> Vec<u8> {
7088
let result = calc_checksum(data, algo);
71-
let check_bytes = hex_to_bytes(&result.hex);
89+
let mut check_bytes: Vec<u8> = (0..result.hex.len())
90+
.step_by(2)
91+
.map(|i| u8::from_str_radix(&result.hex[i..i + 2], 16).unwrap())
92+
.collect();
93+
if lsb {
94+
check_bytes.reverse();
95+
}
7296
let pos = if position >= 0 {
7397
position as usize
7498
} else {
@@ -82,12 +106,3 @@ pub fn apply_checksum(data: &[u8], algo: &str, position: i32) -> Vec<u8> {
82106
out.extend_from_slice(&data[pos..]);
83107
out
84108
}
85-
86-
fn hex_to_bytes(s: &str) -> Vec<u8> {
87-
let s = s.trim();
88-
if s.len() < 2 { return vec![]; }
89-
let bytes: Vec<u8> = (0..s.len()).step_by(2)
90-
.filter_map(|i| u8::from_str_radix(&s[i..i+2], 16).ok())
91-
.collect();
92-
bytes
93-
}

src-tauri/src/encoding_utils.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
fn decode_oem_text(bytes: &[u8]) -> String {
2+
#[cfg(windows)]
3+
{
4+
extern "system" {
5+
fn GetOEMCP() -> u32;
6+
}
7+
let cp = unsafe { GetOEMCP() };
8+
match cp {
9+
936 => encoding_rs::GBK.decode(bytes).0.into_owned(),
10+
932 => encoding_rs::SHIFT_JIS.decode(bytes).0.into_owned(),
11+
949 => encoding_rs::EUC_KR.decode(bytes).0.into_owned(),
12+
950 => encoding_rs::BIG5.decode(bytes).0.into_owned(),
13+
1250 | 1252 | 1254 | 1257 => encoding_rs::WINDOWS_1252.decode(bytes).0.into_owned(),
14+
1251 => encoding_rs::WINDOWS_1251.decode(bytes).0.into_owned(),
15+
1253 => encoding_rs::ISO_8859_7.decode(bytes).0.into_owned(),
16+
1255 => encoding_rs::WINDOWS_1255.decode(bytes).0.into_owned(),
17+
1256 => encoding_rs::WINDOWS_1256.decode(bytes).0.into_owned(),
18+
1258 => encoding_rs::WINDOWS_1258.decode(bytes).0.into_owned(),
19+
_ => String::from_utf8_lossy(bytes).into_owned(),
20+
}
21+
}
22+
#[cfg(not(windows))]
23+
{
24+
String::from_utf8_lossy(bytes).into_owned()
25+
}
26+
}
27+
28+
pub(crate) fn encode_text(text: &str, encoding: &str) -> Vec<u8> {
29+
match encoding {
30+
"gbk" => {
31+
let (cow, _, _) = encoding_rs::GBK.encode(text);
32+
cow.into_owned()
33+
}
34+
_ => text.as_bytes().to_vec(),
35+
}
36+
}
37+
38+
pub(crate) fn parse_hex_string(s: &str) -> Result<Vec<u8>, String> {
39+
let s = s.trim();
40+
if s.is_empty() {
41+
return Ok(vec![]);
42+
}
43+
let hex_chars: String = s.chars().filter(|c| !c.is_whitespace()).collect();
44+
if hex_chars.len() % 2 != 0 {
45+
return Err("Hex string must have even number of characters".into());
46+
}
47+
let bytes: Result<Vec<u8>, _> = (0..hex_chars.len())
48+
.step_by(2)
49+
.map(|i| u8::from_str_radix(&hex_chars[i..i + 2], 16))
50+
.collect();
51+
bytes.map_err(|e| format!("Invalid hex: {}", e))
52+
}
53+
54+
pub(crate) fn get_port_description(name: &str) -> Option<String> {
55+
let mut cmd = std::process::Command::new("wmic");
56+
#[cfg(windows)]
57+
{
58+
use std::os::windows::process::CommandExt;
59+
cmd.creation_flags(0x08000000);
60+
}
61+
let output = cmd
62+
.args([
63+
"path", "Win32_SerialPort",
64+
"where", &format!("DeviceID='{}'", name),
65+
"get", "Name", "/format:value",
66+
])
67+
.output()
68+
.ok()?;
69+
let text = decode_oem_text(&output.stdout);
70+
for line in text.lines() {
71+
let line = line.trim();
72+
if line.is_empty() {
73+
continue;
74+
}
75+
if let Some(value) = line.strip_prefix("Name=") {
76+
let value: String = value.chars().filter(|c| !c.is_control()).collect();
77+
let value = value.trim().trim_matches('"');
78+
if !value.is_empty() {
79+
return Some(value.to_string());
80+
}
81+
}
82+
}
83+
None
84+
}
85+
86+
#[tauri::command]
87+
pub async fn decode_bytes(bytes: Vec<u8>, encoding: String) -> Result<String, String> {
88+
match encoding.as_str() {
89+
"gbk" => {
90+
let (cow, _, _) = encoding_rs::GBK.decode(&bytes);
91+
Ok(cow.into_owned())
92+
}
93+
_ => Ok(String::from_utf8_lossy(&bytes).into_owned()),
94+
}
95+
}

src-tauri/src/lib.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
mod serial_cmd;
22
mod checksum;
33
mod state;
4-
mod mcp_buffer;
4+
mod receive_buffer;
55
mod mcp_server;
6+
mod window_helper;
7+
mod encoding_utils;
8+
mod multi_string;
69

710
use state::SerialState;
8-
use mcp_buffer::McpBuffer;
11+
use receive_buffer::ReceiveBuffer;
912
use mcp_server::McpServerHandle;
1013
use tauri::Manager;
1114
use tauri::menu::{MenuBuilder, MenuItemBuilder};
@@ -26,7 +29,7 @@ pub fn run() {
2629
.plugin(tauri_plugin_fs::init())
2730
.plugin(tauri_plugin_window_state::Builder::default().build())
2831
.manage(SerialState::new())
29-
.manage(McpBuffer::new())
32+
.manage(ReceiveBuffer::new())
3033
.manage(McpServerHandle::new())
3134
.invoke_handler(tauri::generate_handler![
3235
serial_cmd::list_ports,
@@ -37,16 +40,16 @@ pub fn run() {
3740
serial_cmd::send_raw_bytes,
3841
serial_cmd::get_port_info,
3942
serial_cmd::calculate_checksum,
40-
serial_cmd::open_multi_string_window,
41-
serial_cmd::load_multi_strings,
42-
serial_cmd::save_multi_strings,
43-
serial_cmd::decode_bytes,
4443
serial_cmd::set_baud_rate,
44+
multi_string::open_multi_string_window,
45+
multi_string::load_multi_strings,
46+
multi_string::save_multi_strings,
47+
encoding_utils::decode_bytes,
4548
mcp_server::mcp_start,
4649
mcp_server::mcp_stop,
4750
mcp_server::mcp_get_status,
48-
mcp_buffer::mcp_push_lines,
49-
mcp_buffer::mcp_clear_buffer,
51+
receive_buffer::mcp_push_lines,
52+
receive_buffer::mcp_clear_buffer,
5053
open_devtools,
5154
])
5255
.setup(|app| {

0 commit comments

Comments
 (0)