Skip to content

feat(sim): replay a directory of real images as captures (playlist pattern) - #14

Merged
mbreissi merged 2 commits into
mainfrom
feat/sim-playlist
Aug 23, 2026
Merged

feat(sim): replay a directory of real images as captures (playlist pattern)#14
mbreissi merged 2 commits into
mainfrom
feat/sim-playlist

Conversation

@mbreissi

Copy link
Copy Markdown
Contributor

Summary

Adds frame.pattern.playlist to the sim backend (decision D-CAM-31; requested by image-processor D-IP-18): the simulator replays a directory of real image files as captures through the real finalize path — temp write, fsync, sidecar first, atomic visibility, catalog record, ImageCaptured announcement with the file's true image.bytes/image.sha256 — so downstream vision components receive genuine imagery from the real camera contract.

"frame": { "pattern": { "playlist": {
  "directory": "/srv/line-clearance/reference-images",
  "include": ["**/*.jpg", "**/*.jpeg", "**/*.png"],
  "order": "sorted", "loop": true } } }
  • Deterministic: sorted walk, optional seeded shuffle (owned SplitMix64 — no dependency can reorder a seeded playlist), cursor advances once per capture, loop: false fails cleanly when spent.
  • Containment is structural: canonicalized root, symlinks rejected at walk time and re-checked at capture time, per-file size ceiling, JPEG/PNG detected by magic bytes.
  • JPEG members replay byte-for-byte under passthrough; other combinations decode and re-encode (PNG passthrough would need a new PixelFormat variant across all backends — recorded in the D-CAM-31 rationale).
  • backendMetadata.playlist{sourcePath, index} on the terminal body / sidecar / catalog; playlist{count, index, directory} on session status.
  • No new dependencies; the four synthetic patterns parse unchanged (externally tagged enum).

Docs: config.schema.json (oneOf verified against the parser), docs/reference/configuration.md, docs/how-to-guides.md ("Replay real images with the simulator"), DESIGN.md §6.2.1 + register.

Validation

  • cargo test: 655 passed, 0 failed, 1 ignored (+ integration binaries ok); cargo clippy --all-targets -- -D warnings: clean.
  • cargo llvm-cov --fail-under-lines 90: 92.17 % bundle; diff-cover --fail-under=95: 95 % on the diff (the #[cfg(unix)] symlink/containment tests run on the Linux runner).
  • Integration test drives a capture from a temp playlist through the storage path and asserts the sidecar image.sha256 equals the replayed file's digest and that backendMetadata.playlist is present.

breis added 2 commits August 22, 2026 16:33
Adds a `playlist` pattern to the simulator backend so a downstream vision
component receives genuine imagery through the real camera path instead of
synthetic colour bars (D-CAM-31, for image-processor D-IP-18).

`frame.pattern` now accepts `{ "playlist": { directory, include, order, loop,
advance } }` beside the four synthetic tokens. The directory is walked once at
connect - deterministic, symbolic links refused, bounded at 10,000 files and 32
levels, and a directory matching no file fails the connect - and ordered
`sorted` by relative path or `seeded` by a SplitMix64 Fisher-Yates shuffle keyed
by the simulator seed. Each capture yields the next file with that file's own
dimensions and format and runs the unchanged finalize path: temp write, fsync,
sidecar first, atomic visibility, catalog row, `ImageCaptured`.

A JPEG member captured under `passthrough` or `raw` is delivered as the bytes on
disk, so the announced and sidecar `image.sha256` is the source file's own
digest; every other combination decodes to RGB8/Mono8 and re-encodes through the
ordinary encoding stage. Byte-preserving replay stays JPEG-only because
`OutputEncoding::Passthrough` already requires a complete JPEG source.

The terminal body carries `backendMetadata.playlist` (`sourcePath`, `index`), and
the backend session-status surface carries `playlist{count,index,directory}`.

`CaptureRequest` gains an opaque `trigger_key`, derived adapter-side from the
durable `CaptureTrigger`, which `advance: perTrigger` compares for equality, so
no EdgeCommons message shape crosses the backend seam.

`config.schema.json`, `docs/reference/configuration.md`, `docs/how-to-guides.md`,
and `DESIGN.md` (section 6.2.1 plus decision D-CAM-31) are updated in the same
change.
The playlist cursor now advances one file per capture, full stop. `advance`
(`perCapture` / `perTrigger`) is removed from the configuration, the schema, the
reference and how-to docs, and D-CAM-31; the register keeps the reasoning for
dropping it.

`perTrigger` was the only reason a capture had to know which operator action or
schedule occurrence produced it, so `CaptureRequest.trigger_key` and the
`CaptureTrigger`-derived key that fed it are removed as well. That restores the
backend seam and the twelve `CaptureRequest` construction sites in the GenICam,
ONVIF, and RTSP backends to what they were, and leaves the playlist feature
touching only the simulator, its configuration, and its own tests.
@mbreissi
mbreissi merged commit 2d81e20 into main Aug 23, 2026
10 checks passed
@mbreissi
mbreissi deleted the feat/sim-playlist branch August 23, 2026 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant