diff options
-rw-r--r-- | planner.c | 223 | ||||
-rw-r--r-- | qt-ui/diveplanner.cpp | 252 | ||||
-rw-r--r-- | qt-ui/diveplanner.h | 49 | ||||
-rw-r--r-- | qt-ui/diveplanner.ui | 18 | ||||
-rw-r--r-- | qt-ui/models.cpp | 1 |
5 files changed, 227 insertions, 316 deletions
@@ -13,7 +13,7 @@ #include "planner.h" #include "gettext.h" -unsigned int decostoplevels[] = { 0, 3000, 6000, 9000, 12000, 15000, 18000, 21000, 24000, 27000, +int decostoplevels[] = { 0, 3000, 6000, 9000, 12000, 15000, 18000, 21000, 24000, 27000, 30000, 33000, 36000, 39000, 42000, 45000, 48000, 51000, 54000, 57000, 60000, 63000, 66000, 69000, 72000, 75000, 78000, 81000, 84000, 87000, 90000, 100000, 110000, 120000, 130000, 140000, 150000, 160000, 170000, @@ -99,6 +99,18 @@ void get_gas_string(int o2, int he, char *text, int len) snprintf(text, len, "(%d/%d)", (o2 + 5) / 10, (he + 5) / 10); } +double interpolate_transition(struct dive *dive, int t0, int t1, int d0, int d1, const struct gasmix *gasmix, int ppo2) +{ + int j; + double tissue_tolerance; + + for (j = t0; j < t1; j++) { + int depth = interpolate(d0, d1, j - t0, t1 - t0); + tissue_tolerance = add_segment(depth_to_mbar(depth, dive) / 1000.0, gasmix, 1, ppo2, dive); + } + return tissue_tolerance; +} + /* returns the tissue tolerance at the end of this (partial) dive */ double tissue_at_end(struct dive *dive, char **cached_datap) { @@ -133,43 +145,13 @@ double tissue_at_end(struct dive *dive, char **cached_datap) } if (i > 0) lastdepth = psample->depth.mm; - for (j = t0; j < t1; j++) { - int depth = interpolate(lastdepth, sample->depth.mm, j - t0, t1 - t0); - tissue_tolerance = add_segment(depth_to_mbar(depth, dive) / 1000.0, - &dive->cylinder[gasidx].gasmix, 1, sample->po2, dive); - } + tissue_tolerance = interpolate_transition(dive, t0, t1, lastdepth, sample->depth.mm, &dive->cylinder[gasidx].gasmix, sample->po2); psample = sample; t0 = t1; } return tissue_tolerance; } -/* how many seconds until we can ascend to the next stop? */ -static int time_at_last_depth(struct dive *dive, int o2, int he, unsigned int next_stop, char **cached_data_p) -{ - int depth, gasidx; - double surface_pressure, tissue_tolerance; - int wait = 0; - struct sample *sample; - - if (!dive) - return 0; - surface_pressure = dive->dc.surface_pressure.mbar / 1000.0; - tissue_tolerance = tissue_at_end(dive, cached_data_p); - sample = &dive->dc.sample[dive->dc.samples - 1]; - depth = sample->depth.mm; - gasidx = get_gasidx(dive, o2, he); - if (gasidx == -1) { - fprintf(stderr, "cannot find gas (%d/%d), using first gas\n", o2, he); - gasidx = 0; - } - while (deco_allowed_depth(tissue_tolerance, surface_pressure, dive, 1) > next_stop) { - wait++; - tissue_tolerance = add_segment(depth_to_mbar(depth, dive) / 1000.0, - &dive->cylinder[gasidx].gasmix, 1, sample->po2, dive); - } - return wait; -} /* if a default cylinder is set, use that */ void fill_default_cylinder(cylinder_t *cyl) @@ -196,6 +178,7 @@ void fill_default_cylinder(cylinder_t *cyl) if (ti->psi) cyl->type.size.mliter = cuft_to_l(ti->cuft) * 1000 / bar_to_atm(psi_to_bar(ti->psi)); } + cyl->depth.mm = 1600 * 1000 / O2_IN_AIR * 10 - 10000; // MOD of air } static int add_gas(struct dive *dive, int o2, int he) @@ -389,18 +372,18 @@ struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, } struct gaschanges { - unsigned int depth; + int depth; int gasidx; }; -static struct gaschanges *analyze_gaslist(struct diveplan *diveplan, struct dive *dive, int *gaschangenr) +static struct gaschanges *analyze_gaslist(struct diveplan *diveplan, struct dive *dive, int *gaschangenr, int depth) { int nr = 0; struct gaschanges *gaschanges = NULL; struct divedatapoint *dp = diveplan->dp; while (dp) { - if (dp->time == 0) { + if (dp->time == 0 && dp->depth <= depth) { int i = 0, j = 0; nr++; gaschanges = realloc(gaschanges, nr * sizeof(struct gaschanges)); @@ -436,15 +419,15 @@ static struct gaschanges *analyze_gaslist(struct diveplan *diveplan, struct dive } /* sort all the stops into one ordered list */ -static unsigned int *sort_stops(unsigned int *dstops, int dnr, struct gaschanges *gstops, int gnr) +static unsigned int *sort_stops(int *dstops, int dnr, struct gaschanges *gstops, int gnr) { int i, gi, di; int total = dnr + gnr; - unsigned int *stoplevels = malloc(total * sizeof(unsigned int)); + int *stoplevels = malloc(total * sizeof(int)); /* no gaschanges */ if (gnr == 0) { - memcpy(stoplevels, dstops, dnr * sizeof(unsigned int)); + memcpy(stoplevels, dstops, dnr * sizeof(int)); return stoplevels; } i = total - 1; @@ -586,17 +569,29 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive) } #endif +int ascend_velocity(int depth) +{ + /* We need to make this configurable */ + return 10000/60; +} + void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, bool add_deco) { struct dive *dive; struct sample *sample; int wait_time, o2, he, po2; int transitiontime, gi; - unsigned int stopidx, depth, ceiling; + int current_cylinder; + unsigned int stopidx; + int depth, ceiling; double tissue_tolerance; struct gaschanges *gaschanges = NULL; int gaschangenr; - unsigned int *stoplevels = NULL; + int *stoplevels = NULL; + char *trial_cache = NULL; + bool stopping = false; + bool clear_to_ascend; + int clock, previous_point_time; set_gf(diveplan->gflow, diveplan->gfhigh, default_prefs.gf_low_at_maxdepth); if (!diveplan->surface_pressure) @@ -614,6 +609,10 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, b he = dive->cylinder[0].gasmix.he.permille; get_gas_from_events(&dive->dc, sample->time.seconds, &o2, &he); po2 = dive->dc.sample[dive->dc.samples - 1].po2; + if ((current_cylinder = get_gasidx(dive, o2, he)) == -1) { + report_error(translate("gettextFromC", "Can't find gas %d/%d"), (o2 + 5) / 10, (he + 5) / 10); + current_cylinder = 0; + } depth = dive->dc.sample[dive->dc.samples - 1].depth.mm; /* if all we wanted was the dive just get us back to the surface */ @@ -629,90 +628,102 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, b } tissue_tolerance = tissue_at_end(dive, cached_datap); - ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1); + #if DEBUG_PLAN & 4 printf("gas %d/%d\n", o2, he); printf("depth %5.2lfm ceiling %5.2lfm\n", depth / 1000.0, ceiling / 1000.0); #endif - if (depth < ceiling) /* that's not good... */ - depth = ceiling; - if (depth == 0 && ceiling == 0) /* we are done here */ - goto done; + + gaschanges = analyze_gaslist(diveplan, dive, &gaschangenr, depth); for (stopidx = 0; stopidx < sizeof(decostoplevels) / sizeof(int); stopidx++) - if (decostoplevels[stopidx] >= ceiling) + if (decostoplevels[stopidx] >= depth) break; if (stopidx > 0) stopidx--; - - /* so now we know the first decostop level above us - * NOTE, this could be the surface or a long list of potential stops - * we do NOT start only at the ceiling, as the ceiling may come down - * further during the ascent. - * Next we need to figure out if there are better gases available - * and at which depths we are supposed to switch to them */ - gaschanges = analyze_gaslist(diveplan, dive, &gaschangenr); stoplevels = sort_stops(decostoplevels, stopidx + 1, gaschanges, gaschangenr); + stopidx += gaschangenr; + clock = previous_point_time = dive->dc.sample[dive->dc.samples - 1].time.seconds; gi = gaschangenr - 1; - stopidx += gaschangenr; - if (depth > stoplevels[stopidx]) { - /* right now all the transitions are at 30ft/min - this needs to be configurable */ - transitiontime = (depth - stoplevels[stopidx]) / 150; -#if DEBUG_PLAN & 2 - printf("transitiontime %d:%02d to depth %5.2lfm\n", FRACTION(transitiontime, 60), stoplevels[stopidx] / 1000.0); -#endif - plan_add_segment(diveplan, transitiontime, stoplevels[stopidx], o2, he, po2, false); - /* re-create the dive */ - delete_single_dive(dive_table.nr - 1); - *divep = dive = create_dive_from_plan(diveplan); - if (!dive) - goto error_exit; - record_dive(dive); - } - while (stopidx > 0) { /* this indicates that we aren't surfacing directly */ - /* if we are in a double-step, eg, when 3m/10ft stop is disabled, - * just skip the first stop at that depth */ - if (stoplevels[stopidx] == stoplevels[stopidx - 1]) { - stopidx--; - continue; - } + + while (1) { + /* We will break out when we hit the surface */ + do { + /* Ascend in one second steps to next stop depth */ + int deltad = ascend_velocity(depth); + if (depth - deltad < stoplevels[stopidx]) + deltad = depth - stoplevels[stopidx]; + + tissue_tolerance = add_segment(depth_to_mbar(depth, dive) / 1000.0, &dive->cylinder[current_cylinder].gasmix, 1, po2, dive); + ++clock; + depth -= deltad; + } while (depth > stoplevels[stopidx]); + + if (depth <= 0) + break; /* We are at the surface */ + + if (gi >= 0 && stoplevels[stopidx] == gaschanges[gi].depth) { - o2 = dive->cylinder[gaschanges[gi].gasidx].gasmix.o2.permille; - he = dive->cylinder[gaschanges[gi].gasidx].gasmix.he.permille; + plan_add_segment(diveplan, clock - previous_point_time, depth, o2, he, po2, false); + previous_point_time = clock; + stopping = true; + + current_cylinder = gaschanges[gi].gasidx; + o2 = dive->cylinder[current_cylinder].gasmix.o2.permille; + he = dive->cylinder[current_cylinder].gasmix.he.permille; #if DEBUG_PLAN & 16 printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi].gasidx, - (o2 + 5) / 10, (he + 5) / 10, gaschanges[gi].depth / 1000.0); + (o2 + 5) / 10, (he + 5) / 10, gaschanges[gi].depth / 1000.0); #endif gi--; } - wait_time = time_at_last_depth(dive, o2, he, stoplevels[stopidx - 1], cached_datap); - /* typically deco plans are done in one minute increments; we may want to - * make this configurable at some point */ - wait_time = ((wait_time + 59) / 60) * 60; -#if DEBUG_PLAN & 2 - tissue_tolerance = tissue_at_end(dive, cached_datap); - ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1); - printf("waittime %d:%02d at depth %5.2lfm; ceiling %5.2lfm\n", FRACTION(wait_time, 60), - stoplevels[stopidx] / 1000.0, ceiling / 1000.0); -#endif - if (wait_time) - plan_add_segment(diveplan, wait_time, stoplevels[stopidx], o2, he, po2, false); - /* right now all the transitions are at 30ft/min - this needs to be configurable */ - transitiontime = (stoplevels[stopidx] - stoplevels[stopidx - 1]) / 150; -#if DEBUG_PLAN & 2 - printf("transitiontime %d:%02d to depth %5.2lfm\n", FRACTION(transitiontime, 60), stoplevels[stopidx - 1] / 1000.0); -#endif - plan_add_segment(diveplan, transitiontime, stoplevels[stopidx - 1], o2, he, po2, false); - /* re-create the dive */ - delete_single_dive(dive_table.nr - 1); - *divep = dive = create_dive_from_plan(diveplan); - if (!dive) - goto error_exit; - record_dive(dive); - stopidx--; - } -done: + /* trial ascend */ + --stopidx; + int trial_depth = depth; + cache_deco_state(tissue_tolerance, &trial_cache); + while(1) { + /* Try to ascend to next stop, go back and wait if we hit the ceiling on the way */ + clear_to_ascend = true; + while (trial_depth > stoplevels[stopidx]) { + int deltad = ascend_velocity(trial_depth); + tissue_tolerance = add_segment(depth_to_mbar(trial_depth, dive) / 1000.0, &dive->cylinder[current_cylinder].gasmix, 1, po2, dive); + if (deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1) > trial_depth - deltad){ + /* We should have stopped */ + clear_to_ascend = false; + break; + } + trial_depth -= deltad; + } + restore_deco_state(trial_cache); + /* The next stop is clear */ + if(clear_to_ascend) + break; + + /* Wait a minute */ + if (!stopping) { + plan_add_segment(diveplan, clock - previous_point_time, depth, o2, he, po2, false); + previous_point_time = clock; + stopping = true; + } + tissue_tolerance = add_segment(depth_to_mbar(depth, dive) / 1000.0, &dive->cylinder[current_cylinder].gasmix, 60, po2, dive); + cache_deco_state(tissue_tolerance, &trial_cache); + clock += 60; + trial_depth = depth; + } + if (stopping) { + plan_add_segment(diveplan, clock - previous_point_time, depth, o2, he, po2, false); + previous_point_time = clock; + stopping = false; + } + + } + plan_add_segment(diveplan, clock - previous_point_time, 0, o2, he, po2, false); + delete_single_dive(dive_table.nr - 1); + *divep = dive = create_dive_from_plan(diveplan); + if (!dive) + goto error_exit; + record_dive(dive); #if DO_WE_WANT_THIS_IN_QT add_plan_to_notes(diveplan, dive); diff --git a/qt-ui/diveplanner.cpp b/qt-ui/diveplanner.cpp index 5c8973224..cc4565e1a 100644 --- a/qt-ui/diveplanner.cpp +++ b/qt-ui/diveplanner.cpp @@ -41,135 +41,6 @@ QString dpGasToStr(const divedatapoint &p) return gasToStr(p.o2, p.he); } -static DivePlannerDisplay *plannerDisplay = DivePlannerDisplay::instance(); - -DivePlannerDisplay::DivePlannerDisplay(QObject *parent) : QAbstractTableModel(parent) -{ -} - -DivePlannerDisplay *DivePlannerDisplay::instance() -{ - static QScopedPointer<DivePlannerDisplay> self(new DivePlannerDisplay()); - return self.data(); -} - -int DivePlannerDisplay::size() -{ - return computedPoints.size(); -} - -int DivePlannerDisplay::columnCount(const QModelIndex &parent) const -{ - return COLUMNS; -} - -QVariant DivePlannerDisplay::data(const QModelIndex &index, int role) const -{ - if (role == Qt::DisplayRole) { - computedPoint p = computedPoints.at(index.row()); - switch (index.column()) { - case COMPUTED_DEPTH: - return rint(get_depth_units(p.computedDepth, NULL, NULL)); - case COMPUTED_DURATION: - return p.computedTime / 60; - } - } else if (role == Qt::DecorationRole) { - switch (index.column()) { - case REMOVE: - return QIcon(":trash"); - } - } else if (role == Qt::FontRole) { - return defaultModelFont(); - } - return QVariant(); -} - -bool DivePlannerDisplay::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (role == Qt::EditRole) { - computedPoint &p = computedPoints[index.row()]; - switch (index.column()) { - case COMPUTED_DEPTH: - p.computedDepth = units_to_depth(value.toInt()); - break; - case COMPUTED_DURATION: - p.computedTime = value.toInt() * 60; - break; - } - } - return QAbstractItemModel::setData(index, value, role); -} - -QVariant DivePlannerDisplay::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { - switch (section) { - case COMPUTED_DEPTH: - return tr("Comp. Depth"); - case COMPUTED_DURATION: - return tr("Comp. Duration"); - } - } else if (role == Qt::FontRole) { - return defaultModelFont(); - } - return QVariant(); -} - -Qt::ItemFlags DivePlannerDisplay::flags(const QModelIndex &index) const -{ - return QAbstractItemModel::flags(index); -} - -int DivePlannerDisplay::rowCount(const QModelIndex &parent) const -{ - return computedPoints.size(); -} - -struct computedPoint DivePlannerDisplay::at(int row) -{ - return computedPoints.at(row); -} - -void DivePlannerDisplay::clear() -{ - if (rowCount() > 0) { - beginRemoveRows(QModelIndex(), 0, rowCount() - 1); - computedPoints.clear(); - endRemoveRows(); - } -} - -void DivePlannerDisplay::removeStops() -{ - if (rowCount() > 0) { - beginRemoveRows(QModelIndex(), 0, rowCount() - 1); - endRemoveRows(); - } -} - -void DivePlannerDisplay::addStops() -{ - int rows = computedPoints.size(); - if (rows > 0) { - beginInsertRows(QModelIndex(), 0, rows - 1); - endInsertRows(); - } -} - -void DivePlannerDisplay::insertPoint(const struct computedPoint &p) -{ - computedPoints.append(p); -} - -void DivePlannerDisplay::remove(const QModelIndex &index) -{ - if (index.column() != REMOVE) - return; - - beginRemoveRows(QModelIndex(), index.row(), index.row()); - endRemoveRows(); -} - static DivePlannerPointsModel *plannerModel = DivePlannerPointsModel::instance(); DivePlannerGraphics::DivePlannerGraphics(QWidget *parent) : QGraphicsView(parent), @@ -311,7 +182,8 @@ void DivePlannerGraphics::pointInserted(const QModelIndex &parent, int start, in gasChooseBtn->setZValue(10); gasChooseBtn->setFlag(QGraphicsItem::ItemIgnoresTransformations); gases << gasChooseBtn; - drawProfile(); + if(plannerModel->recalcQ()) + drawProfile(); } void DivePlannerGraphics::keyDownAction() @@ -585,6 +457,8 @@ QStringList &DivePlannerPointsModel::getGasList() void DivePlannerGraphics::drawProfile() { + if(!plannerModel->recalcQ()) + return; qDeleteAll(lines); lines.clear(); @@ -598,7 +472,7 @@ void DivePlannerGraphics::drawProfile() return; } while (dp->next) { - if (dp->depth > max_depth) + if (dp->time && dp->depth > max_depth) max_depth = dp->depth; dp = dp->next; } @@ -639,7 +513,16 @@ void DivePlannerGraphics::drawProfile() QPolygonF poly; poly.append(QPointF(lastx, lasty)); - plannerDisplay->clear(); + bool oldRecalc = plannerModel->setRecalc(false); + QVector<int> computedPoints; + for (int i = 0; i < plannerModel->rowCount(); i++) + if (!plannerModel->at(i).entered) + computedPoints.push_back(i); + plannerModel->removeSelectedPoints(computedPoints); + + int lastdepth = 0; + int lasto2 = 0; + int lasthe = 0; for (dp = diveplan.dp; dp != NULL; dp = dp->next) { if (dp->time == 0) // magic entry for available tank continue; @@ -648,21 +531,22 @@ void DivePlannerGraphics::drawProfile() if (!dp->entered) { QGraphicsLineItem *item = new QGraphicsLineItem(lastx, lasty, xpos, ypos); item->setPen(QPen(QBrush(Qt::red), 0)); + scene()->addItem(item); lines << item; if (dp->depth) { - qDebug() << "Time: " << dp->time / 60 << " depth: " << dp->depth / 1000; - computedPoint p(dp->time, dp->depth); - plannerDisplay->insertPoint(p); + if (dp->depth == lastdepth || dp->o2 != dp->next->o2 || dp->he != dp->next->he) + plannerModel->addStop(dp->depth, dp->time, dp->o2, dp->he, 0, false); + lastdepth = dp->depth; + lasto2 = dp->o2; + lasthe = dp->he; } } lastx = xpos; lasty = ypos; poly.append(QPointF(lastx, lasty)); } - - qDebug() << " "; - plannerDisplay->addStops(); + plannerModel->setRecalc(oldRecalc); diveBg->setPolygon(poly); QRectF b = poly.boundingRect(); @@ -1069,9 +953,8 @@ DivePlannerWidget::DivePlannerWidget(QWidget *parent, Qt::WindowFlags f) : QWidg ui.setupUi(this); ui.tableWidget->setTitle(tr("Dive Planner Points")); ui.tableWidget->setModel(DivePlannerPointsModel::instance()); + DivePlannerPointsModel::instance()->setRecalc(true); ui.tableWidget->view()->setItemDelegateForColumn(DivePlannerPointsModel::GAS, new AirTypesDelegate(this)); - ui.tableWidgetComp->setTitle(tr("Computed Waypoints")); - ui.tableWidgetComp->setModel(DivePlannerDisplay::instance()); ui.cylinderTableWidget->setTitle(tr("Available Gases")); ui.cylinderTableWidget->setModel(CylindersModel::instance()); QTableView *view = ui.cylinderTableWidget->view(); @@ -1090,9 +973,14 @@ DivePlannerWidget::DivePlannerWidget(QWidget *parent, Qt::WindowFlags f) : QWidg GasSelectionModel::instance(), SLOT(repopulate())); connect(CylindersModel::instance(), SIGNAL(rowsRemoved(QModelIndex, int, int)), GasSelectionModel::instance(), SLOT(repopulate())); + connect(CylindersModel::instance(), SIGNAL(dataChanged(QModelIndex, int, int)), + plannerModel, SLOT(drawProfile())); + connect(CylindersModel::instance(), SIGNAL(rowsInserted(QModelIndex, int, int)), + plannerModel, SLOT(drawProfile())); + connect(CylindersModel::instance(), SIGNAL(rowsRemoved(QModelIndex, int, int)), + plannerModel, SLOT(drawProfile())); ui.tableWidget->setBtnToolTip(tr("add dive data point")); - ui.tableWidgetComp->setBtnToolTip(tr("This does nothing, and should be removed")); connect(ui.startTime, SIGNAL(timeChanged(QTime)), plannerModel, SLOT(setStartTime(QTime))); connect(ui.ATMPressure, SIGNAL(textChanged(QString)), this, SLOT(atmPressureChanged(QString))); connect(ui.bottomSAC, SIGNAL(textChanged(QString)), this, SLOT(bottomSacChanged(QString))); @@ -1156,6 +1044,18 @@ bool DivePlannerPointsModel::isPlanner() return mode == PLAN; } +bool DivePlannerPointsModel::setRecalc(bool rec) +{ + bool old = recalc; + recalc = rec; + return old; +} + +bool DivePlannerPointsModel::recalcQ() +{ + return recalc; +} + int DivePlannerPointsModel::columnCount(const QModelIndex &parent) const { return COLUMNS; @@ -1170,9 +1070,15 @@ QVariant DivePlannerPointsModel::data(const QModelIndex &index, int role) const return (double)p.po2 / 1000; case DEPTH: return rint(get_depth_units(p.depth, NULL, NULL)); - case DURATION: + case RUNTIME: return p.time / 60; + case DURATION: + if (index.row()) + return (p.time - divepoints.at(index.row() -1).time) / 60; + else + return p.time / 60; case GAS: + return dpGasToStr(p); } } else if (role == Qt::DecorationRole) { @@ -1181,7 +1087,13 @@ QVariant DivePlannerPointsModel::data(const QModelIndex &index, int role) const return QIcon(":trash"); } } else if (role == Qt::FontRole) { - return defaultModelFont(); + if (divepoints.at(index.row()).entered) { + return defaultModelFont(); + } else { + QFont font = defaultModelFont(); + font.setBold(true); + return font; + } } return QVariant(); } @@ -1196,9 +1108,15 @@ bool DivePlannerPointsModel::setData(const QModelIndex &index, const QVariant &v case DEPTH: p.depth = units_to_depth(value.toInt()); break; - case DURATION: + case RUNTIME: p.time = value.toInt() * 60; break; + case DURATION: + if (index.row()) + p.time = value.toInt() * 60 + divepoints[index.row() - 1].time; + else + p.time = value.toInt() * 60; + break; case CCSETPOINT: { int po2 = 0; QByteArray gasv = value.toByteArray(); @@ -1224,6 +1142,8 @@ QVariant DivePlannerPointsModel::headerData(int section, Qt::Orientation orienta switch (section) { case DEPTH: return tr("Final Depth"); + case RUNTIME: + return tr("Run time"); case DURATION: return tr("Duration"); case GAS: @@ -1239,7 +1159,10 @@ QVariant DivePlannerPointsModel::headerData(int section, Qt::Orientation orienta Qt::ItemFlags DivePlannerPointsModel::flags(const QModelIndex &index) const { - return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; + if(index.column() != DURATION) + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; + else + return QAbstractItemModel::flags(index); } int DivePlannerPointsModel::rowCount(const QModelIndex &parent) const @@ -1317,6 +1240,10 @@ bool DivePlannerPointsModel::addGas(int o2, int he) fill_default_cylinder(cyl); cyl->gasmix.o2.permille = o2; cyl->gasmix.he.permille = he; + if(!o2) + cyl->depth.mm = 1600 * 1000 / O2_IN_AIR * 10 - 10000; + else + cyl->depth.mm = 1600 * 1000 / o2 * 10 - 10000; CylindersModel::instance()->setDive(stagingDive); return true; } @@ -1327,12 +1254,20 @@ bool DivePlannerPointsModel::addGas(int o2, int he) return false; } +int DivePlannerPointsModel::lastEnteredPoint() +{ + for (int i = divepoints.count()-1; i >= 0; i--) + if (divepoints.at(i).entered) + return i; + return -1; +} + int DivePlannerPointsModel::addStop(int milimeters, int seconds, int o2, int he, int ccpoint, bool entered) { int row = divepoints.count(); if (seconds == 0 && milimeters == 0 && row != 0) { /* this is only possible if the user clicked on the 'plus' sign on the DivePoints Table */ - struct divedatapoint &t = divepoints.last(); + const divedatapoint t = divepoints.at(lastEnteredPoint()); milimeters = t.depth; seconds = t.time + 600; // 10 minutes. o2 = t.o2; @@ -1386,6 +1321,15 @@ int DivePlannerPointsModel::addStop(int milimeters, int seconds, int o2, int he, } } } + bool oldRecalc = setRecalc(false); + if (oldRecalc){ + QVector<int> computedPoints; + for (int i = 0; i < plannerModel->rowCount(); i++) + if (!plannerModel->at(i).entered) + computedPoints.push_back(i); + plannerModel->removeSelectedPoints(computedPoints); + } + setRecalc(oldRecalc); // add the new stop beginInsertRows(QModelIndex(), row, row); divedatapoint point; @@ -1478,6 +1422,8 @@ bool DivePlannerPointsModel::tankInUse(int o2, int he) divedatapoint &p = divepoints[j]; if (p.time == 0) // special entries that hold the available gases continue; + if (!p.entered) // removing deco gases is ok + continue; if ((p.o2 == o2 && p.he == he) || (is_air(p.o2, p.he) && is_air(o2, he))) return true; @@ -1552,8 +1498,8 @@ void DivePlannerPointsModel::createTemporaryPlan() divedatapoint p = at(i); int deltaT = lastIndex != -1 ? p.time - at(lastIndex).time : p.time; lastIndex = i; - p.entered = true; - plan_add_segment(&diveplan, deltaT, p.depth, p.o2, p.he, p.po2, true); + if (p.entered) + plan_add_segment(&diveplan, deltaT, p.depth, p.o2, p.he, p.po2, true); } char *cache = NULL; tempDive = NULL; @@ -1626,10 +1572,14 @@ void DivePlannerPointsModel::createPlan() // FIXME: The epic assumption that all the cylinders after the first is deco int sac = i ? diveplan.decosac : diveplan.bottomsac; cyl->start.mbar = cyl->type.workingpressure.mbar; - int consumption = ((depth_to_mbar(mean[i], tempDive) * duration[i] / 60) * sac) / (cyl->type.size.mliter / 1000); - cyl->end.mbar = cyl->start.mbar - consumption; + if(cyl->type.size.mliter) { + int consumption = ((depth_to_mbar(mean[i], tempDive) * duration[i] / 60) * sac) / (cyl->type.size.mliter / 1000); + cyl->end.mbar = cyl->start.mbar - consumption; + } else { + /* Cylider without a proper size are easily emptied */ + cyl->end.mbar = 0; + } } - mark_divelist_changed(true); // Remove and clean the diveplan, so we don't delete diff --git a/qt-ui/diveplanner.h b/qt-ui/diveplanner.h index 4d3f0cbaf..8352d6a69 100644 --- a/qt-ui/diveplanner.h +++ b/qt-ui/diveplanner.h @@ -12,48 +12,6 @@ class QListView; class QModelIndex; -struct computedPoint { - int computedTime; - unsigned int computedDepth; - computedPoint(int computedTime_, unsigned int computedDepth_) { - computedTime = computedTime_; - computedDepth = computedDepth_; - }; - computedPoint() {}; -}; - -class DivePlannerDisplay : public QAbstractTableModel { - Q_OBJECT -private: - explicit DivePlannerDisplay(QObject *parent = 0); - QVector<computedPoint> computedPoints; - -public: - static DivePlannerDisplay *instance(); - enum Sections { - REMOVE, - COMPUTED_DEPTH, - COMPUTED_DURATION, - COLUMNS - }; - virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - void clear(); - computedPoint at(int row); - int size(); - void removeStops(); - void addStops(); - void insertPoint(const struct computedPoint &p); - -public -slots: - void remove(const QModelIndex &index); -}; - class DivePlannerPointsModel : public QAbstractTableModel { Q_OBJECT public: @@ -62,6 +20,7 @@ public: REMOVE, DEPTH, DURATION, + RUNTIME, GAS, CCSETPOINT, COLUMNS @@ -83,6 +42,8 @@ public: void createSimpleDive(); void clear(); Mode currentMode() const; + bool setRecalc(bool recalc); + bool recalcQ(); void tanksUpdated(); void rememberTanks(); bool tankInUse(int o2, int he); @@ -96,6 +57,8 @@ public: struct diveplan getDiveplan(); QStringList &getGasList(); QVector<QPair<int, int> > collectGases(dive *d); + int lastEnteredPoint(); + static bool addingDeco; public slots: @@ -115,6 +78,7 @@ slots: void deleteTemporaryPlan(); void loadFromDive(dive *d); void restoreBackupDive(); + signals: void planCreated(); void planCanceled(); @@ -124,6 +88,7 @@ private: bool addGas(int o2, int he); struct diveplan diveplan; Mode mode; + bool recalc; QVector<divedatapoint> divepoints; struct dive *tempDive; struct dive backupDive; diff --git a/qt-ui/diveplanner.ui b/qt-ui/diveplanner.ui index 986f1b9d5..558be069d 100644 --- a/qt-ui/diveplanner.ui +++ b/qt-ui/diveplanner.ui @@ -142,7 +142,7 @@ </property> </widget> </item> - <item row="8" column="0" colspan="1"> + <item row="8" column="0" colspan="2"> <widget class="TableView" name="tableWidget" native="true"> <property name="sizePolicy"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> @@ -158,22 +158,6 @@ </property> </widget> </item> - <item row="8" column="1" colspan="1"> - <widget class="TableView" name="tableWidgetComp" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>50</height> - </size> - </property> - </widget> - </item> <item row="9" column="0" colspan="2"> <widget class="QDialogButtonBox" name="buttonBox"> <property name="standardButtons"> diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 5b011d747..6d64da44b 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -253,6 +253,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in case O2: if (CHANGED()) { cyl->gasmix.o2 = string_to_fraction(vString.toUtf8().data()); + cyl->depth.mm = 1600 * 1000 / cyl->gasmix.o2.permille * 10 - 10000; changed = true; } break; |