Repository navigation
halcyon: Ignore disabled open-circuit gas slots #148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mikeller
wants to merge
5
commits into
subsurface:Subsurface-DS9
Choose a base branch
from
mikeller:feat/symbios-oc-enabled-636
base: Subsurface-DS9
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
86ddda9
halcyon: Ignore disabled open-circuit gas slots
mikeller 4ac0bc9
halcyon: Resolve disabled gas slots referenced by a transmitter
mikeller a55830a
halcyon: Reject conflicting retained gas configurations
mikeller d8bc9d8
halcyon: Run transmitter gas resolution without a prior enabled gas
mikeller 7b4f591
test: Improve Symbios gas-config test clarity and robustness
mikeller File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,227 @@ | ||
| /* Synthetic Symbios records: no device logs or identifying data. */ | ||
| #include <math.h> | ||
| #include <stdio.h> | ||
|
|
||
| #include <libdivecomputer/descriptor.h> | ||
| #include <libdivecomputer/iterator.h> | ||
| #include <libdivecomputer/parser.h> | ||
|
|
||
| #define GAS(id, o2, he, enabled) 0x11, 8, id, o2, he, enabled, 0, 0 | ||
| #define TRANSMITTER(id, serial) 0x06, 12, id, 0, 0, 0, 0xD2, 0x04, serial, 0, 0, 0 | ||
|
|
||
| /* dc_descriptor_get_model() value for the "Symbios Handset", see descriptor.c. */ | ||
| #define HANDSET_MODEL_SYMBIOS 7 | ||
|
|
||
| typedef struct samples_t { | ||
| unsigned int gases[16]; | ||
| unsigned int ngases; | ||
| unsigned int pressures[16]; | ||
| unsigned int npressures; | ||
| } samples_t; | ||
|
|
||
| static int failures = 0; | ||
|
|
||
| static dc_descriptor_t * | ||
| handset_descriptor (void) | ||
| { | ||
| dc_iterator_t *iterator = NULL; | ||
| dc_descriptor_t *descriptor = NULL; | ||
| dc_descriptor_t *item = NULL; | ||
| if (dc_descriptor_iterator (&iterator) != DC_STATUS_SUCCESS) | ||
| return NULL; | ||
| while (dc_iterator_next (iterator, &item) == DC_STATUS_SUCCESS) { | ||
| if (dc_descriptor_get_type (item) == DC_FAMILY_HALCYON_SYMBIOS && | ||
| dc_descriptor_get_model (item) == HANDSET_MODEL_SYMBIOS) { | ||
| descriptor = item; | ||
| break; | ||
| } | ||
| } | ||
| dc_iterator_free (iterator); | ||
| return descriptor; | ||
| } | ||
|
|
||
| static void | ||
| check (const char *name, int condition) | ||
| { | ||
| if (!condition) { | ||
| fprintf (stderr, "FAIL: %s\n", name); | ||
| failures++; | ||
| } | ||
| } | ||
|
|
||
| static void | ||
| sample (dc_sample_type_t type, const dc_sample_value_t *value, void *userdata) | ||
| { | ||
| samples_t *samples = userdata; | ||
| if (type == DC_SAMPLE_GASMIX && samples->ngases < 16) | ||
| samples->gases[samples->ngases++] = value->gasmix; | ||
| if (type == DC_SAMPLE_PRESSURE && samples->npressures < 16) | ||
| samples->pressures[samples->npressures++] = value->pressure.tank; | ||
| } | ||
|
|
||
| static void | ||
| check_mix (dc_parser_t *parser, unsigned int index, unsigned int oxygen, unsigned int helium) | ||
| { | ||
| dc_gasmix_t mix; | ||
| check ("mix field", dc_parser_get_field (parser, DC_FIELD_GASMIX, index, &mix) == DC_STATUS_SUCCESS); | ||
| check ("oxygen fraction", fabs (mix.oxygen - oxygen / 100.0) < 1e-9); | ||
| check ("helium fraction", fabs (mix.helium - helium / 100.0) < 1e-9); | ||
| } | ||
|
|
||
| static void | ||
| check_profile (const unsigned char *data, size_t size, unsigned int expected_count, | ||
| const unsigned int *expected_samples, unsigned int nsamples) | ||
| { | ||
| dc_parser_t *parser = NULL; | ||
| dc_descriptor_t *descriptor = handset_descriptor (); | ||
| samples_t samples = {{0}, 0, {0}, 0}; | ||
| unsigned int count = 0; | ||
| check ("handset descriptor", descriptor != NULL); | ||
| if (descriptor == NULL) | ||
| return; | ||
| check ("create parser", dc_parser_new2 (&parser, NULL, descriptor, data, size) == DC_STATUS_SUCCESS); | ||
| dc_descriptor_free (descriptor); | ||
| if (parser == NULL) | ||
| return; | ||
| check ("parse samples", dc_parser_samples_foreach (parser, sample, &samples) == DC_STATUS_SUCCESS); | ||
| check ("gas count field", dc_parser_get_field (parser, DC_FIELD_GASMIX_COUNT, 0, &count) == DC_STATUS_SUCCESS); | ||
| check ("gas count", count == expected_count); | ||
| check ("sample count", samples.ngases == nsamples); | ||
| for (unsigned int i = 0; i < nsamples && i < samples.ngases; ++i) | ||
| check ("sample gas index", samples.gases[i] == expected_samples[i]); | ||
| if (expected_count == 4) { | ||
| check_mix (parser, 0, 21, 0); | ||
| check_mix (parser, 1, 21, 0); | ||
| check_mix (parser, 2, 21, 35); | ||
| check_mix (parser, 3, 18, 45); | ||
| } else if (expected_count == 8) { | ||
| dc_tank_t tank; | ||
| check_mix (parser, 1, 32, 0); | ||
| check_mix (parser, 2, 30, 0); /* Enabled, never selected. */ | ||
| check_mix (parser, 3, 21, 0); | ||
| check_mix (parser, 6, 40, 0); | ||
| check ("tank count", dc_parser_get_field (parser, DC_FIELD_TANK_COUNT, 0, &count) == DC_STATUS_SUCCESS && count == 2); | ||
| check ("first tank", dc_parser_get_field (parser, DC_FIELD_TANK, 0, &tank) == DC_STATUS_SUCCESS && tank.gasmix == 1); | ||
| check ("second tank", dc_parser_get_field (parser, DC_FIELD_TANK, 1, &tank) == DC_STATUS_SUCCESS && tank.gasmix == 3); | ||
| check ("pressure tank indices", samples.npressures == 2 && samples.pressures[0] == 0 && samples.pressures[1] == 1); | ||
| } | ||
| check ("destroy parser", dc_parser_destroy (parser) == DC_STATUS_SUCCESS); | ||
| } | ||
|
|
||
| int | ||
| main (void) | ||
| { | ||
| /* Native-export pattern: disabled GAS2-5, enabled GAS1, unchanged diluents. */ | ||
| static const unsigned char native_pattern[] = { | ||
| GAS (0, 21, 0, 1), GAS (1, 50, 0, 0), GAS (2, 99, 0, 0), | ||
| GAS (3, 21, 35, 0), GAS (4, 18, 45, 0), GAS (5, 21, 0, 1), | ||
| GAS (6, 21, 35, 0), GAS (7, 18, 45, 0), GAS (0, 21, 0, 1), | ||
| GAS (1, 50, 0, 0), GAS (2, 0, 0, 1) | ||
| }; | ||
| static const unsigned int native_samples[] = {0}; | ||
| /* Non-boolean nonzero enable value; gas 2 remains visible without selection. */ | ||
| static const unsigned char linkage[] = { | ||
| GAS (0, 21, 0, 1), GAS (1, 50, 0, 0), GAS (2, 32, 0, 2), | ||
| GAS (4, 30, 0, 2), | ||
| GAS (5, 21, 0, 0), GAS (6, 21, 35, 1), GAS (7, 18, 45, 0), | ||
| GAS (8, 40, 0, 0), GAS (1, 50, 0, 0), GAS (0, 21, 0, 1), | ||
| TRANSMITTER (2, 1), TRANSMITTER (5, 2), | ||
| 0x02, 4, 36, 0 /* Gas-switch record retains its independent path. */ | ||
| }; | ||
| /* | ||
| * Expected gas count of 8: 7 distinct ids registered from "linkage" | ||
| * (0, 2, 4, 5, 6, 7, 8, at indices 0-6) plus one more gas mix | ||
| * synthesized by the trailing ID_GAS_SWITCH record, which always uses | ||
| * its own untagged id and is appended as index 7. Ids 0, 2 and 4 are | ||
| * registered because they are enabled; ids 5, 7 and 8 are registered | ||
| * regardless of their enable byte because ids >= NGAS_SLOTS are | ||
| * diluents, which skip the enable check; id 6 is both enabled and a | ||
| * diluent. | ||
| * | ||
| * Expected sample indices {0, 1, 3, 7}: index 0 comes from the | ||
| * repeated GAS(0, 21, 0, 1) record re-confirming the already | ||
| * registered gas 0; index 1 and 3 come from TRANSMITTER(2, 1) and | ||
| * TRANSMITTER(5, 2) selecting gases 2 and 5; index 7 comes from the | ||
| * trailing gas-switch record. | ||
| */ | ||
| static const unsigned int linkage_samples[] = {0, 1, 3, 7}; | ||
| /* Gas1 is disabled in its config but later reported by a transmitter; | ||
| * it must resolve instead of being rejected, and only becomes part of | ||
| * the exposed gas list once referenced. */ | ||
| static const unsigned char deferred[] = { | ||
| GAS (0, 21, 0, 1), GAS (1, 32, 0, 0), | ||
| TRANSMITTER (1, 5) | ||
| }; | ||
| /* Gas1 is disabled and reconfigured with a different oxygen percentage | ||
| * before ever being referenced; the conflicting retained configuration | ||
| * must still be rejected, instead of silently keeping the first one. */ | ||
| static const unsigned char deferred_conflict[] = { | ||
| GAS (0, 21, 0, 1), GAS (1, 32, 0, 0), GAS (1, 50, 0, 0) | ||
| }; | ||
| /* The only configured gas is disabled, so no enabled or diluent gas is | ||
| * ever registered; its transmitter must still resolve it instead of | ||
| * leaving the tank's gas mix unknown. */ | ||
| static const unsigned char disabled_only[] = { | ||
| GAS (0, 21, 0, 0), TRANSMITTER (0, 9) | ||
| }; | ||
| dc_parser_t *parser = NULL; | ||
| dc_descriptor_t *descriptor = handset_descriptor (); | ||
|
Copilot marked this conversation as resolved.
Resolved
|
||
| static const unsigned char short_config[] = {0x11, 7, 0, 21, 0, 1, 0}; | ||
| dc_parser_t *deferred_parser = NULL; | ||
| dc_descriptor_t *deferred_descriptor = handset_descriptor (); | ||
| unsigned int deferred_count = 0; | ||
| dc_tank_t deferred_tank; | ||
| dc_parser_t *conflict_parser = NULL; | ||
| dc_descriptor_t *conflict_descriptor = handset_descriptor (); | ||
| dc_parser_t *disabled_only_parser = NULL; | ||
| dc_descriptor_t *disabled_only_descriptor = handset_descriptor (); | ||
| unsigned int disabled_only_count = 0; | ||
| dc_tank_t disabled_only_tank; | ||
|
|
||
| check_profile (native_pattern, sizeof (native_pattern), 4, native_samples, 1); | ||
| check_profile (linkage, sizeof (linkage), 8, linkage_samples, 4); | ||
|
Copilot marked this conversation as resolved.
Resolved
|
||
| check ("create short parser", dc_parser_new2 (&parser, NULL, descriptor, short_config, sizeof (short_config)) == DC_STATUS_SUCCESS); | ||
|
Copilot marked this conversation as resolved.
Resolved
|
||
| dc_descriptor_free (descriptor); | ||
| if (parser) { | ||
| check ("reject short gas config", dc_parser_samples_foreach (parser, NULL, NULL) == DC_STATUS_DATAFORMAT); | ||
| dc_parser_destroy (parser); | ||
| } | ||
|
|
||
| check ("handset descriptor (deferred)", deferred_descriptor != NULL); | ||
| if (deferred_descriptor) { | ||
| check ("create deferred parser", dc_parser_new2 (&deferred_parser, NULL, deferred_descriptor, deferred, sizeof (deferred)) == DC_STATUS_SUCCESS); | ||
| dc_descriptor_free (deferred_descriptor); | ||
| } | ||
| if (deferred_parser) { | ||
| check ("resolve deferred transmitter", dc_parser_samples_foreach (deferred_parser, NULL, NULL) == DC_STATUS_SUCCESS); | ||
| check ("deferred gas count field", dc_parser_get_field (deferred_parser, DC_FIELD_GASMIX_COUNT, 0, &deferred_count) == DC_STATUS_SUCCESS); | ||
| check ("deferred gas count", deferred_count == 2); | ||
| check ("deferred tank gasmix", dc_parser_get_field (deferred_parser, DC_FIELD_TANK, 0, &deferred_tank) == DC_STATUS_SUCCESS && deferred_tank.gasmix == 1); | ||
| dc_parser_destroy (deferred_parser); | ||
| } | ||
|
|
||
| check ("handset descriptor (conflict)", conflict_descriptor != NULL); | ||
| if (conflict_descriptor) { | ||
| check ("create conflict parser", dc_parser_new2 (&conflict_parser, NULL, conflict_descriptor, deferred_conflict, sizeof (deferred_conflict)) == DC_STATUS_SUCCESS); | ||
| dc_descriptor_free (conflict_descriptor); | ||
| } | ||
| if (conflict_parser) { | ||
| check ("reject conflicting retained configuration", dc_parser_samples_foreach (conflict_parser, NULL, NULL) == DC_STATUS_DATAFORMAT); | ||
| dc_parser_destroy (conflict_parser); | ||
| } | ||
|
|
||
| check ("handset descriptor (disabled only)", disabled_only_descriptor != NULL); | ||
| if (disabled_only_descriptor) { | ||
| check ("create disabled-only parser", dc_parser_new2 (&disabled_only_parser, NULL, disabled_only_descriptor, disabled_only, sizeof (disabled_only)) == DC_STATUS_SUCCESS); | ||
| dc_descriptor_free (disabled_only_descriptor); | ||
| } | ||
| if (disabled_only_parser) { | ||
| check ("resolve transmitter without a prior enabled gas", dc_parser_samples_foreach (disabled_only_parser, NULL, NULL) == DC_STATUS_SUCCESS); | ||
| check ("disabled-only gas count field", dc_parser_get_field (disabled_only_parser, DC_FIELD_GASMIX_COUNT, 0, &disabled_only_count) == DC_STATUS_SUCCESS); | ||
| check ("disabled-only gas count", disabled_only_count == 1); | ||
| check ("disabled-only tank gasmix", dc_parser_get_field (disabled_only_parser, DC_FIELD_TANK, 0, &disabled_only_tank) == DC_STATUS_SUCCESS && disabled_only_tank.gasmix == 0); | ||
| dc_parser_destroy (disabled_only_parser); | ||
| } | ||
|
|
||
| return failures != 0; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.