1
0

gated run

This commit is contained in:
Aiden
2026-05-26 17:44:16 +10:00
parent 8d98beb6aa
commit 566b4ab108
14 changed files with 1187 additions and 2 deletions

View File

@@ -611,6 +611,68 @@ This fits the real panel behavior:
- Correct fake-CCU traffic can wake it to `CONNECT: OK`.
- 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
- 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.
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`.
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

View File

@@ -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-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-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:
@@ -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`.
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.
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
@@ -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-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-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-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.

View File

@@ -148,6 +148,11 @@ def label_frame(frame: bytes) -> str:
bytes.fromhex("0780C040A0FD"): "visible_40A0_family_C0",
bytes.fromhex("07804020902D"): "visible_retry_0040_2090_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, "")
if label:

View 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",
]

View 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
}
]
}

View 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
}
]
}

View 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
}
]
}

View 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
}
]
}
]
}

View 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
}
]
}
]
}

View 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
}
]
}
]
}

View 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
}
]
}
]
}

View 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
}
]
}
]
}

View 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
}
]
}
]
}

View 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())