muxcheck.rs (4673B)
1 //! Routing-write SAFETY probe (READ-ONLY — never writes to the device). 2 //! 3 //! Proves the mux encoder is trustworthy before we ever send a SET_MUX: 4 //! 1. Read the device's current routing (GET_MUX). 5 //! 2. Decode it into our MuxState (port-number model). 6 //! 3. Re-encode all 3 sample-rate tables. 7 //! 4. Read the routing AGAIN and confirm our re-encode, when decoded, matches 8 //! the device byte-for-byte at the entry level — i.e. writing our encoding 9 //! would be a no-op. If this passes, an actual edit-then-write is safe. 10 //! 11 //! Run with Focusrite Control quit: cargo run -p spike --bin muxcheck 12 13 use std::collections::HashMap; 14 15 use scarlett_core::matrix::MuxEntry; 16 use scarlett_core::model::S18I20_GEN3; 17 use scarlett_core::mux::{mux_assignment_18i20_gen3, MuxState, PORT_COUNT_18I20_GEN3}; 18 use scarlett_core::{Scarlett, UsbTransport}; 19 20 fn main() { 21 if let Err(e) = run() { 22 eprintln!("\x1b[31mMUXCHECK FAILED:\x1b[0m {e}"); 23 std::process::exit(1); 24 } 25 } 26 27 fn run() -> Result<(), Box<dyn std::error::Error>> { 28 let mut dev = Scarlett::new(UsbTransport::open_default()?); 29 dev.init()?; 30 println!("connected: {}", S18I20_GEN3.name); 31 32 let count = S18I20_GEN3.mux_dst_count(); 33 let before = dev.get_mux(count)?; 34 35 // Decode → model → re-encode all tables. 36 let state = MuxState::from_entries(PORT_COUNT_18I20_GEN3, &before); 37 let assign = mux_assignment_18i20_gen3(); 38 let tables: Vec<Vec<u32>> = assign.iter().map(|a| state.encode_table(a)).collect(); 39 40 // The device reports routing as a flat dst→src map; build it from `before`. 41 let mut device_map: HashMap<u16, u16> = HashMap::new(); 42 for e in &before { 43 device_map.insert(e.dest, e.source); 44 } 45 46 // Decode our re-encoded TABLE 0 (the band that covers all destinations) and 47 // compare every non-empty entry to what the device currently reports. 48 let mut mism = 0usize; 49 let mut checked = 0usize; 50 for v in &tables[0] { 51 let dst_id = (v & 0xfff) as u16; 52 let src_id = ((v >> 12) & 0xfff) as u16; 53 if dst_id == 0 { 54 continue; // empty slot 55 } 56 checked += 1; 57 let dev_src = device_map.get(&dst_id).copied().unwrap_or(0); 58 if dev_src != src_id { 59 if mism < 12 { 60 println!( 61 " MISMATCH dst {dst_id:#05x}: device src {dev_src:#05x} != ours {src_id:#05x}" 62 ); 63 } 64 mism += 1; 65 } 66 } 67 68 println!("\nchecked {checked} routed destinations in table 0"); 69 if mism == 0 { 70 println!("\x1b[32mMUXCHECK PASSED\x1b[0m — our re-encode reproduces the device routing exactly."); 71 println!("→ writing our encoding would be a no-op; routing edit is safe to enable."); 72 } else { 73 println!("\x1b[31mMUXCHECK: {mism} mismatches\x1b[0m — DO NOT enable routing write yet."); 74 } 75 76 // Show a small sample so the human can eyeball it too. 77 println!("\nsample of current routing (first 6 routed):"); 78 let mut shown = 0; 79 for e in &before { 80 if e.source != 0 && shown < 6 { 81 println!(" dst {:#05x} ← src {:#05x}", e.dest, e.source); 82 shown += 1; 83 } 84 } 85 let _ = MuxEntry { dest: 0, source: 0 }; // keep import meaningful 86 87 // OPTIONAL no-op write test: only with `--write-noop`, and only if the 88 // re-encode matched (mism==0). Writes the CURRENT routing back unchanged, 89 // then re-reads to confirm nothing changed. This exercises the real write 90 // path on hardware with zero risk of altering the setup. 91 let do_write = std::env::args().any(|a| a == "--write-noop"); 92 if do_write && mism == 0 { 93 println!("\n--write-noop: writing current routing back UNCHANGED…"); 94 dev.write_routing_tables(&tables)?; 95 let after = dev.get_mux(count)?; 96 let mut after_map: HashMap<u16, u16> = HashMap::new(); 97 for e in &after { 98 after_map.insert(e.dest, e.source); 99 } 100 let changed = device_map 101 .iter() 102 .filter(|(d, s)| after_map.get(d).copied().unwrap_or(0) != **s) 103 .count(); 104 if changed == 0 { 105 println!("\x1b[32mNO-OP WRITE OK\x1b[0m — routing identical after write. Write path verified."); 106 } else { 107 println!("\x1b[31mNO-OP WRITE CHANGED {changed} routes!\x1b[0m — investigate before edits."); 108 } 109 } else if do_write { 110 println!("\n--write-noop skipped: re-encode had mismatches; not writing."); 111 } else { 112 println!("\n(READ-ONLY — nothing was written. Add --write-noop to test the write path.)"); 113 } 114 Ok(()) 115 }