From 8704a8b6f97920090d144919b4546344e7df8bd8 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 4 Sep 2024 07:36:05 +0200 Subject: [PATCH] planner: turn diveplan into a C++ structure No more memory management woes. Signed-off-by: Berthold Stoeger --- core/planner.cpp | 213 ++++++++++------------ core/planner.h | 37 ++-- core/plannernotes.cpp | 109 ++++++------ qt-models/diveplannermodel.cpp | 144 +++++++-------- qt-models/diveplannermodel.h | 6 +- tests/testplan.cpp | 313 +++++++++++++++------------------ 6 files changed, 372 insertions(+), 450 deletions(-) diff --git a/core/planner.cpp b/core/planner.cpp index a457221f9..767024814 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -66,18 +66,19 @@ void dump_plan(struct diveplan *diveplan) } #endif -bool diveplan_empty(struct diveplan *diveplan) +diveplan::diveplan() { - struct divedatapoint *dp; - if (!diveplan || !diveplan->dp) - return true; - dp = diveplan->dp; - while (dp) { - if (dp->time) - return false; - dp = dp->next; - } - return true; +} + +diveplan::~diveplan() +{ +} + +bool diveplan_empty(const struct diveplan &diveplan) +{ + return std::none_of(diveplan.dp.begin(), diveplan.dp.end(), + [](const divedatapoint &dp) + { return dp.time != 0; }); } /* get the cylinder index at a certain time during the dive */ @@ -197,9 +198,8 @@ static void update_cylinder_pressure(struct dive *d, int old_depth, int new_dept /* overwrite the data in dive * return false if something goes wrong */ -static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, struct divecomputer *dc, bool track_gas) +static void create_dive_from_plan(struct diveplan &diveplan, struct dive *dive, struct divecomputer *dc, bool track_gas) { - struct divedatapoint *dp; cylinder_t *cyl; int oldpo2 = 0; int lasttime = 0, last_manual_point = 0; @@ -207,22 +207,21 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, int lastcylid; enum divemode_t type = dc->divemode; - if (!diveplan || !diveplan->dp) + if (diveplan.dp.empty()) return; #if DEBUG_PLAN & 4 printf("in create_dive_from_plan\n"); dump_plan(diveplan); #endif - dive->salinity = diveplan->salinity; + dive->salinity = diveplan.salinity; // reset the cylinders and clear out the samples and events of the // dive-to-be-planned so we can restart reset_cylinders(dive, track_gas); - dc->when = dive->when = diveplan->when; - dc->surface_pressure.mbar = diveplan->surface_pressure; - dc->salinity = diveplan->salinity; + dc->when = dive->when = diveplan.when; + dc->surface_pressure.mbar = diveplan.surface_pressure; + dc->salinity = diveplan.salinity; dc->samples.clear(); dc->events.clear(); - dp = diveplan->dp; /* Create first sample at time = 0, not based on dp because * there is no real dp for time = 0, set first cylinder to 0 * O2 setpoint for this sample will be filled later from next dp */ @@ -233,15 +232,14 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, sample->pressure[0].mbar = cyl->end.mbar; sample->manually_entered = true; lastcylid = 0; - while (dp) { - int po2 = dp->setpoint; - int time = dp->time; - depth_t depth = dp->depth; + for (auto &dp: diveplan.dp) { + int po2 = dp.setpoint; + int time = dp.time; + depth_t depth = dp.depth; if (time == 0) { /* special entries that just inform the algorithm about * additional gases that are available */ - dp = dp->next; continue; } @@ -250,18 +248,18 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, /* this is a bad idea - we should get a different SAMPLE_EVENT type * reserved for this in libdivecomputer... overloading SMAPLE_EVENT_PO2 * with a different meaning will only cause confusion elsewhere in the code */ - if (dp->divemode == type) + if (dp.divemode == type) add_event(dc, lasttime, SAMPLE_EVENT_PO2, 0, po2, QT_TRANSLATE_NOOP("gettextFromC", "SP change")); oldpo2 = po2; } /* Make sure we have the new gas, and create a gas change event */ - if (dp->cylinderid != lastcylid) { + if (dp.cylinderid != lastcylid) { /* need to insert a first sample for the new gas */ - add_gas_switch_event(dive, dc, lasttime + 1, dp->cylinderid); - cyl = dive->get_cylinder(dp->cylinderid); // FIXME: This actually may get one past the last cylinder for "surface air". + add_gas_switch_event(dive, dc, lasttime + 1, dp.cylinderid); + cyl = dive->get_cylinder(dp.cylinderid); // FIXME: This actually may get one past the last cylinder for "surface air". if (!cyl) { - report_error("Invalid cylinder in create_dive_from_plan(): %d", dp->cylinderid); + report_error("Invalid cylinder in create_dive_from_plan(): %d", dp.cylinderid); continue; } sample = prepare_sample(dc); @@ -270,12 +268,12 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, sample[-1].o2sensor[0].mbar = po2; sample->time.seconds = lasttime + 1; sample->depth = lastdepth; - sample->manually_entered = dp->entered; - sample->sac.mliter = dp->entered ? prefs.bottomsac : prefs.decosac; - lastcylid = dp->cylinderid; + sample->manually_entered = dp.entered; + sample->sac.mliter = dp.entered ? prefs.bottomsac : prefs.decosac; + lastcylid = dp.cylinderid; } - if (dp->divemode != type) { - type = dp->divemode; + if (dp.divemode != type) { + type = dp.divemode; add_event(dc, lasttime, SAMPLE_EVENT_BOOKMARK, 0, type, "modechange"); } @@ -286,17 +284,16 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, sample[-1].setpoint.mbar = po2; sample->setpoint.mbar = po2; sample->time.seconds = lasttime = time; - if (dp->entered) last_manual_point = dp->time; + if (dp.entered) last_manual_point = dp.time; sample->depth = lastdepth = depth; - sample->manually_entered = dp->entered; - sample->sac.mliter = dp->entered ? prefs.bottomsac : prefs.decosac; + sample->manually_entered = dp.entered; + sample->sac.mliter = dp.entered ? prefs.bottomsac : prefs.decosac; if (track_gas && !sample[-1].setpoint.mbar) { /* Don't track gas usage for CCR legs of dive */ update_cylinder_pressure(dive, sample[-1].depth.mm, depth.mm, time - sample[-1].time.seconds, - dp->entered ? diveplan->bottomsac : diveplan->decosac, cyl, !dp->entered, type); + dp.entered ? diveplan.bottomsac : diveplan.decosac, cyl, !dp.entered, type); if (cyl->type.workingpressure.mbar) sample->pressure[0].mbar = cyl->end.mbar; } - dp = dp->next; } dc->last_manual_time.seconds = last_manual_point; @@ -306,57 +303,35 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, return; } -void free_dps(struct diveplan *diveplan) +static struct divedatapoint create_dp(int time_incr, int depth, int cylinderid, int po2) { - if (!diveplan) - return; - struct divedatapoint *dp = diveplan->dp; - while (dp) { - struct divedatapoint *ndp = dp->next; - free(dp); - dp = ndp; - } - diveplan->dp = NULL; -} + struct divedatapoint dp; -static struct divedatapoint *create_dp(int time_incr, int depth, int cylinderid, int po2) -{ - struct divedatapoint *dp; - - dp = (divedatapoint *)malloc(sizeof(struct divedatapoint)); - dp->time = time_incr; - dp->depth.mm = depth; - dp->cylinderid = cylinderid; - dp->minimum_gas.mbar = 0; - dp->setpoint = po2; - dp->entered = false; - dp->next = NULL; + dp.time = time_incr; + dp.depth.mm = depth; + dp.cylinderid = cylinderid; + dp.minimum_gas.mbar = 0; + dp.setpoint = po2; + dp.entered = false; return dp; } -static void add_to_end_of_diveplan(struct diveplan *diveplan, struct divedatapoint *dp) +static void add_to_end_of_diveplan(struct diveplan &diveplan, const struct divedatapoint &dp) { - struct divedatapoint **lastdp = &diveplan->dp; - struct divedatapoint *ldp = *lastdp; - int lasttime = 0; - while (*lastdp) { - ldp = *lastdp; - if (ldp->time > lasttime) - lasttime = ldp->time; - lastdp = &(*lastdp)->next; - } - *lastdp = dp; - if (ldp) - dp->time += lasttime; + auto maxtime = std::max_element(diveplan.dp.begin(), diveplan.dp.end(), + [] (const divedatapoint &p1, const divedatapoint &p2) + { return p1.time < p2.time; }); + int lasttime = maxtime != diveplan.dp.end() ? maxtime->time : 0; + diveplan.dp.push_back(dp); + diveplan.dp.back().time += lasttime; } -struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode) +void plan_add_segment(struct diveplan &diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode) { - struct divedatapoint *dp = create_dp(duration, depth, cylinderid, divemode == CCR ? po2 : 0); - dp->entered = entered; - dp->divemode = divemode; + struct divedatapoint dp = create_dp(duration, depth, cylinderid, divemode == CCR ? po2 : 0); + dp.entered = entered; + dp.divemode = divemode; add_to_end_of_diveplan(diveplan, dp); - return dp; } struct gaschanges { @@ -380,47 +355,45 @@ static int setpoint_change(struct dive *dive, int cylinderid) } } -static std::vector analyze_gaslist(struct diveplan *diveplan, struct dive *dive, int dcNr, int depth, int *asc_cylinder, bool ccr, bool &inappropriate_cylinder_use) +static std::vector analyze_gaslist(const struct diveplan &diveplan, struct dive *dive, int dcNr, + int depth, int *asc_cylinder, bool ccr, bool &inappropriate_cylinder_use) { size_t nr = 0; std::vector gaschanges; - struct divedatapoint *dp = diveplan->dp; - struct divedatapoint *best_ascent_dp = NULL; + const struct divedatapoint *best_ascent_dp = nullptr; bool total_time_zero = true; const divecomputer *dc = dive->get_dc(dcNr); - while (dp) { - inappropriate_cylinder_use = inappropriate_cylinder_use || !is_cylinder_use_appropriate(*dc, *dive->get_cylinder(dp->cylinderid), false); + for (auto &dp: diveplan.dp) { + inappropriate_cylinder_use = inappropriate_cylinder_use || !is_cylinder_use_appropriate(*dc, *dive->get_cylinder(dp.cylinderid), false); - if (dp->time == 0 && total_time_zero && (ccr == (bool) setpoint_change(dive, dp->cylinderid))) { - if (dp->depth.mm <= depth) { + if (dp.time == 0 && total_time_zero && (ccr == (bool) setpoint_change(dive, dp.cylinderid))) { + if (dp.depth.mm <= depth) { int i = 0; nr++; gaschanges.resize(nr); while (i < static_cast(nr) - 1) { - if (dp->depth.mm < gaschanges[i].depth) { + if (dp.depth.mm < gaschanges[i].depth) { for (int j = static_cast(nr) - 2; j >= i; j--) gaschanges[j + 1] = gaschanges[j]; break; } i++; } - gaschanges[i].depth = dp->depth.mm; - gaschanges[i].gasidx = dp->cylinderid; + gaschanges[i].depth = dp.depth.mm; + gaschanges[i].gasidx = dp.cylinderid; assert(gaschanges[i].gasidx != -1); } else { /* is there a better mix to start deco? */ - if (!best_ascent_dp || dp->depth.mm < best_ascent_dp->depth.mm) { - best_ascent_dp = dp; + if (!best_ascent_dp || dp.depth.mm < best_ascent_dp->depth.mm) { + best_ascent_dp = &dp; } } } else { total_time_zero = false; } - dp = dp->next; } - if (best_ascent_dp) { + if (best_ascent_dp) *asc_cylinder = best_ascent_dp->cylinderid; - } #if DEBUG_PLAN & 16 for (size_t nr = 0; nr < gaschanges.size(); nr++) { int idx = gaschanges[nr].gasidx; @@ -610,25 +583,23 @@ static int wait_until(struct deco_state *ds, struct dive *dive, int clock, int m return wait_until(ds, dive, clock, min, leap / 2, stepsize, depth, target_depth, avg_depth, bottom_time, gasmix, po2, surface_pressure, divemode); } -static void average_max_depth(struct diveplan *dive, int *avg_depth, int *max_depth) +static void average_max_depth(const struct diveplan &dive, int *avg_depth, int *max_depth) { int integral = 0; int last_time = 0; int last_depth = 0; - struct divedatapoint *dp = dive->dp; *max_depth = 0; - while (dp) { - if (dp->time) { + for (auto &dp: dive.dp) { + if (dp.time) { /* Ignore gas indication samples */ - integral += (dp->depth.mm + last_depth) * (dp->time - last_time) / 2; - last_time = dp->time; - last_depth = dp->depth.mm; - if (dp->depth.mm > *max_depth) - *max_depth = dp->depth.mm; + integral += (dp.depth.mm + last_depth) * (dp.time - last_time) / 2; + last_time = dp.time; + last_depth = dp.depth.mm; + if (dp.depth.mm > *max_depth) + *max_depth = dp.depth.mm; } - dp = dp->next; } if (last_time) *avg_depth = integral / last_time; @@ -636,7 +607,7 @@ static void average_max_depth(struct diveplan *dive, int *avg_depth, int *max_de *avg_depth = *max_depth = 0; } -bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer) +bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer) { int bottom_depth; @@ -673,19 +644,19 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i struct divecomputer *dc = dive->get_dc(dcNr); enum divemode_t divemode = dc->divemode; - set_gf(diveplan->gflow, diveplan->gfhigh); - set_vpmb_conservatism(diveplan->vpmb_conservatism); + set_gf(diveplan.gflow, diveplan.gfhigh); + set_vpmb_conservatism(diveplan.vpmb_conservatism); - if (!diveplan->surface_pressure) { + if (!diveplan.surface_pressure) { // Lets use dive's surface pressure in planner, if have one... if (dc->surface_pressure.mbar) { // First from DC... - diveplan->surface_pressure = dc->surface_pressure.mbar; + diveplan.surface_pressure = dc->surface_pressure.mbar; } else if (dive->surface_pressure.mbar) { // After from user... - diveplan->surface_pressure = dive->surface_pressure.mbar; + diveplan.surface_pressure = dive->surface_pressure.mbar; } else { - diveplan->surface_pressure = SURFACE_PRESSURE; + diveplan.surface_pressure = SURFACE_PRESSURE; } } @@ -766,7 +737,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i gi = static_cast(gaschanges.size()) - 1; /* Set tissue tolerance and initial vpmb gradient at start of ascent phase */ - diveplan->surface_interval = tissue_at_end(ds, dive, dc, cache); + diveplan.surface_interval = tissue_at_end(ds, dive, dc, cache); nuclear_regeneration(ds, clock); vpmb_start_gradient(ds); if (decoMode(true) == RECREATIONAL) { @@ -780,7 +751,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i update_cylinder_pressure(dive, depth, depth, timestep, prefs.bottomsac, dive->get_cylinder(current_cylinder), false, divemode); clock += timestep; } while (trial_ascent(ds, 0, depth, 0, avg_depth, bottom_time, dive->get_cylinder(current_cylinder)->gasmix, - po2, diveplan->surface_pressure / 1000.0, dive, divemode) && + po2, diveplan.surface_pressure / 1000.0, dive, divemode) && enough_gas(dive, current_cylinder) && clock < 6 * 3600); // We did stay one timestep too many. @@ -856,7 +827,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i decostopcounter = 0; is_final_plan = (decoMode(true) == BUEHLMANN) || (previous_deco_time - ds->deco_time < 10); // CVA time converges if (ds->deco_time != 10000000) - vpmb_next_gradient(ds, ds->deco_time, diveplan->surface_pressure / 1000.0, true); + vpmb_next_gradient(ds, ds->deco_time, diveplan.surface_pressure / 1000.0, true); previous_deco_time = ds->deco_time; bottom_cache.restore(ds, true); @@ -871,7 +842,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i stopidx = bottom_stopidx; ds->first_ceiling_pressure.mbar = dive->depth_to_mbar( deco_allowed_depth(tissue_tolerance_calc(ds, dive, dive->depth_to_bar(depth), true), - diveplan->surface_pressure / 1000.0, dive, 1)); + diveplan.surface_pressure / 1000.0, dive, 1)); if (ds->max_bottom_ceiling_pressure.mbar > ds->first_ceiling_pressure.mbar) ds->first_ceiling_pressure.mbar = ds->max_bottom_ceiling_pressure.mbar; @@ -928,7 +899,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i if (current_cylinder != gaschanges[gi].gasidx) { if (!prefs.switch_at_req_stop || !trial_ascent(ds, 0, depth, stoplevels[stopidx - 1], avg_depth, bottom_time, - dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode) || get_o2(dive->get_cylinder(current_cylinder)->gasmix) < 160) { + dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure / 1000.0, dive, divemode) || get_o2(dive->get_cylinder(current_cylinder)->gasmix) < 160) { if (is_final_plan) plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, false, divemode); stopping = true; @@ -964,7 +935,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i while (1) { /* Check if ascending to next stop is clear, go back and wait if we hit the ceiling on the way */ if (trial_ascent(ds, 0, depth, stoplevels[stopidx], avg_depth, bottom_time, - dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode)) { + dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure / 1000.0, dive, divemode)) { decostoptable[decostopcounter].depth = depth; decostoptable[decostopcounter].time = 0; decostopcounter++; @@ -1008,7 +979,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i } int new_clock = wait_until(ds, dive, clock, clock, laststoptime * 2 + 1, timestep, depth, stoplevels[stopidx], avg_depth, - bottom_time, dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, divemode); + bottom_time, dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure / 1000.0, divemode); laststoptime = new_clock - clock; /* Finish infinite deco */ if (laststoptime >= 48 * 3600 && depth >= 6000) { @@ -1083,8 +1054,8 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i plan_add_segment(diveplan, clock - previous_point_time, 0, current_cylinder, po2, false, divemode); if (decoMode(true) == VPMB) { - diveplan->eff_gfhigh = lrint(100.0 * regressionb(ds)); - diveplan->eff_gflow = lrint(100.0 * (regressiona(ds) * first_stop_depth + regressionb(ds))); + diveplan.eff_gfhigh = lrint(100.0 * regressionb(ds)); + diveplan.eff_gflow = lrint(100.0 * (regressiona(ds) * first_stop_depth + regressionb(ds))); } if (prefs.surface_segment != 0) { diff --git a/core/planner.h b/core/planner.h index 9625e69fb..af3246330 100644 --- a/core/planner.h +++ b/core/planner.h @@ -5,6 +5,7 @@ #include "units.h" #include "divemode.h" #include +#include /* this should be converted to use our types */ struct divedatapoint { @@ -14,22 +15,24 @@ struct divedatapoint { pressure_t minimum_gas; int setpoint; bool entered; - struct divedatapoint *next; enum divemode_t divemode; }; struct diveplan { - timestamp_t when; - int surface_pressure; /* mbar */ - int bottomsac; /* ml/min */ - int decosac; /* ml/min */ - int salinity; - short gflow; - short gfhigh; - short vpmb_conservatism; - struct divedatapoint *dp; - int eff_gflow, eff_gfhigh; - int surface_interval; + diveplan(); + ~diveplan(); + + timestamp_t when = 0; + int surface_pressure = 0; /* mbar */ + int bottomsac = 0; /* ml/min */ + int decosac = 0; /* ml/min */ + int salinity = 0; + short gflow = 0; + short gfhigh = 0; + short vpmb_conservatism = 0; + std::vector dp; + int eff_gflow = 0, eff_gfhigh = 0; + int surface_interval = 0; }; struct deco_state_cache; @@ -41,13 +44,11 @@ typedef enum { } planner_error_t; extern int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_t time); -extern bool diveplan_empty(struct diveplan *diveplan); -extern void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer, planner_error_t error); +extern bool diveplan_empty(const struct diveplan &diveplan); +extern void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_disclaimer, planner_error_t error); extern const char *get_planner_disclaimer(); -extern void free_dps(struct diveplan *diveplan); - -struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode); +void plan_add_segment(struct diveplan &diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode); #if DEBUG_PLAN void dump_plan(struct diveplan *diveplan); #endif @@ -57,5 +58,5 @@ struct decostop { }; extern std::string get_planner_disclaimer_formatted(); -extern bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer); +extern bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer); #endif // PLANNER_H diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 55ed8d3b9..9ae4a588d 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -25,17 +25,15 @@ #include "subsurface-string.h" #include "version.h" -static int diveplan_duration(struct diveplan *diveplan) +static int diveplan_duration(const struct diveplan &diveplan) { - struct divedatapoint *dp = diveplan->dp; int duration = 0; int lastdepth = 0; - while(dp) { - if (dp->time > duration && (dp->depth.mm > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD)) { - duration = dp->time; - lastdepth = dp->depth.mm; + for (auto &dp: diveplan.dp) { + if (dp.time > duration && (dp.depth.mm > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD)) { + duration = dp.time; + lastdepth = dp.depth.mm; } - dp = dp->next; } return (duration + 30) / 60; } @@ -96,14 +94,13 @@ extern std::string get_planner_disclaimer_formatted() return format_string_std(get_planner_disclaimer(), deco); } -void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer, planner_error_t error) +void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_disclaimer, planner_error_t error) { std::string buf; std::string icdbuf; const char *segmentsymbol; int lastdepth = 0, lasttime = 0, lastsetpoint = -1, newdepth = 0, lastprintdepth = 0, lastprintsetpoint = -1; struct gasmix lastprintgasmix = gasmix_invalid; - struct divedatapoint *dp = diveplan->dp; bool plan_verbatim = prefs.verbatim_plan; bool plan_display_runtime = prefs.display_runtime; bool plan_display_duration = prefs.display_duration; @@ -115,11 +112,10 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d enum divemode_t lastdivemode = UNDEF_COMP_TYPE; bool lastentered = true; bool icdwarning = false, icdtableheader = true; - struct divedatapoint *nextdp = NULL; - struct divedatapoint *lastbottomdp = NULL; + struct divedatapoint *lastbottomdp = nullptr; struct icd_data icdvalues; - if (!dp) + if (diveplan.dp.empty()) return; if (error != PLAN_OK) { @@ -155,14 +151,14 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d } buf += "
\n"; - if (diveplan->surface_interval < 0) { + if (diveplan.surface_interval < 0) { buf += format_string_std("%s (%s) %s", translate("gettextFromC", "Subsurface"), subsurface_canonical_version(), translate("gettextFromC", "dive plan (overlapping dives detected)")); dive->notes = std::move(buf); return; - } else if (diveplan->surface_interval >= 48 * 60 *60) { + } else if (diveplan.surface_interval >= 48 * 60 *60) { buf += format_string_std("%s (%s) %s %s", translate("gettextFromC", "Subsurface"), subsurface_canonical_version(), @@ -173,7 +169,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d translate("gettextFromC", "Subsurface"), subsurface_canonical_version(), translate("gettextFromC", "dive plan (surface interval "), - FRACTION_TUPLE(diveplan->surface_interval / 60, 60), + FRACTION_TUPLE(diveplan.surface_interval / 60, 60), translate("gettextFromC", "created on"), get_current_date().c_str()); } @@ -197,30 +193,31 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d translate("gettextFromC", "gas")); } - do { + for (auto dp = diveplan.dp.begin(); dp != diveplan.dp.end(); ++dp) { + auto nextdp = std::next(dp); struct gasmix gasmix, newgasmix = {}; const char *depth_unit; double depthvalue; int decimals; bool isascent = (dp->depth.mm < lastdepth); - nextdp = dp->next; if (dp->time == 0) continue; gasmix = dive->get_cylinder(dp->cylinderid)->gasmix; depthvalue = get_depth_units(dp->depth.mm, &decimals, &depth_unit); /* analyze the dive points ahead */ - while (nextdp && nextdp->time == 0) - nextdp = nextdp->next; - if (nextdp) + while (nextdp != diveplan.dp.end() && nextdp->time == 0) + ++nextdp; + bool atend = nextdp == diveplan.dp.end(); + if (!atend) newgasmix = dive->get_cylinder(nextdp->cylinderid)->gasmix; - gaschange_after = (nextdp && (gasmix_distance(gasmix, newgasmix))); + gaschange_after = (!atend && (gasmix_distance(gasmix, newgasmix))); gaschange_before = (gasmix_distance(lastprintgasmix, gasmix)); - rebreatherchange_after = (nextdp && (dp->setpoint != nextdp->setpoint || dp->divemode != nextdp->divemode)); + rebreatherchange_after = (!atend && (dp->setpoint != nextdp->setpoint || dp->divemode != nextdp->divemode)); rebreatherchange_before = lastprintsetpoint != dp->setpoint || lastdivemode != dp->divemode; /* do we want to skip this leg as it is devoid of anything useful? */ if (!dp->entered && - nextdp && + !atend && dp->depth.mm != lastdepth && nextdp->depth.mm != dp->depth.mm && !gaschange_before && @@ -229,14 +226,14 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d !rebreatherchange_after) continue; // Ignore final surface segment for notes - if (lastdepth == 0 && dp->depth.mm == 0 && !dp->next) + if (lastdepth == 0 && dp->depth.mm == 0 && atend) continue; - if ((dp->time - lasttime < 10 && lastdepth == dp->depth.mm) && !(gaschange_after && dp->next && dp->depth.mm != dp->next->depth.mm)) + if ((dp->time - lasttime < 10 && lastdepth == dp->depth.mm) && !(gaschange_after && !atend && dp->depth.mm != nextdp->depth.mm)) continue; /* Store pointer to last entered datapoint for minimum gas calculation */ - if (dp->entered && nextdp && !nextdp->entered) - lastbottomdp = dp; + if (dp->entered && !atend && !nextdp->entered) + lastbottomdp = &*dp; if (plan_verbatim) { /* When displaying a verbatim plan, we output a waypoint for every gas change. @@ -246,7 +243,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d * non-verbatim plan. */ if (dp->depth.mm != lastprintdepth) { - if (plan_display_transitions || dp->entered || !dp->next || (gaschange_after && dp->next && dp->depth.mm != nextdp->depth.mm)) { + if (plan_display_transitions || dp->entered || atend || (gaschange_after && !atend && dp->depth.mm != nextdp->depth.mm)) { if (dp->setpoint) { buf += casprintf_loc(translate("gettextFromC", "%s to %.*f %s in %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar)"), dp->depth.mm < lastprintdepth ? translate("gettextFromC", "Ascend") : translate("gettextFromC", "Descend"), @@ -269,7 +266,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d newdepth = dp->depth.mm; lasttime = dp->time; } else { - if ((nextdp && dp->depth.mm != nextdp->depth.mm) || gaschange_after) { + if ((!atend && dp->depth.mm != nextdp->depth.mm) || gaschange_after) { if (dp->setpoint) { buf += casprintf_loc(translate("gettextFromC", "Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar CCR)"), decimals, depthvalue, depth_unit, @@ -307,12 +304,12 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d * used for a calculated ascent, there is a subsequent gas change before the first deco stop, and zero * time has been allowed for a gas switch. */ - if (plan_display_transitions || dp->entered || !dp->next || - (nextdp && dp->depth.mm != nextdp->depth.mm) || - (!isascent && (gaschange_before || rebreatherchange_before) && nextdp && dp->depth.mm != nextdp->depth.mm) || + if (plan_display_transitions || dp->entered || atend || + (!atend && dp->depth.mm != nextdp->depth.mm) || + (!isascent && (gaschange_before || rebreatherchange_before) && !atend && dp->depth.mm != nextdp->depth.mm) || ((gaschange_after || rebreatherchange_after) && lastentered) || ((gaschange_after || rebreatherchange_after)&& !isascent) || - (isascent && (gaschange_after || rebreatherchange_after) && nextdp && dp->depth.mm != nextdp->depth.mm ) || - (lastentered && !dp->entered && dp->next->depth.mm == dp->depth.mm)) { + (isascent && (gaschange_after || rebreatherchange_after) && !atend && dp->depth.mm != nextdp->depth.mm ) || + (lastentered && !dp->entered && nextdp->depth.mm == dp->depth.mm)) { // Print a symbol to indicate whether segment is an ascent, descent, constant depth (user entered) or deco stop if (isascent) segmentsymbol = "➚"; // up-right arrow for ascent @@ -339,7 +336,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Normally a gas change is displayed on the stopping segment, so only display a gas change at the end of * an ascent segment if it is not followed by a stop */ - if (isascent && gaschange_after && dp->next && nextdp && nextdp->entered) { + if (isascent && gaschange_after && !atend && nextdp->entered) { if (nextdp->setpoint) { temp = casprintf_loc(translate("gettextFromC", "(SP = %.1fbar CCR)"), nextdp->setpoint / 1000.0); buf += format_string_std("%s %s", @@ -395,7 +392,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d // gas switch at this waypoint for verbatim if (plan_verbatim) { if (lastsetpoint >= 0) { - if (nextdp && nextdp->setpoint) { + if (!atend && nextdp->setpoint) { buf += casprintf_loc(translate("gettextFromC", "Switch gas to %s (SP = %.1fbar)"), newgasmix.name().c_str(), (double) nextdp->setpoint / 1000.0); } else { buf += format_string_std(translate("gettextFromC", "Switch gas to %s"), newgasmix.name().c_str()); @@ -419,7 +416,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d lastdepth = dp->depth.mm; lastsetpoint = dp->setpoint; lastentered = dp->entered; - } while ((dp = nextdp) != NULL); + } if (!plan_verbatim) buf += "\n\n
\n"; @@ -433,25 +430,25 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Print the settings for the diveplan next. */ buf += "
\n"; if (decoMode(true) == BUEHLMANN) { - buf += casprintf_loc(translate("gettextFromC", "Deco model: Bühlmann ZHL-16C with GFLow = %d%% and GFHigh = %d%%"), diveplan->gflow, diveplan->gfhigh); + buf += casprintf_loc(translate("gettextFromC", "Deco model: Bühlmann ZHL-16C with GFLow = %d%% and GFHigh = %d%%"), diveplan.gflow, diveplan.gfhigh); } else if (decoMode(true) == VPMB) { - if (diveplan->vpmb_conservatism == 0) + if (diveplan.vpmb_conservatism == 0) buf += translate("gettextFromC", "Deco model: VPM-B at nominal conservatism"); else - buf += casprintf_loc(translate("gettextFromC", "Deco model: VPM-B at +%d conservatism"), diveplan->vpmb_conservatism); - if (diveplan->eff_gflow) - buf += casprintf_loc( translate("gettextFromC", ", effective GF=%d/%d"), diveplan->eff_gflow, diveplan->eff_gfhigh); + buf += casprintf_loc(translate("gettextFromC", "Deco model: VPM-B at +%d conservatism"), diveplan.vpmb_conservatism); + if (diveplan.eff_gflow) + buf += casprintf_loc( translate("gettextFromC", ", effective GF=%d/%d"), diveplan.eff_gflow, diveplan.eff_gfhigh); } else if (decoMode(true) == RECREATIONAL) { buf += casprintf_loc(translate("gettextFromC", "Deco model: Recreational mode based on Bühlmann ZHL-16B with GFLow = %d%% and GFHigh = %d%%"), - diveplan->gflow, diveplan->gfhigh); + diveplan.gflow, diveplan.gfhigh); } buf += "
\n"; { const char *depth_unit; - int altitude = (int) get_depth_units((int) (pressure_to_altitude(diveplan->surface_pressure)), NULL, &depth_unit); + int altitude = (int) get_depth_units((int) (pressure_to_altitude(diveplan.surface_pressure)), NULL, &depth_unit); - buf += casprintf_loc(translate("gettextFromC", "ATM pressure: %dmbar (%d%s)
\n
\n"), diveplan->surface_pressure, altitude, depth_unit); + buf += casprintf_loc(translate("gettextFromC", "ATM pressure: %dmbar (%d%s)
\n
\n"), diveplan.surface_pressure, altitude, depth_unit); } /* Get SAC values and units for printing it in gas consumption */ @@ -584,44 +581,42 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Print warnings for pO2 (move into separate function?) */ { - dp = diveplan->dp; bool o2warning_exist = false; double amb; divemode_loop loop(dive->dcs[0]); if (dive->dcs[0].divemode != CCR) { - while (dp) { - if (dp->time != 0) { + for (auto &dp: diveplan.dp) { + if (dp.time != 0) { std::string temp; - struct gasmix gasmix = dive->get_cylinder(dp->cylinderid)->gasmix; + struct gasmix gasmix = dive->get_cylinder(dp.cylinderid)->gasmix; - divemode_t current_divemode = loop.at(dp->time); - amb = dive->depth_to_atm(dp->depth.mm); + divemode_t current_divemode = loop.at(dp.time); + amb = dive->depth_to_atm(dp.depth.mm); gas_pressures pressures = fill_pressures(amb, gasmix, (current_divemode == OC) ? 0.0 : amb * gasmix.o2.permille / 1000.0, current_divemode); - if (pressures.o2 > (dp->entered ? prefs.bottompo2 : prefs.decopo2) / 1000.0) { + if (pressures.o2 > (dp.entered ? prefs.bottompo2 : prefs.decopo2) / 1000.0) { const char *depth_unit; int decimals; - double depth_value = get_depth_units(dp->depth.mm, &decimals, &depth_unit); + double depth_value = get_depth_units(dp.depth.mm, &decimals, &depth_unit); if (!o2warning_exist) buf += "
\n"; o2warning_exist = true; temp = casprintf_loc(translate("gettextFromC", "high pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"), - pressures.o2, FRACTION_TUPLE(dp->time, 60), gasmix.name().c_str(), decimals, depth_value, depth_unit); + pressures.o2, FRACTION_TUPLE(dp.time, 60), gasmix.name().c_str(), decimals, depth_value, depth_unit); buf += format_string_std("%s %s
\n", translate("gettextFromC", "Warning:"), temp.c_str()); } else if (pressures.o2 < 0.16) { const char *depth_unit; int decimals; - double depth_value = get_depth_units(dp->depth.mm, &decimals, &depth_unit); + double depth_value = get_depth_units(dp.depth.mm, &decimals, &depth_unit); if (!o2warning_exist) buf += "
"; o2warning_exist = true; temp = casprintf_loc(translate("gettextFromC", "low pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"), - pressures.o2, FRACTION_TUPLE(dp->time, 60), gasmix.name().c_str(), decimals, depth_value, depth_unit); + pressures.o2, FRACTION_TUPLE(dp.time, 60), gasmix.name().c_str(), decimals, depth_value, depth_unit); buf += format_string_std("%s %s
\n", translate("gettextFromC", "Warning:"), temp.c_str()); } } - dp = dp->next; } } if (o2warning_exist) diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index f01a5ed9e..57d18211e 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -128,7 +128,7 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) clear(); removeDeco(); - free_dps(&diveplan); + diveplan.dp.clear(); diveplan.when = d->when; // is this a "new" dive where we marked manually entered samples? @@ -396,7 +396,7 @@ bool DivePlannerPointsModel::setData(const QModelIndex &index, const QVariant &v } case CCSETPOINT: { bool ok; - int po2 = lrintf(value.toFloat(&ok) * 100) * 10; + int po2 = static_cast(round(value.toFloat(&ok) * 100) * 10); if (ok) p.setpoint = std::max(po2, 160); @@ -499,7 +499,6 @@ DivePlannerPointsModel::DivePlannerPointsModel(QObject *parent) : QAbstractTable cylinders(true), mode(NOTHING) { - memset(&diveplan, 0, sizeof(diveplan)); startTime.setTimeSpec(Qt::UTC); // use a Qt-connection to send the variations text across thread boundary (in case we // are calculating the variations in a background thread). @@ -890,7 +889,6 @@ int DivePlannerPointsModel::addStop(int milimeters, int seconds, int cylinderid_ point.minimum_gas.mbar = 0; point.entered = entered; point.divemode = divemode; - point.next = NULL; divepoints.insert(divepoints.begin() + row, point); endInsertRows(); return row; @@ -1050,7 +1048,7 @@ void DivePlannerPointsModel::cancelPlan() */ setPlanMode(NOTHING); - free_dps(&diveplan); + diveplan.dp.clear(); emit planCanceled(); } @@ -1086,11 +1084,11 @@ void DivePlannerPointsModel::clear() void DivePlannerPointsModel::createTemporaryPlan() { // Get the user-input and calculate the dive info - free_dps(&diveplan); + diveplan.dp.clear(); for (auto [i, cyl]: enumerated_range(d->cylinders)) { if (cyl.depth.mm && cyl.cylinder_use == OC_GAS) - plan_add_segment(&diveplan, 0, cyl.depth.mm, i, 0, false, OC); + plan_add_segment(diveplan, 0, cyl.depth.mm, i, 0, false, OC); } int lastIndex = -1; @@ -1101,15 +1099,15 @@ void DivePlannerPointsModel::createTemporaryPlan() lastIndex = i; if (i == 0 && mode == PLAN && prefs.drop_stone_mode) { /* Okay, we add a first segment where we go down to depth */ - plan_add_segment(&diveplan, p.depth.mm / prefs.descrate, p.depth.mm, p.cylinderid, divemode == CCR ? p.setpoint : 0, true, divemode); + plan_add_segment(diveplan, p.depth.mm / prefs.descrate, p.depth.mm, p.cylinderid, divemode == CCR ? p.setpoint : 0, true, divemode); deltaT -= p.depth.mm / prefs.descrate; } if (p.entered) - plan_add_segment(&diveplan, deltaT, p.depth.mm, p.cylinderid, divemode == CCR ? p.setpoint : 0, true, divemode); + plan_add_segment(diveplan, deltaT, p.depth.mm, p.cylinderid, divemode == CCR ? p.setpoint : 0, true, divemode); } #if DEBUG_PLAN - dump_plan(&diveplan); + dump_plan(diveplan); #endif } @@ -1123,27 +1121,27 @@ void DivePlannerPointsModel::updateDiveProfile() if (!d) return; createTemporaryPlan(); - if (diveplan_empty(&diveplan)) + if (diveplan_empty(diveplan)) return; deco_state_cache cache; struct decostop stoptable[60]; struct deco_state plan_deco_state; - plan(&plan_deco_state, &diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), false); + plan(&plan_deco_state, diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), false); updateMaxDepth(); if (isPlanner() && shouldComputeVariations()) { - struct diveplan *plan_copy = (struct diveplan *)malloc(sizeof(struct diveplan)); + auto plan_copy = std::make_unique(); lock_planner(); - cloneDiveplan(&diveplan, plan_copy); + cloneDiveplan(diveplan, *plan_copy); unlock_planner(); #ifdef VARIATIONS_IN_BACKGROUND // Since we're calling computeVariations asynchronously and plan_deco_state is allocated // on the stack, it must be copied and freed by the worker-thread. - auto plan_deco_state_copy = std::make_unique(plan_deco_state); + auto deco_copy = std::make_unique(plan_deco_state); - // Ideally, we would pass the unique_ptr to the lambda for QtConcurrent::run(). + // Ideally, we would pass the unique_ptrs to the lambda for QtConcurrent::run(). // This, in principle, can be done as such: // [ptr = std::move(ptr)] () mutable { f(std::move(ptr)) }; // However, this make the lambda uncopyable and QtConcurrent::run() sadly @@ -1154,16 +1152,16 @@ void DivePlannerPointsModel::updateDiveProfile() // exceptions anyway. // Note 2: We also can't use the function / argument syntax of QtConcurrent::run(), // because it likewise uses copy-semantics. How annoying. - QtConcurrent::run([this, plan_copy, deco = plan_deco_state_copy.release()] () - { this->computeVariationsFreeDeco(plan_copy, std::unique_ptr(deco)); }); + QtConcurrent::run([this, plan = plan_copy.release(), deco = deco_copy.release()] () + { this->computeVariationsFreeDeco(std::unique_ptr(plan), + std::unique_ptr(deco)); }); #else - computeVariations(plan_copy, &plan_deco_state); + computeVariations(std::move(plan_copy), &plan_deco_state); #endif final_deco_state = plan_deco_state; } emit calculatedPlanNotes(QString::fromStdString(d->notes)); - #if DEBUG_PLAN save_dive(stderr, *d); dump_plan(&diveplan); @@ -1172,7 +1170,7 @@ void DivePlannerPointsModel::updateDiveProfile() void DivePlannerPointsModel::deleteTemporaryPlan() { - free_dps(&diveplan); + diveplan.dp.clear(); } void DivePlannerPointsModel::savePlan() @@ -1185,26 +1183,9 @@ void DivePlannerPointsModel::saveDuplicatePlan() createPlan(true); } -struct divedatapoint *DivePlannerPointsModel::cloneDiveplan(struct diveplan *plan_src, struct diveplan *plan_copy) +void DivePlannerPointsModel::cloneDiveplan(const struct diveplan &plan_src, struct diveplan &plan_copy) { - divedatapoint *src, *last_segment; - divedatapoint **dp; - - src = plan_src->dp; - *plan_copy = *plan_src; - dp = &plan_copy->dp; - while (src && (!src->time || src->entered)) { - *dp = (struct divedatapoint *)malloc(sizeof(struct divedatapoint)); - **dp = *src; - dp = &(*dp)->next; - src = src->next; - } - (*dp) = NULL; - - last_segment = plan_copy->dp; - while (last_segment && last_segment->next && last_segment->next->next) - last_segment = last_segment->next; - return last_segment; + plan_copy = plan_src; } int DivePlannerPointsModel::analyzeVariations(struct decostop *min, struct decostop *mid, struct decostop *max, const char *unit) @@ -1240,13 +1221,21 @@ int DivePlannerPointsModel::analyzeVariations(struct decostop *min, struct decos return (leftsum + rightsum) / 2; } -void DivePlannerPointsModel::computeVariationsFreeDeco(struct diveplan *original_plan, std::unique_ptr previous_ds) +void DivePlannerPointsModel::computeVariationsFreeDeco(std::unique_ptr original_plan, std::unique_ptr previous_ds) { - computeVariations(original_plan, previous_ds.get()); + computeVariations(std::move(original_plan), previous_ds.get()); // Note: previous ds automatically free()d by virtue of being a unique_ptr. } -void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, const struct deco_state *previous_ds) +// Return reference to second to last element. +// Caller is responsible for checking that there are at least two elements. +template +auto &second_to_last(T &v) +{ + return *std::prev(std::prev(v.end())); +} + +void DivePlannerPointsModel::computeVariations(std::unique_ptr original_plan, const struct deco_state *previous_ds) { // nothing to do unless there's an original plan if (!original_plan) @@ -1257,7 +1246,6 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c struct decostop original[60], deeper[60], shallower[60], shorter[60], longer[60]; deco_state_cache cache, save; struct diveplan plan_copy; - struct divedatapoint *last_segment; struct deco_state ds = *previous_ds; int my_instance = ++instanceCounter; @@ -1276,47 +1264,45 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c depth_units = tr("ft"); } - last_segment = cloneDiveplan(original_plan, &plan_copy); - if (!last_segment) - goto finish; + cloneDiveplan(*original_plan, plan_copy); + if (plan_copy.dp.size() < 2) + return; if (my_instance != instanceCounter) - goto finish; - plan(&ds, &plan_copy, dive.get(), dcNr, 1, original, cache, true, false); - free_dps(&plan_copy); + return; + plan(&ds, plan_copy, dive.get(), dcNr, 1, original, cache, true, false); + plan_copy.dp.clear(); save.restore(&ds, false); - last_segment = cloneDiveplan(original_plan, &plan_copy); - last_segment->depth.mm += delta_depth.mm; - last_segment->next->depth.mm += delta_depth.mm; + cloneDiveplan(*original_plan, plan_copy); + second_to_last(plan_copy.dp).depth.mm += delta_depth.mm; + plan_copy.dp.back().depth.mm += delta_depth.mm; if (my_instance != instanceCounter) - goto finish; - plan(&ds, &plan_copy, dive.get(), dcNr, 1, deeper, cache, true, false); - free_dps(&plan_copy); + return; + plan(&ds, plan_copy, dive.get(), dcNr, 1, deeper, cache, true, false); + plan_copy.dp.clear(); save.restore(&ds, false); - last_segment = cloneDiveplan(original_plan, &plan_copy); - last_segment->depth.mm -= delta_depth.mm; - last_segment->next->depth.mm -= delta_depth.mm; + second_to_last(plan_copy.dp).depth.mm -= delta_depth.mm; + plan_copy.dp.back().depth.mm -= delta_depth.mm; if (my_instance != instanceCounter) - goto finish; - plan(&ds, &plan_copy, dive.get(), dcNr, 1, shallower, cache, true, false); - free_dps(&plan_copy); + return; + plan(&ds, plan_copy, dive.get(), dcNr, 1, shallower, cache, true, false); + plan_copy.dp.clear(); save.restore(&ds, false); - last_segment = cloneDiveplan(original_plan, &plan_copy); - last_segment->next->time += delta_time.seconds; + cloneDiveplan(*original_plan, plan_copy); + plan_copy.dp.back().time += delta_time.seconds; if (my_instance != instanceCounter) - goto finish; - plan(&ds, &plan_copy, dive.get(), dcNr, 1, longer, cache, true, false); - free_dps(&plan_copy); + return; + plan(&ds, plan_copy, dive.get(), dcNr, 1, longer, cache, true, false); + plan_copy.dp.clear(); save.restore(&ds, false); - last_segment = cloneDiveplan(original_plan, &plan_copy); - last_segment->next->time -= delta_time.seconds; + plan_copy.dp.back().time -= delta_time.seconds; if (my_instance != instanceCounter) - goto finish; - plan(&ds, &plan_copy, dive.get(), dcNr, 1, shorter, cache, true, false); - free_dps(&plan_copy); + return; + plan(&ds, plan_copy, dive.get(), dcNr, 1, shorter, cache, true, false); + plan_copy.dp.clear(); save.restore(&ds, false); char buf[200]; @@ -1329,9 +1315,6 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c #ifdef DEBUG_STOPVAR printf("\n\n"); #endif -finish: - free_dps(original_plan); - free(original_plan); } void DivePlannerPointsModel::computeVariationsDone(QString variations) @@ -1360,15 +1343,14 @@ void DivePlannerPointsModel::createPlan(bool saveAsNew) createTemporaryPlan(); struct decostop stoptable[60]; - plan(&ds_after_previous_dives, &diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), true); + plan(&ds_after_previous_dives, diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), true); if (shouldComputeVariations()) { - struct diveplan *plan_copy; - plan_copy = (struct diveplan *)malloc(sizeof(struct diveplan)); + auto plan_copy = std::make_unique(); lock_planner(); - cloneDiveplan(&diveplan, plan_copy); + cloneDiveplan(diveplan, *plan_copy); unlock_planner(); - computeVariations(plan_copy, &ds_after_previous_dives); + computeVariations(std::move(plan_copy), &ds_after_previous_dives); } // Fixup planner notes. @@ -1426,7 +1408,7 @@ void DivePlannerPointsModel::createPlan(bool saveAsNew) // Remove and clean the diveplan, so we don't delete // the dive by mistake. - free_dps(&diveplan); + diveplan.dp.clear(); planCreated(); // This signal will exit the UI from planner state. } diff --git a/qt-models/diveplannermodel.h b/qt-models/diveplannermodel.h index 9acdc7660..be6b07c42 100644 --- a/qt-models/diveplannermodel.h +++ b/qt-models/diveplannermodel.h @@ -130,10 +130,10 @@ private: void updateDiveProfile(); // Creates a temporary plan and updates the dive profile with it. void createTemporaryPlan(); struct diveplan diveplan; - struct divedatapoint *cloneDiveplan(struct diveplan *plan_src, struct diveplan *plan_copy); + void cloneDiveplan(const struct diveplan &plan_src, struct diveplan &plan_copy); void computeVariationsDone(QString text); - void computeVariations(struct diveplan *diveplan, const struct deco_state *ds); - void computeVariationsFreeDeco(struct diveplan *diveplan, std::unique_ptr ds); + void computeVariations(std::unique_ptr plan, const struct deco_state *ds); + void computeVariationsFreeDeco(std::unique_ptr plan, std::unique_ptr ds); int analyzeVariations(struct decostop *min, struct decostop *mid, struct decostop *max, const char *unit); struct dive *d; int dcNr; diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 43a105a39..1b217e513 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -15,7 +15,6 @@ static struct dive dive; static struct decostop stoptable[60]; static struct deco_state test_deco_state; -extern bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer); void setupPrefs() { prefs = default_prefs; @@ -39,14 +38,15 @@ void setupPrefsVpmb() prefs.vpmb_conservatism = 0; } -void setupPlan(struct diveplan *dp) +diveplan setupPlan() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->gfhigh = 100; - dp->gflow = 100; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.gfhigh = 100; + dp.gflow = 100; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{150}, {450}}; struct gasmix ean36 = {{360}, {0}}; @@ -64,23 +64,24 @@ void setupPlan(struct diveplan *dp) cyl1->gasmix = ean36; cyl2->gasmix = oxygen; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(79, 260) * 60 / M_OR_FT(23, 75); plan_add_segment(dp, 0, dive.gas_mod(ean36, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(79, 260), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(79, 260), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb45m30mTx(struct diveplan *dp) +diveplan setupPlanVpmb45m30mTx() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->gfhigh = 100; - dp->gflow = 100; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.gfhigh = 100; + dp.gflow = 100; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {350}}; struct gasmix ean50 = {{500}, {0}}; @@ -98,23 +99,24 @@ void setupPlanVpmb45m30mTx(struct diveplan *dp) cyl1->gasmix = ean50; cyl2->gasmix = oxygen; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(45, 150) * 60 / M_OR_FT(23, 75); plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(45, 150), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(45, 150), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb60m10mTx(struct diveplan *dp) +diveplan setupPlanVpmb60m10mTx() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->gfhigh = 100; - dp->gflow = 100; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.gfhigh = 100; + dp.gflow = 100; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{180}, {450}}; struct gasmix tx50_15 = {{500}, {150}}; @@ -132,21 +134,22 @@ void setupPlanVpmb60m10mTx(struct diveplan *dp) cyl1->gasmix = tx50_15; cyl2->gasmix = oxygen; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(23, 75); plan_add_segment(dp, 0, dive.gas_mod(tx50_15, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 10 * 60 - droptime, M_OR_FT(60, 200), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb60m30minAir(struct diveplan *dp) +diveplan setupPlanVpmb60m30minAir() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {0}}; cylinder_t *cyl0 = dive.get_or_create_cylinder(0); @@ -155,19 +158,20 @@ void setupPlanVpmb60m30minAir(struct diveplan *dp) cyl0->type.workingpressure.mbar = 232000; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, droptime, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(60, 200), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb60m30minEan50(struct diveplan *dp) +diveplan setupPlanVpmb60m30minEan50() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {0}}; struct gasmix ean50 = {{500}, {0}}; @@ -183,20 +187,21 @@ void setupPlanVpmb60m30minEan50(struct diveplan *dp) cyl1->gasmix = ean50; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(60, 200), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb60m30minTx(struct diveplan *dp) +diveplan setupPlanVpmb60m30minTx() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{180}, {450}}; struct gasmix ean50 = {{500}, {0}}; @@ -212,20 +217,21 @@ void setupPlanVpmb60m30minTx(struct diveplan *dp) cyl1->gasmix = ean50; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(60, 200), 0, 0, 1, OC); + return dp; } -void setupPlanVpmbMultiLevelAir(struct diveplan *dp) +diveplan setupPlanVpmbMultiLevelAir() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {0}}; cylinder_t *cyl0 = dive.get_or_create_cylinder(0); @@ -234,21 +240,22 @@ void setupPlanVpmbMultiLevelAir(struct diveplan *dp) cyl0->type.workingpressure.mbar = 232000; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(20, 66) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, droptime, M_OR_FT(20, 66), 0, 0, 1, OC); plan_add_segment(dp, 10 * 60 - droptime, M_OR_FT(20, 66), 0, 0, 1, OC); plan_add_segment(dp, 1 * 60, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 29 * 60, M_OR_FT(60, 200), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb100m60min(struct diveplan *dp) +diveplan setupPlanVpmb100m60min() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{180}, {450}}; struct gasmix ean50 = {{500}, {0}}; @@ -267,21 +274,22 @@ void setupPlanVpmb100m60min(struct diveplan *dp) cyl2->gasmix = oxygen; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(100, 330), 0, 0, 1, OC); plan_add_segment(dp, 60 * 60 - droptime, M_OR_FT(100, 330), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb100m10min(struct diveplan *dp) +diveplan setupPlanVpmb100m10min() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{180}, {450}}; struct gasmix ean50 = {{500}, {0}}; @@ -300,21 +308,22 @@ void setupPlanVpmb100m10min(struct diveplan *dp) cyl2->gasmix = oxygen; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(100, 330), 0, 0, 1, OC); plan_add_segment(dp, 10 * 60 - droptime, M_OR_FT(100, 330), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb30m20min(struct diveplan *dp) +diveplan setupPlanVpmb30m20min() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {0}}; cylinder_t *cyl0 = dive.get_or_create_cylinder(0); @@ -323,19 +332,20 @@ void setupPlanVpmb30m20min(struct diveplan *dp) cyl0->type.workingpressure.mbar = 232000; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(30, 100) * 60 / M_OR_FT(18, 60); plan_add_segment(dp, droptime, M_OR_FT(30, 100), 0, 0, 1, OC); plan_add_segment(dp, 20 * 60 - droptime, M_OR_FT(30, 100), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb100mTo70m30min(struct diveplan *dp) +diveplan setupPlanVpmb100mTo70m30min() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{120}, {650}}; struct gasmix tx21_35 = {{210}, {350}}; @@ -357,7 +367,6 @@ void setupPlanVpmb100mTo70m30min(struct diveplan *dp) cyl3->gasmix = oxygen; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(18, 60); plan_add_segment(dp, 0, dive.gas_mod(tx21_35, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); @@ -367,16 +376,18 @@ void setupPlanVpmb100mTo70m30min(struct diveplan *dp) plan_add_segment(dp, 20 * 60 - droptime, M_OR_FT(100, 330), 0, 0, 1, OC); plan_add_segment(dp, 3 * 60, M_OR_FT(70, 230), 0, 0, 1, OC); plan_add_segment(dp, (30 - 20 - 3) * 60, M_OR_FT(70, 230), 0, 0, 1, OC); + return dp; } /* This tests handling different gases in the manually entered part of the dive */ -void setupPlanSeveralGases(struct diveplan *dp) +diveplan setupPlanSeveralGases() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix ean36 = {{360}, {0}}; struct gasmix tx11_50 = {{110}, {500}}; @@ -392,22 +403,23 @@ void setupPlanSeveralGases(struct diveplan *dp) cyl1->gasmix = tx11_50; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); plan_add_segment(dp, 120, 40000, 0, 0, true, OC); plan_add_segment(dp, 18 * 60, 40000, 0, 0, true, OC); plan_add_segment(dp, 10 * 60, 10000, 1, 0, true, OC); plan_add_segment(dp, 5 * 60, 10000, 0, 0, true, OC); + return dp; } -void setupPlanCcr(struct diveplan *dp) +diveplan setupPlanCcr() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->gflow = 50; - dp->gfhigh = 70; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.gflow = 50; + dp.gfhigh = 70; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; pressure_t po2 = {1600}; struct gasmix diluent = {{200}, {210}}; @@ -429,11 +441,12 @@ void setupPlanCcr(struct diveplan *dp) cyl2->gasmix = tx19_33; cyl2->depth = dive.gas_mod(tx19_33, po2, M_OR_FT(3, 10)); reset_cylinders(&dive, true); - free_dps(dp); plan_add_segment(dp, 0, cyl1->depth.mm, 1, 0, false, OC); plan_add_segment(dp, 0, cyl2->depth.mm, 2, 0, false, OC); plan_add_segment(dp, 20 * 60, M_OR_FT(60, 197), 0, 1300, true, CCR); + + return dp; } /* We compare the calculated runtimes against two values: @@ -480,10 +493,9 @@ void TestPlan::testMetric() prefs.units.length = units::METERS; prefs.planner_deco_mode = BUEHLMANN; - struct diveplan testPlan = {}; - setupPlan(&testPlan); + auto testPlan = setupPlan(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -491,9 +503,7 @@ void TestPlan::testMetric() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 148l); QVERIFY(dive.dcs[0].events.size() >= 2); // check first gas change to EAN36 at 33m @@ -520,10 +530,9 @@ void TestPlan::testImperial() prefs.units.length = units::FEET; prefs.planner_deco_mode = BUEHLMANN; - struct diveplan testPlan = {}; - setupPlan(&testPlan); + auto testPlan = setupPlan(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -531,9 +540,7 @@ void TestPlan::testImperial() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); QVERIFY(dive.dcs[0].events.size() >= 2); // check first gas change to EAN36 at 33m @@ -559,10 +566,9 @@ void TestPlan::testVpmbMetric45m30minTx() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb45m30mTx(&testPlan); + auto testPlan = setupPlanVpmb45m30mTx(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -570,9 +576,7 @@ void TestPlan::testVpmbMetric45m30minTx() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 108l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -588,10 +592,9 @@ void TestPlan::testVpmbMetric60m10minTx() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb60m10mTx(&testPlan); + auto testPlan = setupPlanVpmb60m10mTx(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -599,9 +602,7 @@ void TestPlan::testVpmbMetric60m10minTx() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 162l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -617,10 +618,9 @@ void TestPlan::testVpmbMetric60m30minAir() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb60m30minAir(&testPlan); + auto testPlan = setupPlanVpmb60m30minAir(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -628,9 +628,7 @@ void TestPlan::testVpmbMetric60m30minAir() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 180l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -646,10 +644,9 @@ void TestPlan::testVpmbMetric60m30minEan50() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb60m30minEan50(&testPlan); + auto testPlan = setupPlanVpmb60m30minEan50(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -657,9 +654,7 @@ void TestPlan::testVpmbMetric60m30minEan50() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -681,10 +676,9 @@ void TestPlan::testVpmbMetric60m30minTx() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb60m30minTx(&testPlan); + auto testPlan = setupPlanVpmb60m30minTx(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -692,9 +686,7 @@ void TestPlan::testVpmbMetric60m30minTx() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 159l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -716,10 +708,9 @@ void TestPlan::testVpmbMetric100m60min() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb100m60min(&testPlan); + auto testPlan = setupPlanVpmb100m60min(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -727,9 +718,7 @@ void TestPlan::testVpmbMetric100m60min() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 157l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -757,11 +746,9 @@ void TestPlan::testMultipleGases() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; + auto testPlan = setupPlanSeveralGases(); - setupPlanSeveralGases(&testPlan); - - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -781,10 +768,9 @@ void TestPlan::testVpmbMetricMultiLevelAir() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmbMultiLevelAir(&testPlan); + auto testPlan = setupPlanVpmbMultiLevelAir(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -792,9 +778,7 @@ void TestPlan::testVpmbMetricMultiLevelAir() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 101l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -810,10 +794,9 @@ void TestPlan::testVpmbMetric100m10min() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb100m10min(&testPlan); + auto testPlan = setupPlanVpmb100m10min(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -821,9 +804,7 @@ void TestPlan::testVpmbMetric100m10min() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 175l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -856,10 +837,9 @@ void TestPlan::testVpmbMetricRepeat() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb30m20min(&testPlan); + auto testPlan = setupPlanVpmb30m20min(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -867,9 +847,7 @@ void TestPlan::testVpmbMetricRepeat() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 61l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -878,8 +856,8 @@ void TestPlan::testVpmbMetricRepeat() int firstDiveRunTimeSeconds = dive.dcs[0].duration.seconds; - setupPlanVpmb100mTo70m30min(&testPlan); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + testPlan = setupPlanVpmb100mTo70m30min(); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -887,9 +865,7 @@ void TestPlan::testVpmbMetricRepeat() #endif // check minimum gas result - dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 80l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -915,8 +891,8 @@ void TestPlan::testVpmbMetricRepeat() // we don't have a benchmark, known Subsurface runtime is 126 minutes QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 127u * 60u + 20u, 127u * 60u + 20u)); - setupPlanVpmb30m20min(&testPlan); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + testPlan = setupPlanVpmb30m20min(); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -924,9 +900,7 @@ void TestPlan::testVpmbMetricRepeat() #endif // check minimum gas result - dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 61l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -951,10 +925,9 @@ void TestPlan::testCcrBailoutGasSelection() dive.dcs[0].divemode = CCR; prefs.dobailout = true; - struct diveplan testPlan = {}; - setupPlanCcr(&testPlan); + auto testPlan = setupPlanCcr(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, true, false); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, true, false); #if DEBUG dive.notes.clear();