Skip to content

feat(api): derived hydraulic channels on MachineSnapshot - #802

Open
ChampionDesigns wants to merge 3 commits into
decentespresso:mainfrom
ChampionDesigns:ben/derived-channels-r2
Open

feat(api): derived hydraulic channels on MachineSnapshot#802
ChampionDesigns wants to merge 3 commits into
decentespresso:mainfrom
ChampionDesigns:ben/derived-channels-r2

Conversation

@ChampionDesigns

Copy link
Copy Markdown
Collaborator

Summary

Adds three compute-on-read channels to MachineSnapshot, derived from the pressure and flow fields
that already exist.

Base: main. Independent, and prerequisite for the puck-estimator PR.

puckResistance   R = P / F²    bar·s²/mL²
loadImpedance    Z = P / F     bar·s/mL
hydraulicPower   W = 0.1·P·F   W

They are pure functions of pressure and flow, so any DE1-class machine gets them with no extra
hardware, no firmware change, and no new characteristic. They also apply retroactively to historical
shots, since the inputs were always recorded.

Design notes

Computed on read rather than stored, so there is no migration and no risk of a persisted value
drifting from the pressure/flow it was derived from.

Division guards are explicit. Each channel is gated on flow >= 0.3 mL/s and pressure >= 0.3 bar, and
below either threshold the key is omitted rather than emitted as null, infinity or NaN. Near-zero
flow is the normal state during preinfusion, so this path is hit on every shot.

Note on naming

The follow-up puck-estimator PR renames these to make the derived/measured distinction explicit. On a
Bengle the firmware reports its own measured equivalents from the flow actually passing through the
puck, and the two must not be confused. That rename is in the estimator PR because it necessarily
touches both sides.

Linked Issue

N/A

Verification

  • flutter analyze — clean.

  • flutter testfull suite 3897 passed / 1 skipped, run against current main on 5 Sep 2026.

  • dart format — clean on every changed file.

  • test/models/machine_snapshot_derived_test.dart covers gating, omission and JSON round-trip.

  • No hardware run is required. The channels are pure functions of two existing fields.

  • Verified on hardware. This change ships in the Decaid-Canary build Ben runs on his own
    machine, and has been exercised in normal use rather than only under test.

Impact

  • API: assets/api/rest_v1.yml and assets/api/websocket_v1.yml gain the three fields on
    MachineSnapshot. doc/Api.md documents them and states the omit-when-gated rule.
  • Compatibility: additive and optional. A client that ignores the keys is unaffected.
  • User-visible: none on its own. It gives skins three new channels to plot.
  • Client note: an absent key means "not computable right now", not zero. A client that reads it as
    zero plots a false trace during preinfusion.
  • Security: none.

Contributor Responsibility

AI-assisted development is allowed. The submitter remains responsible for the submitted work.

  • I have reviewed and understand all changes in this PR and take responsibility for their correctness, security, behavior, licensing, and provenance, including any AI-assisted or AI-generated work.

ChampionDesigns and others added 3 commits August 28, 2026 10:47
… channels

Add three compute-on-read channels to MachineSnapshot, derived from the
existing pressure and flow fields:

  puckResistance  R = P / F²    bar·s²/mL²
  loadImpedance   Z = P / F     bar·s/mL
  hydraulicPower  W = 0.1·P·F    W

They are pure functions of pressure and flow, so any DE1-class machine
gets them with no extra hardware or firmware.

- Getters return null (and toJson omits the key entirely, rather than
  emitting null) unless flow >= 0.3 mL/s and pressure >= 0.3 bar. Below
  that the ratios are numerically meaningless, and the omit-not-null
  contract lets consumers treat key presence as the validity signal and
  keeps older clients unaffected.
- A finite guard is mandatory before serialization: jsonEncode throws on
  NaN/Infinity and toJson is streamed on the live machine-snapshot
  websocket, so an unguarded divide would kill the socket.
- fromJson never reads the keys; toJson recomputes them from the raw
  fields, so already-stored history shots gain the channels on read with
  zero migration.

Document the keys in both API specs (same commit per AGENTS.md): the same
toJson serves GET /machine/state and history re-serialization.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 48afe562c9da916b7faf79589afa4705a43c8c9c)
(cherry picked from commit f97d4d54e6bfa1d20f3681844ea4679a480f633d)
Unit tests for MachineSnapshot.puckResistance / loadImpedance /
hydraulicPower: value correctness, the >= 0.3 flow/pressure gate on both
sides, key omission (not null) below the gate, the >= boundary at exactly
0.3, that zero flow keeps NaN/Infinity out of the payload so jsonEncode
does not throw, and that a stored-then-restored snapshot recomputes the
channels from the raw fields.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 32323d2ce7322a5b6c840f3d288b7a26d6c99e15)
(cherry picked from commit 4da994f5b0852dfaa0d6761f913d67bad53a5ee3)
The spec carries the three channels, but Api.md is where a skin author looks
first and it did not mention them. The omit-when-gated rule matters most: the
key disappears rather than going null, so a client that reads absent as zero
plots a lie.
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