Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
274539f
docs(slave-ui): 落地 spec + plan + gitignore 排除 brainstorm 产物
Karl-Dai Apr 20, 2026
86dc416
docs(slave-ui): 正式 spec 与 brainstorm 内容同步
Karl-Dai Apr 20, 2026
07e00a6
feat(theme): 切换冷蓝 palette + 拉开字号梯度 + 新增语义色 token
Karl-Dai Apr 20, 2026
cbe2769
feat(ui-shared): shadcn palette 转冷蓝 + 新增 panel_header/link_action
Karl-Dai Apr 20, 2026
bc11c37
feat(log-panel): 单行 header + 可折叠 + RX/TX 改箭头符号
Karl-Dai Apr 20, 2026
bb46073
docs(ui-shared): 更新模块注释反映冷蓝 palette 与新 helper
Karl-Dai Apr 20, 2026
cf0fe1b
feat(slave-app): SidePanel 收窄 240 + 头部 tiny_caps + shadcn 创建按钮
Karl-Dai Apr 20, 2026
4998d38
feat(slave-app): 主区头改 panel_header + 表头改 tiny_caps + 关掉 striped
Karl-Dai Apr 20, 2026
bcaf0e2
feat(slave-app): SidePanel 重构 — 240px + 头/树/footer 三段
Karl-Dai Apr 20, 2026
986d4d4
feat(slave-app): 值解析可隐藏 + 状态栏视觉化 + V/L/Esc 快捷键
Karl-Dai Apr 20, 2026
1ab3072
style(workspace): cargo fmt --all 整体规整
Karl-Dai Apr 20, 2026
2293044
chore(slave-app): Frame::none → Frame::new (egui 0.33 deprecated)
Karl-Dai Apr 20, 2026
add41d9
feat(slave-app): fmt-pill + 寄存器表格色彩语义化 + 表头下划线
Karl-Dai Apr 20, 2026
b760c9d
style: cargo fmt --all 整体规整
Karl-Dai Apr 20, 2026
3ee53f3
feat(slave-ui): 平衡按钮层级 — 新建/批量添加同级 Outline Sm
Karl-Dai Apr 21, 2026
0314dd1
docs(slave-ui): 连接状态反馈与按钮分工 spec
Karl-Dai Apr 21, 2026
203fc1c
docs(slave-ui): 连接状态反馈 implementation plan (7 task / ~30 step)
Karl-Dai Apr 21, 2026
4225f0c
feat(slave-ui): danger_button_sm helper + SlaveApp.pending_delete 字段
Karl-Dai Apr 21, 2026
1fbd7dc
feat(slave-app): 连接状态视觉化 — 圆点 + 整行染色 + tag 染色
Karl-Dai Apr 21, 2026
76af101
feat(slave-app): 树节点按钮简化 — 单按钮 + 状态色 icon · 删按钮挪 footer
Karl-Dai Apr 21, 2026
648e24f
feat(slave-app): footer 删除连接 — danger_button_sm + 3 秒二次确认
Karl-Dai Apr 21, 2026
37769f8
feat(slave-app): 状态栏脉动 ● + 三态文案(运行中/已停止/未连接)
Karl-Dai Apr 21, 2026
a1595f0
style: cargo fmt --all
Karl-Dai Apr 21, 2026
ca21d4a
fix(jitter): 零值 holding/input 寄存器不动 — 整数除法保底 ±1
Karl-Dai Apr 21, 2026
dc04c9b
fix(slave-app): data source runner 补齐 Coil/DiscreteInput 写入
Karl-Dai Apr 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ Thumbs.db
* [0-9].toml
* [0-9].rs
* [0-9].md

# Brainstorming session artifacts (mockups, state)
.superpowers/
2 changes: 1 addition & 1 deletion crates/modbusmaster-app/build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
fn main() {
tauri_build::build()
tauri_build::build()
}
132 changes: 88 additions & 44 deletions crates/modbusmaster-app/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use modbussim_core::master::{
};
use modbussim_core::parse::{parse_read_function, read_function_to_string};
use modbussim_core::tools;
use modbussim_core::transport::{self, Transport, SerialConfig, Parity, TlsConfig};
use modbussim_core::transport::{self, Parity, SerialConfig, TlsConfig, Transport};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::time::Duration;
Expand Down Expand Up @@ -117,11 +117,32 @@ fn chrono_like_now() -> String {
#[derive(Debug, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum TransportRequest {
Tcp { host: String, port: u16 },
TcpTls { host: String, port: u16 },
Rtu { serial_port: String, baud_rate: u32, data_bits: u8, stop_bits: u8, parity: String },
Ascii { serial_port: String, baud_rate: u32, data_bits: u8, stop_bits: u8, parity: String },
RtuOverTcp { host: String, port: u16 },
Tcp {
host: String,
port: u16,
},
TcpTls {
host: String,
port: u16,
},
Rtu {
serial_port: String,
baud_rate: u32,
data_bits: u8,
stop_bits: u8,
parity: String,
},
Ascii {
serial_port: String,
baud_rate: u32,
data_bits: u8,
stop_bits: u8,
parity: String,
},
RtuOverTcp {
host: String,
port: u16,
},
}

