Skip to content

feat(dive-computer): rank sensorless CCR bailout gases against each other - #1752

Merged
ericgriffin merged 7 commits into
submersion-app:mainfrom
alpheios-one:upstream-ccr-bailout-gas-roles
Sep 12, 2026
Merged

ericgriffin merged 7 commits into
submersion-app:mainfrom
alpheios-one:upstream-ccr-bailout-gas-roles

Conversation

@alpheios-one

Copy link
Copy Markdown
Collaborator

Summary

Without a transmitter, _inferRole() in parsed_tank_resolver.dart scored every reported
gas in isolation against a single fixed threshold (no helium and O2 >= 41% -> Deco,
otherwise Back Gas), regardless of the recognized dive mode. On a CCR dive with several
programmed diluent and bailout gases, that produced wrong roles whenever none of them had
a transmitter/tank record.

libdivecomputer's stock dc_gasmix_t already carries a per-gas dc_usage_t (oxygen/
diluent/sidemount), independent of any tank record - the Shearwater parser sets it - but
the plugin never bridged that field past the C layer (GasMix only ever exposed
o2Percent/hePercent).

This threads GasMix.usage through pigeon and all four platform converters, then uses it
in the sensorless path:

  • A gas whose usage the device reported (oxygen/diluent) keeps that role, regardless of
    dive mode. Sidemount maps to Back Gas (the flag doesn't say left or right, so it can't
    pick between TankRole.sidemountLeft/Right).
  • On a CCR dive, the gases left with no reported usage (the bailout candidates) are ranked
    against each other instead of scored alone: the lowest O2 percentage becomes Bailout
    (tie -> higher helium wins; a further tie gives Bailout to every tied gas; a gas that
    only loses the helium tie-break falls through to the rules below), O2 >= 41% becomes
    Deco, everything else becomes Stage.
  • Every other recognized dive mode keeps the original single-threshold heuristic,
    unchanged.

Related: #1365 (Transmitter Registry, resolved by #1677) sets role only for tanks with
a sensor, or via the registry; this PR is scoped to the sensorless case only, discussed in
#1747.

Native changes

GasMix.usage required bridging dc_gasmix_t.usage through the shared C download engine
(macos/Classes/libdc_download.c, libdc_wrapper.h, used by all platforms) and each
platform converter (Linux, Windows, macOS/iOS Swift, Android JNI + Kotlin), following the
exact pattern already used for TankInfo.usage.

Testing

  • flutter test test/features/dive_computer/data/services/parsed_tank_resolver_test.dart
    • all 30 tests pass, including 5 new cases covering: direct usage mapping (oxygen/
      diluent/sidemount), the bailout/deco/stage ranking, the helium tie-break (both the
      win and the fall-through), the all-tied case, and that non-CCR dive modes are
      unaffected.
  • dart analyze clean on lib/features/dive_computer/ and
    packages/libdivecomputer_plugin/lib.
  • dart format . clean.
  • Regenerated pigeon bindings via dart run pigeon --input pigeons/dive_computer_api.dart.
  • All CI checks (native builds for Android/Linux/Windows/iOS/macOS, C wrapper tests,
    Darwin Swift tests, code generation, analyze & format) passed on the equivalent PR in my
    fork: feat(dive-computer): rank sensorless CCR bailout gases against each other alpheios-one/submersion#61.

Not done here

  • No UI changes.
  • No per-parser audit of which other CCR-capable computers populate
    dc_gasmix_t.usage the same way Shearwater does - only verified against the vendored
    Shearwater parser source.
  • SCR-specific role logic intentionally left as-is (single-threshold heuristic).

Ref: #1747

…ther

Without a transmitter, dive_tanks.role was inferred per gas in isolation
from a single O2/He threshold, ignoring the dive-mode context. That
misclassified programmed CCR diluent and bailout gases the computer never
attaches to a tank record.

libdivecomputer's stock dc_gasmix_t already carries a per-gas dc_usage_t
(oxygen/diluent/sidemount), independent of any tank record, but the plugin
never bridged it past the C layer. Thread GasMix.usage through pigeon and
all four platform converters, then use it in the sensorless resolver:

