mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-30 22:20:21 +00:00
Allow editing sensors through equipment tab
Add a column to the equipment table that shows if a sensor is attached to a tank, or which sensors would be available to attach to a tank that currently doesn't have a pressure sensor associated with it. Changing the sensor assignement can be undone. This column is hidden by default as this is a somewhat unusual activity. Signed-off-by: Michael Andreen <michael@andreen.dev> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
ec96cbaab5
commit
9b4263aa87
10 changed files with 130 additions and 3 deletions
|
@ -314,6 +314,11 @@ int editCylinder(int index, cylinder_t cyl, EditCylinderType type, bool currentD
|
||||||
return execute_edit(new EditCylinder(index, cyl, type, currentDiveOnly));
|
return execute_edit(new EditCylinder(index, cyl, type, currentDiveOnly));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void editSensors(int toCylinder, const int fromCylinder)
|
||||||
|
{
|
||||||
|
execute(new EditSensors(toCylinder, fromCylinder));
|
||||||
|
}
|
||||||
|
|
||||||
// Trip editing related commands
|
// Trip editing related commands
|
||||||
void editTripLocation(dive_trip *trip, const QString &s)
|
void editTripLocation(dive_trip *trip, const QString &s)
|
||||||
{
|
{
|
||||||
|
|
|
@ -111,6 +111,7 @@ enum class EditCylinderType {
|
||||||
GASMIX
|
GASMIX
|
||||||
};
|
};
|
||||||
int editCylinder(int index, cylinder_t cyl, EditCylinderType type, bool currentDiveOnly);
|
int editCylinder(int index, cylinder_t cyl, EditCylinderType type, bool currentDiveOnly);
|
||||||
|
void editSensors(int toCylinder, const int fromCylinder);
|
||||||
#ifdef SUBSURFACE_MOBILE
|
#ifdef SUBSURFACE_MOBILE
|
||||||
// Edits a dive and creates a divesite (if createDs != NULL) or edits a divesite (if changeDs != NULL).
|
// Edits a dive and creates a divesite (if createDs != NULL) or edits a divesite (if changeDs != NULL).
|
||||||
// Takes ownership of newDive and createDs!
|
// Takes ownership of newDive and createDs!
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "core/divelist.h"
|
#include "core/divelist.h"
|
||||||
#include "core/fulltext.h"
|
#include "core/fulltext.h"
|
||||||
#include "core/qthelper.h" // for copy_qstring
|
#include "core/qthelper.h" // for copy_qstring
|
||||||
|
#include "core/sample.h"
|
||||||
#include "core/selection.h"
|
#include "core/selection.h"
|
||||||
#include "core/subsurface-string.h"
|
#include "core/subsurface-string.h"
|
||||||
#include "core/tag.h"
|
#include "core/tag.h"
|
||||||
|
@ -1360,6 +1361,43 @@ void EditCylinder::undo()
|
||||||
redo();
|
redo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EditSensors::EditSensors(int toCylinderIn, int fromCylinderIn)
|
||||||
|
: dc(current_dc), d(current_dive), toCylinder(toCylinderIn), fromCylinder(fromCylinderIn)
|
||||||
|
{
|
||||||
|
if (!d || !dc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
setText(Command::Base::tr("Edit sensors"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditSensors::mapSensors(int toCyl, int fromCyl)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < dc->samples; ++i) {
|
||||||
|
for (int s = 0; s < MAX_SENSORS; ++s) {
|
||||||
|
if (dc->sample[i].pressure[s].mbar && dc->sample[i].sensor[s] == fromCyl)
|
||||||
|
dc->sample[i].sensor[s] = toCyl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit diveListNotifier.diveComputerEdited(dc);
|
||||||
|
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditSensors::undo()
|
||||||
|
{
|
||||||
|
mapSensors(fromCylinder, toCylinder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditSensors::redo()
|
||||||
|
{
|
||||||
|
mapSensors(toCylinder, fromCylinder);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditSensors::workToBeDone()
|
||||||
|
{
|
||||||
|
return d && dc;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SUBSURFACE_MOBILE
|
#ifdef SUBSURFACE_MOBILE
|
||||||
|
|
||||||
EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_site *editDs, location_t dsLocationIn)
|
EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_site *editDs, location_t dsLocationIn)
|
||||||
|
|
|
@ -440,6 +440,22 @@ private:
|
||||||
void redo() override;
|
void redo() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class EditSensors : public Base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EditSensors(int cylIndex, int fromCylinder);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct divecomputer *dc;
|
||||||
|
struct dive *d;
|
||||||
|
int toCylinder;
|
||||||
|
int fromCylinder;
|
||||||
|
void mapSensors(int toCyl, int fromCyl);
|
||||||
|
void undo() override;
|
||||||
|
void redo() override;
|
||||||
|
bool workToBeDone() override;
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef SUBSURFACE_MOBILE
|
#ifdef SUBSURFACE_MOBILE
|
||||||
// Edit a full dive. This is used on mobile where we don't have per-field granularity.
|
// Edit a full dive. This is used on mobile where we don't have per-field granularity.
|
||||||
// It may add or edit a dive site.
|
// It may add or edit a dive site.
|
||||||
|
|
|
@ -96,6 +96,8 @@ signals:
|
||||||
void divesTimeChanged(timestamp_t delta, const QVector<dive *> &dives);
|
void divesTimeChanged(timestamp_t delta, const QVector<dive *> &dives);
|
||||||
void divesImported(); // A general signal when multiple dives have been imported.
|
void divesImported(); // A general signal when multiple dives have been imported.
|
||||||
|
|
||||||
|
void diveComputerEdited(divecomputer *dc);
|
||||||
|
|
||||||
void cylindersReset(const QVector<dive *> &dives);
|
void cylindersReset(const QVector<dive *> &dives);
|
||||||
void cylinderAdded(dive *d, int pos);
|
void cylinderAdded(dive *d, int pos);
|
||||||
void cylinderRemoved(dive *d, int pos);
|
void cylinderRemoved(dive *d, int pos);
|
||||||
|
|
|
@ -49,6 +49,7 @@ DivePlannerWidget::DivePlannerWidget(QWidget *parent) : QWidget(parent, QFlag(0)
|
||||||
view->setColumnHidden(CylindersModel::DEPTH, false);
|
view->setColumnHidden(CylindersModel::DEPTH, false);
|
||||||
view->setColumnHidden(CylindersModel::WORKINGPRESS_INT, true);
|
view->setColumnHidden(CylindersModel::WORKINGPRESS_INT, true);
|
||||||
view->setColumnHidden(CylindersModel::SIZE_INT, true);
|
view->setColumnHidden(CylindersModel::SIZE_INT, true);
|
||||||
|
view->setColumnHidden(CylindersModel::SENSORS, true);
|
||||||
view->setItemDelegateForColumn(CylindersModel::TYPE, new TankInfoDelegate(this));
|
view->setItemDelegateForColumn(CylindersModel::TYPE, new TankInfoDelegate(this));
|
||||||
connect(ui.cylinderTableWidget, &TableView::addButtonClicked, plannerModel, &DivePlannerPointsModel::addCylinder_clicked);
|
connect(ui.cylinderTableWidget, &TableView::addButtonClicked, plannerModel, &DivePlannerPointsModel::addCylinder_clicked);
|
||||||
connect(ui.tableWidget, &TableView::addButtonClicked, plannerModel, &DivePlannerPointsModel::addDefaultStop);
|
connect(ui.tableWidget, &TableView::addButtonClicked, plannerModel, &DivePlannerPointsModel::addDefaultStop);
|
||||||
|
|
|
@ -20,6 +20,15 @@ static bool ignoreHiddenFlag(int i)
|
||||||
i == CylindersModel::WORKINGPRESS_INT || i == CylindersModel::SIZE_INT;
|
i == CylindersModel::WORKINGPRESS_INT || i == CylindersModel::SIZE_INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool hiddenByDefault(int i)
|
||||||
|
{
|
||||||
|
switch (i) {
|
||||||
|
case CylindersModel::SENSORS:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
TabDiveEquipment::TabDiveEquipment(QWidget *parent) : TabBase(parent),
|
TabDiveEquipment::TabDiveEquipment(QWidget *parent) : TabBase(parent),
|
||||||
cylindersModel(new CylindersModel(false, true, this)),
|
cylindersModel(new CylindersModel(false, true, this)),
|
||||||
weightModel(new WeightModel(this))
|
weightModel(new WeightModel(this))
|
||||||
|
@ -81,7 +90,8 @@ TabDiveEquipment::TabDiveEquipment(QWidget *parent) : TabBase(parent),
|
||||||
for (int i = 0; i < CylindersModel::COLUMNS; i++) {
|
for (int i = 0; i < CylindersModel::COLUMNS; i++) {
|
||||||
if (ignoreHiddenFlag(i))
|
if (ignoreHiddenFlag(i))
|
||||||
continue;
|
continue;
|
||||||
bool checked = s.value(QString("column%1_hidden").arg(i)).toBool();
|
auto setting = s.value(QString("column%1_hidden").arg(i));
|
||||||
|
bool checked = setting.isValid() ? setting.toBool() : hiddenByDefault(i) ;
|
||||||
QAction *action = new QAction(cylindersModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(), ui.cylinders->view());
|
QAction *action = new QAction(cylindersModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(), ui.cylinders->view());
|
||||||
action->setCheckable(true);
|
action->setCheckable(true);
|
||||||
action->setData(i);
|
action->setData(i);
|
||||||
|
|
|
@ -77,6 +77,7 @@ ProfileWidget2::ProfileWidget2(DivePlannerPointsModel *plannerModelIn, double dp
|
||||||
connect(&diveListNotifier, &DiveListNotifier::pictureOffsetChanged, this, &ProfileWidget2::pictureOffsetChanged);
|
connect(&diveListNotifier, &DiveListNotifier::pictureOffsetChanged, this, &ProfileWidget2::pictureOffsetChanged);
|
||||||
connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &ProfileWidget2::divesChanged);
|
connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &ProfileWidget2::divesChanged);
|
||||||
connect(&diveListNotifier, &DiveListNotifier::deviceEdited, this, &ProfileWidget2::replot);
|
connect(&diveListNotifier, &DiveListNotifier::deviceEdited, this, &ProfileWidget2::replot);
|
||||||
|
connect(&diveListNotifier, &DiveListNotifier::diveComputerEdited, this, &ProfileWidget2::replot);
|
||||||
#endif // SUBSURFACE_MOBILE
|
#endif // SUBSURFACE_MOBILE
|
||||||
|
|
||||||
#if !defined(QT_NO_DEBUG) && defined(SHOW_PLOT_INFO_TABLE)
|
#if !defined(QT_NO_DEBUG) && defined(SHOW_PLOT_INFO_TABLE)
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "core/color.h"
|
#include "core/color.h"
|
||||||
#include "qt-models/diveplannermodel.h"
|
#include "qt-models/diveplannermodel.h"
|
||||||
#include "core/gettextfromc.h"
|
#include "core/gettextfromc.h"
|
||||||
|
#include "core/sample.h"
|
||||||
#include "core/subsurface-qt/divelistnotifier.h"
|
#include "core/subsurface-qt/divelistnotifier.h"
|
||||||
#include "core/subsurface-string.h"
|
#include "core/subsurface-string.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -19,9 +20,10 @@ CylindersModel::CylindersModel(bool planner, bool hideUnused, QObject *parent) :
|
||||||
tempRow(-1),
|
tempRow(-1),
|
||||||
tempCyl(empty_cylinder)
|
tempCyl(empty_cylinder)
|
||||||
{
|
{
|
||||||
// enum {REMOVE, TYPE, SIZE, WORKINGPRESS, START, END, O2, HE, DEPTH, MOD, MND, USE, IS_USED, WORKINGPRESS_INT, SIZE_INT};
|
// enum {REMOVE, TYPE, SIZE, WORKINGPRESS, START, END, O2, HE, DEPTH, MOD, MND, USE, WORKINGPRESS_INT, SIZE_INT, SENSORS};
|
||||||
setHeaderDataStrings(QStringList() << "" << tr("Type") << tr("Size") << tr("Work press.") << tr("Start press.") << tr("End press.") << tr("O₂%") << tr("He%")
|
setHeaderDataStrings(QStringList() << "" << tr("Type") << tr("Size") << tr("Work press.") << tr("Start press.") << tr("End press.") << tr("O₂%") << tr("He%")
|
||||||
<< tr("Deco switch at") <<tr("Bot. MOD") <<tr("MND") << tr("Use") << "" << "");
|
<< tr("Deco switch at") << tr("Bot. MOD") << tr("MND") << tr("Use") << ""
|
||||||
|
<< "" << tr("Sensors"));
|
||||||
|
|
||||||
connect(&diveListNotifier, &DiveListNotifier::cylindersReset, this, &CylindersModel::cylindersReset);
|
connect(&diveListNotifier, &DiveListNotifier::cylindersReset, this, &CylindersModel::cylindersReset);
|
||||||
connect(&diveListNotifier, &DiveListNotifier::cylinderAdded, this, &CylindersModel::cylinderAdded);
|
connect(&diveListNotifier, &DiveListNotifier::cylinderAdded, this, &CylindersModel::cylinderAdded);
|
||||||
|
@ -237,6 +239,24 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const
|
||||||
return static_cast<int>(cyl->type.workingpressure.mbar);
|
return static_cast<int>(cyl->type.workingpressure.mbar);
|
||||||
case SIZE_INT:
|
case SIZE_INT:
|
||||||
return static_cast<int>(cyl->type.size.mliter);
|
return static_cast<int>(cyl->type.size.mliter);
|
||||||
|
case SENSORS: {
|
||||||
|
std::vector<int16_t> sensors;
|
||||||
|
for (int i = 0; i < current_dc->samples; ++i) {
|
||||||
|
auto &sample = current_dc->sample[i];
|
||||||
|
for (auto s = 0; s < MAX_SENSORS; ++s) {
|
||||||
|
if (sample.pressure[s].mbar) {
|
||||||
|
if (sample.sensor[s] == index.row())
|
||||||
|
return tr("Sensor attached, can't move another sensor here.");
|
||||||
|
else if (std::find(sensors.begin(), sensors.end(), sample.sensor[s]) == sensors.end())
|
||||||
|
sensors.push_back(sample.sensor[s]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QStringList sensorStrings;
|
||||||
|
for (auto s : sensors)
|
||||||
|
sensorStrings << QString::number(s);
|
||||||
|
return tr("Select one of these cylinders: ") + sensorStrings.join(",");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Qt::DecorationRole:
|
case Qt::DecorationRole:
|
||||||
|
@ -272,6 +292,8 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const
|
||||||
return tr("Calculated using Bottom pO₂ preference. Setting MOD adjusts O₂%, set to '*' for best O₂% for max. depth.");
|
return tr("Calculated using Bottom pO₂ preference. Setting MOD adjusts O₂%, set to '*' for best O₂% for max. depth.");
|
||||||
case MND:
|
case MND:
|
||||||
return tr("Calculated using Best Mix END preference. Setting MND adjusts He%, set to '*' for best He% for max. depth.");
|
return tr("Calculated using Best Mix END preference. Setting MND adjusts He%, set to '*' for best He% for max. depth.");
|
||||||
|
case SENSORS:
|
||||||
|
return tr("Index of cylinder that you want to move sensor data from.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -453,6 +475,24 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in
|
||||||
}
|
}
|
||||||
type = Command::EditCylinderType::TYPE;
|
type = Command::EditCylinderType::TYPE;
|
||||||
break;
|
break;
|
||||||
|
case SENSORS: {
|
||||||
|
std::vector<int> sensors;
|
||||||
|
for (auto &sensor : vString.split(",")) {
|
||||||
|
bool ok = false;
|
||||||
|
int s = sensor.toInt(&ok);
|
||||||
|
if (ok && s < MAX_SENSORS)
|
||||||
|
sensors.push_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
int s = vString.toInt(&ok);
|
||||||
|
if (ok) {
|
||||||
|
Command::editSensors(index.row(), s);
|
||||||
|
// We don't use the edit cylinder command and editing sensors is not relevant for planner
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inPlanner) {
|
if (inPlanner) {
|
||||||
|
@ -512,6 +552,18 @@ Qt::ItemFlags CylindersModel::flags(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
if (index.column() == REMOVE || index.column() == USE)
|
if (index.column() == REMOVE || index.column() == USE)
|
||||||
return Qt::ItemIsEnabled;
|
return Qt::ItemIsEnabled;
|
||||||
|
if (index.column() == SENSORS) {
|
||||||
|
for (int i = 0; i < current_dc->samples; ++i) {
|
||||||
|
auto &sample = current_dc->sample[i];
|
||||||
|
for (auto s = 0; s < MAX_SENSORS; ++s) {
|
||||||
|
if (sample.pressure[s].mbar) {
|
||||||
|
if (sample.sensor[s] == index.row())
|
||||||
|
// Sensor attached, not editable.
|
||||||
|
return QAbstractItemModel::flags(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
|
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ public:
|
||||||
USE,
|
USE,
|
||||||
WORKINGPRESS_INT,
|
WORKINGPRESS_INT,
|
||||||
SIZE_INT,
|
SIZE_INT,
|
||||||
|
SENSORS,
|
||||||
COLUMNS
|
COLUMNS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue