gated run
This commit is contained in:
@@ -611,6 +611,68 @@ This fits the real panel behavior:
|
|||||||
- Correct fake-CCU traffic can wake it to `CONNECT: OK`.
|
- Correct fake-CCU traffic can wake it to `CONNECT: OK`.
|
||||||
- Without richer CCU state, readouts illuminate but show placeholders like `----`.
|
- Without richer CCU state, readouts illuminate but show placeholders like `----`.
|
||||||
|
|
||||||
|
## Active-State Local-Control Watch
|
||||||
|
|
||||||
|
Bench evidence now suggests `CONNECT: OK` is maintained by an ongoing CCU refresh stream, not by one magic wake frame. This creates a useful local-control test: keep the RCP in the active state and press/turn physical controls while logging device TX.
|
||||||
|
|
||||||
|
Two JSON scenarios are set up for that:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\active-control-report-watch-quiet.json --parity E --log captures\active-control-report-watch-quiet.txt --result-json captures\active-control-report-watch-quiet-result.json
|
||||||
|
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\active-control-report-watch-broad.json --parity E --log captures\active-control-report-watch-broad.txt --result-json captures\active-control-report-watch-broad-result.json
|
||||||
|
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\active-control-report-watch-gated.json --parity E --log captures\active-control-report-watch-gated.txt --result-json captures\active-control-report-watch-gated-result.json
|
||||||
|
```
|
||||||
|
|
||||||
|
The quiet scenario sends the selector-zero OK seed, then refreshes `E000[0x0093]=0x9020` every 0.60 s. This is the lowest-noise active hold that has already kept `CONNECT: OK` alive.
|
||||||
|
|
||||||
|
The broad scenario streams `E000[0x008F]=0x1800` and `E000[0x0093]=0xFFFF` every cycle. This is noisier, but may open more local-control/status gates.
|
||||||
|
|
||||||
|
The gated scenario first seeds candidate secondary-table feature bits with command 6:
|
||||||
|
|
||||||
|
```text
|
||||||
|
06 00 15 80 00 C9 ; E400[0x0015] OTHERS/COPY visibility candidate
|
||||||
|
06 01 0F 18 00 4A ; E400[0x008F] shutter/OTHERS bits 11+12
|
||||||
|
06 01 13 FF FF 4E ; E400[0x0093] broad local-report/status gate candidate
|
||||||
|
```
|
||||||
|
|
||||||
|
Then it streams `E000[0x008F]=0x1800` and `E000[0x0093]=0x90FF`. This is the better next watch if quiet/broad E000-only refreshes do not produce local-control TX.
|
||||||
|
|
||||||
|
After a run, summarize unexpected device frames with:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\.venv\Scripts\python.exe scripts\serial_scenario_unexpected.py captures\active-control-report-watch-quiet.txt --show-all
|
||||||
|
.\.venv\Scripts\python.exe scripts\serial_scenario_unexpected.py captures\active-control-report-watch-broad.txt --show-all
|
||||||
|
.\.venv\Scripts\python.exe scripts\serial_scenario_unexpected.py captures\active-control-report-watch-gated.txt --show-all
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected refresh responses are ignored by default: heartbeat, table readbacks, and `02 00 02 00 00 5A`. Any remaining checksum-valid frame is a candidate local-control report. This is where physical button/dial reports should appear if the RCP only talks while the CCU keeps the selector tables active.
|
||||||
|
|
||||||
|
The unexpected-frame summarizer also labels known autonomous button frames so they are not mistaken for newly unlocked controls:
|
||||||
|
|
||||||
|
```text
|
||||||
|
00 00 15 80 00 CF ; known CALL active report
|
||||||
|
00 00 15 00 00 4F ; known CALL inactive report
|
||||||
|
00 00 07 80 00 DD ; known CAM POWER report
|
||||||
|
```
|
||||||
|
|
||||||
|
First quiet/broad watch captures on 2026-05-26 showed no unexpected checksum-valid frames in the observed window:
|
||||||
|
|
||||||
|
```text
|
||||||
|
active-control-report-watch-quiet: 114 detected frames, all expected refresh/heartbeat/readback
|
||||||
|
active-control-report-watch-broad: 133 detected frames, all expected refresh/heartbeat/readback
|
||||||
|
```
|
||||||
|
|
||||||
|
Those runs stopped mid-scenario, so this is not a full negative proof. The practical interpretation is narrower: E000 active-state refresh alone did not make the pressed controls emit visible TX in the watched window. The gated command-6 version is the next ROM-supported test.
|
||||||
|
|
||||||
|
The first gated watch capture produced a new periodic active-state response:
|
||||||
|
|
||||||
|
```text
|
||||||
|
02 00 04 00 00 5C ; gated 0x0004 transition candidate, seen twice
|
||||||
|
01 00 04 00 00 5F ; gated 0x0004 active response candidate, seen 328 times
|
||||||
|
```
|
||||||
|
|
||||||
|
After treating those as expected gated refresh traffic, the capture had zero novel frames. A direct search found no known CALL/CAM POWER frames in that log. So the gated setup changed the session/status response shape, but it did not yet prove that physical controls beyond the already-known autonomous buttons are reporting.
|
||||||
|
|
||||||
## What Is Still Unknown
|
## What Is Still Unknown
|
||||||
|
|
||||||
- The official PT2 names for commands and selectors.
|
- The official PT2 names for commands and selectors.
|
||||||
@@ -635,7 +697,7 @@ This fits the real panel behavior:
|
|||||||
5. Dump selector table state before and after CONNECT OK.
|
5. Dump selector table state before and after CONNECT OK.
|
||||||
6. Seed selectors `0x003`, `0x040`, and `0x0F6` after selector-zero OK and watch lamps/readouts.
|
6. Seed selectors `0x003`, `0x040`, and `0x0F6` after selector-zero OK and watch lamps/readouts.
|
||||||
7. Mine selector dispatch handlers for known UI text terms: `IRIS`, `GAIN`, `SHUTTER`, `BARS`, `BLACK`, `CALL`, `AUTO`, `DIAG`.
|
7. Mine selector dispatch handlers for known UI text terms: `IRIS`, `GAIN`, `SHUTTER`, `BARS`, `BLACK`, `CALL`, `AUTO`, `DIAG`.
|
||||||
8. Build a fake-CCU streamer that repeatedly writes a small selector set and logs which RCP reports appear.
|
8. Run the active-control watch scenarios and map any unexpected frames back to physical controls.
|
||||||
|
|
||||||
## Source Files And Reports
|
## Source Files And Reports
|
||||||
|
|
||||||
|
|||||||
@@ -152,6 +152,14 @@ These JSON scenarios are set up for the current bench runner and default to `384
|
|||||||
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-field-groups.json --parity E --log captures\shutter-0093-blackflare-field-groups.txt --result-json captures\shutter-0093-blackflare-field-groups-result.json
|
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-field-groups.json --parity E --log captures\shutter-0093-blackflare-field-groups.txt --result-json captures\shutter-0093-blackflare-field-groups-result.json
|
||||||
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-field-confirm.json --parity E --log captures\shutter-0093-blackflare-field-confirm.txt --result-json captures\shutter-0093-blackflare-field-confirm-result.json
|
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-field-confirm.json --parity E --log captures\shutter-0093-blackflare-field-confirm.txt --result-json captures\shutter-0093-blackflare-field-confirm-result.json
|
||||||
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-field-stream-confirm.json --parity E --log captures\shutter-0093-blackflare-field-stream-confirm.txt --result-json captures\shutter-0093-blackflare-field-stream-confirm-result.json
|
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-field-stream-confirm.json --parity E --log captures\shutter-0093-blackflare-field-stream-confirm.txt --result-json captures\shutter-0093-blackflare-field-stream-confirm-result.json
|
||||||
|
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-confirm-f020.json --parity E --log captures\shutter-0093-blackflare-confirm-f020.txt --result-json captures\shutter-0093-blackflare-confirm-f020-result.json
|
||||||
|
-Manual-auto-manual
|
||||||
|
|
||||||
|
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-confirm-ff20.json --parity E --log captures\shutter-0093-blackflare-confirm-ff20.txt --result-json captures\shutter-0093-blackflare-confirm-ff20-result.json
|
||||||
|
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-confirm-9f20.json --parity E --log captures\shutter-0093-blackflare-confirm-9f20.txt --result-json captures\shutter-0093-blackflare-confirm-9f20-result.json
|
||||||
|
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-confirm-90ff.json --parity E --log captures\shutter-0093-blackflare-confirm-90ff.txt --result-json captures\shutter-0093-blackflare-confirm-90ff-result.json
|
||||||
|
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-confirm-9fff.json --parity E --log captures\shutter-0093-blackflare-confirm-9fff.txt --result-json captures\shutter-0093-blackflare-confirm-9fff-result.json
|
||||||
|
.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-lowbyte-groups.json --parity E --log captures\shutter-0093-blackflare-lowbyte-groups.txt --result-json captures\shutter-0093-blackflare-lowbyte-groups-result.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Run order:
|
Run order:
|
||||||
@@ -167,6 +175,8 @@ Run order:
|
|||||||
9. `shutter-0093-blackflare-field-groups.json`: tests multi-bit field groups after the slow-marked run suggested black/flare AUTO is not one single extra bit over `0x9020`.
|
9. `shutter-0093-blackflare-field-groups.json`: tests multi-bit field groups after the slow-marked run suggested black/flare AUTO is not one single extra bit over `0x9020`.
|
||||||
10. `shutter-0093-blackflare-field-confirm.json`: shorter, longer-hold confirmation of the broad field groups after the first field-groups run produced three MANUAL->AUTO->MANUAL swaps.
|
10. `shutter-0093-blackflare-field-confirm.json`: shorter, longer-hold confirmation of the broad field groups after the first field-groups run produced three MANUAL->AUTO->MANUAL swaps.
|
||||||
11. `shutter-0093-blackflare-field-stream-confirm.json`: repeats each candidate every 0.60 s after the longer-hold confirmation fell into `CONNECT:NOT ACT`, testing the CCU-refresh model directly.
|
11. `shutter-0093-blackflare-field-stream-confirm.json`: repeats each candidate every 0.60 s after the longer-hold confirmation fell into `CONNECT:NOT ACT`, testing the CCU-refresh model directly.
|
||||||
|
12. `shutter-0093-blackflare-confirm-*.json`: one-candidate streamed confirmations. Each run includes a known `0xFFFF` AUTO positive control, then brackets one candidate with `0x9020` manual context. After the positive-control section, the expected candidate pattern is MANUAL -> AUTO -> MANUAL -> AUTO -> MANUAL only if that candidate drives black/flare AUTO.
|
||||||
|
13. `shutter-0093-blackflare-lowbyte-groups.json`: keeps the high byte fixed at `0x90` and varies only the low byte, after `0x90FF` and `0x9FFF` both toggled black/flare AUTO.
|
||||||
|
|
||||||
## Bench Observations 2026-05-26
|
## Bench Observations 2026-05-26
|
||||||
|
|
||||||
@@ -182,7 +192,10 @@ Observed visible effects from the first adjacent-selector run set:
|
|||||||
| `shutter-0093-blackflare-slow-marked` | held `0x9020`, `0xFFFF`, then alternated `0x9020` with each candidate | white-balance stayed stable, black/flare swapped twice with large pauses; panel stayed `CONNECT: OK` throughout | likely only the manual-reference to `0xFFFF` and `0xFFFF` back to `0x9020` changed black/flare, so AUTO is probably a multi-bit field rather than one added bit |
|
| `shutter-0093-blackflare-slow-marked` | held `0x9020`, `0xFFFF`, then alternated `0x9020` with each candidate | white-balance stayed stable, black/flare swapped twice with large pauses; panel stayed `CONNECT: OK` throughout | likely only the manual-reference to `0xFFFF` and `0xFFFF` back to `0x9020` changed black/flare, so AUTO is probably a multi-bit field rather than one added bit |
|
||||||
| `shutter-0093-blackflare-field-groups` | held broad grouped values such as `0xF020`, `0xFF20`, `0x9F20`, `0x90FF`, `0x9FFF` | black/flare made three MANUAL->AUTO->MANUAL swaps | broad field combinations can produce black/flare AUTO; use the shorter confirmation run to identify which grouped windows caused the swaps |
|
| `shutter-0093-blackflare-field-groups` | held broad grouped values such as `0xF020`, `0xFF20`, `0x9F20`, `0x90FF`, `0x9FFF` | black/flare made three MANUAL->AUTO->MANUAL swaps | broad field combinations can produce black/flare AUTO; use the shorter confirmation run to identify which grouped windows caused the swaps |
|
||||||
| `shutter-0093-blackflare-field-confirm` | held `0xFFFF` for 2.8 s, then candidate groups | one MANUAL->AUTO transition at the start, then `CONNECT:NOT ACT`; PRESET white-balance lamp briefly flashed a few times while inactive | a silent 2.8 s hold is too long; traffic must be refreshed, and some lamp latch/update paths can still flash during inactive display cleanup |
|
| `shutter-0093-blackflare-field-confirm` | held `0xFFFF` for 2.8 s, then candidate groups | one MANUAL->AUTO transition at the start, then `CONNECT:NOT ACT`; PRESET white-balance lamp briefly flashed a few times while inactive | a silent 2.8 s hold is too long; traffic must be refreshed, and some lamp latch/update paths can still flash during inactive display cleanup |
|
||||||
|
| `shutter-0093-blackflare-field-stream-confirm` | streamed every candidate at 0.60 s and saw 148 `02 00 02 00 00 5A` OK-path responses with no resync errors | manual/auto/manual groups were visible, then `CONNECT:NOT ACT` after the stream ended | the stream itself kept OK alive; the final inactive state is likely just the refresh stopping. Candidate isolation needs streamed, one-candidate tests |
|
||||||
|
| `shutter-0093-blackflare-confirm-f020`, `ff20`, `9f20` | each completed cleanly with 137 OK-path responses and no resync errors | MANUAL->AUTO->MANUAL only | only the built-in `0xFFFF` positive control produced AUTO; these candidates did not drive black/flare AUTO by themselves |
|
||||||
|
| `shutter-0093-blackflare-confirm-90ff`, `9fff` | each completed cleanly with 137 OK-path responses and no resync errors | black/flare went back and forth a couple of times | `0x90FF` is sufficient to drive the AUTO effect, so the black/flare field is likely in the low byte of `0x0093`; `0x9FFF` is a superset rather than a separate high-byte requirement |
|
||||||
| `shutter-008f-evs-clear-control` | `0x008F` writes acknowledged as `0x0800`, `0x0000`, `0x0800` | SHUTTER swapped between EVS and OFF while iris AUTO lamp stayed on | `0x008F` is a packed shutter/display status word; clearing it does not simply mean "blank" |
|
| `shutter-008f-evs-clear-control` | `0x008F` writes acknowledged as `0x0800`, `0x0000`, `0x0800` | SHUTTER swapped between EVS and OFF while iris AUTO lamp stayed on | `0x008F` is a packed shutter/display status word; clearing it does not simply mean "blank" |
|
||||||
| `shutter-008f-evs-timeout-control` | only `0x008F=0x0800` was sent after OK seed | EVS/iris AUTO, then `CONNECT:NOT ACT` | later blanking is timeout cleanup, not selector-clear evidence |
|
| `shutter-008f-evs-timeout-control` | only `0x008F=0x0800` was sent after OK seed | EVS/iris AUTO, then `CONNECT:NOT ACT` | later blanking is timeout cleanup, not selector-clear evidence |
|
||||||
|
|
||||||
This changes the local naming: the "adjacent shutter" group should be treated as a broader camera-status and panel-output word cluster. `0x008F` currently covers shutter EVS/OFF plus iris AUTO side effects; `0x0093` covers at least white-balance manual/preset and black/flare manual/auto side effects, and still gates some ROM-observed shutter/clear-scan report lanes.
|
This changes the local naming: the "adjacent shutter" group should be treated as a broader camera-status and panel-output word cluster. `0x008F` currently covers shutter EVS/OFF plus iris AUTO side effects; `0x0093` covers at least white-balance manual/preset and black/flare manual/auto side effects, and still gates some ROM-observed shutter/clear-scan report lanes. Current black/flare evidence points to `0x0093` high byte `0x90` as enough context, with the low byte selecting the black/flare MANUAL/AUTO state.
|
||||||
|
|||||||
@@ -148,6 +148,11 @@ def label_frame(frame: bytes) -> str:
|
|||||||
bytes.fromhex("0780C040A0FD"): "visible_40A0_family_C0",
|
bytes.fromhex("0780C040A0FD"): "visible_40A0_family_C0",
|
||||||
bytes.fromhex("07804020902D"): "visible_retry_0040_2090_candidate",
|
bytes.fromhex("07804020902D"): "visible_retry_0040_2090_candidate",
|
||||||
bytes.fromhex("0780C060205D"): "visible_C0_6020_family_candidate",
|
bytes.fromhex("0780C060205D"): "visible_C0_6020_family_candidate",
|
||||||
|
bytes.fromhex("0000158000CF"): "known_call_button_active_report",
|
||||||
|
bytes.fromhex("00001500004F"): "known_call_button_inactive_report",
|
||||||
|
bytes.fromhex("0000078000DD"): "known_cam_power_button_report",
|
||||||
|
bytes.fromhex("01000400005F"): "gated_active_0004_response_candidate",
|
||||||
|
bytes.fromhex("02000400005C"): "gated_active_0004_transition_candidate",
|
||||||
}
|
}
|
||||||
label = labels.get(frame, "")
|
label = labels.get(frame, "")
|
||||||
if label:
|
if label:
|
||||||
|
|||||||
136
h8536/serial_scenario_unexpected.py
Normal file
136
h8536/serial_scenario_unexpected.py
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from collections import Counter
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Iterable, TextIO
|
||||||
|
|
||||||
|
|
||||||
|
DETECT_RE = re.compile(
|
||||||
|
r"^(?P<time>\d\d:\d\d:\d\d\.\d{3})\s+DETECT\s+"
|
||||||
|
r"(?:(?P<label>\S+)\s+)?(?P<frame>(?:[0-9A-Fa-f]{2}\s*){6})\s*$"
|
||||||
|
)
|
||||||
|
|
||||||
|
DEFAULT_IGNORED_LABELS = {
|
||||||
|
"heartbeat",
|
||||||
|
"connect_ok_path_response_candidate",
|
||||||
|
"connect_c0_path_response_candidate",
|
||||||
|
"table_readback_candidate",
|
||||||
|
"gated_active_0004_response_candidate",
|
||||||
|
"gated_active_0004_transition_candidate",
|
||||||
|
}
|
||||||
|
|
||||||
|
KNOWN_FRAME_LABELS = {
|
||||||
|
"00 00 15 80 00 CF": "known_call_button_active_report",
|
||||||
|
"00 00 15 00 00 4F": "known_call_button_inactive_report",
|
||||||
|
"00 00 07 80 00 DD": "known_cam_power_button_report",
|
||||||
|
"01 00 04 00 00 5F": "gated_active_0004_response_candidate",
|
||||||
|
"02 00 04 00 00 5C": "gated_active_0004_transition_candidate",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class DetectedFrame:
|
||||||
|
timestamp: str
|
||||||
|
label: str
|
||||||
|
frame: str
|
||||||
|
line_number: int
|
||||||
|
|
||||||
|
|
||||||
|
def build_arg_parser() -> argparse.ArgumentParser:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Show unexpected DETECT frames from a serial_scenario capture log."
|
||||||
|
)
|
||||||
|
parser.add_argument("log", type=Path, help="serial_scenario capture log")
|
||||||
|
parser.add_argument(
|
||||||
|
"--include-refresh",
|
||||||
|
action="store_true",
|
||||||
|
help="include heartbeat, table-readback, and CONNECT OK refresh frames",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--show-all",
|
||||||
|
action="store_true",
|
||||||
|
help="print every matching DETECT frame after the summary",
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str] | None = None, *, stdout: TextIO = sys.stdout) -> int:
|
||||||
|
args = build_arg_parser().parse_args(argv)
|
||||||
|
text = args.log.read_text(encoding="utf-8")
|
||||||
|
frames = parse_detected_frames(text.splitlines())
|
||||||
|
interesting = frames if args.include_refresh else filter_expected_refresh(frames)
|
||||||
|
print(format_report(frames, interesting, include_refresh=args.include_refresh, show_all=args.show_all), file=stdout)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def parse_detected_frames(lines: Iterable[str]) -> list[DetectedFrame]:
|
||||||
|
frames: list[DetectedFrame] = []
|
||||||
|
for line_number, line in enumerate(lines, start=1):
|
||||||
|
match = DETECT_RE.match(line.strip())
|
||||||
|
if not match:
|
||||||
|
continue
|
||||||
|
frame = " ".join(match.group("frame").upper().split())
|
||||||
|
label = KNOWN_FRAME_LABELS.get(frame, match.group("label") or "checksum_ok_unlabeled")
|
||||||
|
frames.append(
|
||||||
|
DetectedFrame(
|
||||||
|
timestamp=match.group("time"),
|
||||||
|
label=label,
|
||||||
|
frame=frame,
|
||||||
|
line_number=line_number,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return frames
|
||||||
|
|
||||||
|
|
||||||
|
def filter_expected_refresh(frames: Iterable[DetectedFrame]) -> list[DetectedFrame]:
|
||||||
|
return [frame for frame in frames if frame.label not in DEFAULT_IGNORED_LABELS]
|
||||||
|
|
||||||
|
|
||||||
|
def format_report(
|
||||||
|
frames: list[DetectedFrame],
|
||||||
|
interesting: list[DetectedFrame],
|
||||||
|
*,
|
||||||
|
include_refresh: bool,
|
||||||
|
show_all: bool,
|
||||||
|
) -> str:
|
||||||
|
lines = [
|
||||||
|
"Serial scenario unexpected-frame summary",
|
||||||
|
f"detected_frames={len(frames)} mode={'all' if include_refresh else 'unexpected-only'}",
|
||||||
|
]
|
||||||
|
label_counts = Counter(frame.label for frame in frames)
|
||||||
|
if label_counts:
|
||||||
|
lines.append("labels:")
|
||||||
|
for label, count in sorted(label_counts.items()):
|
||||||
|
lines.append(f" {label}: {count}")
|
||||||
|
ignored = len(frames) - len(interesting)
|
||||||
|
if not include_refresh:
|
||||||
|
lines.append(f"ignored_expected_refresh={ignored}")
|
||||||
|
lines.append(f"interesting_frames={len(interesting)}")
|
||||||
|
|
||||||
|
frame_counts = Counter((frame.frame, frame.label) for frame in interesting)
|
||||||
|
if frame_counts:
|
||||||
|
lines.append("interesting frame counts:")
|
||||||
|
for (frame_text, label), count in sorted(frame_counts.items(), key=lambda item: (-item[1], item[0][0])):
|
||||||
|
first = next(frame for frame in interesting if frame.frame == frame_text and frame.label == label)
|
||||||
|
lines.append(f" {count:4d}x {frame_text} label={label} first={first.timestamp} line={first.line_number}")
|
||||||
|
else:
|
||||||
|
lines.append("no unexpected checksum-valid frames found")
|
||||||
|
|
||||||
|
if show_all and interesting:
|
||||||
|
lines.append("timeline:")
|
||||||
|
for frame in interesting:
|
||||||
|
lines.append(f" {frame.timestamp} line={frame.line_number} {frame.frame} label={frame.label}")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"DetectedFrame",
|
||||||
|
"filter_expected_refresh",
|
||||||
|
"format_report",
|
||||||
|
"main",
|
||||||
|
"parse_detected_frames",
|
||||||
|
]
|
||||||
52
scenarios/active-control-report-watch-broad.json
Normal file
52
scenarios/active-control-report-watch-broad.json
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"name": "active-control-report-watch-broad",
|
||||||
|
"notes": [
|
||||||
|
"Hold CONNECT OK while streaming broader camera/status words that may enable more local controls.",
|
||||||
|
"Press or turn one physical control at a time while this runs. Note the rough time and control name.",
|
||||||
|
"This is noisier than the quiet watch: each cycle writes 0x008F and 0x0093, so use the unexpected-frame summarizer after the run."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "power_cycle",
|
||||||
|
"off_seconds": 1.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "wait_ready",
|
||||||
|
"heartbeats": 2,
|
||||||
|
"timeout": 10.0,
|
||||||
|
"require": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "drain",
|
||||||
|
"seconds": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "selector_zero_connect_ok_seed",
|
||||||
|
"frame": "00 00 00 80 00 DA",
|
||||||
|
"listen": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 80,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "broad_refresh_008f_1800",
|
||||||
|
"frame": "00 01 0F 18 00 4C",
|
||||||
|
"listen": 0.30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "broad_refresh_0093_ffff",
|
||||||
|
"frame": "00 01 13 FF FF 48",
|
||||||
|
"listen": 0.30
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "listen",
|
||||||
|
"seconds": 1.50
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
70
scenarios/active-control-report-watch-gated.json
Normal file
70
scenarios/active-control-report-watch-gated.json
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"name": "active-control-report-watch-gated",
|
||||||
|
"notes": [
|
||||||
|
"Hold CONNECT OK and seed candidate secondary-table E400 gates before watching for local-control TX reports.",
|
||||||
|
"This follows the ROM clue that some local report paths require E400 feature bits, not only E000 display/status words.",
|
||||||
|
"Press or turn one physical control at a time while this runs. Note the rough time and control name."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "power_cycle",
|
||||||
|
"off_seconds": 1.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "wait_ready",
|
||||||
|
"heartbeats": 2,
|
||||||
|
"timeout": 10.0,
|
||||||
|
"require": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "drain",
|
||||||
|
"seconds": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "selector_zero_connect_ok_seed",
|
||||||
|
"frame": "00 00 00 80 00 DA",
|
||||||
|
"listen": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "secondary_gate_0015_other_copy_candidate",
|
||||||
|
"frame": "06 00 15 80 00 C9",
|
||||||
|
"listen": 0.30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "secondary_gate_008f_shutter_bits_11_12",
|
||||||
|
"frame": "06 01 0F 18 00 4A",
|
||||||
|
"listen": 0.30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "secondary_gate_0093_all_bits",
|
||||||
|
"frame": "06 01 13 FF FF 4E",
|
||||||
|
"listen": 0.30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 80,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "gated_refresh_008f_1800",
|
||||||
|
"frame": "00 01 0F 18 00 4C",
|
||||||
|
"listen": 0.30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "gated_refresh_0093_90ff",
|
||||||
|
"frame": "00 01 13 90 FF 27",
|
||||||
|
"listen": 0.30
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "listen",
|
||||||
|
"seconds": 1.50
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
46
scenarios/active-control-report-watch-quiet.json
Normal file
46
scenarios/active-control-report-watch-quiet.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "active-control-report-watch-quiet",
|
||||||
|
"notes": [
|
||||||
|
"Hold the panel in CONNECT OK with the least noisy proven refresh stream, then watch for local-control TX reports.",
|
||||||
|
"Press or turn one physical control at a time while this runs. Note the rough time and control name.",
|
||||||
|
"Expected refresh traffic is table readback for 0x0093 plus 02 00 02 00 00 5A OK-path responses; anything else is interesting."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "power_cycle",
|
||||||
|
"off_seconds": 1.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "wait_ready",
|
||||||
|
"heartbeats": 2,
|
||||||
|
"timeout": 10.0,
|
||||||
|
"require": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "drain",
|
||||||
|
"seconds": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "selector_zero_connect_ok_seed",
|
||||||
|
"frame": "00 00 00 80 00 DA",
|
||||||
|
"listen": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 100,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "quiet_active_refresh_0093_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "listen",
|
||||||
|
"seconds": 1.50
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
115
scenarios/shutter-0093-blackflare-confirm-90ff.json
Normal file
115
scenarios/shutter-0093-blackflare-confirm-90ff.json
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
{
|
||||||
|
"name": "shutter-0093-blackflare-confirm-90ff",
|
||||||
|
"notes": [
|
||||||
|
"Single-candidate streaming confirmation for selector 0x0093 value 0x90FF.",
|
||||||
|
"The known 0xFFFF AUTO reference is included first as a positive control.",
|
||||||
|
"Expected observation pattern if this candidate drives black/flare AUTO: MANUAL -> AUTO -> MANUAL -> AUTO -> MANUAL.",
|
||||||
|
"All holds refresh every 0.60s so CONNECT OK should stay alive during the visual test."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "power_cycle",
|
||||||
|
"off_seconds": 1.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "wait_ready",
|
||||||
|
"heartbeats": 2,
|
||||||
|
"timeout": 10.0,
|
||||||
|
"require": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "drain",
|
||||||
|
"seconds": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "selector_zero_connect_ok_seed",
|
||||||
|
"frame": "00 00 00 80 00 DA",
|
||||||
|
"listen": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_reference_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 6,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "positive_auto_reference_ffff",
|
||||||
|
"frame": "00 01 13 FF FF 48",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_after_positive_reference_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_90ff",
|
||||||
|
"frame": "00 01 13 90 FF 27",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_return_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_90ff_repeat",
|
||||||
|
"frame": "00 01 13 90 FF 27",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_tail_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
115
scenarios/shutter-0093-blackflare-confirm-9f20.json
Normal file
115
scenarios/shutter-0093-blackflare-confirm-9f20.json
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
{
|
||||||
|
"name": "shutter-0093-blackflare-confirm-9f20",
|
||||||
|
"notes": [
|
||||||
|
"Single-candidate streaming confirmation for selector 0x0093 value 0x9F20.",
|
||||||
|
"The known 0xFFFF AUTO reference is included first as a positive control.",
|
||||||
|
"Expected observation pattern if this candidate drives black/flare AUTO: MANUAL -> AUTO -> MANUAL -> AUTO -> MANUAL.",
|
||||||
|
"All holds refresh every 0.60s so CONNECT OK should stay alive during the visual test."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "power_cycle",
|
||||||
|
"off_seconds": 1.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "wait_ready",
|
||||||
|
"heartbeats": 2,
|
||||||
|
"timeout": 10.0,
|
||||||
|
"require": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "drain",
|
||||||
|
"seconds": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "selector_zero_connect_ok_seed",
|
||||||
|
"frame": "00 00 00 80 00 DA",
|
||||||
|
"listen": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_reference_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 6,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "positive_auto_reference_ffff",
|
||||||
|
"frame": "00 01 13 FF FF 48",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_after_positive_reference_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_9f20",
|
||||||
|
"frame": "00 01 13 9F 20 F7",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_return_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_9f20_repeat",
|
||||||
|
"frame": "00 01 13 9F 20 F7",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_tail_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
115
scenarios/shutter-0093-blackflare-confirm-9fff.json
Normal file
115
scenarios/shutter-0093-blackflare-confirm-9fff.json
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
{
|
||||||
|
"name": "shutter-0093-blackflare-confirm-9fff",
|
||||||
|
"notes": [
|
||||||
|
"Single-candidate streaming confirmation for selector 0x0093 value 0x9FFF.",
|
||||||
|
"The known 0xFFFF AUTO reference is included first as a positive control.",
|
||||||
|
"Expected observation pattern if this candidate drives black/flare AUTO: MANUAL -> AUTO -> MANUAL -> AUTO -> MANUAL.",
|
||||||
|
"All holds refresh every 0.60s so CONNECT OK should stay alive during the visual test."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "power_cycle",
|
||||||
|
"off_seconds": 1.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "wait_ready",
|
||||||
|
"heartbeats": 2,
|
||||||
|
"timeout": 10.0,
|
||||||
|
"require": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "drain",
|
||||||
|
"seconds": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "selector_zero_connect_ok_seed",
|
||||||
|
"frame": "00 00 00 80 00 DA",
|
||||||
|
"listen": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_reference_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 6,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "positive_auto_reference_ffff",
|
||||||
|
"frame": "00 01 13 FF FF 48",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_after_positive_reference_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_9fff",
|
||||||
|
"frame": "00 01 13 9F FF 28",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_return_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_9fff_repeat",
|
||||||
|
"frame": "00 01 13 9F FF 28",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_tail_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
115
scenarios/shutter-0093-blackflare-confirm-f020.json
Normal file
115
scenarios/shutter-0093-blackflare-confirm-f020.json
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
{
|
||||||
|
"name": "shutter-0093-blackflare-confirm-f020",
|
||||||
|
"notes": [
|
||||||
|
"Single-candidate streaming confirmation for selector 0x0093 value 0xF020.",
|
||||||
|
"The known 0xFFFF AUTO reference is included first as a positive control.",
|
||||||
|
"Expected observation pattern if this candidate drives black/flare AUTO: MANUAL -> AUTO -> MANUAL -> AUTO -> MANUAL.",
|
||||||
|
"All holds refresh every 0.60s so CONNECT OK should stay alive during the visual test."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "power_cycle",
|
||||||
|
"off_seconds": 1.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "wait_ready",
|
||||||
|
"heartbeats": 2,
|
||||||
|
"timeout": 10.0,
|
||||||
|
"require": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "drain",
|
||||||
|
"seconds": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "selector_zero_connect_ok_seed",
|
||||||
|
"frame": "00 00 00 80 00 DA",
|
||||||
|
"listen": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_reference_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 6,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "positive_auto_reference_ffff",
|
||||||
|
"frame": "00 01 13 FF FF 48",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_after_positive_reference_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_f020",
|
||||||
|
"frame": "00 01 13 F0 20 98",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_return_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_f020_repeat",
|
||||||
|
"frame": "00 01 13 F0 20 98",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_tail_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
115
scenarios/shutter-0093-blackflare-confirm-ff20.json
Normal file
115
scenarios/shutter-0093-blackflare-confirm-ff20.json
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
{
|
||||||
|
"name": "shutter-0093-blackflare-confirm-ff20",
|
||||||
|
"notes": [
|
||||||
|
"Single-candidate streaming confirmation for selector 0x0093 value 0xFF20.",
|
||||||
|
"The known 0xFFFF AUTO reference is included first as a positive control.",
|
||||||
|
"Expected observation pattern if this candidate drives black/flare AUTO: MANUAL -> AUTO -> MANUAL -> AUTO -> MANUAL.",
|
||||||
|
"All holds refresh every 0.60s so CONNECT OK should stay alive during the visual test."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "power_cycle",
|
||||||
|
"off_seconds": 1.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "wait_ready",
|
||||||
|
"heartbeats": 2,
|
||||||
|
"timeout": 10.0,
|
||||||
|
"require": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "drain",
|
||||||
|
"seconds": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "selector_zero_connect_ok_seed",
|
||||||
|
"frame": "00 00 00 80 00 DA",
|
||||||
|
"listen": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_reference_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 6,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "positive_auto_reference_ffff",
|
||||||
|
"frame": "00 01 13 FF FF 48",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_after_positive_reference_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_ff20",
|
||||||
|
"frame": "00 01 13 FF 20 97",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_return_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_ff20_repeat",
|
||||||
|
"frame": "00 01 13 FF 20 97",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_tail_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
210
scenarios/shutter-0093-blackflare-lowbyte-groups.json
Normal file
210
scenarios/shutter-0093-blackflare-lowbyte-groups.json
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
{
|
||||||
|
"name": "shutter-0093-blackflare-lowbyte-groups",
|
||||||
|
"notes": [
|
||||||
|
"Narrow black/flare AUTO now that 0x90FF and 0x9FFF both toggled AUTO but 0xF020/0xFF20/0x9F20 did not.",
|
||||||
|
"This keeps the high byte fixed at 0x90 and varies only the low byte.",
|
||||||
|
"Use 0x9020 as the manual reference and 0x90FF as the low-byte AUTO positive control."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "power_cycle",
|
||||||
|
"off_seconds": 1.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "wait_ready",
|
||||||
|
"heartbeats": 2,
|
||||||
|
"timeout": 10.0,
|
||||||
|
"require": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "drain",
|
||||||
|
"seconds": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "selector_zero_connect_ok_seed",
|
||||||
|
"frame": "00 00 00 80 00 DA",
|
||||||
|
"listen": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_reference_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 6,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "positive_lowbyte_auto_90ff",
|
||||||
|
"frame": "00 01 13 90 FF 27",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_before_9000",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_9000_lowbyte_none",
|
||||||
|
"frame": "00 01 13 90 00 D8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_before_901f",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_901f_low_bits_0_to_4",
|
||||||
|
"frame": "00 01 13 90 1F C7",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_before_903f",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_903f_low_bits_0_to_5",
|
||||||
|
"frame": "00 01 13 90 3F E7",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_before_90c0",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_90c0_low_bits_6_to_7",
|
||||||
|
"frame": "00 01 13 90 C0 18",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_before_90e0",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_90e0_low_bits_5_to_7",
|
||||||
|
"frame": "00 01 13 90 E0 38",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_before_90df",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 5,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "candidate_90df_all_except_bit5",
|
||||||
|
"frame": "00 01 13 90 DF 07",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "repeat",
|
||||||
|
"count": 8,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"action": "send",
|
||||||
|
"label": "manual_tail_9020",
|
||||||
|
"frame": "00 01 13 90 20 F8",
|
||||||
|
"listen": 0.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
16
scripts/serial_scenario_unexpected.py
Normal file
16
scripts/serial_scenario_unexpected.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Summarize unexpected device frames from a serial_scenario log."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
if str(ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
|
||||||
|
from h8536.serial_scenario_unexpected import main
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user