Skip to content

Publish shaping: batching + deadband engine above the session - #3

Merged
mbreissi merged 1 commit into
mainfrom
feat/publish-shaping
Jul 28, 2026
Merged

Publish shaping: batching + deadband engine above the session#3
mbreissi merged 1 commit into
mainfrom
feat/publish-shaping

Conversation

@mbreissi

Copy link
Copy Markdown
Contributor

The per-signal publish config (mode, batchMs, deadband) now governs publication. A pure, virtual-clock-tested shaping engine (src/shaping.rs) sits above the device session (ADP-5), so the mtconnect and sim backends are shaped identically; src/supervisor.rs drives it with one timer per instance task.

Behavior

  • Unconfigured signals untouched. No policy / batchMs: 0 publishes each reading immediately, exactly as before.
  • Batching. batchMs > 0 buffers GOOD readings per signal and flushes on window expiry as ONE SouthboundSignalUpdate whose samples[] carries every buffered reading in arrival order — each sample keeping its own serverTs and extras (sequence, receivedTs, …). mode: "interval" keeps only the latest reading per window.
  • Quality outranks the window. A BAD/UNCERTAIN reading flushes its signal's buffer immediately, itself included.
  • Deadband on entry. SAMPLE-category numerics only (compiled against the probe model by the session; the sim fallback compiles static config). Compared against the last accepted value; quality/qualityRaw changes, non-numeric/array values, and the first reading after connect/resync/resume always pass.
  • Lifecycle. Pause clears open windows (the resume snapshot republishes the current truth — flushing pre-pause readings after it would publish stale data out of order); resume re-arms the deadband; repoll and the resume snapshot bypass shaping; shutdown/reconnect/link-loss flush open windows; a reload or model drift swaps the policy table atomically with the signal-set swap, flushing changed signals' windows with their old policy.
  • defaults.publishMode now resolves at compile exactly like defaults.batchMs (D-L10) and scopes to selection-derived SAMPLE signals.

Observability

New MtconnectAdapterShaping family (dims: instance): published / coalesced / deadbandDropped Total+Interval pairs. A new family rather than an extension of MtconnectStream, whose agentId dimension and shared-acquisition scope cannot carry a per-instance publication fact.

Docs

configuration.md publish rows + a new "Publish shaping" section describe the real behavior; metrics.md documents the new family; messaging-interface.md documents multi-sample samples[]; schema descriptions aligned; DESIGN.md records the decisions as D-MtconnectAdapter-L11.

Validation

  • cargo test: 362 passed, 0 failed (was 329 — all pre-existing tests unmodified except compile_mtconnect signature call sites).
  • cargo clippy --all-targets -- -D warnings: clean.
  • cargo llvm-cov --fail-under-lines 90 (CI filter): 96.74% lines; shaping.rs 99.77%.
  • Live compose harness (tests/compose.mtconnect-agent.yaml, cppagent 2.7): full agent_integration suite 6/6 green, including the new live leg — three streamed values coalesced into one multi-sample update on the tiny device (EVIDENCE live shaping: buffered=[41.25, 42.25, 43.25] flushed_samples=4 (one update)).

…he session

The per-signal publish policy (mode, batchMs, deadband) now governs publication
through a shaping engine (src/shaping.rs) that sits above the device session
(ADP-5), shaping the mtconnect and sim backends identically:

- batchMs=0 (default) publishes each reading immediately - unconfigured signals
  are untouched; batchMs>0 buffers GOOD readings per signal and flushes on
  window expiry as ONE SouthboundSignalUpdate whose samples[] carries the
  window's readings in arrival order, each keeping its own timestamps/extras.
- mode: interval keeps only the latest reading per window; on-change keeps all.
- BAD/UNCERTAIN readings flush their window immediately - a quality transition
  never sits in a window.
- deadband (SAMPLE-category numerics, compiled against the probe model) applies
  on entry against the last accepted value; quality/qualityRaw changes,
  non-numeric values, and the first reading after connect/resync/resume pass.
- Lifecycle: pause clears open windows (the resume snapshot republishes the
  current truth); shutdown/reconnect/link-loss flush them; repoll and the
  resume snapshot bypass shaping; reloads and model drift swap the policy
  table atomically with the signal-set swap, flushing changed windows on the
  old policy. One timer per instance task, virtual-clock testable.
- defaults.publishMode now resolves at compile like defaults.batchMs and
  scopes to selection-derived SAMPLE signals.
- New MtconnectAdapterShaping family (instance): published / coalesced /
  deadbandDropped pairs.
- Docs describe the real behavior (configuration.md publish-shaping section,
  metrics.md, messaging-interface.md, schema descriptions); DESIGN.md records
  the decisions as D-MtconnectAdapter-L11.
- Tests: shaping engine tables on a virtual clock, seam policy compilation,
  wire-shape + sim parity suite, and a live cppagent batching leg (run green
  against the compose harness).
@mbreissi
mbreissi merged commit 7ad6c57 into main Jul 28, 2026
4 checks passed
@mbreissi
mbreissi deleted the feat/publish-shaping branch July 28, 2026 16:00
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