Skip to content

MacDive: decode ZRAWDATA profiles via libdivecomputer#264

Merged
ericgriffin merged 7 commits into
mainfrom
feature/macdive-profile-zrawdata
Apr 23, 2026
Merged

MacDive: decode ZRAWDATA profiles via libdivecomputer#264
ericgriffin merged 7 commits into
mainfrom
feature/macdive-profile-zrawdata

Conversation

@ericgriffin
Copy link
Copy Markdown
Member

Summary

Closes the profile-decoding gap from #256 (which landed metadata-only MacDive SQLite import) using the pivot documented in the Phase 1 spike (closed PR #260).

  • Decodes ZDIVE.ZRAWDATA via the already-integrated libdivecomputer_plugin.parseRawDiveData() API.
  • Produces full profile samples (depth, temp, tank pressure, ppO2, NDL, deco state) for 100% of Shearwater dives in the sample DB (267/267: 217 Teric + 50 Tern).
  • Dives without ZRAWDATA (non-Shearwater computers, manual entries, older imports) continue to import metadata-only. Decoder failures emit one ImportWarning.warning; unmapped computers emit profile: [] silently.
  • FFI-unavailable fallback: on platforms where the native plugin isn't registered, the decoder detects the first MissingPluginException/PlatformException(UNSUPPORTED|channel-error), emits one info warning, and skips FFI for remaining dives — avoiding O(N) failing plugin round-trips.

Why not ZSAMPLES

See docs/import-formats/macdive-zsamples.md — MacDive's proprietary ZSAMPLES column is per-dive AES-encrypted. Phase 1 spike ruled it NO-GO; this PR is the pivot to ZRAWDATA.

Changes

  • lib/features/universal_import/data/services/macdive_dive_mapper.darttoPayload is now async with an optional parseRawDiveData parameter (for tests). New helpers: _decodeProfile, _projectSamples, _vendorProductFromZComputer. Projection intentionally duplicates ShearwaterDiveMapper.mergeWithParsedDive (same sample-map keys, same conditional emission).
  • lib/features/universal_import/data/parsers/macdive_sqlite_parser.dart — adds await to the mapper call.
  • test/features/universal_import/data/services/macdive_dive_mapper_test.dart — 10 existing callsites now use await; 4 new tests: decode success, decode failure, null ZRAWDATA, MissingPluginException disables FFI.
  • test/features/universal_import/data/parsers/macdive_sqlite_real_sample_test.dart — replaces the stale "profile always empty" assertion with 4 ZRAWDATA-specific tests (Shearwater decode rate ≥95%, expected sample-map keys, non-Shearwater stays empty, warning count bounded).

Vendor / model mapping

_vendorProductFromZComputer covers: Shearwater Teric, Shearwater Tern, Shearwater Petrel, Shearwater Perdix, Shearwater Perdix 2, Shearwater Nerd, Shearwater NERD 2. Extend if a user provides a DB with other dive computers that libdivecomputer supports.

Test plan

  • flutter analyze clean
  • flutter test test/features/universal_import/data/services/macdive_dive_mapper_test.dart — 14/14 passing
  • flutter test test/features/universal_import/data/parsers/macdive_sqlite_parser_test.dart — 6/6 passing
  • Pre-push hook passes (dart format + flutter analyze + flutter test)
  • Manual: import the sample MacDive SQLite via the import wizard, open a Shearwater dive, confirm the depth-over-time chart renders with tank pressure overlay
  • Gated real-sample test passes locally: MACDIVE_SQLITE_REAL_SAMPLE_PATH=/path/to/MacDive.sqlite flutter test --run-skipped --tags=real-data test/features/universal_import/data/parsers/macdive_sqlite_real_sample_test.dart

Prior work

Known limitations (documented in the findings doc)

  • Non-Shearwater dives in the sample DB (Oceanic Matrix Master — 113 dives) have no ZRAWDATA and no support in the shipped libdivecomputer build. They remain profile-less; users wanting those profiles should continue to use MacDive UDDF export.
  • In test-host environments without a native plugin, the generic Pigeon exception bypasses our two-tier catch and emits per-dive warnings rather than one aggregate info. This is a known limitation of the test environment (not production) and matches ShearwaterDiveMapper's behavior.

