From 55a8b9089f72961c2203c8e4ab7d3c9fb34f5134 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Thu, 16 Jan 2025 16:21:52 +1300 Subject: [PATCH 1/2] Desktop: Show Warning for Dives with No Viable Cylinders. Show a warning when a dive is opened / edited and doesn't have any usable cylinders for the selected dive mode (i.e. no open circuit gas for open circuit / PSCR, or no diluent for CCR). Fixes #4413. Reported-by: @kruegerha Signed-off-by: Michael Keller --- core/dive.cpp | 24 ++++++++++++++++++++++++ core/gas.cpp | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/core/dive.cpp b/core/dive.cpp index 89d2d60e0..544900989 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -1042,6 +1042,27 @@ static void fixup_dc_sample_sensors(struct dive &dive, struct divecomputer &dc) } } +static void fixup_dc_cylinder_use(struct dive &dive, struct divecomputer &dc) +{ + + if (dc.divemode != OC && dc.divemode != CCR && dc.divemode != PSCR) + return; + + // Check that we have at least one cylinder that is suitable for the dive + + // For OC we default to air if we don't have any cylinders + + if (dc.divemode == OC && dive.cylinders.empty()) + return; + + for (auto &cylinder: dive.cylinders) + if ((dc.divemode == CCR && cylinder.cylinder_use == DILUENT) || ((dc.divemode == PSCR || dc.divemode == OC) && cylinder.cylinder_use == OC_GAS)) + return; + + report_error("Dive: %u, dive computer: %s: %s dive, but no %s cylinder found. Please add or select the correct cylinder use.", dive.number, dc.model.c_str(), dc.divemode == OC ? "open circuit" : dc.divemode == CCR ? "CCR" : "PSCR", dc.divemode == OC ? "open circuit" : dc.divemode == CCR ? "diluent" : "drive gas"); +} + + static void fixup_dive_dc(struct dive &dive, struct divecomputer &dc) { /* Fixup duration and mean depth */ @@ -1070,6 +1091,9 @@ static void fixup_dive_dc(struct dive &dive, struct divecomputer &dc) /* Fixup CCR / PSCR dives with o2sensor values, but without no_o2sensors */ fixup_no_o2sensors(dc); + /* Fixup cylinder use */ + fixup_dc_cylinder_use(dive, dc); + /* If there are no samples, generate a fake profile based on depth and time */ if (dc.samples.empty()) fake_dc(&dc); diff --git a/core/gas.cpp b/core/gas.cpp index 0ef4033ba..ca1e076fe 100644 --- a/core/gas.cpp +++ b/core/gas.cpp @@ -62,8 +62,9 @@ void sanitize_gasmix(struct gasmix &mix) /* Sane mix? */ if (o2 <= 1000 && he <= 1000 && o2 + he <= 1000) return; - report_info("Odd gasmix: %u O2 %u He", o2, he); + mix = gasmix_air; + report_error("Odd gasmix: %u O2 %u He, switched to air.", o2, he); } int gasmix_distance(struct gasmix a, struct gasmix b) From b5f823c85a8521199d17a52a0c9ccb86616cdcfb Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Wed, 22 Jan 2025 14:00:28 +1300 Subject: [PATCH 2/2] Warn if Dive Mode / Cylinders are Changed to an Incorrect Configuration. Signed-off-by: Michael Keller --- core/dive.cpp | 14 +++++++------- core/dive.h | 2 ++ desktop-widgets/divelistview.cpp | 24 ++++++++++++++++++++++++ desktop-widgets/divelistview.h | 2 ++ 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 544900989..cff87412d 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -1042,24 +1042,24 @@ static void fixup_dc_sample_sensors(struct dive &dive, struct divecomputer &dc) } } -static void fixup_dc_cylinder_use(struct dive &dive, struct divecomputer &dc) +int check_dc_cylinder_use(struct dive &dive, struct divecomputer &dc) { if (dc.divemode != OC && dc.divemode != CCR && dc.divemode != PSCR) - return; + return 1; // Check that we have at least one cylinder that is suitable for the dive // For OC we default to air if we don't have any cylinders if (dc.divemode == OC && dive.cylinders.empty()) - return; + return 1; for (auto &cylinder: dive.cylinders) if ((dc.divemode == CCR && cylinder.cylinder_use == DILUENT) || ((dc.divemode == PSCR || dc.divemode == OC) && cylinder.cylinder_use == OC_GAS)) - return; + return 1; - report_error("Dive: %u, dive computer: %s: %s dive, but no %s cylinder found. Please add or select the correct cylinder use.", dive.number, dc.model.c_str(), dc.divemode == OC ? "open circuit" : dc.divemode == CCR ? "CCR" : "PSCR", dc.divemode == OC ? "open circuit" : dc.divemode == CCR ? "diluent" : "drive gas"); + return report_error("Dive: %u, dive computer: %s: %s dive, but no %s cylinder found. Please add or select the correct cylinder use.", dive.number, dc.model.c_str(), dc.divemode == OC ? "open circuit" : dc.divemode == CCR ? "CCR" : "PSCR", dc.divemode == OC ? "open circuit" : dc.divemode == CCR ? "diluent" : "drive gas"); } @@ -1091,8 +1091,8 @@ static void fixup_dive_dc(struct dive &dive, struct divecomputer &dc) /* Fixup CCR / PSCR dives with o2sensor values, but without no_o2sensors */ fixup_no_o2sensors(dc); - /* Fixup cylinder use */ - fixup_dc_cylinder_use(dive, dc); + /* Check cylinder use */ + check_dc_cylinder_use(dive, dc); /* If there are no samples, generate a fake profile based on depth and time */ if (dc.samples.empty()) diff --git a/core/dive.h b/core/dive.h index 476b63716..ea72b9c00 100644 --- a/core/dive.h +++ b/core/dive.h @@ -206,6 +206,8 @@ extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id extern void update_setpoint_events(const struct dive *dive, struct divecomputer *dc); +extern int check_dc_cylinder_use(struct dive &dive, struct divecomputer &dc); + /* Make pointers to dive and dive_trip "Qt metatypes" so that they can be passed through * QVariants and through QML. */ diff --git a/desktop-widgets/divelistview.cpp b/desktop-widgets/divelistview.cpp index fc4e32763..be58d68e0 100644 --- a/desktop-widgets/divelistview.cpp +++ b/desktop-widgets/divelistview.cpp @@ -43,6 +43,9 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), connect(m, &MultiFilterSortModel::divesSelected, this, &DiveListView::divesSelectedSlot); connect(m, &MultiFilterSortModel::tripSelected, this, &DiveListView::tripSelected); connect(&diveListNotifier, &DiveListNotifier::settingsChanged, this, &DiveListView::settingsChanged); + connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &DiveListView::divesChanged); + connect(&diveListNotifier, &DiveListNotifier::cylinderEdited, this, &DiveListView::cylinderEdited); + connect(&diveListNotifier, &DiveListNotifier::cylinderRemoved, this, &DiveListView::cylinderEdited); setSortingEnabled(true); setContextMenuPolicy(Qt::DefaultContextMenu); @@ -389,6 +392,27 @@ void DiveListView::settingsChanged() } } +static void check_cylinder_use(struct dive &dive) +{ + for (auto &divecomputer: dive.dcs) + check_dc_cylinder_use(dive, divecomputer); +} + + +void DiveListView::divesChanged(const QVector &dives, DiveField field) +{ + if (!field.mode) + return; + + for (auto &dive: dives) + check_cylinder_use(*dive); +} + +void DiveListView::cylinderEdited(struct dive *dive, int) +{ + check_cylinder_use(*dive); +} + void DiveListView::toggleColumnVisibilityByIndex() { QAction *action = qobject_cast(sender()); diff --git a/desktop-widgets/divelistview.h b/desktop-widgets/divelistview.h index 5e7f854c2..ae380d78f 100644 --- a/desktop-widgets/divelistview.h +++ b/desktop-widgets/divelistview.h @@ -54,6 +54,8 @@ slots: void shiftTimes(); void divesSelectedSlot(const QVector &indices, QModelIndex currentDive, int currentDC); void tripSelected(QModelIndex trip, QModelIndex currentDive); + void divesChanged(const QVector &dives, DiveField field); + void cylinderEdited(dive *dive, int); private: void rowsInserted(const QModelIndex &parent, int start, int end) override; void reset() override;