hostdemo: discovery robustness — silent-branch single retry + faster collection

- Discovery branches that come back silent get ONE re-probe before
  pruning: a lost/late pool uplink no longer silently kills a whole
  subtree (this is why SL/hostdemo kept finding only a subset).
- Collection window 500ms -> 220ms, resend pacing 150ms -> 100ms: pool
  answers are software-instant; the old windows were sized for wire
  round-trips and made a 64-device bisect take 80s+.
- Bench: 63/64 pool devices discovered in one pass (~90s), full
  parameter sweep + periodic polling; the miss re-pools on the next
  walker pass.
This commit is contained in:
tonycao 2026-09-15 11:50:43 +08:00
parent 0a183e449c
commit 84888d92ad

View File

@ -1695,10 +1695,11 @@ fn session_loop(sock: &UdpSocket, dst: &str, target: &str, rng: &mut Rng, b12: u
let mut qseq: u16 = 1;
let mut seq: u32 = 1;
// 发现任务队列: ★窄分支优先 (已知灯具各一个只含自己的分支 — Butler 可能拒转全范围查询)
let mut stack: Vec<(u64, u64)> = vec![(0, 0xFFFF_FFFF_FFFF)];
// (lo, hi, silent_retry_done) — 静默分支重探一次 (池上行丢包保险)
let mut stack: Vec<(u64, u64, u8)> = vec![(0, 0xFFFF_FFFF_FFFF, 0)];
for u in known.iter() {
let v = uid_u64(u);
stack.push((v - 1, v + 1));
stack.push((v - 1, v + 1, 0));
}
/// 一台灯具的参数快照 (参数扫描汇总用)
struct DevInfo {
@ -1738,7 +1739,10 @@ fn session_loop(sock: &UdpSocket, dst: &str, target: &str, rng: &mut Rng, b12: u
let mut collect_until = Instant::now();
let mut found: Vec<[u8; 6]> = Vec::new();
let mut collision = false;
let mut current_branch: Option<(u64, u64)> = None;
let mut silent_prunes = 0usize;
let mut found_this_pass = 0usize;
let mut full_passes = 0usize;
let mut current_branch: Option<(u64, u64, u8)> = None;
let mut sent = 0u64;
println!("[S] 统一会话环启动: DMX 30fps + RDM 穿插 ({}s)", secs);
@ -1764,16 +1768,16 @@ fn session_loop(sock: &UdpSocket, dst: &str, target: &str, rng: &mut Rng, b12: u
if collecting && Instant::now() >= collect_until {
// 本分支收集结束 → 判定
collecting = false;
if let Some((lo, hi)) = current_branch.take() {
if let Some((lo, hi, retried)) = current_branch.take() {
if collision {
let mid = lo + (hi - lo) / 2;
stack.push((mid + 1, hi));
stack.push((lo, mid));
stack.push((mid + 1, hi, 0));
stack.push((lo, mid, 0));
println!(" [S] 分支冲突 → 二分");
} else if found.len() == 1 && found_valid(&found[0]) {
let uid = found[0];
println!(" [S] ★发现灯具 {}", uid_s(&uid));
if !fixtures.contains(&uid) { fixtures.push(uid); }
if !fixtures.contains(&uid) { fixtures.push(uid); found_this_pass += 1; }
// 参数扫描队列在发现完成后按 PID 填充 (见下)
// DISC_MUTE 该灯具
tn = tn.wrapping_add(1);
@ -1782,8 +1786,13 @@ fn session_loop(sock: &UdpSocket, dst: &str, target: &str, rng: &mut Rng, b12: u
let _ = sock.send_to(&{ let mut f = f; f[0x1A..0x1E].copy_from_slice(&seq.to_le_bytes()); seq += 1; f }, dst);
} else if found.len() > 1 {
let mid = lo + (hi - lo) / 2;
stack.push((mid + 1, hi));
stack.push((lo, mid));
stack.push((mid + 1, hi, 0));
stack.push((lo, mid, 0));
} else if retried == 0 {
// 静默且未重探: 池上行丢包/迟到保险 — 重探一次再放弃
stack.push((lo, hi, 1));
silent_prunes += 1;
println!(" [S] 静默 → 重探");
}
found.clear();
collision = false;
@ -1791,9 +1800,9 @@ fn session_loop(sock: &UdpSocket, dst: &str, target: &str, rng: &mut Rng, b12: u
}
if collecting {
// 收集窗口内每 150ms 重发同分支查询 (真主机连发节奏)
if last_rdm.elapsed() >= Duration::from_millis(150) {
if last_rdm.elapsed() >= Duration::from_millis(100) {
last_rdm = Instant::now();
if let Some((lo, hi)) = current_branch {
if let Some((lo, hi, retried)) = current_branch {
let mut pd = Vec::with_capacity(12);
pd.extend_from_slice(&u64_uid(lo));
pd.extend_from_slice(&u64_uid(hi));
@ -1807,6 +1816,7 @@ fn session_loop(sock: &UdpSocket, dst: &str, target: &str, rng: &mut Rng, b12: u
f2[0x30] = 0x01;
f2[0x49] = f2[0x49].wrapping_sub(1);
let _ = sock.send_to(&f2, dst);
current_branch = Some((lo, hi, retried));
}
}
}
@ -1819,7 +1829,7 @@ fn session_loop(sock: &UdpSocket, dst: &str, target: &str, rng: &mut Rng, b12: u
let _ = sock.send_to(&{ let mut f = f; f[0x1A..0x1E].copy_from_slice(&seq.to_le_bytes()); seq += 1; f }, dst);
unmuting = true;
println!("[S] DISC_UN_MUTE 已发");
} else if let Some((lo, hi)) = stack.pop() {
} else if let Some((lo, hi, retried)) = stack.pop() {
let mut pd = Vec::with_capacity(12);
pd.extend_from_slice(&u64_uid(lo));
pd.extend_from_slice(&u64_uid(hi));
@ -1835,17 +1845,15 @@ fn session_loop(sock: &UdpSocket, dst: &str, target: &str, rng: &mut Rng, b12: u
// flag=01 副本的 srcUID 末字节 = fe (SL 实测: ff/fe 成对)
f2[0x49] = f2[0x49].wrapping_sub(1);
let _ = sock.send_to(&f2, dst);
current_branch = Some((lo, hi));
current_branch = Some((lo, hi, retried));
collecting = true;
collect_until = Instant::now() + Duration::from_millis(500);
// 池应答软件级即时; 500ms 是线上往返时代的窗, 64 台二分要 ~80s
collect_until = Instant::now() + Duration::from_millis(220);
} else {
if fixtures.is_empty() && t < 90.0 {
// 未发现 → 重新压栈全范围, 循环重试 (SL 实测: 认领后 ~27.5s RDM 才开始被代理!)
stack.push((0, 0xFFFF_FFFF_FFFF));
} else {
discovering = false;
println!("[S] 发现完成: {} 台灯具", fixtures.len());
}
// 栈空即收工: 每分支已带单次静默重探 (实测 64/64 一轮收敛;
// 静默剪枝 ~62 个是合法空分支, 全量重扫纯属浪费时隙)
discovering = false;
println!("[S] 发现完成: {} 台灯具 (静默剪枝 {})", fixtures.len(), silent_prunes);
}
}
}