Skip to content

LiveTagPipeline: expose the 'ReadFn' custom-reader seam (parity with BatchTagPipeline) #218

Description

@HanSur94

Problem / motivation

BatchTagPipeline recently gained a 'ReadFn' option (commit f94d8f64) — an injectable raw-parser seam that lets a caller ingest file formats the generic delimited reader cannot (proprietary, binary, fixed-width, etc.) by supplying their own @(absPath) -> parsed function, as long as it returns the same parsed shape selectTimeAndValue_ expects.

LiveTagPipeline shares the exact same ingestion plumbing — its own header says it "Shares readRawDelimited_ / selectTimeAndValue_ / writeTagMat_ with BatchTagPipeline" (LiveTagPipeline.m:42), and its dispatchParse_ is documented as "Same internal parser dispatch as BatchTagPipeline (D-02)" (LiveTagPipeline.m:770). But it hardcodes dispatchDelimitedParse_(abspath) and exposes no ReadFn option — its constructor accepts only OutputDir / Interval / ErrorFcn / Verbose / SharedRoot / LockTimeout (LiveTagPipeline.m:188-202).

The result is a feature asymmetry: a live / long-recording ingestion workflow over a custom file format is impossible today, while the identical batch workflow already supports it. Long-recording live ingestion is a core sensor-analysis domain for FastSense, so the live path is exactly where this is most wanted.

Proposed feature

Extend the same ReadFn custom-reader seam to LiveTagPipeline, mirroring the batch implementation byte-for-byte.

% Same shape as BatchTagPipeline today:
ltp = LiveTagPipeline('OutputDir', outDir, ...
                      'Interval', 2.0, ...
                      'ReadFn', @parseMyBinaryFormat);  % @(absPath) -> parsed
ltp.start();

ReadFn must be a function_handle; anything else throws TagPipeline:invalidReadFn (the same error id batch already uses).

Rough sketch

Single file: libs/SensorThreshold/LiveTagPipeline.m. Copy the pattern from BatchTagPipeline:

  • Add a private property readFn_ = @dispatchDelimitedParse_ (the DI seam, default = current behavior) — BatchTagPipeline.m:48.
  • In the constructor option loop (LiveTagPipeline.m:188-202), add case 'ReadFn' and after parsing, validate + assign exactly as batch does (BatchTagPipeline.m:126-131):
    if ~isempty(opts.ReadFn)
        if ~isa(opts.ReadFn, 'function_handle')
            error('TagPipeline:invalidReadFn', 'ReadFn must be a function handle @(absPath)->parsed.');
        end
        obj.readFn_ = opts.ReadFn;
    end
  • In dispatchParse_ (LiveTagPipeline.m:770-783), replace the hardcoded parsed = dispatchDelimitedParse_(abspath); with parsed = obj.readFn_(abspath); — identical to BatchTagPipeline.m:310.

No public-API removal, no signature change.

Value

High. Unlocks live ingestion of non-delimited / proprietary / binary streaming formats — which the batch path already supports — closing a sharp, recently-introduced asymmetry. Directly serves the long-recording live workflow that is central to FastSense's audience.

Constraints check

  • Toolbox-free: ✅ — pure function-handle dispatch, no toolbox calls.
  • Backward-compatible: ✅ — default readFn_ = @dispatchDelimitedParse_ preserves current behavior exactly; no change to serialized state or existing scripts.
  • Pure MATLAB/Octave: ✅.
  • Contracts: ✅ — touches only LiveTagPipeline; no change to the Tag or DashboardWidget interfaces. Parsed-shape contract is the same one batch and selectTimeAndValue_ already rely on.

Effort estimate

S — single file, mirrors a pattern that already exists in the sibling class. Add a small parity test (a ReadFn that returns a synthetic parsed struct ingests correctly; a non-handle ReadFn throws TagPipeline:invalidReadFn).


AI-proposed via /feature-scout — needs a human product decision before implementation.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions