updates
This commit is contained in:
167
scripts/build_rom_button_output_sweep.py
Normal file
167
scripts/build_rom_button_output_sweep.py
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build a fresh-boot webcam sweep for ROM-derived panel-output candidates."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
CHECKSUM_SEED = 0x5A
|
||||
CONNECT_OK_FRAME = "00 00 00 80 00 DA"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Candidate:
|
||||
label: str
|
||||
selector: int
|
||||
value: int
|
||||
note: str
|
||||
|
||||
|
||||
CANDIDATES: tuple[Candidate, ...] = (
|
||||
Candidate("positive_0013_4000_iris_mblack_link", 0x0013, 0x4000, "known IRIS/M.BLACK LINK lamp positive control"),
|
||||
Candidate("positive_0013_8000_slave", 0x0013, 0x8000, "known SLAVE lamp positive control"),
|
||||
Candidate("positive_0015_8000_call", 0x0015, 0x8000, "known CALL lamp positive control"),
|
||||
Candidate("positive_0017_8000_bars", 0x0017, 0x8000, "known BARS lamp positive control"),
|
||||
Candidate("positive_0110_8000_knee_auto", 0x0110, 0x8000, "known KNEE AUTO positive control"),
|
||||
Candidate("rom_001a_0808_multi_button_default", 0x001A, 0x0808, "F6D3 group default/fallback value"),
|
||||
Candidate("rom_001a_2020_f6d3_bit3_family", 0x001A, 0x2020, "F6D3.3-style packed state candidate"),
|
||||
Candidate("rom_001a_4040_f6d3_bit4_family", 0x001A, 0x4040, "F6D3.4-style packed state candidate"),
|
||||
Candidate("rom_001a_8080_f6d3_bit5_family", 0x001A, 0x8080, "F6D3.5-style packed state candidate"),
|
||||
Candidate("rom_006b_8000_f6d4_bit6_candidate", 0x006B, 0x8000, "F6D4.6 handler report value"),
|
||||
Candidate("rom_0083_0004_f6d0_step_candidate", 0x0083, 0x0004, "F6D0.1 lower-step value candidate"),
|
||||
Candidate("rom_0083_4000_high_tag_candidate", 0x0083, 0x4000, "0x0083 high-bit/tag candidate"),
|
||||
Candidate("rom_0083_2000_high_tag_candidate", 0x0083, 0x2000, "0x0083 high-bit/tag candidate"),
|
||||
Candidate("rom_008f_8000_f6d0_bit7_local", 0x008F, 0x8000, "F6D0.7 local SHUTTER/OTHERS report bit"),
|
||||
Candidate("rom_008f_2000_f6d0_bit6_local", 0x008F, 0x2000, "F6D0.6 local SHUTTER/OTHERS report bit"),
|
||||
Candidate("known_008f_0800_evs_display", 0x008F, 0x0800, "known EVS/shutter display positive control"),
|
||||
Candidate("known_008f_1000_off_display", 0x008F, 0x1000, "known OFF/shutter display positive control"),
|
||||
Candidate("rom_0093_1020_f6dc_bit5_context", 0x0093, 0x1020, "F6DC.5 handler context candidate"),
|
||||
Candidate("rom_0093_4040_f6dc_bit4_context", 0x0093, 0x4040, "F6DC.4 handler context candidate"),
|
||||
Candidate("rom_0093_8040_f6dc_bit3_context", 0x0093, 0x8040, "F6DC.3 handler context candidate"),
|
||||
Candidate("rom_0093_0020_f6dc_bit1_context", 0x0093, 0x0020, "F6DC.1 handler low-field candidate"),
|
||||
Candidate("rom_0093_0040_f6dc_bit0_context", 0x0093, 0x0040, "F6DC.0 handler low-field candidate"),
|
||||
Candidate("rom_009a_0800_iris_auto_candidate", 0x009A, 0x0800, "F6DB.3 IRIS AUTO report candidate"),
|
||||
Candidate("rom_00b7_2000_f6d4_bit0_bundle", 0x00B7, 0x2000, "F6D4.0 bundle selector candidate"),
|
||||
Candidate("rom_00b9_4000_f6dc_bit7_candidate", 0x00B9, 0x4000, "F6DC.7 handler value candidate"),
|
||||
Candidate("rom_00c4_8000_f6d4_bit0_bundle", 0x00C4, 0x8000, "F6D4.0 bundle selector candidate"),
|
||||
Candidate("rom_00c6_8000_f6d4_bit0_bundle", 0x00C6, 0x8000, "F6D4.0 bundle selector candidate"),
|
||||
Candidate("rom_00f8_8000_f6d4_bit1_candidate", 0x00F8, 0x8000, "F6D4.1 handler candidate"),
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = build_arg_parser().parse_args()
|
||||
scenario = build_scenario(args)
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(json.dumps(scenario, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"wrote {args.output}")
|
||||
print(f"candidates={len(CANDIDATES)} snapshots={len(CANDIDATES)}")
|
||||
print(f"estimated_hold_time={len(CANDIDATES) * args.listen / 60:.1f}min plus power-cycle/ready time")
|
||||
return 0
|
||||
|
||||
|
||||
def build_arg_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"output",
|
||||
nargs="?",
|
||||
type=Path,
|
||||
default=Path("scenarios/panel-atlas-rom-button-output-candidates-v1.json"),
|
||||
)
|
||||
parser.add_argument("--listen", type=float, default=0.75, help="seconds to listen after each candidate send")
|
||||
parser.add_argument("--ok-listen", type=float, default=0.25, help="seconds to listen after CONNECT OK seeds")
|
||||
parser.add_argument("--drain", type=float, default=0.25, help="seconds to drain after ready")
|
||||
parser.add_argument("--off-seconds", type=float, default=1.5, help="relay power-off time before each candidate")
|
||||
parser.add_argument("--ready-heartbeats", type=int, default=2, help="heartbeats before each candidate")
|
||||
parser.add_argument("--ready-timeout", type=float, default=10.0, help="ready wait timeout")
|
||||
return parser
|
||||
|
||||
|
||||
def build_scenario(args: argparse.Namespace) -> dict[str, object]:
|
||||
steps: list[dict[str, object]] = []
|
||||
for index, candidate in enumerate(CANDIDATES, start=1):
|
||||
steps.extend(candidate_steps(args, index, candidate))
|
||||
steps.append({"action": "listen", "seconds": 0.8})
|
||||
return {
|
||||
"name": "panel-atlas-rom-button-output-candidates-v1",
|
||||
"notes": [
|
||||
"Fresh-boot webcam sweep for ROM-derived button/report output candidates.",
|
||||
"This deliberately skips physical RCP button presses: each candidate sends command 0 directly.",
|
||||
"Each candidate gets its own power-cycle/CONNECT-OK baseline to reduce latch contamination.",
|
||||
"Candidate snapshots only are enabled; setup, CONNECT OK seeds, and clears should not produce webcam images.",
|
||||
"Run with --camera-index 4 --snapshot-delays 0.5 on the current bench.",
|
||||
],
|
||||
"steps": steps,
|
||||
}
|
||||
|
||||
|
||||
def candidate_steps(args: argparse.Namespace, index: int, candidate: Candidate) -> list[dict[str, object]]:
|
||||
label_base = f"case{index:03d}_{candidate.label}"
|
||||
return [
|
||||
{"action": "power_cycle", "off_seconds": args.off_seconds},
|
||||
{
|
||||
"action": "wait_ready",
|
||||
"heartbeats": args.ready_heartbeats,
|
||||
"timeout": args.ready_timeout,
|
||||
"require": True,
|
||||
},
|
||||
{"action": "drain", "seconds": args.drain},
|
||||
send_step(f"{label_base}_ok_seed_1", CONNECT_OK_FRAME, args.ok_listen, snapshot=False),
|
||||
send_step(f"{label_base}_ok_seed_2", CONNECT_OK_FRAME, args.ok_listen, snapshot=False),
|
||||
{
|
||||
"action": "note",
|
||||
"message": (
|
||||
f"{label_base}: selector 0x{candidate.selector:04X}=0x{candidate.value:04X}; "
|
||||
f"{candidate.note}"
|
||||
),
|
||||
},
|
||||
send_step(label_base, frame_hex(0x00, candidate.selector, candidate.value), args.listen, snapshot=True),
|
||||
send_step(
|
||||
f"{label_base}_clear",
|
||||
frame_hex(0x00, candidate.selector, 0x0000),
|
||||
0.12,
|
||||
snapshot=False,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def send_step(label: str, frame: str, listen: float, *, snapshot: bool) -> dict[str, object]:
|
||||
step: dict[str, object] = {
|
||||
"action": "send",
|
||||
"label": label,
|
||||
"frame": frame,
|
||||
"listen": listen,
|
||||
}
|
||||
if not snapshot:
|
||||
step["snapshot"] = False
|
||||
return step
|
||||
|
||||
|
||||
def frame_hex(command: int, selector: int, value: int) -> str:
|
||||
selector_hi, selector_lo = selector_bytes(selector)
|
||||
data = bytes([command & 0xFF, selector_hi, selector_lo, (value >> 8) & 0xFF, value & 0xFF])
|
||||
return " ".join(f"{byte:02X}" for byte in data + bytes([frame_checksum(data)]))
|
||||
|
||||
|
||||
def selector_bytes(selector: int) -> tuple[int, int]:
|
||||
selector &= 0x01FF
|
||||
if selector <= 0x007F:
|
||||
return 0x00, selector
|
||||
if selector <= 0x017F:
|
||||
return 0x01, selector - 0x0080
|
||||
return 0x02, selector - 0x0180
|
||||
|
||||
|
||||
def frame_checksum(data: bytes) -> int:
|
||||
value = CHECKSUM_SEED
|
||||
for byte in data[:5]:
|
||||
value ^= byte & 0xFF
|
||||
return value & 0xFF
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user