Skip to content

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:

  1. Simple streaming — read events as fast as possible, no threshold configuration, no metadata needed. Used by kazunoko read and quick scripts.
  2. 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 measure and get_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() raises RuntimeError if called twice, preventing accidental re-initialization mid-session.
  • Example scripts that only need simple streaming import Reader and avoid pulling in MeasureConfig / MeasureMetadata.
  • New streaming variants (e.g., multi-device, filtered streams) can subclass Streamer and inherit the full interface.