MacDiveDiveMapper now decodes ZRAWDATA via parseRawDiveData (same API
ShearwaterDiveMapper uses for Shearwater Cloud). toPayload becomes
async and threads warnings through. _vendorProductFromZComputer maps
ZCOMPUTER strings to libdivecomputer (vendor, product) pairs.

Sample projection matches ShearwaterDiveMapper.mergeWithParsedDive
exactly (same keys, same conditional emission). Dives without
ZRAWDATA or with unmapped computers emit profile:[] silently;
decode failures emit profile:[] + ImportWarning.

Closes the ZRAWDATA part of the MacDive SQLite profile gap from #256.
Mirrors ShearwaterDiveMapper's two-tier exception pattern:
MissingPluginException and PlatformException(UNSUPPORTED|channel-error)
propagate from _decodeProfile so toPayload can disable FFI for the
remaining dives — avoids O(N) failing plugin round-trips on platforms
where the native channel isn't registered.

Also adds Shearwater Perdix 2 and NERD 2 to the ZCOMPUTER -> (vendor,
product) mapping, updates a stale test comment, and adds a regression
test covering the FFI-unavailable fallback path.
Replaces the now-stale 'profile always empty' assertion with four new
tests that verify:
- Shearwater dives (267 in the sample DB) get decoded profiles via
  libdivecomputer (>=95% success rate)
- Decoded sample maps carry at least timestamp + depth
- Non-Shearwater dives still emit profile:[] silently
- Decode-failure warnings stay bounded (<5% of Shearwater dive count)

All four tests guard against the FFI-unavailable unit-test environment
(no native plugin registered) by detecting when all profiles are empty
or all dives emit decode failures, and returning early rather than
producing false failures on the build server.
Copilot AI review requested due to automatic review settings April 23, 2026 22:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds libdivecomputer-backed decoding of MacDive SQLite ZDIVE.ZRAWDATA so Shearwater dives imported from MacDive gain full time-series profile samples (while preserving metadata-only behavior for dives without ZRAWDATA / unsupported computers and providing a one-time “FFI unavailable” fallback warning).

Changes:

  • Make MacDiveDiveMapper.toPayload async and decode ZRAWDATA into canonical profile sample maps via libdivecomputer_plugin.parseRawDiveData, emitting bounded warnings and short-circuiting repeated FFI failures.
  • Update MacDiveSqliteParser to await the async mapper.
  • Extend/adjust tests (synthetic + gated real-sample) to cover decode success/failure, null ZRAWDATA, and “MissingPluginException disables FFI” behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
lib/features/universal_import/data/services/macdive_dive_mapper.dart Adds async profile decoding from ZRAWDATA via libdivecomputer, warning aggregation, and vendor/product mapping for Shearwater models.
lib/features/universal_import/data/parsers/macdive_sqlite_parser.dart Awaits the now-async mapper so mapping failures are caught within parser error handling.
test/features/universal_import/data/services/macdive_dive_mapper_test.dart Updates existing tests to await async mapper and adds targeted profile-decoding unit tests.
test/features/universal_import/data/parsers/macdive_sqlite_real_sample_test.dart Replaces “profile always empty” checks with gated real-sample assertions for Shearwater decode rate/shape and bounded warning behavior.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…ple fields

Raises patch coverage toward the 85% target by exercising previously
untested branches in MacDiveDiveMapper:

- PlatformException(UNSUPPORTED) — disables FFI for remaining dives
- PlatformException(channel-error) — same fatal-error path
- PlatformException with non-fatal code — per-dive warning, FFI stays on
- _defaultParse invoked via toPayload with no parseRawDiveData — exercises
  the real platform-channel code path that tests mock away by default
- _projectSamples on samples with pressureBar + tankIndex, deco ceiling
  (decoType != 0), NDL (decoType == 0), heart rate, setpoint, ppO2, cns,
  rbt, tts — verifies the conditional emission branches
@ericgriffin ericgriffin moved this from Backlog to In review in Submersion Release Tracker Apr 23, 2026
@ericgriffin ericgriffin merged commit 97d7300 into main Apr 23, 2026
16 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in Submersion Release Tracker Apr 23, 2026
@ericgriffin ericgriffin deleted the feature/macdive-profile-zrawdata branch April 23, 2026 23:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants