Skip to content

Commit 57bd572

Browse files
Karl-Daiclaude
andcommitted
Merge branch 'feat/slave-tls-ui-2026-04' into main
包含两批工作: - Slave 端 TLS 传输支持:新建对话框 + 配置持久化 - 空状态 Hero 动画升级:搬共享模块 hero_anim、新增 try_count_within 心跳驱动振幅、三级重绘节流、为 Master 端预留复用 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 parents c28543b + 02f5dfa commit 57bd572

8 files changed

Lines changed: 1409 additions & 28 deletions

File tree

crates/modbusmaster-egui/src/app.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,11 @@ impl MasterApp {
612612

613613
proj.connections.push(MasterConnectionSave {
614614
label: s.label.clone(),
615-
tcp: TcpSpec { host, port },
615+
tcp: TcpSpec {
616+
host,
617+
port,
618+
tls: None,
619+
},
616620
slave_id: s.slave_id,
617621
timeout_ms,
618622
poll,

crates/modbussim-core/src/log_collector.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,24 @@ impl LogCollector {
7272
self.entries.try_read().ok().map(|g| g.clone())
7373
}
7474

75+
/// Non-blocking count of entries whose timestamp is within the last `window`.
76+
/// Returns `None` if a writer holds the lock.
77+
///
78+
/// Uses the fact that entries are pushed in time order: scans from the tail
79+
/// and stops at the first entry older than the cutoff. O(k) where k is the
80+
/// number of recent entries (typically small even if the buffer is large).
81+
pub fn try_count_within(&self, window: std::time::Duration) -> Option<usize> {
82+
let chrono_window = chrono::Duration::from_std(window).ok()?;
83+
let cutoff = chrono::Utc::now() - chrono_window;
84+
self.entries.try_read().ok().map(|guard| {
85+
guard
86+
.iter()
87+
.rev()
88+
.take_while(|e| e.timestamp >= cutoff)
89+
.count()
90+
})
91+
}
92+
7593
/// Get all log entries (blocking version).
7694
pub fn get_all_blocking(&self) -> Vec<LogEntry> {
7795
self.entries.blocking_read().clone()
@@ -272,4 +290,32 @@ mod tests {
272290
assert!(recent[0].detail.contains("R 7 x1"));
273291
assert!(recent[2].detail.contains("R 9 x1"));
274292
}
293+
294+
#[tokio::test]
295+
async fn test_try_count_within_recent_only() {
296+
let collector = LogCollector::new();
297+
let mut old = LogEntry::new(Direction::Rx, FunctionCode::ReadHoldingRegisters, "old");
298+
old.timestamp = chrono::Utc::now() - chrono::Duration::seconds(10);
299+
collector.add(old).await;
300+
for i in 0..3 {
301+
collector
302+
.add(LogEntry::new(
303+
Direction::Rx,
304+
FunctionCode::ReadHoldingRegisters,
305+
format!("new {}", i),
306+
))
307+
.await;
308+
}
309+
let count = collector.try_count_within(std::time::Duration::from_secs(1));
310+
assert_eq!(count, Some(3));
311+
}
312+
313+
#[tokio::test]
314+
async fn test_try_count_within_empty() {
315+
let collector = LogCollector::new();
316+
assert_eq!(
317+
collector.try_count_within(std::time::Duration::from_secs(1)),
318+
Some(0),
319+
);
320+
}
275321
}

0 commit comments

Comments
 (0)