diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index d9fa691a7..bffa80c17 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -391,7 +391,7 @@ AddDive::AddDive(std::unique_ptr d, bool autogroup, bool newNumber) setText(Command::Base::tr("add dive")); d->maxdepth.mm = 0; d->dcs[0].maxdepth.mm = 0; - fixup_dive(d.get()); + divelog.dives.fixup_dive(*d); // this only matters if undoit were called before redoit currentDive = nullptr; diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 163e1f7ca..6276896c0 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -256,7 +256,7 @@ void EditWaterTemp::set(struct dive *d, int value) const d->watertemp.mkelvin = value > 0 ? (uint32_t)value : 0u; // re-populate the temperatures - easiest way to do this is by calling fixup_dive - fixup_dive(d); + divelog.dives.fixup_dive(*d); } int EditWaterTemp::data(struct dive *d) const @@ -787,7 +787,7 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive), // Fix source. Things might be inconsistent after modifying the profile. source->maxdepth.mm = source->dcs[0].maxdepth.mm = 0; - fixup_dive(source); + divelog.dives.fixup_dive(*source); when = source->when; maxdepth = source->maxdepth; @@ -824,7 +824,7 @@ void ReplanDive::undo() std::swap(d->surface_pressure, surface_pressure); std::swap(d->duration, duration); std::swap(d->salinity, salinity); - fixup_dive(d); + divelog.dives.fixup_dive(*d); invalidate_dive_cache(d); // Ensure that dive is written in git_save() QVector divesToNotify = { d }; @@ -899,7 +899,7 @@ void EditProfile::undo() std::swap(d->maxdepth, maxdepth); std::swap(d->meandepth, meandepth); std::swap(d->duration, duration); - fixup_dive(d); + divelog.dives.fixup_dive(*d); invalidate_dive_cache(d); // Ensure that dive is written in git_save() QVector divesToNotify = { d }; @@ -1101,7 +1101,7 @@ void AddCylinder::undo() { for (size_t i = 0; i < dives.size(); ++i) { remove_cylinder(dives[i], indexes[i]); - divelog.dives.update_cylinder_related_info(dives[i]); + divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } @@ -1114,7 +1114,7 @@ void AddCylinder::redo() int index = first_hidden_cylinder(d); indexes.push_back(index); add_cylinder(&d->cylinders, index, cyl); - divelog.dives.update_cylinder_related_info(d); + divelog.dives.update_cylinder_related_info(*d); emit diveListNotifier.cylinderAdded(d, index); invalidate_dive_cache(d); // Ensure that dive is written in git_save() } @@ -1201,7 +1201,7 @@ void RemoveCylinder::undo() std::vector mapping = get_cylinder_map_for_add(dives[i]->cylinders.size(), indexes[i]); add_cylinder(&dives[i]->cylinders, indexes[i], cyl[i]); cylinder_renumber(*dives[i], &mapping[0]); - divelog.dives.update_cylinder_related_info(dives[i]); + divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderAdded(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } @@ -1213,7 +1213,7 @@ void RemoveCylinder::redo() std::vector mapping = get_cylinder_map_for_remove(dives[i]->cylinders.size(), indexes[i]); remove_cylinder(dives[i], indexes[i]); cylinder_renumber(*dives[i], &mapping[0]); - divelog.dives.update_cylinder_related_info(dives[i]); + divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } @@ -1273,7 +1273,7 @@ void EditCylinder::redo() const std::string &name = cyl[i].type.description; set_tank_info_data(tank_info_table, name, cyl[i].type.size, cyl[i].type.workingpressure); std::swap(*get_cylinder(dives[i], indexes[i]), cyl[i]); - divelog.dives.update_cylinder_related_info(dives[i]); + divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderEdited(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } diff --git a/commands/command_event.cpp b/commands/command_event.cpp index da43b4f95..da727780f 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -2,6 +2,8 @@ #include "command_event.h" #include "core/dive.h" +#include "core/divelist.h" +#include "core/divelog.h" #include "core/selection.h" #include "core/subsurface-qt/divelistnotifier.h" #include "core/libdivecomputer.h" @@ -145,7 +147,7 @@ void RemoveEvent::post() const if (cylinder < 0) return; - fixup_dive(d); + divelog.dives.fixup_dive(*d); emit diveListNotifier.cylinderEdited(d, cylinder); // TODO: This is silly we send a DURATION change event so that the statistics are recalculated. @@ -199,7 +201,7 @@ void AddGasSwitch::redoit() eventsToRemove = std::move(newEventsToRemove); // this means we potentially have a new tank that is being used and needs to be shown - fixup_dive(d); + divelog.dives.fixup_dive(*d); for (int idx: cylinders) emit diveListNotifier.cylinderEdited(d, idx); diff --git a/core/dive.cpp b/core/dive.cpp index 4dc440e35..149b13067 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -28,8 +28,6 @@ #include "trip.h" #include "fulltext.h" -#include - // For user visible text but still not translated const char *divemode_text_ui[] = { QT_TRANSLATE_NOOP("gettextFromC", "Open circuit"), @@ -53,22 +51,6 @@ dive::dive(dive &&) = default; dive &dive::operator=(const dive &) = default; dive::~dive() = default; -// create a dive an hour from now with a default depth (15m/45ft) and duration (40 minutes) -// as a starting point for the user to edit -std::unique_ptr dive::default_dive() -{ - auto d = std::make_unique(); - d->when = time(nullptr) + gettimezoneoffset() + 3600; - d->dcs[0].duration.seconds = 40 * 60; - d->dcs[0].maxdepth.mm = M_OR_FT(15, 45); - d->dcs[0].meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop - make_manually_added_dive_dc(&d->dcs[0]); - fake_dc(&d->dcs[0]); - add_default_cylinder(d.get()); - fixup_dive(d.get()); - return d; -} - /* * The legacy format for sample pressures has a single pressure * for each sample that can have any sensor, plus a possible @@ -446,13 +428,13 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i } } -static void update_min_max_temperatures(struct dive *dive, temperature_t temperature) +static void update_min_max_temperatures(struct dive &dive, temperature_t temperature) { if (temperature.mkelvin) { - if (!dive->maxtemp.mkelvin || temperature.mkelvin > dive->maxtemp.mkelvin) - dive->maxtemp = temperature; - if (!dive->mintemp.mkelvin || temperature.mkelvin < dive->mintemp.mkelvin) - dive->mintemp = temperature; + if (!dive.maxtemp.mkelvin || temperature.mkelvin > dive.maxtemp.mkelvin) + dive.maxtemp = temperature; + if (!dive.mintemp.mkelvin || temperature.mkelvin < dive.mintemp.mkelvin) + dive.mintemp = temperature; } } @@ -604,9 +586,9 @@ static void sanitize_cylinder_type(cylinder_type_t &type) match_standard_cylinder(type); } -static void sanitize_cylinder_info(struct dive *dive) +static void sanitize_cylinder_info(struct dive &dive) { - for (auto &cyl :dive->cylinders) { + for (auto &cyl: dive.cylinders) { sanitize_gasmix(cyl.gasmix); sanitize_cylinder_type(cyl.type); } @@ -640,9 +622,9 @@ pressure_t dive::calculate_surface_pressure() const return res; } -static void fixup_surface_pressure(struct dive *dive) +static void fixup_surface_pressure(struct dive &dive) { - dive->surface_pressure = dive->calculate_surface_pressure(); + dive.surface_pressure = dive.calculate_surface_pressure(); } /* if the surface pressure in the dive data is redundant to the calculated @@ -654,12 +636,12 @@ pressure_t dive::un_fixup_surface_pressure() const pressure_t() : surface_pressure; } -static void fixup_water_salinity(struct dive *dive) +static void fixup_water_salinity(struct dive &dive) { int sum = 0, nr = 0; - bool logged = dive->is_logged(); - for (auto &dc: dive->dcs) { + bool logged = dive.is_logged(); + for (auto &dc: dive.dcs) { if ((logged || !is_dc_planner(&dc)) && dc.salinity) { if (dc.salinity < 500) dc.salinity += FRESHWATER_SALINITY; @@ -668,7 +650,7 @@ static void fixup_water_salinity(struct dive *dive) } } if (nr) - dive->salinity = (sum + nr / 2) / nr; + dive.salinity = (sum + nr / 2) / nr; } int get_dive_salinity(const struct dive *dive) @@ -676,12 +658,12 @@ int get_dive_salinity(const struct dive *dive) return dive->user_salinity ? dive->user_salinity : dive->salinity; } -static void fixup_meandepth(struct dive *dive) +static void fixup_meandepth(struct dive &dive) { int sum = 0, nr = 0; - bool logged = dive->is_logged(); - for (auto &dc: dive->dcs) { + bool logged = dive.is_logged(); + for (auto &dc: dive.dcs) { if ((logged || !is_dc_planner(&dc)) && dc.meandepth.mm) { sum += dc.meandepth.mm; nr++; @@ -689,31 +671,31 @@ static void fixup_meandepth(struct dive *dive) } if (nr) - dive->meandepth.mm = (sum + nr / 2) / nr; + dive.meandepth.mm = (sum + nr / 2) / nr; } -static void fixup_duration(struct dive *dive) +static void fixup_duration(struct dive &dive) { - duration_t duration = { }; + duration_t duration; - bool logged = dive->is_logged(); - for (auto &dc: dive->dcs) { + bool logged = dive.is_logged(); + for (auto &dc: dive.dcs) { if (logged || !is_dc_planner(&dc)) duration.seconds = std::max(duration.seconds, dc.duration.seconds); } - dive->duration.seconds = duration.seconds; + dive.duration.seconds = duration.seconds; } -static void fixup_watertemp(struct dive *dive) +static void fixup_watertemp(struct dive &dive) { - if (!dive->watertemp.mkelvin) - dive->watertemp = dive->dc_watertemp(); + if (!dive.watertemp.mkelvin) + dive.watertemp = dive.dc_watertemp(); } -static void fixup_airtemp(struct dive *dive) +static void fixup_airtemp(struct dive &dive) { - if (!dive->airtemp.mkelvin) - dive->airtemp = dive->dc_airtemp(); + if (!dive.airtemp.mkelvin) + dive.airtemp = dive.dc_airtemp(); } /* if the air temperature in the dive data is redundant to the one in its @@ -772,7 +754,7 @@ static int interpolate_depth(struct divecomputer &dc, int idx, int lastdepth, in return interpolate(lastdepth, nextdepth, now-lasttime, nexttime-lasttime); } -static void fixup_dc_depths(struct dive *dive, struct divecomputer &dc) +static void fixup_dc_depths(struct dive &dive, struct divecomputer &dc) { int maxdepth = dc.maxdepth.mm; int lasttime = 0, lastdepth = 0; @@ -793,14 +775,14 @@ static void fixup_dc_depths(struct dive *dive, struct divecomputer &dc) lastdepth = depth; lasttime = time; - if (sample.cns > dive->maxcns) - dive->maxcns = sample.cns; + if (sample.cns > dive.maxcns) + dive.maxcns = sample.cns; } update_depth(&dc.maxdepth, maxdepth); - if (!dive->is_logged() || !is_dc_planner(&dc)) - if (maxdepth > dive->maxdepth.mm) - dive->maxdepth.mm = maxdepth; + if (!dive.is_logged() || !is_dc_planner(&dc)) + if (maxdepth > dive.maxdepth.mm) + dive.maxdepth.mm = maxdepth; } static void fixup_dc_ndl(struct divecomputer &dc) @@ -813,7 +795,7 @@ static void fixup_dc_ndl(struct divecomputer &dc) } } -static void fixup_dc_temp(struct dive *dive, struct divecomputer &dc) +static void fixup_dc_temp(struct dive &dive, struct divecomputer &dc) { int mintemp = 0, lasttemp = 0; @@ -866,19 +848,19 @@ static void simplify_dc_pressures(struct divecomputer &dc) } /* Do we need a sensor -> cylinder mapping? */ -static void fixup_start_pressure(struct dive *dive, int idx, pressure_t p) +static void fixup_start_pressure(struct dive &dive, int idx, pressure_t p) { - if (idx >= 0 && static_cast(idx) < dive->cylinders.size()) { - cylinder_t &cyl = dive->cylinders[idx]; + if (idx >= 0 && static_cast(idx) < dive.cylinders.size()) { + cylinder_t &cyl = dive.cylinders[idx]; if (p.mbar && !cyl.sample_start.mbar) cyl.sample_start = p; } } -static void fixup_end_pressure(struct dive *dive, int idx, pressure_t p) +static void fixup_end_pressure(struct dive &dive, int idx, pressure_t p) { - if (idx >= 0 && static_cast(idx) < dive->cylinders.size()) { - cylinder_t &cyl = dive->cylinders[idx]; + if (idx >= 0 && static_cast(idx) < dive.cylinders.size()) { + cylinder_t &cyl = dive.cylinders[idx]; if (p.mbar && !cyl.sample_end.mbar) cyl.sample_end = p; } @@ -897,7 +879,7 @@ static void fixup_end_pressure(struct dive *dive, int idx, pressure_t p) * for computers like the Uemis Zurich that end up saving * quite a bit of samples after the dive has ended). */ -static void fixup_dive_pressures(struct dive *dive, struct divecomputer &dc) +static void fixup_dive_pressures(struct dive &dive, struct divecomputer &dc) { /* Walk the samples from the beginning to find starting pressures.. */ for (auto &sample: dc.samples) { @@ -923,7 +905,7 @@ static void fixup_dive_pressures(struct dive *dive, struct divecomputer &dc) /* * Match a gas change event against the cylinders we have */ -static bool validate_gaschange(struct dive *dive, struct event &event) +static bool validate_gaschange(struct dive &dive, struct event &event) { int index; int o2, he, value; @@ -936,13 +918,13 @@ static bool validate_gaschange(struct dive *dive, struct event &event) if (event.gas.index >= 0) return true; - index = find_best_gasmix_match(event.gas.mix, dive->cylinders); - if (index < 0 || static_cast(index) >= dive->cylinders.size()) + index = find_best_gasmix_match(event.gas.mix, dive.cylinders); + if (index < 0 || static_cast(index) >= dive.cylinders.size()) return false; /* Fix up the event to have the right information */ event.gas.index = index; - event.gas.mix = dive->cylinders[index].gasmix; + event.gas.mix = dive.cylinders[index].gasmix; /* Convert to odd libdivecomputer format */ o2 = get_o2(event.gas.mix); @@ -960,19 +942,19 @@ static bool validate_gaschange(struct dive *dive, struct event &event) } /* Clean up event, return true if event is ok, false if it should be dropped as bogus */ -static bool validate_event(struct dive *dive, struct event &event) +static bool validate_event(struct dive &dive, struct event &event) { if (event.is_gaschange()) return validate_gaschange(dive, event); return true; } -static void fixup_dc_gasswitch(struct dive *dive, struct divecomputer &dc) +static void fixup_dc_gasswitch(struct dive &dive, struct divecomputer &dc) { // erase-remove idiom auto &events = dc.events; events.erase(std::remove_if(events.begin(), events.end(), - [dive](auto &ev) { return !validate_event(dive, ev); }), + [&dive](auto &ev) { return !validate_event(dive, ev); }), events.end()); } @@ -1000,7 +982,7 @@ static void fixup_no_o2sensors(struct divecomputer &dc) } } -static void fixup_dc_sample_sensors(struct dive *dive, struct divecomputer &dc) +static void fixup_dc_sample_sensors(struct dive &dive, struct divecomputer &dc) { unsigned long sensor_mask = 0; @@ -1027,16 +1009,16 @@ static void fixup_dc_sample_sensors(struct dive *dive, struct divecomputer &dc) } // Ignore the sensors we have cylinders for - sensor_mask >>= dive->cylinders.size(); + sensor_mask >>= dive.cylinders.size(); // Do we need to add empty cylinders? while (sensor_mask) { - add_empty_cylinder(&dive->cylinders); + add_empty_cylinder(&dive.cylinders); sensor_mask >>= 1; } } -static void fixup_dive_dc(struct dive *dive, struct divecomputer &dc) +static void fixup_dive_dc(struct dive &dive, struct divecomputer &dc) { /* Fixup duration and mean depth */ fixup_dc_duration(dc); @@ -1069,44 +1051,38 @@ static void fixup_dive_dc(struct dive *dive, struct divecomputer &dc) fake_dc(&dc); } -struct dive *fixup_dive(struct dive *dive) +void dive::fixup_no_cylinder() { - sanitize_cylinder_info(dive); - dive->maxcns = dive->cns; + sanitize_cylinder_info(*this); + maxcns = cns; /* * Use the dive's temperatures for minimum and maximum in case * we do not have temperatures recorded by DC. */ - update_min_max_temperatures(dive, dive->watertemp); + update_min_max_temperatures(*this, watertemp); - for (auto &dc: dive->dcs) - fixup_dive_dc(dive, dc); + for (auto &dc: dcs) + fixup_dive_dc(*this, dc); - fixup_water_salinity(dive); - if (!dive->surface_pressure.mbar) - fixup_surface_pressure(dive); - fixup_meandepth(dive); - fixup_duration(dive); - fixup_watertemp(dive); - fixup_airtemp(dive); - for (auto &cyl: dive->cylinders) { + fixup_water_salinity(*this); + if (!surface_pressure.mbar) + fixup_surface_pressure(*this); + fixup_meandepth(*this); + fixup_duration(*this); + fixup_watertemp(*this); + fixup_airtemp(*this); + for (auto &cyl: cylinders) { add_cylinder_description(cyl.type); if (same_rounded_pressure(cyl.sample_start, cyl.start)) cyl.start.mbar = 0; if (same_rounded_pressure(cyl.sample_end, cyl.end)) cyl.end.mbar = 0; } - divelog.dives.update_cylinder_related_info(dive); - for (auto &ws: dive->weightsystems) - add_weightsystem_description(ws); - /* we should always have a uniq ID as that gets assigned during dive creation, - * but we want to make sure... */ - if (!dive->id) - dive->id = dive_getUniqID(); - return dive; + for (auto &ws: weightsystems) + add_weightsystem_description(ws); } /* Don't pick a zero for MERGE_MIN() */ @@ -2316,62 +2292,10 @@ merge_result merge_dives(const struct dive &a_in, const struct dive &b_in, int o * Keep the dive site, but add the GPS data */ res.site->location = b->dive_site->location; } - fixup_dive(res.dive.get()); + divelog.dives.fixup_dive(*res.dive); return res; } -struct start_end_pressure { - pressure_t start; - pressure_t end; -}; - -static void force_fixup_dive(struct dive *d) -{ - struct divecomputer *dc = &d->dcs[0]; - int old_temp = dc->watertemp.mkelvin; - int old_mintemp = d->mintemp.mkelvin; - int old_maxtemp = d->maxtemp.mkelvin; - duration_t old_duration = d->duration; - std::vector old_pressures(d->cylinders.size()); - - d->maxdepth.mm = 0; - dc->maxdepth.mm = 0; - d->watertemp.mkelvin = 0; - dc->watertemp.mkelvin = 0; - d->duration.seconds = 0; - d->maxtemp.mkelvin = 0; - d->mintemp.mkelvin = 0; - for (auto [i, cyl]: enumerated_range(d->cylinders)) { - old_pressures[i].start = cyl.start; - old_pressures[i].end = cyl.end; - cyl.start.mbar = 0; - cyl.end.mbar = 0; - } - - fixup_dive(d); - - if (!d->watertemp.mkelvin) - d->watertemp.mkelvin = old_temp; - - if (!dc->watertemp.mkelvin) - dc->watertemp.mkelvin = old_temp; - - if (!d->maxtemp.mkelvin) - d->maxtemp.mkelvin = old_maxtemp; - - if (!d->mintemp.mkelvin) - d->mintemp.mkelvin = old_mintemp; - - if (!d->duration.seconds) - d->duration = old_duration; - for (auto [i, cyl]: enumerated_range(d->cylinders)) { - if (!cyl.start.mbar) - cyl.start = old_pressures[i].start; - if (!cyl.end.mbar) - cyl.end = old_pressures[i].end; - } -} - /* * Split a dive that has a surface interval from samples 'a' to 'b' * into two dives, but don't add them to the log yet. @@ -2457,8 +2381,8 @@ static std::array, 2> split_dive_at(const struct dive &div ++it2; } - force_fixup_dive(d1.get()); - force_fixup_dive(d2.get()); + divelog.dives.force_fixup_dive(*d1); + divelog.dives.force_fixup_dive(*d2); /* * Was the dive numbered? If it was the last dive, then we'll @@ -2677,7 +2601,7 @@ std::unique_ptr clone_delete_divecomputer(const struct dive &d, int dc_num res->dcs.erase(res->dcs.begin() + dc_number); - force_fixup_dive(res.get()); + divelog.dives.force_fixup_dive(*res); return res; } @@ -2715,8 +2639,8 @@ std::array, 2> split_divecomputer(const struct dive &src, } // Recalculate gas data, etc. - fixup_dive(out1.get()); - fixup_dive(out2.get()); + divelog.dives.fixup_dive(*out1); + divelog.dives.fixup_dive(*out2); return { std::move(out1), std::move(out2) }; } diff --git a/core/dive.h b/core/dive.h index 22ee0b1bf..fceacc68c 100644 --- a/core/dive.h +++ b/core/dive.h @@ -78,8 +78,8 @@ struct dive { dive(const dive &); dive(dive &&); dive &operator=(const dive &); - static std::unique_ptr default_dive(); + void fixup_no_cylinder(); /* to fix cylinders, we need the divelist (to calculate cns) */ timestamp_t endtime() const; /* maximum over divecomputers (with samples) */ duration_t totaltime() const; /* maximum over divecomputers (with samples) */ temperature_t dc_airtemp() const; /* average over divecomputers */ @@ -182,7 +182,6 @@ extern int legacy_format_o2pressures(const struct dive *dive, const struct divec extern bool dive_less_than(const struct dive &a, const struct dive &b); extern bool dive_less_than_ptr(const struct dive *a, const struct dive *b); extern bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b); -extern struct dive *fixup_dive(struct dive *dive); extern int get_dive_salinity(const struct dive *dive); extern int dive_getUniqID(); extern std::array, 2> split_dive(const struct dive &dive); diff --git a/core/divelist.cpp b/core/divelist.cpp index 9a9d6882d..27939711e 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -22,12 +22,88 @@ #include "sample.h" #include "trip.h" +#include + void dive_table::record_dive(std::unique_ptr d) { - fixup_dive(d.get()); + fixup_dive(*d); put(std::move(d)); } +void dive_table::fixup_dive(struct dive &dive) const +{ + dive.fixup_no_cylinder(); + update_cylinder_related_info(dive); +} + +struct start_end_pressure { + pressure_t start; + pressure_t end; +}; + +void dive_table::force_fixup_dive(struct dive &d) const +{ + struct divecomputer *dc = &d.dcs[0]; + int old_temp = dc->watertemp.mkelvin; + int old_mintemp = d.mintemp.mkelvin; + int old_maxtemp = d.maxtemp.mkelvin; + duration_t old_duration = d.duration; + std::vector old_pressures(d.cylinders.size()); + + d.maxdepth.mm = 0; + dc->maxdepth.mm = 0; + d.watertemp.mkelvin = 0; + dc->watertemp.mkelvin = 0; + d.duration.seconds = 0; + d.maxtemp.mkelvin = 0; + d.mintemp.mkelvin = 0; + for (auto [i, cyl]: enumerated_range(d.cylinders)) { + old_pressures[i].start = cyl.start; + old_pressures[i].end = cyl.end; + cyl.start.mbar = 0; + cyl.end.mbar = 0; + } + + fixup_dive(d); + + if (!d.watertemp.mkelvin) + d.watertemp.mkelvin = old_temp; + + if (!dc->watertemp.mkelvin) + dc->watertemp.mkelvin = old_temp; + + if (!d.maxtemp.mkelvin) + d.maxtemp.mkelvin = old_maxtemp; + + if (!d.mintemp.mkelvin) + d.mintemp.mkelvin = old_mintemp; + + if (!d.duration.seconds) + d.duration = old_duration; + for (auto [i, cyl]: enumerated_range(d.cylinders)) { + if (!cyl.start.mbar) + cyl.start = old_pressures[i].start; + if (!cyl.end.mbar) + cyl.end = old_pressures[i].end; + } +} + +// create a dive an hour from now with a default depth (15m/45ft) and duration (40 minutes) +// as a starting point for the user to edit +std::unique_ptr dive_table::default_dive() +{ + auto d = std::make_unique(); + d->when = time(nullptr) + gettimezoneoffset() + 3600; + d->dcs[0].duration.seconds = 40 * 60; + d->dcs[0].maxdepth.mm = M_OR_FT(15, 45); + d->dcs[0].meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop + make_manually_added_dive_dc(&d->dcs[0]); + fake_dc(&d->dcs[0]); + add_default_cylinder(d.get()); + fixup_dive(*d); + return d; +} + /* * Get "maximal" dive gas for a dive. * Rules: @@ -78,14 +154,14 @@ int total_weight(const struct dive *dive) return total_grams; } -static int active_o2(const struct dive *dive, const struct divecomputer *dc, duration_t time) +static int active_o2(const struct dive &dive, const struct divecomputer *dc, duration_t time) { - struct gasmix gas = get_gasmix_at_time(*dive, *dc, time); + struct gasmix gas = get_gasmix_at_time(dive, *dc, time); return get_o2(gas); } // Do not call on first sample as it acccesses the previous sample -static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, const struct sample &sample, const struct sample &psample) +static int get_sample_o2(const struct dive &dive, const struct divecomputer *dc, const struct sample &sample, const struct sample &psample) { int po2i, po2f, po2; // Use sensor[0] if available @@ -95,13 +171,13 @@ static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, po2 = (po2f + po2i) / 2; } else if (sample.setpoint.mbar > 0) { po2 = std::min((int) sample.setpoint.mbar, - dive->depth_to_mbar(sample.depth.mm)); + dive.depth_to_mbar(sample.depth.mm)); } else { - double amb_presure = dive->depth_to_bar(sample.depth.mm); - double pamb_pressure = dive->depth_to_bar(psample.depth.mm ); + double amb_presure = dive.depth_to_bar(sample.depth.mm); + double pamb_pressure = dive.depth_to_bar(psample.depth.mm ); if (dc->divemode == PSCR) { - po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(*dive, *dc, psample.time)); - po2f = pscr_o2(amb_presure, get_gasmix_at_time(*dive, *dc, sample.time)); + po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, *dc, psample.time)); + po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, *dc, sample.time)); } else { int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2. po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment @@ -119,10 +195,10 @@ static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, Comroe Jr. JH et al. (1945) Oxygen toxicity. J. Am. Med. Assoc. 128,710-717 Clark JM & CJ Lambertsen (1970) Pulmonary oxygen tolerance in man and derivation of pulmonary oxygen tolerance curves. Inst. env. Med. Report 1-70, University of Pennsylvania, Philadelphia, USA. */ -static int calculate_otu(const struct dive *dive) +static int calculate_otu(const struct dive &dive) { double otu = 0.0; - const struct divecomputer *dc = &dive->dcs[0]; + const struct divecomputer *dc = &dive.dcs[0]; for (auto [psample, sample]: pairwise_range(dc->samples)) { int t; int po2i, po2f; @@ -135,18 +211,18 @@ static int calculate_otu(const struct dive *dive) } else { if (sample.setpoint.mbar > 0) { po2f = std::min((int) sample.setpoint.mbar, - dive->depth_to_mbar(sample.depth.mm)); + dive.depth_to_mbar(sample.depth.mm)); if (psample.setpoint.mbar > 0) po2i = std::min((int) psample.setpoint.mbar, - dive->depth_to_mbar(psample.depth.mm)); + dive.depth_to_mbar(psample.depth.mm)); else po2i = po2f; } else { // For OC and rebreather without o2 sensor/setpoint - double amb_presure = dive->depth_to_bar(sample.depth.mm); - double pamb_pressure = dive->depth_to_bar(psample.depth.mm); + double amb_presure = dive.depth_to_bar(sample.depth.mm); + double pamb_pressure = dive.depth_to_bar(psample.depth.mm); if (dc->divemode == PSCR) { - po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(*dive, *dc, psample.time)); - po2f = pscr_o2(amb_presure, get_gasmix_at_time(*dive, *dc, sample.time)); + po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, *dc, psample.time)); + po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, *dc, sample.time)); } else { int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2. po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment @@ -189,7 +265,7 @@ static double calculate_cns_dive(const struct dive &dive) /* Calculate the CNS for each sample in this dive and sum them */ for (auto [psample, sample]: pairwise_range(dc->samples)) { int t = sample.time.seconds - psample.time.seconds; - int po2 = get_sample_o2(&dive, dc, sample, psample); + int po2 = get_sample_o2(dive, dc, sample, psample); /* Don't increase CNS when po2 below 500 matm */ if (po2 <= 500) continue; @@ -201,19 +277,19 @@ static double calculate_cns_dive(const struct dive &dive) return cns; } -/* this only gets called if dive->maxcns == 0 which means we know that +/* this only gets called if dive.maxcns == 0 which means we know that * none of the divecomputers has tracked any CNS for us * so we calculated it "by hand" */ -int dive_table::calculate_cns(struct dive *dive) const +int dive_table::calculate_cns(struct dive &dive) const { double cns = 0.0; timestamp_t last_starttime, last_endtime = 0; /* shortcut */ - if (dive->cns) - return dive->cns; + if (dive.cns) + return dive.cns; - size_t divenr = get_idx(dive); + size_t divenr = get_idx(&dive); int nr_dives = static_cast(size()); int i = divenr != std::string::npos ? static_cast(divenr) : nr_dives; @@ -225,20 +301,20 @@ int dive_table::calculate_cns(struct dive *dive) const #endif /* Look at next dive in dive list table and correct i when needed */ while (i < nr_dives - 1) { - if ((*this)[i]->when > dive->when) + if ((*this)[i]->when > dive.when) break; i++; } /* Look at previous dive in dive list table and correct i when needed */ while (i > 0) { - if ((*this)[i - 1]->when < dive->when) + if ((*this)[i - 1]->when < dive.when) break; i--; } #if DECO_CALC_DEBUG & 2 printf("Dive number corrected to #%d\n", i); #endif - last_starttime = dive->when; + last_starttime = dive.when; /* Walk backwards to check previous dives - how far do we need to go back? */ while (i--) { if (static_cast(i) == divenr && i > 0) @@ -249,13 +325,13 @@ int dive_table::calculate_cns(struct dive *dive) const const struct dive &pdive = *(*this)[i]; /* we don't want to mix dives from different trips as we keep looking * for how far back we need to go */ - if (dive->divetrip && pdive.divetrip != dive->divetrip) { + if (dive.divetrip && pdive.divetrip != dive.divetrip) { #if DECO_CALC_DEBUG & 2 printf("No - other dive trip\n"); #endif continue; } - if (pdive.when >= dive->when || pdive.endtime() + 12 * 60 * 60 < last_starttime) { + if (pdive.when >= dive.when || pdive.endtime() + 12 * 60 * 60 < last_starttime) { #if DECO_CALC_DEBUG & 2 printf("No\n"); #endif @@ -273,14 +349,14 @@ int dive_table::calculate_cns(struct dive *dive) const #endif const struct dive &pdive = *(*this)[i]; /* again skip dives from different trips */ - if (dive->divetrip && dive->divetrip != pdive.divetrip) { + if (dive.divetrip && dive.divetrip != pdive.divetrip) { #if DECO_CALC_DEBUG & 2 printf("No - other dive trip\n"); #endif continue; } /* Don't add future dives */ - if (pdive.when >= dive->when) { + if (pdive.when >= dive.when) { #if DECO_CALC_DEBUG & 2 printf("No - future or same dive\n"); #endif @@ -315,32 +391,32 @@ int dive_table::calculate_cns(struct dive *dive) const /* CNS reduced with 90min halftime during surface interval */ if (last_endtime) - cns /= pow(2, (dive->when - last_endtime) / (90.0 * 60.0)); + cns /= pow(2, (dive.when - last_endtime) / (90.0 * 60.0)); #if DECO_CALC_DEBUG & 2 printf("CNS after last surface interval: %f\n", cns); #endif - cns += calculate_cns_dive(*dive); + cns += calculate_cns_dive(dive); #if DECO_CALC_DEBUG & 2 printf("CNS after dive: %f\n", cns); #endif /* save calculated cns in dive struct */ - dive->cns = lrint(cns); - return dive->cns; + dive.cns = lrint(cns); + return dive.cns; } /* * Return air usage (in liters). */ -static double calculate_airuse(const struct dive *dive) +static double calculate_airuse(const struct dive &dive) { int airuse = 0; // SAC for a CCR dive does not make sense. - if (dive->dcs[0].divemode == CCR) + if (dive.dcs[0].divemode == CCR) return 0.0; - for (auto [i, cyl]: enumerated_range(dive->cylinders)) { + for (auto [i, cyl]: enumerated_range(dive.cylinders)) { pressure_t start, end; start = cyl.start.mbar ? cyl.start : cyl.sample_start; @@ -350,7 +426,7 @@ static double calculate_airuse(const struct dive *dive) // better not pretend we know the total gas use. // Eventually, logic should be fixed to compute average depth and total time // for those segments where cylinders with known pressure drop are breathed from. - if (is_cylinder_used(dive, i)) + if (is_cylinder_used(&dive, i)) return 0.0; else continue; @@ -362,9 +438,9 @@ static double calculate_airuse(const struct dive *dive) } /* this only uses the first divecomputer to calculate the SAC rate */ -static int calculate_sac(const struct dive *dive) +static int calculate_sac(const struct dive &dive) { - const struct divecomputer *dc = &dive->dcs[0]; + const struct divecomputer *dc = &dive.dcs[0]; double airuse, pressure, sac; int duration, meandepth; @@ -381,7 +457,7 @@ static int calculate_sac(const struct dive *dive) return 0; /* Mean pressure in ATM (SAC calculations are in atm*l/min) */ - pressure = dive->depth_to_atm(meandepth); + pressure = dive.depth_to_atm(meandepth); sac = airuse / pressure * 60 / duration; /* milliliters per minute.. */ @@ -389,12 +465,12 @@ static int calculate_sac(const struct dive *dive) } /* for now we do this based on the first divecomputer */ -static void add_dive_to_deco(struct deco_state *ds, const struct dive *dive, bool in_planner) +static void add_dive_to_deco(struct deco_state *ds, const struct dive &dive, bool in_planner) { - const struct divecomputer *dc = &dive->dcs[0]; + const struct divecomputer *dc = &dive.dcs[0]; - gasmix_loop loop(*dive, dive->dcs[0]); - divemode_loop loop_d(dive->dcs[0]); + gasmix_loop loop(dive, dive.dcs[0]); + divemode_loop loop_d(dive.dcs[0]); for (auto [psample, sample]: pairwise_range(dc->samples)) { int t0 = psample.time.seconds; int t1 = sample.time.seconds; @@ -403,8 +479,8 @@ static void add_dive_to_deco(struct deco_state *ds, const struct dive *dive, boo for (j = t0; j < t1; j++) { int depth = interpolate(psample.depth.mm, sample.depth.mm, j - t0, t1 - t0); auto gasmix = loop.next(j); - add_segment(ds, dive->depth_to_bar(depth), gasmix, 1, sample.setpoint.mbar, - loop_d.next(j), dive->sac, + add_segment(ds, dive.depth_to_bar(depth), gasmix, 1, sample.setpoint.mbar, + loop_d.next(j), dive.sac, in_planner); } } @@ -536,7 +612,7 @@ int dive_table::init_decompression(struct deco_state *ds, const struct dive *div #endif } - add_dive_to_deco(ds, &pdive, in_planner); + add_dive_to_deco(ds, pdive, in_planner); last_starttime = pdive.when; last_endtime = pdive.endtime(); @@ -578,14 +654,12 @@ int dive_table::init_decompression(struct deco_state *ds, const struct dive *div return surface_time; } -void dive_table::update_cylinder_related_info(struct dive *dive) const +void dive_table::update_cylinder_related_info(struct dive &dive) const { - if (dive != NULL) { - dive->sac = calculate_sac(dive); - dive->otu = calculate_otu(dive); - if (dive->maxcns == 0) - dive->maxcns = calculate_cns(dive); - } + dive.sac = calculate_sac(dive); + dive.otu = calculate_otu(dive); + if (dive.maxcns == 0) + dive.maxcns = calculate_cns(dive); } /* Compare list of dive computers by model name */ diff --git a/core/divelist.h b/core/divelist.h index 5278224de..9d68540bf 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -21,14 +21,20 @@ struct dive_table : public sorted_owning_table { void record_dive(std::unique_ptr d); // call fixup_dive() before adding dive to table. struct dive *register_dive(std::unique_ptr d); std::unique_ptr unregister_dive(int idx); + std::unique_ptr default_dive(); // generate a sensible looking defaultdive 1h from now. + // Some of these functions act on dives, but need data from adjacent dives, + // notably to calculate CNS, surface interval, etc. Therefore, they are called + // on the dive_table and not on the dive. + void fixup_dive(struct dive &dive) const; + void force_fixup_dive(struct dive &d) const; int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner) const; - void update_cylinder_related_info(struct dive *) const; + void update_cylinder_related_info(struct dive &dive) const; int get_dive_nr_at_idx(int idx) const; timestamp_t get_surface_interval(timestamp_t when) const; struct dive *find_next_visible_dive(timestamp_t when); private: - int calculate_cns(struct dive *dive) const; // Note: writes into dive->cns + int calculate_cns(struct dive &dive) const; // Note: writes into dive->cns }; /* this is used for both git and xml format */ diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 316dced1d..6790b6321 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -410,7 +410,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Print the CNS and OTU next.*/ dive->cns = 0; dive->maxcns = 0; - divelog.dives.update_cylinder_related_info(dive); + divelog.dives.update_cylinder_related_info(*dive); buf += casprintf_loc("
\n%s: %i%%", translate("gettextFromC", "CNS"), dive->cns); buf += casprintf_loc("
\n%s: %i
\n
\n", translate("gettextFromC", "OTU"), dive->otu); diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index a2b1903fa..38f565e4f 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -698,7 +698,7 @@ void MainWindow::on_actionAddDive_triggered() if (!plannerStateClean()) return; - auto d = dive::default_dive(); + auto d = divelog.dives.default_dive(); Command::addDive(std::move(d), divelog.autogroup, true); } diff --git a/desktop-widgets/profilewidget.cpp b/desktop-widgets/profilewidget.cpp index 81439ad96..0e48e9a1f 100644 --- a/desktop-widgets/profilewidget.cpp +++ b/desktop-widgets/profilewidget.cpp @@ -347,7 +347,7 @@ void ProfileWidget::exitEditMode() static void calcDepth(dive &d, int dcNr) { d.maxdepth.mm = get_dive_dc(&d, dcNr)->maxdepth.mm = 0; - fixup_dive(&d); + divelog.dives.fixup_dive(d); } // Silly RAII-variable setter class: reset variable when going out of scope. diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 15cb887b4..83c5ae065 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1358,7 +1358,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt d->meandepth.mm = d->dcs[0].meandepth.mm = 0; fake_dc(&d->dcs[0]); } - fixup_dive(d); + divelog.dives.fixup_dive(*d); Command::editDive(orig, d_ptr.release(), dsChange.createdDs.release(), dsChange.editDs, dsChange.location); // With release() we're giving up ownership changesNeedSaving(); } @@ -1717,7 +1717,7 @@ int QMLManager::addDive() { // we do NOT save the modified data at this stage because of the UI flow here... this will // be saved once the user finishes editing the newly added dive - auto d = dive::default_dive(); + auto d = divelog.dives.default_dive(); int diveId = d->id; Command::addDive(std::move(d), divelog.autogroup, true); diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 211a49299..f1b70b983 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -708,7 +708,7 @@ void DiveTripModelTree::populate() uiNotification(QObject::tr("populate data model")); uiNotification(QObject::tr("start processing")); for (auto &d: divelog.dives) { - divelog.dives.update_cylinder_related_info(d.get()); + divelog.dives.update_cylinder_related_info(*d); if (d->hidden_by_filter) continue; dive_trip *trip = d->divetrip;