TN-004: Reader vs Measure — Two Streaming Classes¶
Summary¶
Event streaming is split into two classes, Reader and Measure, sharing a common Streamer base. Reader is a lightweight reader with no setup; Measure is a full measurement session manager that configures the device and merges metadata into every event.
Background¶
Two distinct usage patterns emerged from real measurement workflows:
- Simple streaming — read events as fast as possible, no threshold configuration, no metadata needed. Used by
kazunoko readand quick scripts. - Systematic measurements — configure thresholds, sync the RTC, collect device metadata (MAC, firmware version, threshold values), and embed metadata into every event for traceability. Used by
kazunoko measureandget_events.py.
The question was whether to handle both in one class (via optional configuration) or two separate classes.
Decision¶
Two classes with a shared abstract base:
Streamer (abstract)
├── Reader — no setup, lightweight
└── Measure — setup() required, metadata merged into events
Streamer defines the streaming interface (stream_by_count, stream_by_time, read_by_count, read_by_time, abstract read_event). Both subclasses inherit this interface, so all consumers (CLI, scripts) use the same API regardless of which class they hold.
Measure overrides stream_by_count and stream_by_time to enforce that setup() has been called first, raising RuntimeError otherwise. This prevents silent misconfigured sessions where thresholds were never applied.
Reader.read_event() implements timeout retry: if the device does not return an event within event_timeout, it retries until the cumulative wait exceeds the limit, then raises EventTimeout. Measure.read_event() adds metadata merging on top of the same logic.
Alternatives Considered¶
| Alternative | Reason Not Chosen |
|---|---|
Single Measure class with optional config |
Callers doing simple streaming must always pass a no-op config; misleading API |
Optional setup() in Measure |
Silent error: users forget to call setup() and measure with wrong thresholds |
| No base class; duplicate streaming logic | stream_by_count / stream_by_time logic is non-trivial; duplication causes drift |
Reader as a simplified wrapper around Measure |
Wrong direction; Measure is the heavyweight variant, not the default |
Consequences¶
- CLI commands (
read,measure) use different classes but identical streaming calls — no special-casing in the CLI. Measure.setup()raisesRuntimeErrorif called twice, preventing accidental re-initialization mid-session.- Example scripts that only need simple streaming import
Readerand avoid pulling inMeasureConfig/MeasureMetadata. - New streaming variants (e.g., multi-device, filtered streams) can subclass
Streamerand inherit the full interface.