- usage reported by the device (oxygen/diluent/sidemount) is authoritative,
  regardless of dive mode.
- On a CCR dive, the remaining gases (bailout candidates) are ranked
  against each other instead of scored alone: lowest O2 is bailout (ties
  broken by higher helium, a further tie gives bailout to all tied gases),
  O2 >= 41% is deco, everything else is stage.
- Every other dive mode keeps the original single-threshold heuristic
  unchanged.

Ref: submersion-app#1747
CI's dart format --set-exit-if-changed . flagged all three files touched
by the previous commit. The pigeon-generated dive_computer_api.g.dart
needs a format pass after regeneration too, not just the hand-written
files -- no re-run of pigeon or build_runner needed, the generated
content itself was already correct.
… not a sidemount role

Copilot review on #61: the doc comment and test name implied sidemount
usage kept a distinct "sidemount" role, but it actually maps to
TankRole.backGas -- the device flag says the gas is on a sidemount
cylinder, not which side, so it cannot pick between sidemountLeft and
sidemountRight. Correct the comment and rename the test to match the
real behavior; no logic change.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new CCR bailout ranking logic relies on exact floating-point equality for tie detection, which can misclassify roles due to rounding differences from native-to-Dart percent conversions.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR improves sensorless (tankless) dive imports by bridging libdivecomputer’s per-gas dc_usage_t into GasMix.usage across all platforms and using that data (plus CCR-specific relative ranking) to assign more accurate cylinder roles for CCR dives without transmitters/tank records.

Changes:

  • Add GasMix.usage to the Pigeon API and propagate it through the shared C layer plus Android/JNI, Darwin/Swift, Linux, and Windows converters.
  • Update sensorless role inference to respect device-reported gas usage (oxygen/diluent/sidemount) and to rank CCR bailout candidates relative to each other.
  • Add unit tests covering CCR ranking and usage mapping behavior.
File summaries
File Description
lib/features/dive_computer/data/services/parsed_tank_resolver.dart Uses GasMix.usage for sensorless role inference and adds CCR bailout ranking logic.
test/features/dive_computer/data/services/parsed_tank_resolver_test.dart Adds coverage for CCR sensorless ranking and direct usage mapping.
packages/libdivecomputer_plugin/pigeons/dive_computer_api.dart Extends the Pigeon GasMix model with optional usage.
packages/libdivecomputer_plugin/lib/src/generated/dive_computer_api.g.dart Regenerates Dart bindings to encode/decode GasMix.usage.
packages/libdivecomputer_plugin/android/src/main/kotlin/com/submersion/libdivecomputer/DiveComputerApi.g.kt Regenerates Kotlin bindings for GasMix.usage.
packages/libdivecomputer_plugin/android/src/main/kotlin/com/submersion/libdivecomputer/SerialDownloadRunner.kt Reads bridged gasmix usage and populates GasMix.usage.
packages/libdivecomputer_plugin/android/src/main/kotlin/com/submersion/libdivecomputer/DiveComputerHostApiImpl.kt Reads bridged gasmix usage and populates GasMix.usage.
packages/libdivecomputer_plugin/android/src/main/cpp/libdc_jni.cpp Extends JNI gasmix array payload to include usage.
packages/libdivecomputer_plugin/darwin/Sources/LibDCDarwin/DiveComputerHostApiImpl.swift Populates GasMix.usage from bridged gm.usage.
packages/libdivecomputer_plugin/ios/Classes/DiveComputerApi.g.swift Regenerates Swift bindings for GasMix.usage.
packages/libdivecomputer_plugin/macos/Classes/libdc_wrapper.h Adds usage to libdc_gasmix_t.
packages/libdivecomputer_plugin/macos/Classes/libdc_download.c Copies dc_gasmix_t.usage into the shared parsed dive structure.
packages/libdivecomputer_plugin/linux/dive_converter.c Passes gasmix usage through the Linux converter into the generated API object.
packages/libdivecomputer_plugin/linux/dive_computer_api.g.h Regenerates Linux header bindings to include GasMix.usage.
packages/libdivecomputer_plugin/linux/dive_computer_api.g.cc Regenerates Linux C++ bindings to store/serialize GasMix.usage.
packages/libdivecomputer_plugin/windows/dive_converter.cc Passes gasmix usage through the Windows converter into the generated API object.
packages/libdivecomputer_plugin/windows/dive_computer_api.g.h Regenerates Windows header bindings to include GasMix.usage.
packages/libdivecomputer_plugin/windows/dive_computer_api.g.cc Regenerates Windows C++ bindings to store/serialize GasMix.usage.
Review details
  • Files reviewed: 17/18 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/features/dive_computer/data/services/parsed_tank_resolver.dart