fn parse_parity(s: &str) -> Parity {
Expand All @@ -134,29 +155,44 @@ fn parse_parity(s: &str) -> Parity {

fn to_transport(req: &TransportRequest) -> Transport {
match req {
TransportRequest::Tcp { host, port } => Transport::Tcp { host: host.clone(), port: *port },
TransportRequest::TcpTls { host, port } => Transport::TcpTls { host: host.clone(), port: *port },
TransportRequest::Rtu { serial_port, baud_rate, data_bits, stop_bits, parity } => {
Transport::Rtu(SerialConfig {
port: serial_port.clone(),
baud_rate: *baud_rate,
data_bits: *data_bits,
stop_bits: *stop_bits,
parity: parse_parity(parity),
})
}
TransportRequest::Ascii { serial_port, baud_rate, data_bits, stop_bits, parity } => {
Transport::Ascii(SerialConfig {
port: serial_port.clone(),
baud_rate: *baud_rate,
data_bits: *data_bits,
stop_bits: *stop_bits,
parity: parse_parity(parity),
})
}
TransportRequest::RtuOverTcp { host, port } => {
Transport::RtuOverTcp { host: host.clone(), port: *port }
}
TransportRequest::Tcp { host, port } => Transport::Tcp {
host: host.clone(),
port: *port,
},
TransportRequest::TcpTls { host, port } => Transport::TcpTls {
host: host.clone(),
port: *port,
},
TransportRequest::Rtu {
serial_port,
baud_rate,
data_bits,
stop_bits,
parity,
} => Transport::Rtu(SerialConfig {
port: serial_port.clone(),
baud_rate: *baud_rate,
data_bits: *data_bits,
stop_bits: *stop_bits,
parity: parse_parity(parity),
}),
TransportRequest::Ascii {
serial_port,
baud_rate,
data_bits,
stop_bits,
parity,
} => Transport::Ascii(SerialConfig {
port: serial_port.clone(),
baud_rate: *baud_rate,
data_bits: *data_bits,
stop_bits: *stop_bits,
parity: parse_parity(parity),
}),
TransportRequest::RtuOverTcp { host, port } => Transport::RtuOverTcp {
host: host.clone(),
port: *port,
},
}
}

Expand Down Expand Up @@ -217,7 +253,8 @@ pub async fn create_master_connection(
};

let log_collector = Arc::new(LogCollector::new());
let connection = MasterConnection::new(config.clone(), transport).with_log_collector(log_collector.clone());
let connection =
MasterConnection::new(config.clone(), transport).with_log_collector(log_collector.clone());

let info = MasterConnectionInfo {
id: id.clone(),
Expand Down Expand Up @@ -507,7 +544,9 @@ pub async fn list_scan_groups(
/// Internal helper: start polling for a single group and spawn bridge task.
async fn start_polling_inner(
app: &AppHandle,
master_conns: &std::sync::Arc<tokio::sync::RwLock<std::collections::HashMap<String, MasterConnectionState>>>,
master_conns: &std::sync::Arc<
tokio::sync::RwLock<std::collections::HashMap<String, MasterConnectionState>>,
>,
connection_id: &str,
group_id: &str,
) -> Result<(), String> {
Expand Down Expand Up @@ -885,8 +924,7 @@ pub struct PlcToModbusResult {

#[tauri::command]
pub fn convert_plc_to_modbus(request: PlcToModbusRequest) -> Result<PlcToModbusResult, String> {
let result =
tools::plc_to_modbus_address(request.plc_address).map_err(|e| format!("{}", e))?;
let result = tools::plc_to_modbus_address(request.plc_address).map_err(|e| format!("{}", e))?;
Ok(PlcToModbusResult {
register_type: format!("{:?}", result.address_type),
modbus_address: result.address,
Expand Down Expand Up @@ -959,7 +997,11 @@ pub async fn start_slave_id_scan(
// Create cancel channel
let (cancel_tx, cancel_rx) = oneshot::channel();
let scan_key = format!("{}:slave_scan", connection_id);
state.active_scans.write().await.insert(scan_key.clone(), cancel_tx);
state
.active_scans
.write()
.await
.insert(scan_key.clone(), cancel_tx);

let (progress_tx, mut progress_rx) = mpsc::channel(32);
let conn_id = connection_id.clone();
Expand Down Expand Up @@ -1005,7 +1047,6 @@ pub async fn start_slave_id_scan(
Ok(())
}


#[derive(Debug, Deserialize)]
pub struct RegisterScanRequest {
pub function: String,
Expand Down Expand Up @@ -1038,7 +1079,11 @@ pub async fn start_register_scan(
// Create cancel channel
let (cancel_tx, cancel_rx) = oneshot::channel();
let scan_key = format!("{}:register_scan", connection_id);
state.active_scans.write().await.insert(scan_key.clone(), cancel_tx);
state
.active_scans
.write()
.await
.insert(scan_key.clone(), cancel_tx);

let (progress_tx, mut progress_rx) = mpsc::channel(32);
let conn_id = connection_id.clone();
Expand Down Expand Up @@ -1126,10 +1171,7 @@ fn read_function_to_fc(function: ReadFunction) -> u8 {
}

#[tauri::command]
pub async fn save_project_file(
state: State<'_, AppState>,
path: String,
) -> Result<(), String> {
pub async fn save_project_file(state: State<'_, AppState>, path: String) -> Result<(), String> {
let conns = state.master_connections.read().await;
let mut proj = ProjectFile::new_master();

Expand Down Expand Up @@ -1183,16 +1225,18 @@ pub async fn save_project_file(
name,
transport: proj_transport,
devices: vec![],
scan_groups: conn_state.scan_groups.iter().map(|sg| {
project::ScanGroupConfig {
scan_groups: conn_state
.scan_groups
.iter()
.map(|sg| project::ScanGroupConfig {
name: sg.name.clone(),
slave_id: sg.slave_id.unwrap_or(config.slave_id),
function_code: read_function_to_fc(sg.function),
start_address: sg.start_address,
count: sg.quantity,
interval_ms: sg.interval_ms,
}
}).collect(),
})
.collect(),
};
proj.connections.push(conn_config);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/modbusmaster-app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

fn main() {
modbusmaster_app_lib::run();
modbusmaster_app_lib::run();
}
Loading
Loading