@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

📦 Build artifacts for this PR · commit 28a86bf

Platform Download
Android (APK) android-apk
macOS macos-build
Windows windows-build
Linux linux-build

Artifacts expire in 7 days. Downloading requires being signed in to GitHub. macOS needs two extractions: unzip the downloaded artifact, then unzip the submersion-macos.zip inside it to get a runnable submersion.app. The build is ad-hoc signed — right-click → Open on first launch.

Updated automatically on each push.

…-break

Copilot review on submersion-app#1752: the lowest-O2 and helium tie-break comparisons
used exact double equality. Each of the four platform converters computes
o2Percent/hePercent independently as fraction * 100.0 from the native
dc_gasmix_t, so two mixes the diver set to the same nominal percentage
can differ by a few ULPs instead of being bit-identical -- exact == would
then miss a real tie and misassign bailout/deco/stage roles.

Add a small epsilon comparison and a regression test simulating that
floating-point noise.
Copilot AI review requested due to automatic review settings September 10, 2026 19:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The cross-platform GasMix.usage bridging and the updated CCR sensorless role inference are consistent with the PR’s stated behavior and are backed by targeted unit tests for the new ranking/tie-break logic.

Review details
  • Files reviewed: 17/18 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread lib/features/dive_computer/data/services/parsed_tank_resolver.dart Outdated
alpheios-one added a commit to alpheios-one/submersion that referenced this pull request Sep 10, 2026
… comment

Copilot review on submersion-app#1752: [TankRole.sidemountLeft]/`Right` only linked
sidemountLeft and left Right as a stray inline-code fragment. Link both
roles properly.
… comment

Copilot review on submersion-app#1752: [TankRole.sidemountLeft]/`Right` only linked
sidemountLeft and left Right as a stray inline-code fragment. Link both
roles properly.
Copilot AI review requested due to automatic review settings September 10, 2026 19:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It touches cross-platform native bridging (C/JNI/Kotlin/Swift/C++/GLib) where subtle ABI/serialization issues can slip through despite unit tests, so it warrants final human review.

Review details
  • Files reviewed: 17/18 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings September 11, 2026 16:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Broad cross-platform native changes and an uncovered native usage handoff merit final human verification.

Review details

Suppressed comments (1)

packages/libdivecomputer_plugin/macos/Classes/libdc_download.c:593

  • The production handoff of dc_gasmix_t.usage is not covered by the native tests: test_dive_converter.c only asserts the oxygen/helium fields on libdc_gasmix_t, while the Dart resolver tests construct GasMix directly. A regression in this C extraction or the shared struct field could therefore leave usage null on every platform while the current tests still pass; add a native regression assertion for a nonzero usage value (and the zero/none case) through the wrapper/converter path.
                dive->gasmixes[i].usage = gm.usage;
  • Files reviewed: 17/18 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@alpheios-one

Copy link
Copy Markdown
Collaborator Author

Hi @ericgriffin, just a friendly nudge on this one whenever you have a moment - all CI checks are green, the branch is up to date with main, and both points from the Copilot review are addressed. Happy to make any further adjustments if you'd like a different approach. Thanks!

@ericgriffin ericgriffin added the enhancement New feature or request label Sep 12, 2026
@ericgriffin ericgriffin moved this from Backlog to In review in Submersion Release Tracker Sep 12, 2026
@ericgriffin
ericgriffin merged commit d33ee21 into submersion-app:main Sep 12, 2026
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants