Compare commits
119 Commits
master
...
bstoeger-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bf2daa732 | ||
|
|
db0a220d52 | ||
|
|
5ea71572e3 | ||
|
|
c97901afcf | ||
|
|
b59682fa43 | ||
|
|
d25e41a39d | ||
|
|
3f7daf225f | ||
|
|
ec88a9887e | ||
|
|
87fb957ad1 | ||
|
|
90c7c5d936 | ||
|
|
d9fc0be382 | ||
|
|
98c4242f31 | ||
|
|
aef45b4913 | ||
|
|
adbd8b1a0a | ||
|
|
aab88385ce | ||
|
|
c50c5d97bc | ||
|
|
2dd7ff6da8 | ||
|
|
b46b42dabd | ||
|
|
7b0dff0dff | ||
|
|
ffed8dfea6 | ||
|
|
02177e5d3b | ||
|
|
c1678c23fc | ||
|
|
4c9915b9bf | ||
|
|
34419ece66 | ||
|
|
99f87cba4b | ||
|
|
e171cdf1eb | ||
|
|
e06225eb8a | ||
|
|
1f8b151d8f | ||
|
|
4a438bd63f | ||
|
|
3bf8658861 | ||
|
|
9015429c2a | ||
|
|
040af97b82 | ||
|
|
d59e80b44d | ||
|
|
2e876f139d | ||
|
|
c8389a5644 | ||
|
|
c0e834cffc | ||
|
|
076d8a7fce | ||
|
|
a72a0543e1 | ||
|
|
a3f37e3c9e | ||
|
|
42fbb40eab | ||
|
|
62303a015a | ||
|
|
e29c044d91 | ||
|
|
21a28aeb62 | ||
|
|
3447441e81 | ||
|
|
965d1f359b | ||
|
|
2d18da5fc9 | ||
|
|
8962aadb23 | ||
|
|
553151b12f | ||
|
|
9fae66f163 | ||
|
|
a05d80f9ae | ||
|
|
ec48e05dc6 | ||
|
|
a584b4926b | ||
|
|
1fd7c2f091 | ||
|
|
1a6f42e781 | ||
|
|
ed302b373a | ||
|
|
d611400897 | ||
|
|
0cd01640e5 | ||
|
|
e9b49d197b | ||
|
|
fc725d0803 | ||
|
|
98e9fee8bd | ||
|
|
c26b5e4706 | ||
|
|
eb5f8415e1 | ||
|
|
865cac7bf8 | ||
|
|
950578d2f9 | ||
|
|
c3eadff759 | ||
|
|
d7b05ffff7 | ||
|
|
50d65183e7 | ||
|
|
43dc398f51 | ||
|
|
92d3ae4029 | ||
|
|
6bdb8537e6 | ||
|
|
86831aeffe | ||
|
|
c28f9e7749 | ||
|
|
f65b656f23 | ||
|
|
4c94dcaac0 | ||
|
|
fe41bc06d0 | ||
|
|
a8fad49dd6 | ||
|
|
a1e4cdb580 | ||
|
|
02fb80113b | ||
|
|
d6796d2588 | ||
|
|
51c4b83f55 | ||
|
|
34db7cf0e8 | ||
|
|
d328ce5201 | ||
|
|
3ff22eb9fb | ||
|
|
54a3ffbff7 | ||
|
|
c28586ea1e | ||
|
|
6bf0345c13 | ||
|
|
ca270a1c98 | ||
|
|
43491ea180 | ||
|
|
02fdf6a609 | ||
|
|
19a46de3d2 | ||
|
|
5128d0ed5f | ||
|
|
a054d81e7c | ||
|
|
71418b116a | ||
|
|
1898036bff | ||
|
|
3754e92d13 | ||
|
|
7e5babf57f | ||
|
|
0c9de9bd98 | ||
|
|
1dc31ecab6 | ||
|
|
cb5e5e77ce | ||
|
|
4eb054785b | ||
|
|
b884c4262c | ||
|
|
68630ffa30 | ||
|
|
891e72fce2 | ||
|
|
74ab57feb2 | ||
|
|
c5ba85e4da | ||
|
|
54c25c97b8 | ||
|
|
37120003f0 | ||
|
|
3c0398f9ff | ||
|
|
4a2b0b0fc9 | ||
|
|
2613e11b81 | ||
|
|
f616538f52 | ||
|
|
8fec9bc49b | ||
|
|
5835456235 | ||
|
|
64ca63cb91 | ||
|
|
37df7e8564 | ||
|
|
25ecc3f45a | ||
|
|
1f4def49aa | ||
|
|
aaa4effa26 | ||
|
|
5c95ad6249 |
@ -10,7 +10,7 @@ ColumnLimit: 0
|
|||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
ConstructorInitializerIndentWidth: 8
|
ConstructorInitializerIndentWidth: 8
|
||||||
ContinuationIndentWidth: 8
|
ContinuationIndentWidth: 8
|
||||||
ForEachMacros: [ 'for_each_dc', 'for_each_relevant_dc', 'for_each_dive', 'for_each_line' ]
|
ForEachMacros: [ 'for_each_line' ]
|
||||||
IndentFunctionDeclarationAfterType: false #personal taste, good for long methods
|
IndentFunctionDeclarationAfterType: false #personal taste, good for long methods
|
||||||
IndentWidth: 8
|
IndentWidth: 8
|
||||||
MaxEmptyLinesToKeep: 2
|
MaxEmptyLinesToKeep: 2
|
||||||
|
|||||||
@ -401,7 +401,6 @@ close to our coding standards.
|
|||||||
filetype plugin indent on
|
filetype plugin indent on
|
||||||
filetype detect
|
filetype detect
|
||||||
set cindent tabstop=8 shiftwidth=8 cinoptions=l1,:0,(0,g0
|
set cindent tabstop=8 shiftwidth=8 cinoptions=l1,:0,(0,g0
|
||||||
" TODO: extern "C" gets indented
|
|
||||||
|
|
||||||
" And some sane defaults, optional, but quite nice
|
" And some sane defaults, optional, but quite nice
|
||||||
set nocompatible
|
set nocompatible
|
||||||
|
|||||||
@ -45,28 +45,28 @@ SOURCES += subsurface-mobile-main.cpp \
|
|||||||
core/fulltext.cpp \
|
core/fulltext.cpp \
|
||||||
core/subsurfacestartup.cpp \
|
core/subsurfacestartup.cpp \
|
||||||
core/subsurface-string.cpp \
|
core/subsurface-string.cpp \
|
||||||
core/pref.c \
|
core/pref.cpp \
|
||||||
core/profile.cpp \
|
core/profile.cpp \
|
||||||
core/device.cpp \
|
core/device.cpp \
|
||||||
core/dive.cpp \
|
core/dive.cpp \
|
||||||
core/divecomputer.c \
|
core/divecomputer.cpp \
|
||||||
core/divefilter.cpp \
|
core/divefilter.cpp \
|
||||||
core/event.c \
|
core/event.cpp \
|
||||||
core/eventtype.cpp \
|
core/eventtype.cpp \
|
||||||
core/filterconstraint.cpp \
|
core/filterconstraint.cpp \
|
||||||
core/filterpreset.cpp \
|
core/filterpreset.cpp \
|
||||||
core/divelist.c \
|
core/filterpresettable.cpp \
|
||||||
|
core/divelist.cpp \
|
||||||
core/divelog.cpp \
|
core/divelog.cpp \
|
||||||
core/gas-model.c \
|
core/gas-model.cpp \
|
||||||
core/gaspressures.c \
|
core/gaspressures.cpp \
|
||||||
core/git-access.cpp \
|
core/git-access.cpp \
|
||||||
core/globals.cpp \
|
core/globals.cpp \
|
||||||
core/liquivision.cpp \
|
core/liquivision.cpp \
|
||||||
core/load-git.cpp \
|
core/load-git.cpp \
|
||||||
core/parse-xml.cpp \
|
core/parse-xml.cpp \
|
||||||
core/parse.cpp \
|
core/parse.cpp \
|
||||||
core/picture.c \
|
core/picture.cpp \
|
||||||
core/pictureobj.cpp \
|
|
||||||
core/sample.cpp \
|
core/sample.cpp \
|
||||||
core/import-suunto.cpp \
|
core/import-suunto.cpp \
|
||||||
core/import-shearwater.cpp \
|
core/import-shearwater.cpp \
|
||||||
@ -75,37 +75,38 @@ SOURCES += subsurface-mobile-main.cpp \
|
|||||||
core/import-divinglog.cpp \
|
core/import-divinglog.cpp \
|
||||||
core/import-csv.cpp \
|
core/import-csv.cpp \
|
||||||
core/save-html.cpp \
|
core/save-html.cpp \
|
||||||
core/statistics.c \
|
core/statistics.cpp \
|
||||||
core/worldmap-save.cpp \
|
core/worldmap-save.cpp \
|
||||||
core/libdivecomputer.cpp \
|
core/libdivecomputer.cpp \
|
||||||
core/version.c \
|
core/version.cpp \
|
||||||
core/save-git.cpp \
|
core/save-git.cpp \
|
||||||
core/datatrak.cpp \
|
core/datatrak.cpp \
|
||||||
core/ostctools.c \
|
core/ostctools.cpp \
|
||||||
core/planner.cpp \
|
core/planner.cpp \
|
||||||
core/save-xml.cpp \
|
core/save-xml.cpp \
|
||||||
core/cochran.cpp \
|
core/cochran.cpp \
|
||||||
core/deco.cpp \
|
core/deco.cpp \
|
||||||
core/divesite.c \
|
core/divesite.cpp \
|
||||||
core/equipment.c \
|
core/equipment.cpp \
|
||||||
core/gas.c \
|
core/gas.cpp \
|
||||||
core/membuffer.cpp \
|
core/membuffer.cpp \
|
||||||
core/selection.cpp \
|
core/selection.cpp \
|
||||||
core/sha1.c \
|
core/sha1.cpp \
|
||||||
core/string-format.cpp \
|
core/string-format.cpp \
|
||||||
core/strtod.c \
|
core/strtod.cpp \
|
||||||
core/tag.cpp \
|
core/tag.cpp \
|
||||||
core/taxonomy.c \
|
core/taxonomy.cpp \
|
||||||
core/time.cpp \
|
core/time.cpp \
|
||||||
core/trip.c \
|
core/trip.cpp \
|
||||||
core/units.c \
|
core/triptable.cpp \
|
||||||
core/uemis.c \
|
core/units.cpp \
|
||||||
|
core/uemis.cpp \
|
||||||
core/btdiscovery.cpp \
|
core/btdiscovery.cpp \
|
||||||
core/connectionlistmodel.cpp \
|
core/connectionlistmodel.cpp \
|
||||||
core/qt-ble.cpp \
|
core/qt-ble.cpp \
|
||||||
core/uploadDiveShare.cpp \
|
core/uploadDiveShare.cpp \
|
||||||
core/uploadDiveLogsDE.cpp \
|
core/uploadDiveLogsDE.cpp \
|
||||||
core/save-profiledata.c \
|
core/save-profiledata.cpp \
|
||||||
core/xmlparams.cpp \
|
core/xmlparams.cpp \
|
||||||
core/settings/qPref.cpp \
|
core/settings/qPref.cpp \
|
||||||
core/settings/qPrefCloudStorage.cpp \
|
core/settings/qPrefCloudStorage.cpp \
|
||||||
@ -207,7 +208,6 @@ HEADERS += \
|
|||||||
core/extradata.h \
|
core/extradata.h \
|
||||||
core/git-access.h \
|
core/git-access.h \
|
||||||
core/globals.h \
|
core/globals.h \
|
||||||
core/owning_ptrs.h \
|
|
||||||
core/pref.h \
|
core/pref.h \
|
||||||
core/profile.h \
|
core/profile.h \
|
||||||
core/qthelper.h \
|
core/qthelper.h \
|
||||||
@ -217,9 +217,9 @@ HEADERS += \
|
|||||||
core/units.h \
|
core/units.h \
|
||||||
core/version.h \
|
core/version.h \
|
||||||
core/picture.h \
|
core/picture.h \
|
||||||
core/pictureobj.h \
|
|
||||||
core/planner.h \
|
core/planner.h \
|
||||||
core/divesite.h \
|
core/divesite.h \
|
||||||
|
core/divesitetable.h \
|
||||||
core/checkcloudconnection.h \
|
core/checkcloudconnection.h \
|
||||||
core/cochran.h \
|
core/cochran.h \
|
||||||
core/color.h \
|
core/color.h \
|
||||||
@ -229,6 +229,7 @@ HEADERS += \
|
|||||||
core/divefilter.h \
|
core/divefilter.h \
|
||||||
core/filterconstraint.h \
|
core/filterconstraint.h \
|
||||||
core/filterpreset.h \
|
core/filterpreset.h \
|
||||||
|
core/filterpresettable.h \
|
||||||
core/divelist.h \
|
core/divelist.h \
|
||||||
core/divelog.h \
|
core/divelog.h \
|
||||||
core/divelogexportlogic.h \
|
core/divelogexportlogic.h \
|
||||||
@ -249,6 +250,8 @@ HEADERS += \
|
|||||||
core/subsurfacestartup.h \
|
core/subsurfacestartup.h \
|
||||||
core/subsurfacesysinfo.h \
|
core/subsurfacesysinfo.h \
|
||||||
core/taxonomy.h \
|
core/taxonomy.h \
|
||||||
|
core/trip.h \
|
||||||
|
core/triptable.h \
|
||||||
core/uemis.h \
|
core/uemis.h \
|
||||||
core/webservice.h \
|
core/webservice.h \
|
||||||
core/windowtitleupdate.h \
|
core/windowtitleupdate.h \
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include "core/divesite.h"
|
#include "core/divesite.h"
|
||||||
#include "core/picture.h"
|
#include "core/picture.h"
|
||||||
#include "core/pref.h"
|
#include "core/pref.h"
|
||||||
|
#include "core/range.h"
|
||||||
#include "core/sample.h"
|
#include "core/sample.h"
|
||||||
#include "core/selection.h"
|
#include "core/selection.h"
|
||||||
#include "core/taxonomy.h"
|
#include "core/taxonomy.h"
|
||||||
@ -40,12 +41,12 @@ static constexpr int profileScale = 4;
|
|||||||
static constexpr int profileWidth = 800 * profileScale;
|
static constexpr int profileWidth = 800 * profileScale;
|
||||||
static constexpr int profileHeight = 600 * profileScale;
|
static constexpr int profileHeight = 600 * profileScale;
|
||||||
|
|
||||||
static void exportProfile(ProfileScene *profile, const struct dive *dive, const QString &filename)
|
static void exportProfile(ProfileScene &profile, const struct dive &dive, const QString &filename)
|
||||||
{
|
{
|
||||||
QImage image = QImage(QSize(profileWidth, profileHeight), QImage::Format_RGB32);
|
QImage image = QImage(QSize(profileWidth, profileHeight), QImage::Format_RGB32);
|
||||||
QPainter paint;
|
QPainter paint;
|
||||||
paint.begin(&image);
|
paint.begin(&image);
|
||||||
profile->draw(&paint, QRect(0, 0, profileWidth, profileHeight), dive, 0, nullptr, false);
|
profile.draw(&paint, QRect(0, 0, profileWidth, profileHeight), &dive, 0, nullptr, false);
|
||||||
image.save(filename);
|
image.save(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,17 +57,15 @@ static std::unique_ptr<ProfileScene> getPrintProfile()
|
|||||||
|
|
||||||
void exportProfile(QString filename, bool selected_only, ExportCallback &cb)
|
void exportProfile(QString filename, bool selected_only, ExportCallback &cb)
|
||||||
{
|
{
|
||||||
struct dive *dive;
|
|
||||||
int i;
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
if (!filename.endsWith(".png", Qt::CaseInsensitive))
|
if (!filename.endsWith(".png", Qt::CaseInsensitive))
|
||||||
filename = filename.append(".png");
|
filename = filename.append(".png");
|
||||||
QFileInfo fi(filename);
|
QFileInfo fi(filename);
|
||||||
|
|
||||||
int todo = selected_only ? amount_selected : divelog.dives->nr;
|
int todo = selected_only ? amount_selected : static_cast<int>(divelog.dives.size());
|
||||||
int done = 0;
|
int done = 0;
|
||||||
auto profile = getPrintProfile();
|
auto profile = getPrintProfile();
|
||||||
for_each_dive (i, dive) {
|
for (auto &dive: divelog.dives) {
|
||||||
if (cb.canceled())
|
if (cb.canceled())
|
||||||
return;
|
return;
|
||||||
if (selected_only && !dive->selected)
|
if (selected_only && !dive->selected)
|
||||||
@ -74,7 +73,7 @@ void exportProfile(QString filename, bool selected_only, ExportCallback &cb)
|
|||||||
cb.setProgress(done++ * 1000 / todo);
|
cb.setProgress(done++ * 1000 / todo);
|
||||||
QString fn = count ? fi.path() + QDir::separator() + fi.completeBaseName().append(QString("-%1.").arg(count)) + fi.suffix()
|
QString fn = count ? fi.path() + QDir::separator() + fi.completeBaseName().append(QString("-%1.").arg(count)) + fi.suffix()
|
||||||
: filename;
|
: filename;
|
||||||
exportProfile(profile.get(), dive, fn);
|
exportProfile(*profile, *dive, fn);
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,14 +82,12 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
|
|||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
QDir texdir = QFileInfo(filename).dir();
|
QDir texdir = QFileInfo(filename).dir();
|
||||||
struct dive *dive;
|
|
||||||
const struct units *units = get_units();
|
const struct units *units = get_units();
|
||||||
const char *unit;
|
const char *unit;
|
||||||
const char *ssrf;
|
const char *ssrf;
|
||||||
int i;
|
|
||||||
bool need_pagebreak = false;
|
bool need_pagebreak = false;
|
||||||
|
|
||||||
struct membufferpp buf;
|
membuffer buf;
|
||||||
|
|
||||||
if (plain) {
|
if (plain) {
|
||||||
ssrf = "";
|
ssrf = "";
|
||||||
@ -132,34 +129,29 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
|
|||||||
|
|
||||||
put_format(&buf, "\n%%%%%%%%%% Begin Dive Data: %%%%%%%%%%\n");
|
put_format(&buf, "\n%%%%%%%%%% Begin Dive Data: %%%%%%%%%%\n");
|
||||||
|
|
||||||
int todo = selected_only ? amount_selected : divelog.dives->nr;
|
int todo = selected_only ? amount_selected : static_cast<int>(divelog.dives.size());
|
||||||
int done = 0;
|
int done = 0;
|
||||||
auto profile = getPrintProfile();
|
auto profile = getPrintProfile();
|
||||||
for_each_dive (i, dive) {
|
for (auto &dive: divelog.dives) {
|
||||||
if (cb.canceled())
|
if (cb.canceled())
|
||||||
return;
|
return;
|
||||||
if (selected_only && !dive->selected)
|
if (selected_only && !dive->selected)
|
||||||
continue;
|
continue;
|
||||||
cb.setProgress(done++ * 1000 / todo);
|
cb.setProgress(done++ * 1000 / todo);
|
||||||
exportProfile(profile.get(), dive, texdir.filePath(QString("profile%1.png").arg(dive->number)));
|
exportProfile(*profile, *dive, texdir.filePath(QString("profile%1.png").arg(dive->number)));
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
utc_mkdate(dive->when, &tm);
|
utc_mkdate(dive->when, &tm);
|
||||||
|
|
||||||
const char *country = NULL;
|
std::string country;
|
||||||
dive_site *site = dive->dive_site;
|
dive_site *site = dive->dive_site;
|
||||||
if (site)
|
if (site)
|
||||||
country = taxonomy_get_country(&site->taxonomy);
|
country = taxonomy_get_country(site->taxonomy);
|
||||||
pressure_t delta_p = {.mbar = 0};
|
pressure_t delta_p;
|
||||||
|
|
||||||
QString star = "*";
|
QString star = "*";
|
||||||
QString viz = star.repeated(dive->visibility);
|
QString viz = star.repeated(dive->visibility);
|
||||||
QString rating = star.repeated(dive->rating);
|
QString rating = star.repeated(dive->rating);
|
||||||
|
|
||||||
int i;
|
|
||||||
int qty_cyl;
|
|
||||||
int qty_weight;
|
|
||||||
double total_weight;
|
|
||||||
|
|
||||||
if (need_pagebreak) {
|
if (need_pagebreak) {
|
||||||
if (plain)
|
if (plain)
|
||||||
put_format(&buf, "\\vfill\\eject\n");
|
put_format(&buf, "\\vfill\\eject\n");
|
||||||
@ -174,13 +166,13 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
|
|||||||
put_format(&buf, "\\def\\%shour{%02u}\n", ssrf, tm.tm_hour);
|
put_format(&buf, "\\def\\%shour{%02u}\n", ssrf, tm.tm_hour);
|
||||||
put_format(&buf, "\\def\\%sminute{%02u}\n", ssrf, tm.tm_min);
|
put_format(&buf, "\\def\\%sminute{%02u}\n", ssrf, tm.tm_min);
|
||||||
put_format(&buf, "\\def\\%snumber{%d}\n", ssrf, dive->number);
|
put_format(&buf, "\\def\\%snumber{%d}\n", ssrf, dive->number);
|
||||||
put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name : "");
|
put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name.c_str() : "");
|
||||||
put_format(&buf, "\\def\\%sspot{}\n", ssrf);
|
put_format(&buf, "\\def\\%sspot{}\n", ssrf);
|
||||||
put_format(&buf, "\\def\\%ssitename{%s}\n", ssrf, site ? site->name : "");
|
put_format(&buf, "\\def\\%ssitename{%s}\n", ssrf, site ? site->name.c_str() : "");
|
||||||
site ? put_format(&buf, "\\def\\%sgpslat{%f}\n", ssrf, site->location.lat.udeg / 1000000.0) : put_format(&buf, "\\def\\%sgpslat{}\n", ssrf);
|
site ? put_format(&buf, "\\def\\%sgpslat{%f}\n", ssrf, site->location.lat.udeg / 1000000.0) : put_format(&buf, "\\def\\%sgpslat{}\n", ssrf);
|
||||||
site ? put_format(&buf, "\\def\\%sgpslon{%f}\n", ssrf, site->location.lon.udeg / 1000000.0) : put_format(&buf, "\\def\\gpslon{}\n");
|
site ? put_format(&buf, "\\def\\%sgpslon{%f}\n", ssrf, site->location.lon.udeg / 1000000.0) : put_format(&buf, "\\def\\gpslon{}\n");
|
||||||
put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dc.model);
|
put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dcs[0].model.c_str());
|
||||||
put_format(&buf, "\\def\\%scountry{%s}\n", ssrf, country ?: "");
|
put_format(&buf, "\\def\\%scountry{%s}\n", ssrf, country.c_str());
|
||||||
put_format(&buf, "\\def\\%stime{%u:%02u}\n", ssrf, FRACTION_TUPLE(dive->duration.seconds, 60));
|
put_format(&buf, "\\def\\%stime{%u:%02u}\n", ssrf, FRACTION_TUPLE(dive->duration.seconds, 60));
|
||||||
|
|
||||||
put_format(&buf, "\n%% Dive Profile Details:\n");
|
put_format(&buf, "\n%% Dive Profile Details:\n");
|
||||||
@ -191,24 +183,23 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
|
|||||||
dive->maxdepth.mm ? put_format(&buf, "\\def\\%smaximumdepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->maxdepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%smaximumdepth{}\n", ssrf);
|
dive->maxdepth.mm ? put_format(&buf, "\\def\\%smaximumdepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->maxdepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%smaximumdepth{}\n", ssrf);
|
||||||
dive->meandepth.mm ? put_format(&buf, "\\def\\%smeandepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->meandepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%smeandepth{}\n", ssrf);
|
dive->meandepth.mm ? put_format(&buf, "\\def\\%smeandepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->meandepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%smeandepth{}\n", ssrf);
|
||||||
|
|
||||||
std::string tags = taglist_get_tagstring(dive->tag_list);
|
std::string tags = taglist_get_tagstring(dive->tags);
|
||||||
put_format(&buf, "\\def\\%stype{%s}\n", ssrf, tags.c_str());
|
put_format(&buf, "\\def\\%stype{%s}\n", ssrf, tags.c_str());
|
||||||
put_format(&buf, "\\def\\%sviz{%s}\n", ssrf, qPrintable(viz));
|
put_format(&buf, "\\def\\%sviz{%s}\n", ssrf, qPrintable(viz));
|
||||||
put_format(&buf, "\\def\\%srating{%s}\n", ssrf, qPrintable(rating));
|
put_format(&buf, "\\def\\%srating{%s}\n", ssrf, qPrintable(rating));
|
||||||
put_format(&buf, "\\def\\%splot{\\includegraphics[width=9cm,height=4cm]{profile%d}}\n", ssrf, dive->number);
|
put_format(&buf, "\\def\\%splot{\\includegraphics[width=9cm,height=4cm]{profile%d}}\n", ssrf, dive->number);
|
||||||
put_format(&buf, "\\def\\%sprofilename{profile%d}\n", ssrf, dive->number);
|
put_format(&buf, "\\def\\%sprofilename{profile%d}\n", ssrf, dive->number);
|
||||||
put_format(&buf, "\\def\\%scomment{%s}\n", ssrf, dive->notes ? dive->notes : "");
|
put_format(&buf, "\\def\\%scomment{%s}\n", ssrf, dive->notes.c_str());
|
||||||
put_format(&buf, "\\def\\%sbuddy{%s}\n", ssrf, dive->buddy ? dive->buddy : "");
|
put_format(&buf, "\\def\\%sbuddy{%s}\n", ssrf, dive->buddy.c_str());
|
||||||
put_format(&buf, "\\def\\%sdivemaster{%s}\n", ssrf, dive->diveguide ? dive->diveguide : "");
|
put_format(&buf, "\\def\\%sdivemaster{%s}\n", ssrf, dive->diveguide.c_str());
|
||||||
put_format(&buf, "\\def\\%ssuit{%s}\n", ssrf, dive->suit ? dive->suit : "");
|
put_format(&buf, "\\def\\%ssuit{%s}\n", ssrf, dive->suit.c_str());
|
||||||
|
|
||||||
// Print cylinder data
|
// Print cylinder data
|
||||||
put_format(&buf, "\n%% Gas use information:\n");
|
put_format(&buf, "\n%% Gas use information:\n");
|
||||||
qty_cyl = 0;
|
int qty_cyl = 0;
|
||||||
for (i = 0; i < dive->cylinders.nr; i++){
|
for (auto [i, cyl]: enumerated_range(dive->cylinders)) {
|
||||||
const cylinder_t &cyl = *get_cylinder(dive, i);
|
if (is_cylinder_used(dive.get(), i) || (prefs.include_unused_tanks && !cyl.type.description.empty())){
|
||||||
if (is_cylinder_used(dive, i) || (prefs.include_unused_tanks && cyl.type.description)){
|
put_format(&buf, "\\def\\%scyl%cdescription{%s}\n", ssrf, 'a' + i, cyl.type.description.c_str());
|
||||||
put_format(&buf, "\\def\\%scyl%cdescription{%s}\n", ssrf, 'a' + i, cyl.type.description);
|
|
||||||
put_format(&buf, "\\def\\%scyl%cgasname{%s}\n", ssrf, 'a' + i, gasname(cyl.gasmix));
|
put_format(&buf, "\\def\\%scyl%cgasname{%s}\n", ssrf, 'a' + i, gasname(cyl.gasmix));
|
||||||
put_format(&buf, "\\def\\%scyl%cmixO2{%.1f\\%%}\n", ssrf, 'a' + i, get_o2(cyl.gasmix)/10.0);
|
put_format(&buf, "\\def\\%scyl%cmixO2{%.1f\\%%}\n", ssrf, 'a' + i, get_o2(cyl.gasmix)/10.0);
|
||||||
put_format(&buf, "\\def\\%scyl%cmixHe{%.1f\\%%}\n", ssrf, 'a' + i, get_he(cyl.gasmix)/10.0);
|
put_format(&buf, "\\def\\%scyl%cmixHe{%.1f\\%%}\n", ssrf, 'a' + i, get_he(cyl.gasmix)/10.0);
|
||||||
@ -235,11 +226,10 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
|
|||||||
|
|
||||||
//Code block prints all weights listed in dive.
|
//Code block prints all weights listed in dive.
|
||||||
put_format(&buf, "\n%% Weighting information:\n");
|
put_format(&buf, "\n%% Weighting information:\n");
|
||||||
qty_weight = 0;
|
int qty_weight = 0;
|
||||||
total_weight = 0;
|
double total_weight = 0;
|
||||||
for (i = 0; i < dive->weightsystems.nr; i++) {
|
for (auto [i, w]: enumerated_range(dive->weightsystems)) {
|
||||||
weightsystem_t w = dive->weightsystems.weightsystems[i];
|
put_format(&buf, "\\def\\%sweight%ctype{%s}\n", ssrf, 'a' + i, w.description.c_str());
|
||||||
put_format(&buf, "\\def\\%sweight%ctype{%s}\n", ssrf, 'a' + i, w.description);
|
|
||||||
put_format(&buf, "\\def\\%sweight%camt{%.3f\\%sweightunit}\n", ssrf, 'a' + i, get_weight_units(w.weight.grams, NULL, &unit), ssrf);
|
put_format(&buf, "\\def\\%sweight%camt{%.3f\\%sweightunit}\n", ssrf, 'a' + i, get_weight_units(w.weight.grams, NULL, &unit), ssrf);
|
||||||
qty_weight += 1;
|
qty_weight += 1;
|
||||||
total_weight += get_weight_units(w.weight.grams, NULL, &unit);
|
total_weight += get_weight_units(w.weight.grams, NULL, &unit);
|
||||||
@ -251,7 +241,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
|
|||||||
// Legacy fields
|
// Legacy fields
|
||||||
put_format(&buf, "\\def\\%sspot{}\n", ssrf);
|
put_format(&buf, "\\def\\%sspot{}\n", ssrf);
|
||||||
put_format(&buf, "\\def\\%sentrance{}\n", ssrf);
|
put_format(&buf, "\\def\\%sentrance{}\n", ssrf);
|
||||||
put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name : "");
|
put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name.c_str() : "");
|
||||||
dive->maxdepth.mm ? put_format(&buf, "\\def\\%sdepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->maxdepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%sdepth{}\n", ssrf);
|
dive->maxdepth.mm ? put_format(&buf, "\\def\\%sdepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->maxdepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%sdepth{}\n", ssrf);
|
||||||
|
|
||||||
put_format(&buf, "\\%spage\n", ssrf);
|
put_format(&buf, "\\%spage\n", ssrf);
|
||||||
@ -275,26 +265,22 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
|
|||||||
void export_depths(const char *filename, bool selected_only)
|
void export_depths(const char *filename, bool selected_only)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
struct dive *dive;
|
|
||||||
depth_t depth;
|
|
||||||
int i;
|
|
||||||
const char *unit = NULL;
|
const char *unit = NULL;
|
||||||
|
|
||||||
struct membufferpp buf;
|
membuffer buf;
|
||||||
|
|
||||||
for_each_dive (i, dive) {
|
for (auto &dive: divelog.dives) {
|
||||||
if (selected_only && !dive->selected)
|
if (selected_only && !dive->selected)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
FOR_EACH_PICTURE (dive) {
|
for (auto &picture: dive->pictures) {
|
||||||
int n = dive->dc.samples;
|
depth_t depth;
|
||||||
struct sample *s = dive->dc.sample;
|
for (auto &s: dive->dcs[0].samples) {
|
||||||
depth.mm = 0;
|
if ((int32_t)s.time.seconds > picture.offset.seconds)
|
||||||
while (--n >= 0 && (int32_t)s->time.seconds <= picture->offset.seconds) {
|
break;
|
||||||
depth.mm = s->depth.mm;
|
depth = s.depth;
|
||||||
s++;
|
|
||||||
}
|
}
|
||||||
put_format(&buf, "%s\t%.1f", picture->filename, get_depth_units(depth.mm, NULL, &unit));
|
put_format(&buf, "%s\t%.1f", picture.filename.c_str(), get_depth_units(depth.mm, NULL, &unit));
|
||||||
put_format(&buf, "%s\n", unit);
|
put_format(&buf, "%s\n", unit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -318,28 +304,23 @@ std::vector<const dive_site *> getDiveSitesToExport(bool selectedOnly)
|
|||||||
if (selectedOnly && DiveFilter::instance()->diveSiteMode()) {
|
if (selectedOnly && DiveFilter::instance()->diveSiteMode()) {
|
||||||
// Special case in dive site mode: export all selected dive sites,
|
// Special case in dive site mode: export all selected dive sites,
|
||||||
// not the dive sites of selected dives.
|
// not the dive sites of selected dives.
|
||||||
QVector<dive_site *> sites = DiveFilter::instance()->filteredDiveSites();
|
for (auto ds: DiveFilter::instance()->filteredDiveSites())
|
||||||
res.reserve(sites.size());
|
|
||||||
for (const dive_site *ds: sites)
|
|
||||||
res.push_back(ds);
|
res.push_back(ds);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.reserve(divelog.sites->nr);
|
res.reserve(divelog.sites.size());
|
||||||
for (int i = 0; i < divelog.sites->nr; i++) {
|
for (const auto &ds: divelog.sites) {
|
||||||
struct dive_site *ds = get_dive_site(i, divelog.sites);
|
if (ds->is_empty())
|
||||||
if (dive_site_is_empty(ds))
|
|
||||||
continue;
|
continue;
|
||||||
if (selectedOnly && !is_dive_site_selected(ds))
|
if (selectedOnly && !ds->is_selected())
|
||||||
continue;
|
continue;
|
||||||
res.push_back(ds);
|
res.push_back(ds.get());
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/* walk the dive site list */
|
/* walk the dive site list */
|
||||||
int i;
|
for (const auto &ds: divelog.sites)
|
||||||
const struct dive_site *ds;
|
res.push_back(ds.get());
|
||||||
for_each_dive_site (i, ds, divelog.sites)
|
|
||||||
res.push_back(get_dive_site(i, divelog.sites));
|
|
||||||
#endif
|
#endif
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -134,9 +134,9 @@ void addDiveSite(const QString &name)
|
|||||||
execute(new AddDiveSite(name));
|
execute(new AddDiveSite(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
void importDiveSites(struct dive_site_table *sites, const QString &source)
|
void importDiveSites(dive_site_table sites, const QString &source)
|
||||||
{
|
{
|
||||||
execute(new ImportDiveSites(sites, source));
|
execute(new ImportDiveSites(std::move(sites), source));
|
||||||
}
|
}
|
||||||
|
|
||||||
void mergeDiveSites(dive_site *ds, const QVector<dive_site *> &sites)
|
void mergeDiveSites(dive_site *ds, const QVector<dive_site *> &sites)
|
||||||
@ -352,14 +352,14 @@ void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO
|
|||||||
execute(new AddEventSetpointChange(d, dcNr, seconds, pO2));
|
execute(new AddEventSetpointChange(d, dcNr, seconds, pO2));
|
||||||
}
|
}
|
||||||
|
|
||||||
void renameEvent(struct dive *d, int dcNr, struct event *ev, const char *name)
|
void renameEvent(struct dive *d, int dcNr, int idx, const std::string name)
|
||||||
{
|
{
|
||||||
execute(new RenameEvent(d, dcNr, ev, name));
|
execute(new RenameEvent(d, dcNr, idx, std::move(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeEvent(struct dive *d, int dcNr, struct event *ev)
|
void removeEvent(struct dive *d, int dcNr, int idx)
|
||||||
{
|
{
|
||||||
execute(new RemoveEvent(d, dcNr, ev));
|
execute(new RemoveEvent(d, dcNr, idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank)
|
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank)
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include "core/divelog.h"
|
#include "core/divelog.h"
|
||||||
#include "core/equipment.h"
|
#include "core/equipment.h"
|
||||||
#include "core/pictureobj.h"
|
#include "core/picture.h"
|
||||||
#include "core/taxonomy.h"
|
#include "core/taxonomy.h"
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
@ -68,7 +68,7 @@ void editDiveSiteCountry(dive_site *ds, const QString &value);
|
|||||||
void editDiveSiteLocation(dive_site *ds, location_t value);
|
void editDiveSiteLocation(dive_site *ds, location_t value);
|
||||||
void editDiveSiteTaxonomy(dive_site *ds, taxonomy_data &value); // value is consumed (i.e. will be erased after call)!
|
void editDiveSiteTaxonomy(dive_site *ds, taxonomy_data &value); // value is consumed (i.e. will be erased after call)!
|
||||||
void addDiveSite(const QString &name);
|
void addDiveSite(const QString &name);
|
||||||
void importDiveSites(struct dive_site_table *sites, const QString &source);
|
void importDiveSites(dive_site_table sites, const QString &source); // takes ownership of dive site table
|
||||||
void mergeDiveSites(dive_site *ds, const QVector<dive_site *> &sites);
|
void mergeDiveSites(dive_site *ds, const QVector<dive_site *> &sites);
|
||||||
void purgeUnusedDiveSites();
|
void purgeUnusedDiveSites();
|
||||||
|
|
||||||
@ -132,8 +132,8 @@ void editTripNotes(dive_trip *trip, const QString &s);
|
|||||||
void addEventBookmark(struct dive *d, int dcNr, int seconds);
|
void addEventBookmark(struct dive *d, int dcNr, int seconds);
|
||||||
void addEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode);
|
void addEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode);
|
||||||
void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2);
|
void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2);
|
||||||
void renameEvent(struct dive *d, int dcNr, struct event *ev, const char *name);
|
void renameEvent(struct dive *d, int dcNr, int idx, std::string name);
|
||||||
void removeEvent(struct dive *d, int dcNr, struct event *ev);
|
void removeEvent(struct dive *d, int dcNr, int idx);
|
||||||
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank);
|
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank);
|
||||||
|
|
||||||
// 7) Picture (media) commands
|
// 7) Picture (media) commands
|
||||||
@ -144,7 +144,7 @@ struct PictureListForDeletion {
|
|||||||
};
|
};
|
||||||
struct PictureListForAddition {
|
struct PictureListForAddition {
|
||||||
dive *d;
|
dive *d;
|
||||||
std::vector<PictureObj> pics;
|
std::vector<picture> pics;
|
||||||
};
|
};
|
||||||
void setPictureOffset(dive *d, const QString &filename, offset_t offset);
|
void setPictureOffset(dive *d, const QString &filename, offset_t offset);
|
||||||
void removePictures(const std::vector<PictureListForDeletion> &pictures);
|
void removePictures(const std::vector<PictureListForDeletion> &pictures);
|
||||||
|
|||||||
@ -66,7 +66,7 @@ QString diveNumberOrDate(struct dive *d)
|
|||||||
QString getListOfDives(const std::vector<struct dive*> &dives)
|
QString getListOfDives(const std::vector<struct dive*> &dives)
|
||||||
{
|
{
|
||||||
QString listOfDives;
|
QString listOfDives;
|
||||||
if ((int)dives.size() == divelog.dives->nr)
|
if (dives.size() == divelog.dives.size())
|
||||||
return Base::tr("all dives");
|
return Base::tr("all dives");
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (dive *d: dives) {
|
for (dive *d: dives) {
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
#include "core/divesite.h"
|
#include "core/divesite.h"
|
||||||
#include "core/trip.h"
|
#include "core/trip.h"
|
||||||
#include "core/dive.h"
|
#include "core/dive.h"
|
||||||
#include "core/owning_ptrs.h"
|
|
||||||
|
|
||||||
#include <QUndoCommand>
|
#include <QUndoCommand>
|
||||||
#include <QCoreApplication> // For Q_DECLARE_TR_FUNCTIONS
|
#include <QCoreApplication> // For Q_DECLARE_TR_FUNCTIONS
|
||||||
@ -106,9 +105,8 @@
|
|||||||
// 1) Dive 2 was deleted with the "add dive 2" command, because that was the owner.
|
// 1) Dive 2 was deleted with the "add dive 2" command, because that was the owner.
|
||||||
// 2) Dive 1 was not deleted, because it is owned by the backend.
|
// 2) Dive 1 was not deleted, because it is owned by the backend.
|
||||||
//
|
//
|
||||||
// To take ownership of dives/trips, the OnwingDivePtr and OwningTripPtr types are used. These
|
// To take ownership of dives/trips, std::unique_ptr<>s are used.
|
||||||
// are simply derived from std::unique_ptr and therefore use well-established semantics.
|
// Expressed in C-terms: std::unique_ptr<T> is the same as T* with the following
|
||||||
// Expressed in C-terms: std::unique_ptr<T> is exactly the same as T* with the following
|
|
||||||
// twists:
|
// twists:
|
||||||
// 1) default-initialized to NULL.
|
// 1) default-initialized to NULL.
|
||||||
// 2) if it goes out of scope (local scope or containing object destroyed), it does:
|
// 2) if it goes out of scope (local scope or containing object destroyed), it does:
|
||||||
@ -122,8 +120,8 @@
|
|||||||
// move-semantics and Qt's containers are incompatible, owing to COW semantics.
|
// move-semantics and Qt's containers are incompatible, owing to COW semantics.
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// OwningDivePtr dPtr; // Initialize to null-state: not owning any dive.
|
// std::unique_ptr<dive> dPtr; // Initialize to null-state: not owning any dive.
|
||||||
// OwningDivePtr dPtr(dive); // Take ownership of dive (which is of type struct dive *).
|
// std::unique_ptr<dive> dPtr(dive); // Take ownership of dive (which is of type struct dive *).
|
||||||
// // If dPtr goes out of scope, the dive will be freed with free_dive().
|
// // If dPtr goes out of scope, the dive will be freed with free_dive().
|
||||||
// struct dive *d = dPtr.release(); // Give up ownership of dive. dPtr is reset to null.
|
// struct dive *d = dPtr.release(); // Give up ownership of dive. dPtr is reset to null.
|
||||||
// struct dive *d = d.get(); // Get pointer dive, but don't release ownership.
|
// struct dive *d = d.get(); // Get pointer dive, but don't release ownership.
|
||||||
@ -131,10 +129,10 @@
|
|||||||
// dPtr.reset(); // Delete currently owned dive and reset to null.
|
// dPtr.reset(); // Delete currently owned dive and reset to null.
|
||||||
// dPtr2 = dPtr1; // Fails to compile.
|
// dPtr2 = dPtr1; // Fails to compile.
|
||||||
// dPtr2 = std::move(dPtr1); // dPtr2 takes ownership, dPtr1 is reset to null.
|
// dPtr2 = std::move(dPtr1); // dPtr2 takes ownership, dPtr1 is reset to null.
|
||||||
// OwningDivePtr fun();
|
// std::unique_ptr<dive> fun();
|
||||||
// dPtr1 = fun(); // Compiles. Simply put: the compiler knows that the result of fun() will
|
// dPtr1 = fun(); // Compiles. Simply put: the compiler knows that the result of fun() will
|
||||||
// // be trashed and therefore can be moved-from.
|
// // be trashed and therefore can be moved-from.
|
||||||
// std::vector<OwningDivePtr> v: // Define an empty vector of owning pointers.
|
// std::vector<std::unique_ptr<dive>> v: // Define an empty vector of owning pointers.
|
||||||
// v.emplace_back(dive); // Take ownership of dive and add at end of vector
|
// v.emplace_back(dive); // Take ownership of dive and add at end of vector
|
||||||
// // If the vector goes out of scope, all dives will be freed with free_dive().
|
// // If the vector goes out of scope, all dives will be freed with free_dive().
|
||||||
// v.clear(v); // Reset the vector to zero length. If the elements weren't release()d,
|
// v.clear(v); // Reset the vector to zero length. If the elements weren't release()d,
|
||||||
|
|||||||
@ -13,20 +13,19 @@ EditDeviceNickname::EditDeviceNickname(const struct divecomputer *dc, const QStr
|
|||||||
if (index == -1)
|
if (index == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
setText(Command::Base::tr("Set nickname of device %1 (serial %2) to %3").arg(dc->model, dc->serial, nicknameIn));
|
setText(Command::Base::tr("Set nickname of device %1 (serial %2) to %3").arg(dc->model.c_str(), dc->serial.c_str(), nicknameIn));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditDeviceNickname::workToBeDone()
|
bool EditDeviceNickname::workToBeDone()
|
||||||
{
|
{
|
||||||
return get_device(divelog.devices, index) != nullptr;
|
return index >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDeviceNickname::redo()
|
void EditDeviceNickname::redo()
|
||||||
{
|
{
|
||||||
device *dev = get_device_mutable(divelog.devices, index);
|
if (index < 0 || static_cast<size_t>(index) >= divelog.devices.size())
|
||||||
if (!dev)
|
|
||||||
return;
|
return;
|
||||||
std::swap(dev->nickName, nickname);
|
std::swap(divelog.devices[index].nickName, nickname);
|
||||||
emit diveListNotifier.deviceEdited();
|
emit diveListNotifier.deviceEdited();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,23 +9,22 @@
|
|||||||
#include "qt-models/filtermodels.h"
|
#include "qt-models/filtermodels.h"
|
||||||
#include "core/divefilter.h"
|
#include "core/divefilter.h"
|
||||||
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
namespace Command {
|
namespace Command {
|
||||||
|
|
||||||
// Helper function that takes care to unselect trips that are removed from the backend
|
// Helper function that takes care to unselect trips that are removed from the backend
|
||||||
static void remove_trip_from_backend(dive_trip *trip)
|
static std::unique_ptr<dive_trip> remove_trip_from_backend(dive_trip *trip)
|
||||||
{
|
{
|
||||||
if (trip->selected)
|
if (trip->selected)
|
||||||
deselect_trip(trip);
|
deselect_trip(trip);
|
||||||
remove_trip(trip, divelog.trips); // Remove trip from backend
|
auto [t, idx] = divelog.trips.pull(trip);
|
||||||
|
return std::move(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This helper function removes a dive, takes ownership of the dive and adds it to a DiveToAdd structure.
|
// This helper function removes a dive, takes ownership of the dive and adds it to a DiveToAdd structure.
|
||||||
// If the trip the dive belongs to becomes empty, it is removed and added to the tripsToAdd vector.
|
// If the trip the dive belongs to becomes empty, it is removed and added to the tripsToAdd vector.
|
||||||
// It is crucial that dives are added in reverse order of deletion, so that the indices are correctly
|
// It is crucial that dives are added in reverse order of deletion, so that the indices are correctly
|
||||||
// set and that the trips are added before they are used!
|
// set and that the trips are added before they are used!
|
||||||
DiveToAdd DiveListBase::removeDive(struct dive *d, std::vector<OwningTripPtr> &tripsToAdd)
|
DiveToAdd DiveListBase::removeDive(struct dive *d, std::vector<std::unique_ptr<dive_trip>> &tripsToAdd)
|
||||||
{
|
{
|
||||||
// If the dive was the current dive, reset the current dive. The calling
|
// If the dive was the current dive, reset the current dive. The calling
|
||||||
// command is responsible of finding a new dive.
|
// command is responsible of finding a new dive.
|
||||||
@ -39,18 +38,19 @@ DiveToAdd DiveListBase::removeDive(struct dive *d, std::vector<OwningTripPtr> &t
|
|||||||
if (d->dive_site)
|
if (d->dive_site)
|
||||||
diveSiteCountChanged(d->dive_site);
|
diveSiteCountChanged(d->dive_site);
|
||||||
res.site = unregister_dive_from_dive_site(d);
|
res.site = unregister_dive_from_dive_site(d);
|
||||||
if (res.trip && res.trip->dives.nr == 0) {
|
if (res.trip && res.trip->dives.empty()) {
|
||||||
remove_trip_from_backend(res.trip); // Remove trip from backend
|
divelog.trips.sort(); // Removal of dives has changed order of trips! (TODO: remove this)
|
||||||
tripsToAdd.emplace_back(res.trip); // Take ownership of trip
|
auto trip = remove_trip_from_backend(res.trip); // Remove trip from backend
|
||||||
|
tripsToAdd.push_back(std::move(trip)); // Take ownership of trip
|
||||||
}
|
}
|
||||||
|
|
||||||
int idx = get_divenr(d);
|
size_t idx = divelog.dives.get_idx(d);
|
||||||
if (idx < 0)
|
if (idx == std::string::npos)
|
||||||
qWarning("Deletion of unknown dive!");
|
qWarning("Deletion of unknown dive!");
|
||||||
|
|
||||||
DiveFilter::instance()->diveRemoved(d);
|
DiveFilter::instance()->diveRemoved(d);
|
||||||
|
|
||||||
res.dive.reset(unregister_dive(idx)); // Remove dive from backend
|
res.dive = std::move(divelog.dives.unregister_dive(idx)); // Remove dive from backend
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -67,23 +67,12 @@ void DiveListBase::diveSiteCountChanged(struct dive_site *ds)
|
|||||||
dive *DiveListBase::addDive(DiveToAdd &d)
|
dive *DiveListBase::addDive(DiveToAdd &d)
|
||||||
{
|
{
|
||||||
if (d.trip)
|
if (d.trip)
|
||||||
add_dive_to_trip(d.dive.get(), d.trip);
|
d.trip->add_dive(d.dive.get());
|
||||||
if (d.site) {
|
if (d.site) {
|
||||||
add_dive_to_dive_site(d.dive.get(), d.site);
|
d.site->add_dive(d.dive.get());
|
||||||
diveSiteCountChanged(d.site);
|
diveSiteCountChanged(d.site);
|
||||||
}
|
}
|
||||||
dive *res = d.dive.release(); // Give up ownership of dive
|
return register_dive(std::move(d.dive)); // Transfer ownership to core and update fulltext index
|
||||||
|
|
||||||
// When we add dives, we start in hidden-by-filter status. Once all
|
|
||||||
// dives have been added, their status will be updated.
|
|
||||||
res->hidden_by_filter = true;
|
|
||||||
|
|
||||||
int idx = dive_table_get_insertion_index(divelog.dives, res);
|
|
||||||
fulltext_register(res); // Register the dive's fulltext cache
|
|
||||||
add_to_dive_table(divelog.dives, idx, res); // Return ownership to backend
|
|
||||||
invalidate_dive_cache(res); // Ensure that dive is written in git_save()
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some signals are sent in batches per trip. To avoid writing the same loop
|
// Some signals are sent in batches per trip. To avoid writing the same loop
|
||||||
@ -99,7 +88,7 @@ void processByTrip(std::vector<std::pair<dive_trip *, dive *>> &dives, Function
|
|||||||
// Sort lexicographically by trip then according to the dive_less_than() function.
|
// Sort lexicographically by trip then according to the dive_less_than() function.
|
||||||
std::sort(dives.begin(), dives.end(),
|
std::sort(dives.begin(), dives.end(),
|
||||||
[](const std::pair<dive_trip *, dive *> &e1, const std::pair<dive_trip *, dive *> &e2)
|
[](const std::pair<dive_trip *, dive *> &e1, const std::pair<dive_trip *, dive *> &e2)
|
||||||
{ return e1.first == e2.first ? dive_less_than(e1.second, e2.second) : e1.first < e2.first; });
|
{ return e1.first == e2.first ? dive_less_than(*e1.second, *e2.second) : e1.first < e2.first; });
|
||||||
|
|
||||||
// Then, process the dives in batches by trip
|
// Then, process the dives in batches by trip
|
||||||
size_t i, j; // Begin and end of batch
|
size_t i, j; // Begin and end of batch
|
||||||
@ -124,8 +113,8 @@ void processByTrip(std::vector<std::pair<dive_trip *, dive *>> &dives, Function
|
|||||||
DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSitesToDelete)
|
DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSitesToDelete)
|
||||||
{
|
{
|
||||||
std::vector<DiveToAdd> divesToAdd;
|
std::vector<DiveToAdd> divesToAdd;
|
||||||
std::vector<OwningTripPtr> tripsToAdd;
|
std::vector<std::unique_ptr<dive_trip>> tripsToAdd;
|
||||||
std::vector<OwningDiveSitePtr> sitesToAdd;
|
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
|
||||||
divesToAdd.reserve(divesAndSitesToDelete.dives.size());
|
divesToAdd.reserve(divesAndSitesToDelete.dives.size());
|
||||||
sitesToAdd.reserve(divesAndSitesToDelete.sites.size());
|
sitesToAdd.reserve(divesAndSitesToDelete.sites.size());
|
||||||
|
|
||||||
@ -135,16 +124,17 @@ DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSite
|
|||||||
// Make sure that the dive list is sorted. The added dives will be sent in a signal
|
// Make sure that the dive list is sorted. The added dives will be sent in a signal
|
||||||
// and the recipients assume that the dives are sorted the same way as they are
|
// and the recipients assume that the dives are sorted the same way as they are
|
||||||
// in the core list.
|
// in the core list.
|
||||||
std::sort(divesAndSitesToDelete.dives.begin(), divesAndSitesToDelete.dives.end(), dive_less_than);
|
std::sort(divesAndSitesToDelete.dives.begin(), divesAndSitesToDelete.dives.end(),
|
||||||
|
[](const dive *d1, const dive *d2) { return dive_less_than(*d1, *d2); });
|
||||||
|
|
||||||
for (dive *d: divesAndSitesToDelete.dives)
|
for (dive *d: divesAndSitesToDelete.dives)
|
||||||
divesToAdd.push_back(removeDive(d, tripsToAdd));
|
divesToAdd.push_back(removeDive(d, tripsToAdd));
|
||||||
divesAndSitesToDelete.dives.clear();
|
divesAndSitesToDelete.dives.clear();
|
||||||
|
|
||||||
for (dive_site *ds: divesAndSitesToDelete.sites) {
|
for (dive_site *ds: divesAndSitesToDelete.sites) {
|
||||||
int idx = unregister_dive_site(ds);
|
auto res = divelog.sites.pull(ds);
|
||||||
sitesToAdd.emplace_back(ds);
|
sitesToAdd.push_back(std::move(res.ptr));
|
||||||
emit diveListNotifier.diveSiteDeleted(ds, idx);
|
emit diveListNotifier.diveSiteDeleted(ds, res.idx);
|
||||||
}
|
}
|
||||||
divesAndSitesToDelete.sites.clear();
|
divesAndSitesToDelete.sites.clear();
|
||||||
|
|
||||||
@ -159,7 +149,7 @@ DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSite
|
|||||||
processByTrip(dives, [&](dive_trip *trip, const QVector<dive *> &divesInTrip) {
|
processByTrip(dives, [&](dive_trip *trip, const QVector<dive *> &divesInTrip) {
|
||||||
// Check if this trip is supposed to be deleted, by checking if it was marked as "add it".
|
// Check if this trip is supposed to be deleted, by checking if it was marked as "add it".
|
||||||
bool deleteTrip = trip &&
|
bool deleteTrip = trip &&
|
||||||
std::find_if(tripsToAdd.begin(), tripsToAdd.end(), [trip](const OwningTripPtr &ptr)
|
std::find_if(tripsToAdd.begin(), tripsToAdd.end(), [trip](const std::unique_ptr<dive_trip> &ptr)
|
||||||
{ return ptr.get() == trip; }) != tripsToAdd.end();
|
{ return ptr.get() == trip; }) != tripsToAdd.end();
|
||||||
emit diveListNotifier.divesDeleted(trip, deleteTrip, divesInTrip);
|
emit diveListNotifier.divesDeleted(trip, deleteTrip, divesInTrip);
|
||||||
});
|
});
|
||||||
@ -188,7 +178,7 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd)
|
|||||||
// in the core list.
|
// in the core list.
|
||||||
std::sort(toAdd.dives.begin(), toAdd.dives.end(),
|
std::sort(toAdd.dives.begin(), toAdd.dives.end(),
|
||||||
[](const DiveToAdd &d, const DiveToAdd &d2)
|
[](const DiveToAdd &d, const DiveToAdd &d2)
|
||||||
{ return dive_less_than(d.dive.get(), d2.dive.get()); });
|
{ return dive_less_than(*d.dive, *d2.dive); });
|
||||||
|
|
||||||
// Now, add the dives
|
// Now, add the dives
|
||||||
// Note: the idiomatic STL-way would be std::transform, but let's use a loop since
|
// Note: the idiomatic STL-way would be std::transform, but let's use a loop since
|
||||||
@ -209,17 +199,17 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd)
|
|||||||
// Remember the pointers so that we can later check if a trip was newly added
|
// Remember the pointers so that we can later check if a trip was newly added
|
||||||
std::vector<dive_trip *> addedTrips;
|
std::vector<dive_trip *> addedTrips;
|
||||||
addedTrips.reserve(toAdd.trips.size());
|
addedTrips.reserve(toAdd.trips.size());
|
||||||
for (OwningTripPtr &trip: toAdd.trips) {
|
for (std::unique_ptr<dive_trip> &trip: toAdd.trips) {
|
||||||
addedTrips.push_back(trip.get());
|
auto [t, idx] = divelog.trips.put(std::move(trip)); // Return ownership to backend
|
||||||
insert_trip(trip.release(), divelog.trips); // Return ownership to backend
|
addedTrips.push_back(t);
|
||||||
}
|
}
|
||||||
toAdd.trips.clear();
|
toAdd.trips.clear();
|
||||||
|
|
||||||
// Finally, add any necessary dive sites
|
// Finally, add any necessary dive sites
|
||||||
for (OwningDiveSitePtr &ds: toAdd.sites) {
|
for (std::unique_ptr<dive_site> &ds: toAdd.sites) {
|
||||||
sites.push_back(ds.get());
|
auto res = divelog.sites.register_site(std::move(ds));
|
||||||
int idx = register_dive_site(ds.release()); // Return ownership to backend
|
sites.push_back(res.ptr);
|
||||||
emit diveListNotifier.diveSiteAdded(sites.back(), idx);
|
emit diveListNotifier.diveSiteAdded(sites.back(), res.idx);
|
||||||
}
|
}
|
||||||
toAdd.sites.clear();
|
toAdd.sites.clear();
|
||||||
|
|
||||||
@ -260,26 +250,24 @@ static void renumberDives(QVector<QPair<dive *, int>> &divesToRenumber)
|
|||||||
// passed-in structure. This means that calling the function twice on the same
|
// passed-in structure. This means that calling the function twice on the same
|
||||||
// object is a no-op concerning the dive. If the old trip was deleted from the
|
// object is a no-op concerning the dive. If the old trip was deleted from the
|
||||||
// core, an owning pointer to the removed trip is returned, otherwise a null pointer.
|
// core, an owning pointer to the removed trip is returned, otherwise a null pointer.
|
||||||
static OwningTripPtr moveDiveToTrip(DiveToTrip &diveToTrip)
|
static std::unique_ptr<dive_trip> moveDiveToTrip(DiveToTrip &diveToTrip)
|
||||||
{
|
{
|
||||||
// Firstly, check if we move to the same trip and bail if this is a no-op.
|
// Firstly, check if we move to the same trip and bail if this is a no-op.
|
||||||
if (diveToTrip.trip == diveToTrip.dive->divetrip)
|
if (diveToTrip.trip == diveToTrip.dive->divetrip)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
// Remove from old trip
|
// Remove from old trip
|
||||||
OwningTripPtr res;
|
std::unique_ptr<dive_trip> res;
|
||||||
|
|
||||||
// Remove dive from trip - if this is the last dive in the trip, remove the whole trip.
|
// Remove dive from trip - if this is the last dive in the trip, remove the whole trip.
|
||||||
dive_trip *trip = unregister_dive_from_trip(diveToTrip.dive);
|
dive_trip *trip = unregister_dive_from_trip(diveToTrip.dive);
|
||||||
if (trip && trip->dives.nr == 0) {
|
if (trip && trip->dives.empty())
|
||||||
remove_trip_from_backend(trip); // Remove trip from backend
|
res = remove_trip_from_backend(trip); // Remove trip from backend
|
||||||
res.reset(trip);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store old trip and get new trip we should associate this dive with
|
// Store old trip and get new trip we should associate this dive with
|
||||||
std::swap(trip, diveToTrip.trip);
|
std::swap(trip, diveToTrip.trip);
|
||||||
if (trip)
|
if (trip)
|
||||||
add_dive_to_trip(diveToTrip.dive, trip);
|
trip->add_dive(diveToTrip.dive);
|
||||||
invalidate_dive_cache(diveToTrip.dive); // Ensure that dive is written in git_save()
|
invalidate_dive_cache(diveToTrip.dive); // Ensure that dive is written in git_save()
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -298,15 +286,14 @@ static void moveDivesBetweenTrips(DivesToTrip &dives)
|
|||||||
createdTrips.reserve(dives.tripsToAdd.size());
|
createdTrips.reserve(dives.tripsToAdd.size());
|
||||||
|
|
||||||
// First, bring back the trip(s)
|
// First, bring back the trip(s)
|
||||||
for (OwningTripPtr &trip: dives.tripsToAdd) {
|
for (std::unique_ptr<dive_trip> &trip: dives.tripsToAdd) {
|
||||||
dive_trip *t = trip.release(); // Give up ownership
|
auto [t, idx] = divelog.trips.put(std::move(trip)); // Return ownership to backend
|
||||||
createdTrips.push_back(t);
|
createdTrips.push_back(t);
|
||||||
insert_trip(t, divelog.trips); // Return ownership to backend
|
|
||||||
}
|
}
|
||||||
dives.tripsToAdd.clear();
|
dives.tripsToAdd.clear();
|
||||||
|
|
||||||
for (DiveToTrip &dive: dives.divesToMove) {
|
for (DiveToTrip &dive: dives.divesToMove) {
|
||||||
OwningTripPtr tripToAdd = moveDiveToTrip(dive);
|
std::unique_ptr<dive_trip> tripToAdd = moveDiveToTrip(dive);
|
||||||
// register trips that we'll have to readd
|
// register trips that we'll have to readd
|
||||||
if (tripToAdd)
|
if (tripToAdd)
|
||||||
dives.tripsToAdd.push_back(std::move(tripToAdd));
|
dives.tripsToAdd.push_back(std::move(tripToAdd));
|
||||||
@ -350,7 +337,7 @@ static void moveDivesBetweenTrips(DivesToTrip &dives)
|
|||||||
std::find_if(divesMoved.begin() + j, divesMoved.end(), // Is this the last occurence of "from"?
|
std::find_if(divesMoved.begin() + j, divesMoved.end(), // Is this the last occurence of "from"?
|
||||||
[from](const DiveMoved &entry) { return entry.from == from; }) == divesMoved.end() &&
|
[from](const DiveMoved &entry) { return entry.from == from; }) == divesMoved.end() &&
|
||||||
std::find_if(dives.tripsToAdd.begin(), dives.tripsToAdd.end(), // Is "from" in tripsToAdd?
|
std::find_if(dives.tripsToAdd.begin(), dives.tripsToAdd.end(), // Is "from" in tripsToAdd?
|
||||||
[from](const OwningTripPtr &trip) { return trip.get() == from; }) != dives.tripsToAdd.end();
|
[from](const std::unique_ptr<dive_trip> &trip) { return trip.get() == from; }) != dives.tripsToAdd.end();
|
||||||
// Check if the to-trip has to be created. For this purpose, we saved an array of trips to be created.
|
// Check if the to-trip has to be created. For this purpose, we saved an array of trips to be created.
|
||||||
bool createTo = false;
|
bool createTo = false;
|
||||||
if (to) {
|
if (to) {
|
||||||
@ -404,20 +391,20 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber)
|
|||||||
setText(Command::Base::tr("add dive"));
|
setText(Command::Base::tr("add dive"));
|
||||||
// By convention, d is a pointer to "displayed dive" or a temporary variable and can be overwritten.
|
// By convention, d is a pointer to "displayed dive" or a temporary variable and can be overwritten.
|
||||||
d->maxdepth.mm = 0;
|
d->maxdepth.mm = 0;
|
||||||
d->dc.maxdepth.mm = 0;
|
d->dcs[0].maxdepth.mm = 0;
|
||||||
fixup_dive(d);
|
fixup_dive(d);
|
||||||
|
|
||||||
// this only matters if undoit were called before redoit
|
// this only matters if undoit were called before redoit
|
||||||
currentDive = nullptr;
|
currentDive = nullptr;
|
||||||
|
|
||||||
// Get an owning pointer to a moved dive.
|
// Get an owning pointer to a moved dive.
|
||||||
OwningDivePtr divePtr(move_dive(d));
|
std::unique_ptr<dive> divePtr = move_dive(d);
|
||||||
divePtr->selected = false; // If we clone a planned dive, it might have been selected.
|
divePtr->selected = false; // If we clone a planned dive, it might have been selected.
|
||||||
// We have to clear the flag, as selections will be managed
|
// We have to clear the flag, as selections will be managed
|
||||||
// on dive-addition.
|
// on dive-addition.
|
||||||
|
|
||||||
// If we alloc a new-trip for autogrouping, get an owning pointer to it.
|
// If we alloc a new-trip for autogrouping, get an owning pointer to it.
|
||||||
OwningTripPtr allocTrip;
|
std::unique_ptr<dive_trip> allocTrip;
|
||||||
dive_trip *trip = divePtr->divetrip;
|
dive_trip *trip = divePtr->divetrip;
|
||||||
dive_site *site = divePtr->dive_site;
|
dive_site *site = divePtr->dive_site;
|
||||||
// We have to delete the pointers to trip and site, because this would prevent the core from adding to the
|
// We have to delete the pointers to trip and site, because this would prevent the core from adding to the
|
||||||
@ -425,13 +412,12 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber)
|
|||||||
divePtr->divetrip = nullptr;
|
divePtr->divetrip = nullptr;
|
||||||
divePtr->dive_site = nullptr;
|
divePtr->dive_site = nullptr;
|
||||||
if (!trip && autogroup) {
|
if (!trip && autogroup) {
|
||||||
bool alloc;
|
auto [t, allocated] = get_trip_for_new_dive(divelog, divePtr.get());
|
||||||
trip = get_trip_for_new_dive(divePtr.get(), &alloc);
|
trip = t;
|
||||||
if (alloc)
|
allocTrip = std::move(allocated);
|
||||||
allocTrip.reset(trip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int idx = dive_table_get_insertion_index(divelog.dives, divePtr.get());
|
int idx = divelog.dives.get_insertion_index(divePtr.get());
|
||||||
if (newNumber)
|
if (newNumber)
|
||||||
divePtr->number = get_dive_nr_at_idx(idx);
|
divePtr->number = get_dive_nr_at_idx(idx);
|
||||||
|
|
||||||
@ -452,7 +438,7 @@ void AddDive::redoit()
|
|||||||
currentDive = current_dive;
|
currentDive = current_dive;
|
||||||
|
|
||||||
divesAndSitesToRemove = addDives(divesToAdd);
|
divesAndSitesToRemove = addDives(divesToAdd);
|
||||||
sort_trip_table(divelog.trips); // Though unlikely, adding a dive may reorder trips
|
divelog.trips.sort(); // Though unlikely, adding a dive may reorder trips
|
||||||
|
|
||||||
// Select the newly added dive
|
// Select the newly added dive
|
||||||
setSelection(divesAndSitesToRemove.dives, divesAndSitesToRemove.dives[0], -1);
|
setSelection(divesAndSitesToRemove.dives, divesAndSitesToRemove.dives[0], -1);
|
||||||
@ -462,7 +448,7 @@ void AddDive::undoit()
|
|||||||
{
|
{
|
||||||
// Simply remove the dive that was previously added...
|
// Simply remove the dive that was previously added...
|
||||||
divesToAdd = removeDives(divesAndSitesToRemove);
|
divesToAdd = removeDives(divesAndSitesToRemove);
|
||||||
sort_trip_table(divelog.trips); // Though unlikely, removing a dive may reorder trips
|
divelog.trips.sort(); // Though unlikely, removing a dive may reorder trips
|
||||||
|
|
||||||
// ...and restore the selection
|
// ...and restore the selection
|
||||||
setSelection(selection, currentDive, -1);
|
setSelection(selection, currentDive, -1);
|
||||||
@ -470,33 +456,28 @@ void AddDive::undoit()
|
|||||||
|
|
||||||
ImportDives::ImportDives(struct divelog *log, int flags, const QString &source)
|
ImportDives::ImportDives(struct divelog *log, int flags, const QString &source)
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("import %n dive(s) from %1", "", log->dives->nr).arg(source));
|
setText(Command::Base::tr("import %n dive(s) from %1", "", log->dives.size()).arg(source));
|
||||||
|
|
||||||
// this only matters if undoit were called before redoit
|
// this only matters if undoit were called before redoit
|
||||||
currentDive = nullptr;
|
currentDive = nullptr;
|
||||||
|
|
||||||
struct dive_table dives_to_add = empty_dive_table;
|
auto [dives_to_add, dives_to_remove, trips_to_add, sites_to_add, devices_to_add] =
|
||||||
struct dive_table dives_to_remove = empty_dive_table;
|
process_imported_dives(*log, flags);
|
||||||
struct trip_table trips_to_add = empty_trip_table;
|
|
||||||
struct dive_site_table sites_to_add = empty_dive_site_table;
|
// Add devices to devicesToAddAndRemove structure
|
||||||
process_imported_dives(log, flags,
|
devicesToAddAndRemove = std::move(devices_to_add);
|
||||||
&dives_to_add, &dives_to_remove, &trips_to_add,
|
|
||||||
&sites_to_add, &devicesToAddAndRemove);
|
|
||||||
|
|
||||||
// Add trips to the divesToAdd.trips structure
|
// Add trips to the divesToAdd.trips structure
|
||||||
divesToAdd.trips.reserve(trips_to_add.nr);
|
divesToAdd.trips.reserve(trips_to_add.size());
|
||||||
for (int i = 0; i < trips_to_add.nr; ++i)
|
for (auto &trip: trips_to_add)
|
||||||
divesToAdd.trips.emplace_back(trips_to_add.trips[i]);
|
divesToAdd.trips.push_back(std::move(trip));
|
||||||
|
|
||||||
// Add sites to the divesToAdd.sites structure
|
// Add sites to the divesToAdd.sites structure
|
||||||
divesToAdd.sites.reserve(sites_to_add.nr);
|
divesToAdd.sites = std::move(sites_to_add);
|
||||||
for (int i = 0; i < sites_to_add.nr; ++i)
|
|
||||||
divesToAdd.sites.emplace_back(sites_to_add.dive_sites[i]);
|
|
||||||
|
|
||||||
// Add dives to the divesToAdd.dives structure
|
// Add dives to the divesToAdd.dives structure
|
||||||
divesToAdd.dives.reserve(dives_to_add.nr);
|
divesToAdd.dives.reserve(dives_to_add.size());
|
||||||
for (int i = 0; i < dives_to_add.nr; ++i) {
|
for (auto &divePtr: dives_to_add) {
|
||||||
OwningDivePtr divePtr(dives_to_add.dives[i]);
|
|
||||||
divePtr->selected = false; // See above in AddDive::AddDive()
|
divePtr->selected = false; // See above in AddDive::AddDive()
|
||||||
dive_trip *trip = divePtr->divetrip;
|
dive_trip *trip = divePtr->divetrip;
|
||||||
divePtr->divetrip = nullptr; // See above in AddDive::AddDive()
|
divePtr->divetrip = nullptr; // See above in AddDive::AddDive()
|
||||||
@ -507,25 +488,18 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add dive to be deleted to the divesToRemove structure
|
// Add dive to be deleted to the divesToRemove structure
|
||||||
divesAndSitesToRemove.dives.reserve(dives_to_remove.nr);
|
divesAndSitesToRemove.dives = std::move(dives_to_remove);
|
||||||
for (int i = 0; i < dives_to_remove.nr; ++i)
|
|
||||||
divesAndSitesToRemove.dives.push_back(dives_to_remove.dives[i]);
|
|
||||||
|
|
||||||
// When encountering filter presets with equal names, check whether they are
|
// When encountering filter presets with equal names, check whether they are
|
||||||
// the same. If they are, ignore them.
|
// the same. If they are, ignore them.
|
||||||
for (const filter_preset &preset: *log->filter_presets) {
|
for (const filter_preset &preset: log->filter_presets) {
|
||||||
std::string name = preset.name;
|
std::string name = preset.name;
|
||||||
auto it = std::find_if(divelog.filter_presets->begin(), divelog.filter_presets->end(),
|
auto it = std::find_if(divelog.filter_presets.begin(), divelog.filter_presets.end(),
|
||||||
[&name](const filter_preset &preset) { return preset.name == name; });
|
[&name](const filter_preset &preset) { return preset.name == name; });
|
||||||
if (it != divelog.filter_presets->end() && it->data == preset.data)
|
if (it != divelog.filter_presets.end() && it->data == preset.data)
|
||||||
continue;
|
continue;
|
||||||
filterPresetsToAdd.emplace_back(preset.name, preset.data);
|
filterPresetsToAdd.emplace_back(preset.name, preset.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(dives_to_add.dives);
|
|
||||||
free(dives_to_remove.dives);
|
|
||||||
free(trips_to_add.trips);
|
|
||||||
free(sites_to_add.dive_sites);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImportDives::workToBeDone()
|
bool ImportDives::workToBeDone()
|
||||||
@ -551,12 +525,12 @@ void ImportDives::redoit()
|
|||||||
divesAndSitesToRemove = std::move(divesAndSitesToRemoveNew);
|
divesAndSitesToRemove = std::move(divesAndSitesToRemoveNew);
|
||||||
|
|
||||||
// Add devices
|
// Add devices
|
||||||
for (const device &dev: devicesToAddAndRemove.devices)
|
for (const device &dev: devicesToAddAndRemove)
|
||||||
add_to_device_table(divelog.devices, &dev);
|
add_to_device_table(divelog.devices, dev);
|
||||||
|
|
||||||
// Add new filter presets
|
// Add new filter presets
|
||||||
for (auto &it: filterPresetsToAdd) {
|
for (auto &it: filterPresetsToAdd) {
|
||||||
filterPresetsToRemove.push_back(filter_preset_add(it.first, it.second));
|
filterPresetsToRemove.push_back(divelog.filter_presets.add(it.first, it.second));
|
||||||
emit diveListNotifier.filterPresetAdded(filterPresetsToRemove.back());
|
emit diveListNotifier.filterPresetAdded(filterPresetsToRemove.back());
|
||||||
}
|
}
|
||||||
filterPresetsToAdd.clear();
|
filterPresetsToAdd.clear();
|
||||||
@ -579,15 +553,16 @@ void ImportDives::undoit()
|
|||||||
setSelection(selection, currentDive, -1);
|
setSelection(selection, currentDive, -1);
|
||||||
|
|
||||||
// Remove devices
|
// Remove devices
|
||||||
for (const device &dev: devicesToAddAndRemove.devices)
|
for (const device &dev: devicesToAddAndRemove)
|
||||||
remove_device(divelog.devices, &dev);
|
remove_device(divelog.devices, dev);
|
||||||
|
|
||||||
// Remove filter presets. Do this in reverse order.
|
// Remove filter presets. Do this in reverse order.
|
||||||
for (auto it = filterPresetsToRemove.rbegin(); it != filterPresetsToRemove.rend(); ++it) {
|
for (auto it = filterPresetsToRemove.rbegin(); it != filterPresetsToRemove.rend(); ++it) {
|
||||||
int index = *it;
|
int index = *it;
|
||||||
std::string oldName = filter_preset_name(index);
|
const filter_preset &preset = divelog.filter_presets[index];
|
||||||
FilterData oldData = filter_preset_get(index);
|
std::string oldName = preset.name;
|
||||||
filter_preset_delete(index);
|
FilterData oldData = preset.data;
|
||||||
|
divelog.filter_presets.remove(index);
|
||||||
emit diveListNotifier.filterPresetRemoved(index);
|
emit diveListNotifier.filterPresetRemoved(index);
|
||||||
filterPresetsToAdd.emplace_back(oldName, oldData);
|
filterPresetsToAdd.emplace_back(oldName, oldData);
|
||||||
}
|
}
|
||||||
@ -609,7 +584,7 @@ bool DeleteDive::workToBeDone()
|
|||||||
void DeleteDive::undoit()
|
void DeleteDive::undoit()
|
||||||
{
|
{
|
||||||
divesToDelete = addDives(divesToAdd);
|
divesToDelete = addDives(divesToAdd);
|
||||||
sort_trip_table(divelog.trips); // Though unlikely, removing a dive may reorder trips
|
divelog.trips.sort(); // Though unlikely, removing a dive may reorder trips
|
||||||
|
|
||||||
// Select all re-added dives and make the first one current
|
// Select all re-added dives and make the first one current
|
||||||
dive *currentDive = !divesToDelete.dives.empty() ? divesToDelete.dives[0] : nullptr;
|
dive *currentDive = !divesToDelete.dives.empty() ? divesToDelete.dives[0] : nullptr;
|
||||||
@ -619,7 +594,7 @@ void DeleteDive::undoit()
|
|||||||
void DeleteDive::redoit()
|
void DeleteDive::redoit()
|
||||||
{
|
{
|
||||||
divesToAdd = removeDives(divesToDelete);
|
divesToAdd = removeDives(divesToDelete);
|
||||||
sort_trip_table(divelog.trips); // Though unlikely, adding a dive may reorder trips
|
divelog.trips.sort(); // Though unlikely, adding a dive may reorder trips
|
||||||
|
|
||||||
// Deselect all dives and select dive that was close to the first deleted dive
|
// Deselect all dives and select dive that was close to the first deleted dive
|
||||||
dive *newCurrent = nullptr;
|
dive *newCurrent = nullptr;
|
||||||
@ -647,10 +622,10 @@ void ShiftTime::redoit()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Changing times may have unsorted the dive and trip tables
|
// Changing times may have unsorted the dive and trip tables
|
||||||
sort_dive_table(divelog.dives);
|
divelog.dives.sort();
|
||||||
sort_trip_table(divelog.trips);
|
divelog.trips.sort();
|
||||||
for (dive_trip *trip: trips)
|
for (dive_trip *trip: trips)
|
||||||
sort_dive_table(&trip->dives); // Keep the trip-table in order
|
trip->sort_dives();
|
||||||
|
|
||||||
// Send signals
|
// Send signals
|
||||||
QVector<dive *> dives = stdToQt<dive *>(diveList);
|
QVector<dive *> dives = stdToQt<dive *>(diveList);
|
||||||
@ -716,7 +691,7 @@ bool TripBase::workToBeDone()
|
|||||||
void TripBase::redoit()
|
void TripBase::redoit()
|
||||||
{
|
{
|
||||||
moveDivesBetweenTrips(divesToMove);
|
moveDivesBetweenTrips(divesToMove);
|
||||||
sort_trip_table(divelog.trips); // Though unlikely, moving dives may reorder trips
|
divelog.trips.sort(); // Though unlikely, moving dives may reorder trips
|
||||||
|
|
||||||
// Select the moved dives
|
// Select the moved dives
|
||||||
std::vector<dive *> dives;
|
std::vector<dive *> dives;
|
||||||
@ -754,11 +729,9 @@ RemoveAutogenTrips::RemoveAutogenTrips()
|
|||||||
{
|
{
|
||||||
setText(Command::Base::tr("remove autogenerated trips"));
|
setText(Command::Base::tr("remove autogenerated trips"));
|
||||||
// TODO: don't touch core-innards directly
|
// TODO: don't touch core-innards directly
|
||||||
int i;
|
for (auto &d: divelog.dives) {
|
||||||
struct dive *dive;
|
if (d->divetrip && d->divetrip->autogen)
|
||||||
for_each_dive(i, dive) {
|
divesToMove.divesToMove.push_back( {d.get(), nullptr} );
|
||||||
if (dive->divetrip && dive->divetrip->autogen)
|
|
||||||
divesToMove.divesToMove.push_back( {dive, nullptr} );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,25 +749,22 @@ CreateTrip::CreateTrip(const QVector<dive *> &divesToAddIn)
|
|||||||
if (divesToAddIn.isEmpty())
|
if (divesToAddIn.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dive_trip *trip = create_trip_from_dive(divesToAddIn[0]);
|
auto trip = create_trip_from_dive(divesToAddIn[0]);
|
||||||
divesToMove.tripsToAdd.emplace_back(trip);
|
|
||||||
for (dive *d: divesToAddIn)
|
for (dive *d: divesToAddIn)
|
||||||
divesToMove.divesToMove.push_back( {d, trip} );
|
divesToMove.divesToMove.push_back( { d, trip.get() });
|
||||||
|
divesToMove.tripsToAdd.push_back(std::move(trip));
|
||||||
}
|
}
|
||||||
|
|
||||||
AutogroupDives::AutogroupDives()
|
AutogroupDives::AutogroupDives()
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("autogroup dives"));
|
setText(Command::Base::tr("autogroup dives"));
|
||||||
|
|
||||||
dive_trip *trip;
|
for (auto &entry: get_dives_to_autogroup(divelog.dives)) {
|
||||||
bool alloc;
|
|
||||||
int from, to;
|
|
||||||
for(int i = 0; (trip = get_dives_to_autogroup(divelog.dives, i, &from, &to, &alloc)) != NULL; i = to) {
|
|
||||||
// If this is an allocated trip, take ownership
|
// If this is an allocated trip, take ownership
|
||||||
if (alloc)
|
if (entry.created_trip)
|
||||||
divesToMove.tripsToAdd.emplace_back(trip);
|
divesToMove.tripsToAdd.push_back(std::move(entry.created_trip));
|
||||||
for (int j = from; j < to; ++j)
|
for (auto it = divelog.dives.begin() + entry.from; it != divelog.dives.begin() + entry.to; ++it)
|
||||||
divesToMove.divesToMove.push_back( { get_dive(j), trip } );
|
divesToMove.divesToMove.push_back( { it->get(), entry.trip } );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,18 +772,15 @@ MergeTrips::MergeTrips(dive_trip *trip1, dive_trip *trip2)
|
|||||||
{
|
{
|
||||||
if (trip1 == trip2)
|
if (trip1 == trip2)
|
||||||
return;
|
return;
|
||||||
dive_trip *newTrip = combine_trips(trip1, trip2);
|
std::unique_ptr<dive_trip> newTrip = combine_trips(trip1, trip2);
|
||||||
divesToMove.tripsToAdd.emplace_back(newTrip);
|
for (dive *d: trip1->dives)
|
||||||
for (int i = 0; i < trip1->dives.nr; ++i)
|
divesToMove.divesToMove.push_back( { d, newTrip.get() } );
|
||||||
divesToMove.divesToMove.push_back( { trip1->dives.dives[i], newTrip } );
|
for (dive *d: trip2->dives)
|
||||||
for (int i = 0; i < trip2->dives.nr; ++i)
|
divesToMove.divesToMove.push_back( { d, newTrip.get() } );
|
||||||
divesToMove.divesToMove.push_back( { trip2->dives.dives[i], newTrip } );
|
divesToMove.tripsToAdd.push_back(std::move(newTrip));
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::array<dive *, 2> is the same as struct *dive[2], with the fundamental
|
SplitDivesBase::SplitDivesBase(dive *d, std::array<std::unique_ptr<dive>, 2> newDives)
|
||||||
// difference that it can be returned from functions. Thus, this constructor
|
|
||||||
// can be chained with the result of a function.
|
|
||||||
SplitDivesBase::SplitDivesBase(dive *d, std::array<dive *, 2> newDives)
|
|
||||||
{
|
{
|
||||||
// If either of the new dives is null, simply return. Empty arrays indicate that nothing is to be done.
|
// If either of the new dives is null, simply return. Empty arrays indicate that nothing is to be done.
|
||||||
if (!newDives[0] || !newDives[1])
|
if (!newDives[0] || !newDives[1])
|
||||||
@ -833,10 +800,10 @@ SplitDivesBase::SplitDivesBase(dive *d, std::array<dive *, 2> newDives)
|
|||||||
|
|
||||||
diveToSplit.dives.push_back(d);
|
diveToSplit.dives.push_back(d);
|
||||||
splitDives.dives.resize(2);
|
splitDives.dives.resize(2);
|
||||||
splitDives.dives[0].dive.reset(newDives[0]);
|
splitDives.dives[0].dive = std::move(newDives[0]);
|
||||||
splitDives.dives[0].trip = d->divetrip;
|
splitDives.dives[0].trip = d->divetrip;
|
||||||
splitDives.dives[0].site = d->dive_site;
|
splitDives.dives[0].site = d->dive_site;
|
||||||
splitDives.dives[1].dive.reset(newDives[1]);
|
splitDives.dives[1].dive = std::move(newDives[1]);
|
||||||
splitDives.dives[1].trip = d->divetrip;
|
splitDives.dives[1].trip = d->divetrip;
|
||||||
splitDives.dives[1].site = d->dive_site;
|
splitDives.dives[1].site = d->dive_site;
|
||||||
}
|
}
|
||||||
@ -865,16 +832,13 @@ void SplitDivesBase::undoit()
|
|||||||
setSelection(diveToSplit.dives, diveToSplit.dives[0], -1);
|
setSelection(diveToSplit.dives, diveToSplit.dives[0], -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::array<dive *, 2> doSplitDives(const dive *d, duration_t time)
|
static std::array<std::unique_ptr<dive>, 2> doSplitDives(const dive *d, duration_t time)
|
||||||
{
|
{
|
||||||
// Split the dive
|
// Split the dive
|
||||||
dive *new1, *new2;
|
|
||||||
if (time.seconds < 0)
|
if (time.seconds < 0)
|
||||||
split_dive(d, &new1, &new2);
|
return split_dive(*d);
|
||||||
else
|
else
|
||||||
split_dive_at_time(d, time, &new1, &new2);
|
return split_dive_at_time(*d, time);
|
||||||
|
|
||||||
return { new1, new2 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SplitDives::SplitDives(dive *d, duration_t time) : SplitDivesBase(d, doSplitDives(d, time))
|
SplitDives::SplitDives(dive *d, duration_t time) : SplitDivesBase(d, doSplitDives(d, time))
|
||||||
@ -882,25 +846,13 @@ SplitDives::SplitDives(dive *d, duration_t time) : SplitDivesBase(d, doSplitDive
|
|||||||
setText(Command::Base::tr("split dive"));
|
setText(Command::Base::tr("split dive"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::array<dive *, 2> splitDiveComputer(const dive *d, int dc_num)
|
SplitDiveComputer::SplitDiveComputer(dive *d, int dc_num) :
|
||||||
{
|
SplitDivesBase(d, split_divecomputer(*d, dc_num))
|
||||||
// Refuse to do anything if the dive has only one dive computer.
|
|
||||||
// Yes, this should have been checked by the UI, but let's just make sure.
|
|
||||||
if (!d->dc.next)
|
|
||||||
return { nullptr, nullptr};
|
|
||||||
|
|
||||||
dive *new1, *new2;
|
|
||||||
split_divecomputer(d, dc_num, &new1, &new2);
|
|
||||||
|
|
||||||
return { new1, new2 };
|
|
||||||
}
|
|
||||||
|
|
||||||
SplitDiveComputer::SplitDiveComputer(dive *d, int dc_num) : SplitDivesBase(d, splitDiveComputer(d, dc_num))
|
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("split dive computer"));
|
setText(Command::Base::tr("split dive computer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
DiveComputerBase::DiveComputerBase(dive *old_dive, dive *new_dive, int dc_nr_before, int dc_nr_after) :
|
DiveComputerBase::DiveComputerBase(dive *old_dive, std::unique_ptr<dive> new_dive, int dc_nr_before, int dc_nr_after) :
|
||||||
dc_nr_before(dc_nr_before),
|
dc_nr_before(dc_nr_before),
|
||||||
dc_nr_after(dc_nr_after)
|
dc_nr_after(dc_nr_after)
|
||||||
{
|
{
|
||||||
@ -920,7 +872,7 @@ DiveComputerBase::DiveComputerBase(dive *old_dive, dive *new_dive, int dc_nr_bef
|
|||||||
new_dive->dive_site = nullptr;
|
new_dive->dive_site = nullptr;
|
||||||
|
|
||||||
diveToAdd.dives.resize(1);
|
diveToAdd.dives.resize(1);
|
||||||
diveToAdd.dives[0].dive.reset(new_dive);
|
diveToAdd.dives[0].dive = std::move(new_dive);
|
||||||
diveToAdd.dives[0].trip = old_dive->divetrip;
|
diveToAdd.dives[0].trip = old_dive->divetrip;
|
||||||
diveToAdd.dives[0].site = old_dive->dive_site;
|
diveToAdd.dives[0].site = old_dive->dive_site;
|
||||||
}
|
}
|
||||||
@ -950,13 +902,13 @@ void DiveComputerBase::undoit()
|
|||||||
}
|
}
|
||||||
|
|
||||||
MoveDiveComputerToFront::MoveDiveComputerToFront(dive *d, int dc_num)
|
MoveDiveComputerToFront::MoveDiveComputerToFront(dive *d, int dc_num)
|
||||||
: DiveComputerBase(d, make_first_dc(d, dc_num), dc_num, 0)
|
: DiveComputerBase(d, clone_make_first_dc(*d, dc_num), dc_num, 0)
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("move dive computer to front"));
|
setText(Command::Base::tr("move dive computer to front"));
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteDiveComputer::DeleteDiveComputer(dive *d, int dc_num)
|
DeleteDiveComputer::DeleteDiveComputer(dive *d, int dc_num)
|
||||||
: DiveComputerBase(d, clone_delete_divecomputer(d, dc_num), dc_num, std::min((int)number_of_computers(d) - 1, dc_num))
|
: DiveComputerBase(d, clone_delete_divecomputer(*d, dc_num), dc_num, std::min((int)number_of_computers(d) - 1, dc_num))
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("delete dive computer"));
|
setText(Command::Base::tr("delete dive computer"));
|
||||||
}
|
}
|
||||||
@ -965,16 +917,14 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
|
|||||||
{
|
{
|
||||||
setText(Command::Base::tr("merge dive"));
|
setText(Command::Base::tr("merge dive"));
|
||||||
|
|
||||||
// Just a safety check - if there's not two or more dives - do nothing
|
// Just a safety check - if there's not two or more dives - do nothing.
|
||||||
// The caller should have made sure that this doesn't happen.
|
// The caller should have made sure that this doesn't happen.
|
||||||
if (dives.count() < 2) {
|
if (dives.count() < 2) {
|
||||||
qWarning("Merging less than two dives");
|
qWarning("Merging less than two dives");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dive_trip *preferred_trip;
|
auto [d, trip, site] = merge_dives(*dives[0], *dives[1], dives[1]->when - dives[0]->when, false);
|
||||||
dive_site *preferred_site;
|
|
||||||
OwningDivePtr d(merge_dives(dives[0], dives[1], dives[1]->when - dives[0]->when, false, &preferred_trip, &preferred_site));
|
|
||||||
|
|
||||||
// Currently, the core code selects the dive -> this is not what we want, as
|
// Currently, the core code selects the dive -> this is not what we want, as
|
||||||
// we manually manage the selection post-command.
|
// we manually manage the selection post-command.
|
||||||
@ -982,18 +932,15 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
|
|||||||
d->selected = false;
|
d->selected = false;
|
||||||
|
|
||||||
// Set the preferred dive trip, so that for subsequent merges the better trip can be selected
|
// Set the preferred dive trip, so that for subsequent merges the better trip can be selected
|
||||||
d->divetrip = preferred_trip;
|
d->divetrip = trip;
|
||||||
for (int i = 2; i < dives.count(); ++i) {
|
for (int i = 2; i < dives.count(); ++i) {
|
||||||
d.reset(merge_dives(d.get(), dives[i], dives[i]->when - d->when, false, &preferred_trip, &preferred_site));
|
auto [d2, trip, site] = merge_dives(*d, *dives[i], dives[i]->when - d->when, false);
|
||||||
|
d = std::move(d2);
|
||||||
// Set the preferred dive trip and site, so that for subsequent merges the better trip and site can be selected
|
// Set the preferred dive trip and site, so that for subsequent merges the better trip and site can be selected
|
||||||
d->divetrip = preferred_trip;
|
d->divetrip = trip;
|
||||||
d->dive_site = preferred_site;
|
d->dive_site = site;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We got our preferred trip and site, so now the references can be deleted from the newly generated dive
|
|
||||||
d->divetrip = nullptr;
|
|
||||||
d->dive_site = nullptr;
|
|
||||||
|
|
||||||
// The merged dive gets the number of the first dive with a non-zero number
|
// The merged dive gets the number of the first dive with a non-zero number
|
||||||
for (const dive *dive: dives) {
|
for (const dive *dive: dives) {
|
||||||
if (dive->number) {
|
if (dive->number) {
|
||||||
@ -1005,9 +952,9 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
|
|||||||
// We will only renumber the remaining dives if the joined dives are consecutive.
|
// We will only renumber the remaining dives if the joined dives are consecutive.
|
||||||
// Otherwise all bets are off concerning what the user wanted and doing nothing seems
|
// Otherwise all bets are off concerning what the user wanted and doing nothing seems
|
||||||
// like the best option.
|
// like the best option.
|
||||||
int idx = get_divenr(dives[0]);
|
size_t idx = divelog.dives.get_idx(dives[0]);
|
||||||
int num = dives.count();
|
size_t num = dives.count();
|
||||||
if (idx < 0 || idx + num > divelog.dives->nr) {
|
if (idx == std::string::npos) {
|
||||||
// It was the callers responsibility to pass only known dives.
|
// It was the callers responsibility to pass only known dives.
|
||||||
// Something is seriously wrong - give up.
|
// Something is seriously wrong - give up.
|
||||||
qWarning("Merging unknown dives");
|
qWarning("Merging unknown dives");
|
||||||
@ -1015,7 +962,8 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
|
|||||||
}
|
}
|
||||||
// std::equal compares two ranges. The parameters are (begin_range1, end_range1, begin_range2).
|
// std::equal compares two ranges. The parameters are (begin_range1, end_range1, begin_range2).
|
||||||
// Here, we can compare C-arrays, because QVector guarantees contiguous storage.
|
// Here, we can compare C-arrays, because QVector guarantees contiguous storage.
|
||||||
if (std::equal(&dives[0], &dives[0] + num, &divelog.dives->dives[idx]) &&
|
if (std::equal(&dives[0], &dives[0] + num, divelog.dives.begin() + idx, [](dive *d1,
|
||||||
|
const std::unique_ptr<dive> &d2) { return d1 == d2.get(); }) &&
|
||||||
dives[0]->number && dives.last()->number && dives[0]->number < dives.last()->number) {
|
dives[0]->number && dives.last()->number && dives[0]->number < dives.last()->number) {
|
||||||
// We have a consecutive set of dives. Rename all following dives according to the
|
// We have a consecutive set of dives. Rename all following dives according to the
|
||||||
// number of erased dives. This considers that there might be missing numbers.
|
// number of erased dives. This considers that there might be missing numbers.
|
||||||
@ -1031,24 +979,28 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
|
|||||||
// consecutive, and the difference will be 1, so the
|
// consecutive, and the difference will be 1, so the
|
||||||
// above example is not supposed to be normal.
|
// above example is not supposed to be normal.
|
||||||
int diff = dives.last()->number - dives[0]->number;
|
int diff = dives.last()->number - dives[0]->number;
|
||||||
divesToRenumber.reserve(divelog.dives->nr - idx - num);
|
|
||||||
int previousnr = dives[0]->number;
|
int previousnr = dives[0]->number;
|
||||||
for (int i = idx + num; i < divelog.dives->nr; ++i) {
|
for (size_t i = idx + num; i < divelog.dives.size(); ++i) {
|
||||||
int newnr = divelog.dives->dives[i]->number - diff;
|
int newnr = divelog.dives[i]->number - diff;
|
||||||
|
|
||||||
// Stop renumbering if stuff isn't in order (see also core/divelist.c)
|
// Stop renumbering if stuff isn't in order (see also core/divelist.c)
|
||||||
if (newnr <= previousnr)
|
if (newnr <= previousnr)
|
||||||
break;
|
break;
|
||||||
divesToRenumber.append(QPair<dive *,int>(divelog.dives->dives[i], newnr));
|
divesToRenumber.append(QPair<dive *,int>(divelog.dives[i].get(), newnr));
|
||||||
previousnr = newnr;
|
previousnr = newnr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mergedDive.dives.resize(1);
|
mergedDive.dives.resize(1);
|
||||||
mergedDive.dives[0].dive = std::move(d);
|
mergedDive.dives[0].trip = d->divetrip;
|
||||||
mergedDive.dives[0].trip = preferred_trip;
|
mergedDive.dives[0].site = d->dive_site;
|
||||||
mergedDive.dives[0].site = preferred_site;
|
|
||||||
divesToMerge.dives = std::vector<dive *>(dives.begin(), dives.end());
|
divesToMerge.dives = std::vector<dive *>(dives.begin(), dives.end());
|
||||||
|
|
||||||
|
// We got our preferred trip and site, so now the references can be deleted from the newly generated dive
|
||||||
|
d->divetrip = nullptr;
|
||||||
|
d->dive_site = nullptr;
|
||||||
|
|
||||||
|
mergedDive.dives[0].dive = std::move(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MergeDives::workToBeDone()
|
bool MergeDives::workToBeDone()
|
||||||
|
|||||||
@ -15,16 +15,16 @@ namespace Command {
|
|||||||
|
|
||||||
// This helper structure describes a dive that we want to add.
|
// This helper structure describes a dive that we want to add.
|
||||||
struct DiveToAdd {
|
struct DiveToAdd {
|
||||||
OwningDivePtr dive; // Dive to add
|
std::unique_ptr<struct dive> dive; // Dive to add
|
||||||
dive_trip *trip; // Trip the dive belongs to, may be null
|
dive_trip *trip; // Trip the dive belongs to, may be null
|
||||||
dive_site *site; // Site the dive is associated with, may be null
|
dive_site *site; // Site the dive is associated with, may be null
|
||||||
};
|
};
|
||||||
|
|
||||||
// Multiple trips, dives and dive sites that have to be added for a command
|
// Multiple trips, dives and dive sites that have to be added for a command
|
||||||
struct DivesAndTripsToAdd {
|
struct DivesAndTripsToAdd {
|
||||||
std::vector<DiveToAdd> dives;
|
std::vector<DiveToAdd> dives;
|
||||||
std::vector<OwningTripPtr> trips;
|
std::vector<std::unique_ptr<dive_trip>> trips;
|
||||||
std::vector<OwningDiveSitePtr> sites;
|
std::vector<std::unique_ptr<dive_site>> sites;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Dives and sites that have to be removed for a command
|
// Dives and sites that have to be removed for a command
|
||||||
@ -48,7 +48,7 @@ struct DiveToTrip
|
|||||||
struct DivesToTrip
|
struct DivesToTrip
|
||||||
{
|
{
|
||||||
std::vector<DiveToTrip> divesToMove; // If dive_trip is null, remove from trip
|
std::vector<DiveToTrip> divesToMove; // If dive_trip is null, remove from trip
|
||||||
std::vector<OwningTripPtr> tripsToAdd;
|
std::vector<std::unique_ptr<dive_trip>> tripsToAdd;
|
||||||
};
|
};
|
||||||
|
|
||||||
// All divelist commands derive from a common base class. It keeps track
|
// All divelist commands derive from a common base class. It keeps track
|
||||||
@ -58,7 +58,7 @@ struct DivesToTrip
|
|||||||
class DiveListBase : public Base {
|
class DiveListBase : public Base {
|
||||||
protected:
|
protected:
|
||||||
// These are helper functions to add / remove dive from the C-core structures.
|
// These are helper functions to add / remove dive from the C-core structures.
|
||||||
DiveToAdd removeDive(struct dive *d, std::vector<OwningTripPtr> &tripsToAdd);
|
DiveToAdd removeDive(struct dive *d, std::vector<std::unique_ptr<dive_trip>> &tripsToAdd);
|
||||||
dive *addDive(DiveToAdd &d);
|
dive *addDive(DiveToAdd &d);
|
||||||
DivesAndTripsToAdd removeDives(DivesAndSitesToRemove &divesAndSitesToDelete);
|
DivesAndTripsToAdd removeDives(DivesAndSitesToRemove &divesAndSitesToDelete);
|
||||||
DivesAndSitesToRemove addDives(DivesAndTripsToAdd &toAdd);
|
DivesAndSitesToRemove addDives(DivesAndTripsToAdd &toAdd);
|
||||||
@ -108,10 +108,10 @@ private:
|
|||||||
// For redo and undo
|
// For redo and undo
|
||||||
DivesAndTripsToAdd divesToAdd;
|
DivesAndTripsToAdd divesToAdd;
|
||||||
DivesAndSitesToRemove divesAndSitesToRemove;
|
DivesAndSitesToRemove divesAndSitesToRemove;
|
||||||
struct device_table devicesToAddAndRemove;
|
device_table devicesToAddAndRemove;
|
||||||
|
|
||||||
// For redo
|
// For redo
|
||||||
std::vector<OwningDiveSitePtr> sitesToAdd;
|
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
|
||||||
std::vector<std::pair<std::string,FilterData>>
|
std::vector<std::pair<std::string,FilterData>>
|
||||||
filterPresetsToAdd;
|
filterPresetsToAdd;
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ private:
|
|||||||
// For redo
|
// For redo
|
||||||
DivesAndSitesToRemove divesToDelete;
|
DivesAndSitesToRemove divesToDelete;
|
||||||
|
|
||||||
std::vector<OwningTripPtr> tripsToAdd;
|
std::vector<std::unique_ptr<dive_trip>> tripsToAdd;
|
||||||
DivesAndTripsToAdd divesToAdd;
|
DivesAndTripsToAdd divesToAdd;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ struct MergeTrips : public TripBase {
|
|||||||
|
|
||||||
class SplitDivesBase : public DiveListBase {
|
class SplitDivesBase : public DiveListBase {
|
||||||
protected:
|
protected:
|
||||||
SplitDivesBase(dive *old, std::array<dive *, 2> newDives);
|
SplitDivesBase(dive *old, std::array<std::unique_ptr<dive>, 2> newDives);
|
||||||
private:
|
private:
|
||||||
void undoit() override;
|
void undoit() override;
|
||||||
void redoit() override;
|
void redoit() override;
|
||||||
@ -237,7 +237,7 @@ class DiveComputerBase : public DiveListBase {
|
|||||||
protected:
|
protected:
|
||||||
// old_dive must be a dive known to the core.
|
// old_dive must be a dive known to the core.
|
||||||
// new_dive must be new dive whose ownership is taken.
|
// new_dive must be new dive whose ownership is taken.
|
||||||
DiveComputerBase(dive *old_dive, dive *new_dive, int dc_nr_before, int dc_nr_after);
|
DiveComputerBase(dive *old_dive, std::unique_ptr<dive> new_dive, int dc_nr_before, int dc_nr_after);
|
||||||
private:
|
private:
|
||||||
void undoit() override;
|
void undoit() override;
|
||||||
void redoit() override;
|
void redoit() override;
|
||||||
|
|||||||
@ -15,25 +15,24 @@ namespace Command {
|
|||||||
|
|
||||||
// Add a set of dive sites to the core. The dives that were associated with
|
// Add a set of dive sites to the core. The dives that were associated with
|
||||||
// that dive site will be restored to that dive site.
|
// that dive site will be restored to that dive site.
|
||||||
static std::vector<dive_site *> addDiveSites(std::vector<OwningDiveSitePtr> &sites)
|
static std::vector<dive_site *> addDiveSites(std::vector<std::unique_ptr<dive_site>> &sites)
|
||||||
{
|
{
|
||||||
std::vector<dive_site *> res;
|
std::vector<dive_site *> res;
|
||||||
QVector<dive *> changedDives;
|
QVector<dive *> changedDives;
|
||||||
res.reserve(sites.size());
|
res.reserve(sites.size());
|
||||||
|
|
||||||
for (OwningDiveSitePtr &ds: sites) {
|
for (std::unique_ptr<dive_site> &ds: sites) {
|
||||||
// Readd the dives that belonged to this site
|
// Readd the dives that belonged to this site
|
||||||
for (int i = 0; i < ds->dives.nr; ++i) {
|
for (dive *d: ds->dives) {
|
||||||
// TODO: send dive site changed signal
|
// TODO: send dive site changed signal
|
||||||
struct dive *d = ds->dives.dives[i];
|
|
||||||
d->dive_site = ds.get();
|
d->dive_site = ds.get();
|
||||||
changedDives.push_back(d);
|
changedDives.push_back(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add dive site to core, but remember a non-owning pointer first.
|
// Add dive site to core, but remember a non-owning pointer first.
|
||||||
res.push_back(ds.get());
|
auto add_res = divelog.sites.put(std::move(ds)); // Return ownership to backend.
|
||||||
int idx = register_dive_site(ds.release()); // Return ownership to backend.
|
res.push_back(add_res.ptr);
|
||||||
emit diveListNotifier.diveSiteAdded(res.back(), idx); // Inform frontend of new dive site.
|
emit diveListNotifier.diveSiteAdded(res.back(), add_res.idx); // Inform frontend of new dive site.
|
||||||
}
|
}
|
||||||
|
|
||||||
emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE);
|
emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE);
|
||||||
@ -47,24 +46,23 @@ static std::vector<dive_site *> addDiveSites(std::vector<OwningDiveSitePtr> &sit
|
|||||||
// Remove a set of dive sites. Get owning pointers to them. The dives are set to
|
// Remove a set of dive sites. Get owning pointers to them. The dives are set to
|
||||||
// being at no dive site, but the dive site will retain a list of dives, so
|
// being at no dive site, but the dive site will retain a list of dives, so
|
||||||
// that the dives can be readded to the site on undo.
|
// that the dives can be readded to the site on undo.
|
||||||
static std::vector<OwningDiveSitePtr> removeDiveSites(std::vector<dive_site *> &sites)
|
static std::vector<std::unique_ptr<dive_site>> removeDiveSites(std::vector<dive_site *> &sites)
|
||||||
{
|
{
|
||||||
std::vector<OwningDiveSitePtr> res;
|
std::vector<std::unique_ptr<dive_site>> res;
|
||||||
QVector<dive *> changedDives;
|
QVector<dive *> changedDives;
|
||||||
res.reserve(sites.size());
|
res.reserve(sites.size());
|
||||||
|
|
||||||
for (dive_site *ds: sites) {
|
for (dive_site *ds: sites) {
|
||||||
// Reset the dive_site field of the affected dives
|
// Reset the dive_site field of the affected dives
|
||||||
for (int i = 0; i < ds->dives.nr; ++i) {
|
for (dive *d: ds->dives) {
|
||||||
struct dive *d = ds->dives.dives[i];
|
|
||||||
d->dive_site = nullptr;
|
d->dive_site = nullptr;
|
||||||
changedDives.push_back(d);
|
changedDives.push_back(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove dive site from core and take ownership.
|
// Remove dive site from core and take ownership.
|
||||||
int idx = unregister_dive_site(ds);
|
auto pull_res = divelog.sites.pull(ds);
|
||||||
res.emplace_back(ds);
|
res.push_back(std::move(pull_res.ptr));
|
||||||
emit diveListNotifier.diveSiteDeleted(ds, idx); // Inform frontend of removed dive site.
|
emit diveListNotifier.diveSiteDeleted(ds, pull_res.idx); // Inform frontend of removed dive site.
|
||||||
}
|
}
|
||||||
|
|
||||||
emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE);
|
emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE);
|
||||||
@ -77,8 +75,8 @@ static std::vector<OwningDiveSitePtr> removeDiveSites(std::vector<dive_site *> &
|
|||||||
AddDiveSite::AddDiveSite(const QString &name)
|
AddDiveSite::AddDiveSite(const QString &name)
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("add dive site"));
|
setText(Command::Base::tr("add dive site"));
|
||||||
sitesToAdd.emplace_back(alloc_dive_site());
|
sitesToAdd.push_back(std::make_unique<dive_site>());
|
||||||
sitesToAdd.back()->name = copy_qstring(name);
|
sitesToAdd.back()->name = name.toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddDiveSite::workToBeDone()
|
bool AddDiveSite::workToBeDone()
|
||||||
@ -96,25 +94,17 @@ void AddDiveSite::undo()
|
|||||||
sitesToAdd = removeDiveSites(sitesToRemove);
|
sitesToAdd = removeDiveSites(sitesToRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImportDiveSites::ImportDiveSites(struct dive_site_table *sites, const QString &source)
|
ImportDiveSites::ImportDiveSites(dive_site_table sites, const QString &source)
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("import dive sites from %1").arg(source));
|
setText(Command::Base::tr("import dive sites from %1").arg(source));
|
||||||
|
|
||||||
for (int i = 0; i < sites->nr; ++i) {
|
for (auto &new_ds: sites) {
|
||||||
struct dive_site *new_ds = sites->dive_sites[i];
|
// Don't import dive sites that already exist.
|
||||||
|
// We might want to be smarter here and merge dive site data, etc.
|
||||||
// Don't import dive sites that already exist. Currently we only check for
|
if (divelog.sites.get_same(*new_ds))
|
||||||
// the same name. We might want to be smarter here and merge dive site data, etc.
|
|
||||||
struct dive_site *old_ds = get_same_dive_site(new_ds);
|
|
||||||
if (old_ds) {
|
|
||||||
free_dive_site(new_ds);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
sitesToAdd.push_back(std::move(new_ds));
|
||||||
sitesToAdd.emplace_back(new_ds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All site have been consumed
|
|
||||||
sites->nr = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImportDiveSites::workToBeDone()
|
bool ImportDiveSites::workToBeDone()
|
||||||
@ -155,10 +145,9 @@ void DeleteDiveSites::undo()
|
|||||||
PurgeUnusedDiveSites::PurgeUnusedDiveSites()
|
PurgeUnusedDiveSites::PurgeUnusedDiveSites()
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("purge unused dive sites"));
|
setText(Command::Base::tr("purge unused dive sites"));
|
||||||
for (int i = 0; i < divelog.sites->nr; ++i) {
|
for (const auto &ds: divelog.sites) {
|
||||||
dive_site *ds = divelog.sites->dive_sites[i];
|
if (ds->dives.empty())
|
||||||
if (ds->dives.nr == 0)
|
sitesToRemove.push_back(ds.get());
|
||||||
sitesToRemove.push_back(ds);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,24 +166,15 @@ void PurgeUnusedDiveSites::undo()
|
|||||||
sitesToRemove = addDiveSites(sitesToAdd);
|
sitesToRemove = addDiveSites(sitesToAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function: swap C and Qt string
|
|
||||||
static void swap(char *&c, QString &q)
|
|
||||||
{
|
|
||||||
QString s = c;
|
|
||||||
free(c);
|
|
||||||
c = copy_qstring(q);
|
|
||||||
q = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
EditDiveSiteName::EditDiveSiteName(dive_site *dsIn, const QString &name) : ds(dsIn),
|
EditDiveSiteName::EditDiveSiteName(dive_site *dsIn, const QString &name) : ds(dsIn),
|
||||||
value(name)
|
value(name.toStdString())
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("Edit dive site name"));
|
setText(Command::Base::tr("Edit dive site name"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditDiveSiteName::workToBeDone()
|
bool EditDiveSiteName::workToBeDone()
|
||||||
{
|
{
|
||||||
return value != QString(ds->name);
|
return value != ds->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDiveSiteName::redo()
|
void EditDiveSiteName::redo()
|
||||||
@ -210,14 +190,14 @@ void EditDiveSiteName::undo()
|
|||||||
}
|
}
|
||||||
|
|
||||||
EditDiveSiteDescription::EditDiveSiteDescription(dive_site *dsIn, const QString &description) : ds(dsIn),
|
EditDiveSiteDescription::EditDiveSiteDescription(dive_site *dsIn, const QString &description) : ds(dsIn),
|
||||||
value(description)
|
value(description.toStdString())
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("Edit dive site description"));
|
setText(Command::Base::tr("Edit dive site description"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditDiveSiteDescription::workToBeDone()
|
bool EditDiveSiteDescription::workToBeDone()
|
||||||
{
|
{
|
||||||
return value != QString(ds->description);
|
return value != ds->description;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDiveSiteDescription::redo()
|
void EditDiveSiteDescription::redo()
|
||||||
@ -233,14 +213,14 @@ void EditDiveSiteDescription::undo()
|
|||||||
}
|
}
|
||||||
|
|
||||||
EditDiveSiteNotes::EditDiveSiteNotes(dive_site *dsIn, const QString ¬es) : ds(dsIn),
|
EditDiveSiteNotes::EditDiveSiteNotes(dive_site *dsIn, const QString ¬es) : ds(dsIn),
|
||||||
value(notes)
|
value(notes.toStdString())
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("Edit dive site notes"));
|
setText(Command::Base::tr("Edit dive site notes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditDiveSiteNotes::workToBeDone()
|
bool EditDiveSiteNotes::workToBeDone()
|
||||||
{
|
{
|
||||||
return value != QString(ds->notes);
|
return value != ds->notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDiveSiteNotes::redo()
|
void EditDiveSiteNotes::redo()
|
||||||
@ -256,20 +236,20 @@ void EditDiveSiteNotes::undo()
|
|||||||
}
|
}
|
||||||
|
|
||||||
EditDiveSiteCountry::EditDiveSiteCountry(dive_site *dsIn, const QString &country) : ds(dsIn),
|
EditDiveSiteCountry::EditDiveSiteCountry(dive_site *dsIn, const QString &country) : ds(dsIn),
|
||||||
value(country)
|
value(country.toStdString())
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("Edit dive site country"));
|
setText(Command::Base::tr("Edit dive site country"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditDiveSiteCountry::workToBeDone()
|
bool EditDiveSiteCountry::workToBeDone()
|
||||||
{
|
{
|
||||||
return !same_string(qPrintable(value), taxonomy_get_country(&ds->taxonomy));
|
return value == taxonomy_get_country(ds->taxonomy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDiveSiteCountry::redo()
|
void EditDiveSiteCountry::redo()
|
||||||
{
|
{
|
||||||
QString old = taxonomy_get_country(&ds->taxonomy);
|
std::string old = taxonomy_get_country(ds->taxonomy);
|
||||||
taxonomy_set_country(&ds->taxonomy, qPrintable(value), taxonomy_origin::GEOMANUAL);
|
taxonomy_set_country(ds->taxonomy, value, taxonomy_origin::GEOMANUAL);
|
||||||
value = old;
|
value = old;
|
||||||
emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::TAXONOMY); // Inform frontend of changed dive site.
|
emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::TAXONOMY); // Inform frontend of changed dive site.
|
||||||
}
|
}
|
||||||
@ -292,7 +272,7 @@ bool EditDiveSiteLocation::workToBeDone()
|
|||||||
bool old_ok = has_location(&ds->location);
|
bool old_ok = has_location(&ds->location);
|
||||||
if (ok != old_ok)
|
if (ok != old_ok)
|
||||||
return true;
|
return true;
|
||||||
return ok && !same_location(&value, &ds->location);
|
return ok && value != ds->location;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDiveSiteLocation::redo()
|
void EditDiveSiteLocation::redo()
|
||||||
@ -310,14 +290,11 @@ void EditDiveSiteLocation::undo()
|
|||||||
EditDiveSiteTaxonomy::EditDiveSiteTaxonomy(dive_site *dsIn, taxonomy_data &taxonomy) : ds(dsIn),
|
EditDiveSiteTaxonomy::EditDiveSiteTaxonomy(dive_site *dsIn, taxonomy_data &taxonomy) : ds(dsIn),
|
||||||
value(taxonomy)
|
value(taxonomy)
|
||||||
{
|
{
|
||||||
// We did a dumb copy. Erase the source to remove double references to strings.
|
|
||||||
memset(&taxonomy, 0, sizeof(taxonomy));
|
|
||||||
setText(Command::Base::tr("Edit dive site taxonomy"));
|
setText(Command::Base::tr("Edit dive site taxonomy"));
|
||||||
}
|
}
|
||||||
|
|
||||||
EditDiveSiteTaxonomy::~EditDiveSiteTaxonomy()
|
EditDiveSiteTaxonomy::~EditDiveSiteTaxonomy()
|
||||||
{
|
{
|
||||||
free_taxonomy(&value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditDiveSiteTaxonomy::workToBeDone()
|
bool EditDiveSiteTaxonomy::workToBeDone()
|
||||||
@ -364,10 +341,10 @@ void MergeDiveSites::redo()
|
|||||||
// The dives of the above dive sites were reset to no dive sites.
|
// The dives of the above dive sites were reset to no dive sites.
|
||||||
// Add them to the merged-into dive site. Thankfully, we remember
|
// Add them to the merged-into dive site. Thankfully, we remember
|
||||||
// the dives in the sitesToAdd vector.
|
// the dives in the sitesToAdd vector.
|
||||||
for (const OwningDiveSitePtr &site: sitesToAdd) {
|
for (const std::unique_ptr<dive_site> &site: sitesToAdd) {
|
||||||
for (int i = 0; i < site->dives.nr; ++i) {
|
for (dive *d: site->dives) {
|
||||||
add_dive_to_dive_site(site->dives.dives[i], ds);
|
ds->add_dive(d);
|
||||||
divesChanged.push_back(site->dives.dives[i]);
|
divesChanged.push_back(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emit diveListNotifier.divesChanged(divesChanged, DiveField::DIVESITE);
|
emit diveListNotifier.divesChanged(divesChanged, DiveField::DIVESITE);
|
||||||
@ -380,10 +357,10 @@ void MergeDiveSites::undo()
|
|||||||
|
|
||||||
// Before readding the dive sites, unregister the corresponding dives so that they can be
|
// Before readding the dive sites, unregister the corresponding dives so that they can be
|
||||||
// readded to their old dive sites.
|
// readded to their old dive sites.
|
||||||
for (const OwningDiveSitePtr &site: sitesToAdd) {
|
for (const std::unique_ptr<dive_site> &site: sitesToAdd) {
|
||||||
for (int i = 0; i < site->dives.nr; ++i) {
|
for (dive *d: site->dives) {
|
||||||
unregister_dive_from_dive_site(site->dives.dives[i]);
|
unregister_dive_from_dive_site(d);
|
||||||
divesChanged.push_back(site->dives.dives[i]);
|
divesChanged.push_back(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,9 +382,9 @@ ApplyGPSFixes::ApplyGPSFixes(const std::vector<DiveAndLocation> &fixes)
|
|||||||
siteLocations.push_back({ ds, dl.location });
|
siteLocations.push_back({ ds, dl.location });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ds = create_dive_site(qPrintable(dl.name), divelog.sites);
|
ds = divelog.sites.create(dl.name.toStdString());
|
||||||
ds->location = dl.location;
|
ds->location = dl.location;
|
||||||
add_dive_to_dive_site(dl.d, ds);
|
ds->add_dive(dl.d);
|
||||||
dl.d->dive_site = nullptr; // This will be set on redo()
|
dl.d->dive_site = nullptr; // This will be set on redo()
|
||||||
sitesToAdd.emplace_back(ds);
|
sitesToAdd.emplace_back(ds);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,13 +31,13 @@ private:
|
|||||||
std::vector<dive_site *> sitesToRemove;
|
std::vector<dive_site *> sitesToRemove;
|
||||||
|
|
||||||
// For redo
|
// For redo
|
||||||
std::vector<OwningDiveSitePtr> sitesToAdd;
|
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ImportDiveSites : public Base {
|
class ImportDiveSites : public Base {
|
||||||
public:
|
public:
|
||||||
// Note: the dive site table is consumed after the call it will be empty.
|
// Note: Takes ownership of dive site table
|
||||||
ImportDiveSites(struct dive_site_table *sites, const QString &source);
|
ImportDiveSites(dive_site_table sites, const QString &source);
|
||||||
private:
|
private:
|
||||||
bool workToBeDone() override;
|
bool workToBeDone() override;
|
||||||
void undo() override;
|
void undo() override;
|
||||||
@ -47,7 +47,7 @@ private:
|
|||||||
std::vector<dive_site *> sitesToRemove;
|
std::vector<dive_site *> sitesToRemove;
|
||||||
|
|
||||||
// For redo
|
// For redo
|
||||||
std::vector<OwningDiveSitePtr> sitesToAdd;
|
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeleteDiveSites : public Base {
|
class DeleteDiveSites : public Base {
|
||||||
@ -62,7 +62,7 @@ private:
|
|||||||
std::vector<dive_site *> sitesToRemove;
|
std::vector<dive_site *> sitesToRemove;
|
||||||
|
|
||||||
// For undo
|
// For undo
|
||||||
std::vector<OwningDiveSitePtr> sitesToAdd;
|
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PurgeUnusedDiveSites : public Base {
|
class PurgeUnusedDiveSites : public Base {
|
||||||
@ -77,7 +77,7 @@ private:
|
|||||||
std::vector<dive_site *> sitesToRemove;
|
std::vector<dive_site *> sitesToRemove;
|
||||||
|
|
||||||
// For undo
|
// For undo
|
||||||
std::vector<OwningDiveSitePtr> sitesToAdd;
|
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EditDiveSiteName : public Base {
|
class EditDiveSiteName : public Base {
|
||||||
@ -89,7 +89,7 @@ private:
|
|||||||
void redo() override;
|
void redo() override;
|
||||||
|
|
||||||
dive_site *ds;
|
dive_site *ds;
|
||||||
QString value; // Value to be set
|
std::string value; // Value to be set
|
||||||
};
|
};
|
||||||
|
|
||||||
class EditDiveSiteDescription : public Base {
|
class EditDiveSiteDescription : public Base {
|
||||||
@ -101,7 +101,7 @@ private:
|
|||||||
void redo() override;
|
void redo() override;
|
||||||
|
|
||||||
dive_site *ds;
|
dive_site *ds;
|
||||||
QString value; // Value to be set
|
std::string value; // Value to be set
|
||||||
};
|
};
|
||||||
|
|
||||||
class EditDiveSiteNotes : public Base {
|
class EditDiveSiteNotes : public Base {
|
||||||
@ -113,7 +113,7 @@ private:
|
|||||||
void redo() override;
|
void redo() override;
|
||||||
|
|
||||||
dive_site *ds;
|
dive_site *ds;
|
||||||
QString value; // Value to be set
|
std::string value; // Value to be set
|
||||||
};
|
};
|
||||||
|
|
||||||
class EditDiveSiteCountry : public Base {
|
class EditDiveSiteCountry : public Base {
|
||||||
@ -125,7 +125,7 @@ private:
|
|||||||
void redo() override;
|
void redo() override;
|
||||||
|
|
||||||
dive_site *ds;
|
dive_site *ds;
|
||||||
QString value; // Value to be set
|
std::string value; // Value to be set
|
||||||
};
|
};
|
||||||
|
|
||||||
class EditDiveSiteLocation : public Base {
|
class EditDiveSiteLocation : public Base {
|
||||||
@ -167,7 +167,7 @@ private:
|
|||||||
std::vector<dive_site *> sitesToRemove;
|
std::vector<dive_site *> sitesToRemove;
|
||||||
|
|
||||||
// For undo
|
// For undo
|
||||||
std::vector<OwningDiveSitePtr> sitesToAdd;
|
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ApplyGPSFixes : public Base {
|
class ApplyGPSFixes : public Base {
|
||||||
@ -183,7 +183,7 @@ private:
|
|||||||
std::vector<dive_site *> sitesToRemove;
|
std::vector<dive_site *> sitesToRemove;
|
||||||
|
|
||||||
// For redo
|
// For redo
|
||||||
std::vector<OwningDiveSitePtr> sitesToAdd;
|
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
|
||||||
|
|
||||||
// For redo and undo
|
// For redo and undo
|
||||||
struct SiteAndLocation {
|
struct SiteAndLocation {
|
||||||
|
|||||||
@ -3,8 +3,10 @@
|
|||||||
#include "command_edit.h"
|
#include "command_edit.h"
|
||||||
#include "core/divelist.h"
|
#include "core/divelist.h"
|
||||||
#include "core/divelog.h"
|
#include "core/divelog.h"
|
||||||
|
#include "core/event.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/range.h"
|
||||||
#include "core/sample.h"
|
#include "core/sample.h"
|
||||||
#include "core/selection.h"
|
#include "core/selection.h"
|
||||||
#include "core/subsurface-string.h"
|
#include "core/subsurface-string.h"
|
||||||
@ -41,17 +43,16 @@ T EditDefaultSetter<T, ID, PTR>::data(struct dive *d) const
|
|||||||
return d->*PTR;
|
return d->*PTR;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <DiveField::Flags ID, char *dive::*PTR>
|
template <DiveField::Flags ID, std::string dive::*PTR>
|
||||||
void EditStringSetter<ID, PTR>::set(struct dive *d, QString v) const
|
void EditStringSetter<ID, PTR>::set(struct dive *d, QString v) const
|
||||||
{
|
{
|
||||||
free(d->*PTR);
|
d->*PTR = v.toStdString();
|
||||||
d->*PTR = copy_qstring(v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <DiveField::Flags ID, char *dive::*PTR>
|
template <DiveField::Flags ID, std::string dive::*PTR>
|
||||||
QString EditStringSetter<ID, PTR>::data(struct dive *d) const
|
QString EditStringSetter<ID, PTR>::data(struct dive *d) const
|
||||||
{
|
{
|
||||||
return QString(d->*PTR);
|
return QString::fromStdString(d->*PTR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<dive *> getDives(bool currentDiveOnly)
|
static std::vector<dive *> getDives(bool currentDiveOnly)
|
||||||
@ -61,11 +62,9 @@ static std::vector<dive *> getDives(bool currentDiveOnly)
|
|||||||
: std::vector<dive *> { };
|
: std::vector<dive *> { };
|
||||||
|
|
||||||
std::vector<dive *> res;
|
std::vector<dive *> res;
|
||||||
struct dive *d;
|
for (auto &d: divelog.dives) {
|
||||||
int i;
|
|
||||||
for_each_dive (i, d) {
|
|
||||||
if (d->selected)
|
if (d->selected)
|
||||||
res.push_back(d);
|
res.push_back(d.get());
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -305,11 +304,11 @@ QString EditAtmPress::fieldName() const
|
|||||||
// ***** Duration *****
|
// ***** Duration *****
|
||||||
void EditDuration::set(struct dive *d, int value) const
|
void EditDuration::set(struct dive *d, int value) const
|
||||||
{
|
{
|
||||||
d->dc.duration.seconds = value;
|
d->dcs[0].duration.seconds = value;
|
||||||
d->duration = d->dc.duration;
|
d->duration = d->dcs[0].duration;
|
||||||
d->dc.meandepth.mm = 0;
|
d->dcs[0].meandepth.mm = 0;
|
||||||
d->dc.samples = 0;
|
d->dcs[0].samples.clear();
|
||||||
fake_dc(&d->dc);
|
fake_dc(&d->dcs[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int EditDuration::data(struct dive *d) const
|
int EditDuration::data(struct dive *d) const
|
||||||
@ -325,11 +324,11 @@ QString EditDuration::fieldName() const
|
|||||||
// ***** Depth *****
|
// ***** Depth *****
|
||||||
void EditDepth::set(struct dive *d, int value) const
|
void EditDepth::set(struct dive *d, int value) const
|
||||||
{
|
{
|
||||||
d->dc.maxdepth.mm = value;
|
d->dcs[0].maxdepth.mm = value;
|
||||||
d->maxdepth = d->dc.maxdepth;
|
d->maxdepth = d->dcs[0].maxdepth;
|
||||||
d->dc.meandepth.mm = 0;
|
d->dcs[0].meandepth.mm = 0;
|
||||||
d->dc.samples = 0;
|
d->dcs[0].samples.clear();
|
||||||
fake_dc(&d->dc);
|
fake_dc(&d->dcs[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int EditDepth::data(struct dive *d) const
|
int EditDepth::data(struct dive *d) const
|
||||||
@ -346,7 +345,7 @@ QString EditDepth::fieldName() const
|
|||||||
void EditDiveSite::set(struct dive *d, struct dive_site *dive_site) const
|
void EditDiveSite::set(struct dive *d, struct dive_site *dive_site) const
|
||||||
{
|
{
|
||||||
unregister_dive_from_dive_site(d);
|
unregister_dive_from_dive_site(d);
|
||||||
add_dive_to_dive_site(d, dive_site);
|
dive_site->add_dive(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dive_site *EditDiveSite::data(struct dive *d) const
|
struct dive_site *EditDiveSite::data(struct dive *d) const
|
||||||
@ -374,14 +373,11 @@ void EditDiveSite::redo()
|
|||||||
EditDiveSite::undo(); // Undo and redo do the same
|
EditDiveSite::undo(); // Undo and redo do the same
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dive_site *createDiveSite(const QString &name)
|
static struct dive_site *createDiveSite(const std::string &name)
|
||||||
{
|
{
|
||||||
struct dive_site *ds = alloc_dive_site();
|
struct dive_site *ds = new dive_site;
|
||||||
struct dive_site *old = current_dive ? current_dive->dive_site : nullptr;
|
if (current_dive && current_dive->dive_site)
|
||||||
if (old) {
|
*ds = *current_dive->dive_site;
|
||||||
copy_dive_site(old, ds);
|
|
||||||
free(ds->name); // Free name, as we will overwrite it with our own version
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the current dive has a location, use that as location for the new dive site
|
// If the current dive has a location, use that as location for the new dive site
|
||||||
if (current_dive) {
|
if (current_dive) {
|
||||||
@ -390,12 +386,12 @@ static struct dive_site *createDiveSite(const QString &name)
|
|||||||
ds->location = loc;
|
ds->location = loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
ds->name = copy_qstring(name);
|
ds->name = name;
|
||||||
return ds;
|
return ds;
|
||||||
}
|
}
|
||||||
|
|
||||||
EditDiveSiteNew::EditDiveSiteNew(const QString &newName, bool currentDiveOnly) :
|
EditDiveSiteNew::EditDiveSiteNew(const QString &newName, bool currentDiveOnly) :
|
||||||
EditDiveSite(createDiveSite(newName), currentDiveOnly),
|
EditDiveSite(createDiveSite(newName.toStdString()), currentDiveOnly),
|
||||||
diveSiteToAdd(value),
|
diveSiteToAdd(value),
|
||||||
diveSiteToRemove(nullptr)
|
diveSiteToRemove(nullptr)
|
||||||
{
|
{
|
||||||
@ -404,17 +400,17 @@ EditDiveSiteNew::EditDiveSiteNew(const QString &newName, bool currentDiveOnly) :
|
|||||||
void EditDiveSiteNew::undo()
|
void EditDiveSiteNew::undo()
|
||||||
{
|
{
|
||||||
EditDiveSite::undo();
|
EditDiveSite::undo();
|
||||||
int idx = unregister_dive_site(diveSiteToRemove);
|
auto res = divelog.sites.pull(diveSiteToRemove);
|
||||||
diveSiteToAdd.reset(diveSiteToRemove);
|
diveSiteToAdd = std::move(res.ptr);
|
||||||
emit diveListNotifier.diveSiteDeleted(diveSiteToRemove, idx); // Inform frontend of removed dive site.
|
emit diveListNotifier.diveSiteDeleted(diveSiteToRemove, res.idx); // Inform frontend of removed dive site.
|
||||||
diveSiteToRemove = nullptr;
|
diveSiteToRemove = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDiveSiteNew::redo()
|
void EditDiveSiteNew::redo()
|
||||||
{
|
{
|
||||||
diveSiteToRemove = diveSiteToAdd.get();
|
auto res = divelog.sites.register_site(std::move(diveSiteToAdd)); // Return ownership to backend.
|
||||||
int idx = register_dive_site(diveSiteToAdd.release()); // Return ownership to backend.
|
diveSiteToRemove = res.ptr;
|
||||||
emit diveListNotifier.diveSiteAdded(diveSiteToRemove, idx); // Inform frontend of new dive site.
|
emit diveListNotifier.diveSiteAdded(diveSiteToRemove, res.idx); // Inform frontend of new dive site.
|
||||||
EditDiveSite::redo();
|
EditDiveSite::redo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,18 +561,17 @@ void EditTagsBase::redo()
|
|||||||
QStringList EditTags::data(struct dive *d) const
|
QStringList EditTags::data(struct dive *d) const
|
||||||
{
|
{
|
||||||
QStringList res;
|
QStringList res;
|
||||||
for (const struct tag_entry *tag = d->tag_list; tag; tag = tag->next)
|
for (const divetag *tag: d->tags)
|
||||||
res.push_back(QString::fromStdString(tag->tag->name));
|
res.push_back(QString::fromStdString(tag->name));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditTags::set(struct dive *d, const QStringList &v) const
|
void EditTags::set(struct dive *d, const QStringList &v) const
|
||||||
{
|
{
|
||||||
taglist_free(d->tag_list);
|
d->tags.clear();
|
||||||
d->tag_list = NULL;
|
|
||||||
for (const QString &tag: v)
|
for (const QString &tag: v)
|
||||||
taglist_add_tag(&d->tag_list, qPrintable(tag));
|
taglist_add_tag(d->tags, tag.toStdString());
|
||||||
taglist_cleanup(&d->tag_list);
|
taglist_cleanup(d->tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EditTags::fieldName() const
|
QString EditTags::fieldName() const
|
||||||
@ -587,14 +582,13 @@ QString EditTags::fieldName() const
|
|||||||
// ***** Buddies *****
|
// ***** Buddies *****
|
||||||
QStringList EditBuddies::data(struct dive *d) const
|
QStringList EditBuddies::data(struct dive *d) const
|
||||||
{
|
{
|
||||||
return stringToList(d->buddy);
|
return stringToList(QString::fromStdString(d->buddy));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditBuddies::set(struct dive *d, const QStringList &v) const
|
void EditBuddies::set(struct dive *d, const QStringList &v) const
|
||||||
{
|
{
|
||||||
QString text = v.join(", ");
|
QString text = v.join(", ");
|
||||||
free(d->buddy);
|
d->buddy = text.toStdString();
|
||||||
d->buddy = copy_qstring(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EditBuddies::fieldName() const
|
QString EditBuddies::fieldName() const
|
||||||
@ -605,14 +599,13 @@ QString EditBuddies::fieldName() const
|
|||||||
// ***** DiveGuide *****
|
// ***** DiveGuide *****
|
||||||
QStringList EditDiveGuide::data(struct dive *d) const
|
QStringList EditDiveGuide::data(struct dive *d) const
|
||||||
{
|
{
|
||||||
return stringToList(d->diveguide);
|
return stringToList(QString::fromStdString(d->diveguide));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditDiveGuide::set(struct dive *d, const QStringList &v) const
|
void EditDiveGuide::set(struct dive *d, const QStringList &v) const
|
||||||
{
|
{
|
||||||
QString text = v.join(", ");
|
QString text = v.join(", ");
|
||||||
free(d->diveguide);
|
d->diveguide = text.toStdString();
|
||||||
d->diveguide = copy_qstring(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EditDiveGuide::fieldName() const
|
QString EditDiveGuide::fieldName() const
|
||||||
@ -620,19 +613,8 @@ QString EditDiveGuide::fieldName() const
|
|||||||
return Command::Base::tr("dive guide");
|
return Command::Base::tr("dive guide");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void swapCandQString(QString &q, char *&c)
|
PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn)
|
||||||
{
|
{
|
||||||
QString tmp(c);
|
|
||||||
free(c);
|
|
||||||
c = copy_qstring(q);
|
|
||||||
q = std::move(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn),
|
|
||||||
tags(nullptr)
|
|
||||||
{
|
|
||||||
memset(&cylinders, 0, sizeof(cylinders));
|
|
||||||
memset(&weightsystems, 0, sizeof(weightsystems));
|
|
||||||
if (what.notes)
|
if (what.notes)
|
||||||
notes = data->notes;
|
notes = data->notes;
|
||||||
if (what.diveguide)
|
if (what.diveguide)
|
||||||
@ -656,16 +638,16 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
|
|||||||
if (what.divesite)
|
if (what.divesite)
|
||||||
divesite = data->dive_site;
|
divesite = data->dive_site;
|
||||||
if (what.tags)
|
if (what.tags)
|
||||||
tags = taglist_copy(data->tag_list);
|
tags = data->tags;
|
||||||
if (what.cylinders) {
|
if (what.cylinders) {
|
||||||
copy_cylinders(&data->cylinders, &cylinders);
|
cylinders = data->cylinders;
|
||||||
// Paste cylinders is "special":
|
// Paste cylinders is "special":
|
||||||
// 1) For cylinders that exist in the destination dive we keep the gas-mix and pressures.
|
// 1) For cylinders that exist in the destination dive we keep the gas-mix and pressures.
|
||||||
// 2) For cylinders that do not yet exist in the destination dive, we set the pressures to 0, i.e. unset.
|
// 2) For cylinders that do not yet exist in the destination dive, we set the pressures to 0, i.e. unset.
|
||||||
// Moreover, for these we set the manually_added flag, because they weren't downloaded from a DC.
|
// Moreover, for these we set the manually_added flag, because they weren't downloaded from a DC.
|
||||||
for (int i = 0; i < d->cylinders.nr && i < cylinders.nr; ++i) {
|
for (size_t i = 0; i < d->cylinders.size() && i < cylinders.size(); ++i) {
|
||||||
const cylinder_t &src = *get_cylinder(d, i);
|
const cylinder_t &src = d->cylinders[i];
|
||||||
cylinder_t &dst = cylinders.cylinders[i];
|
cylinder_t &dst = cylinders[i];
|
||||||
dst.gasmix = src.gasmix;
|
dst.gasmix = src.gasmix;
|
||||||
dst.start = src.start;
|
dst.start = src.start;
|
||||||
dst.end = src.end;
|
dst.end = src.end;
|
||||||
@ -679,8 +661,8 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
|
|||||||
dst.bestmix_o2 = src.bestmix_o2;
|
dst.bestmix_o2 = src.bestmix_o2;
|
||||||
dst.bestmix_he = src.bestmix_he;
|
dst.bestmix_he = src.bestmix_he;
|
||||||
}
|
}
|
||||||
for (int i = d->cylinders.nr; i < cylinders.nr; ++i) {
|
for (size_t i = d->cylinders.size(); i < cylinders.size(); ++i) {
|
||||||
cylinder_t &cyl = cylinders.cylinders[i];
|
cylinder_t &cyl = cylinders[i];
|
||||||
cyl.start.mbar = 0;
|
cyl.start.mbar = 0;
|
||||||
cyl.end.mbar = 0;
|
cyl.end.mbar = 0;
|
||||||
cyl.sample_start.mbar = 0;
|
cyl.sample_start.mbar = 0;
|
||||||
@ -689,7 +671,7 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (what.weights)
|
if (what.weights)
|
||||||
copy_weights(&data->weightsystems, &weightsystems);
|
weightsystems = data->weightsystems;
|
||||||
if (what.number)
|
if (what.number)
|
||||||
number = data->number;
|
number = data->number;
|
||||||
if (what.when)
|
if (what.when)
|
||||||
@ -698,22 +680,18 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
|
|||||||
|
|
||||||
PasteState::~PasteState()
|
PasteState::~PasteState()
|
||||||
{
|
{
|
||||||
taglist_free(tags);
|
|
||||||
clear_cylinder_table(&cylinders);
|
|
||||||
clear_weightsystem_table(&weightsystems);
|
|
||||||
free(weightsystems.weightsystems);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PasteState::swap(dive_components what)
|
void PasteState::swap(dive_components what)
|
||||||
{
|
{
|
||||||
if (what.notes)
|
if (what.notes)
|
||||||
swapCandQString(notes, d->notes);
|
std::swap(notes, d->notes);
|
||||||
if (what.diveguide)
|
if (what.diveguide)
|
||||||
swapCandQString(diveguide, d->diveguide);
|
std::swap(diveguide, d->diveguide);
|
||||||
if (what.buddy)
|
if (what.buddy)
|
||||||
swapCandQString(buddy, d->buddy);
|
std::swap(buddy, d->buddy);
|
||||||
if (what.suit)
|
if (what.suit)
|
||||||
swapCandQString(suit, d->suit);
|
std::swap(suit, d->suit);
|
||||||
if (what.rating)
|
if (what.rating)
|
||||||
std::swap(rating, d->rating);
|
std::swap(rating, d->rating);
|
||||||
if (what.visibility)
|
if (what.visibility)
|
||||||
@ -729,7 +707,7 @@ void PasteState::swap(dive_components what)
|
|||||||
if (what.divesite)
|
if (what.divesite)
|
||||||
std::swap(divesite, d->dive_site);
|
std::swap(divesite, d->dive_site);
|
||||||
if (what.tags)
|
if (what.tags)
|
||||||
std::swap(tags, d->tag_list);
|
std::swap(tags, d->tags);
|
||||||
if (what.cylinders)
|
if (what.cylinders)
|
||||||
std::swap(cylinders, d->cylinders);
|
std::swap(cylinders, d->cylinders);
|
||||||
if (what.weights)
|
if (what.weights)
|
||||||
@ -800,40 +778,34 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive),
|
|||||||
when(0),
|
when(0),
|
||||||
maxdepth({0}),
|
maxdepth({0}),
|
||||||
meandepth({0}),
|
meandepth({0}),
|
||||||
dc({ 0 }),
|
|
||||||
notes(nullptr),
|
|
||||||
surface_pressure({0}),
|
surface_pressure({0}),
|
||||||
duration({0}),
|
duration({0}),
|
||||||
salinity(0)
|
salinity(0)
|
||||||
{
|
{
|
||||||
memset(&cylinders, 0, sizeof(cylinders));
|
|
||||||
if (!d)
|
if (!d)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Fix source. Things might be inconsistent after modifying the profile.
|
// Fix source. Things might be inconsistent after modifying the profile.
|
||||||
source->maxdepth.mm = source->dc.maxdepth.mm = 0;
|
source->maxdepth.mm = source->dcs[0].maxdepth.mm = 0;
|
||||||
fixup_dive(source);
|
fixup_dive(source);
|
||||||
|
|
||||||
when = source->when;
|
when = source->when;
|
||||||
maxdepth = source->maxdepth;
|
maxdepth = source->maxdepth;
|
||||||
meandepth = source->meandepth;
|
meandepth = source->meandepth;
|
||||||
notes = copy_string(source->notes);
|
notes = source->notes;
|
||||||
duration = source->duration;
|
duration = source->duration;
|
||||||
salinity = source->salinity;
|
salinity = source->salinity;
|
||||||
surface_pressure = source->surface_pressure;
|
surface_pressure = source->surface_pressure;
|
||||||
|
|
||||||
// This resets the dive computers and cylinders of the source dive, avoiding deep copies.
|
// This resets the dive computers and cylinders of the source dive, avoiding deep copies.
|
||||||
std::swap(source->cylinders, cylinders);
|
std::swap(source->cylinders, cylinders);
|
||||||
std::swap(source->dc, dc);
|
std::swap(source->dcs[0], dc);
|
||||||
|
|
||||||
setText(Command::Base::tr("Replan dive"));
|
setText(Command::Base::tr("Replan dive"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReplanDive::~ReplanDive()
|
ReplanDive::~ReplanDive()
|
||||||
{
|
{
|
||||||
clear_cylinder_table(&cylinders);
|
|
||||||
free_dive_dcs(&dc);
|
|
||||||
free(notes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReplanDive::workToBeDone()
|
bool ReplanDive::workToBeDone()
|
||||||
@ -847,7 +819,7 @@ void ReplanDive::undo()
|
|||||||
std::swap(d->maxdepth, maxdepth);
|
std::swap(d->maxdepth, maxdepth);
|
||||||
std::swap(d->meandepth, meandepth);
|
std::swap(d->meandepth, meandepth);
|
||||||
std::swap(d->cylinders, cylinders);
|
std::swap(d->cylinders, cylinders);
|
||||||
std::swap(d->dc, dc);
|
std::swap(d->dcs[0], dc);
|
||||||
std::swap(d->notes, notes);
|
std::swap(d->notes, notes);
|
||||||
std::swap(d->surface_pressure, surface_pressure);
|
std::swap(d->surface_pressure, surface_pressure);
|
||||||
std::swap(d->duration, duration);
|
std::swap(d->duration, duration);
|
||||||
@ -888,10 +860,9 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int
|
|||||||
maxdepth({0}),
|
maxdepth({0}),
|
||||||
meandepth({0}),
|
meandepth({0}),
|
||||||
dcmaxdepth({0}),
|
dcmaxdepth({0}),
|
||||||
duration({0}),
|
duration({0})
|
||||||
dc({ 0 })
|
|
||||||
{
|
{
|
||||||
const struct divecomputer *sdc = get_dive_dc_const(source, dcNr);
|
const struct divecomputer *sdc = get_dive_dc(source, dcNr);
|
||||||
if (!sdc)
|
if (!sdc)
|
||||||
d = nullptr; // Signal that we refuse to do anything.
|
d = nullptr; // Signal that we refuse to do anything.
|
||||||
if (!d)
|
if (!d)
|
||||||
@ -902,15 +873,14 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int
|
|||||||
meandepth = source->meandepth;
|
meandepth = source->meandepth;
|
||||||
duration = source->duration;
|
duration = source->duration;
|
||||||
|
|
||||||
copy_samples(sdc, &dc);
|
dc.samples = sdc->samples;
|
||||||
copy_events(sdc, &dc);
|
dc.events = sdc->events;
|
||||||
|
|
||||||
setText(editProfileTypeToString(type, count) + " " + diveNumberOrDate(d));
|
setText(editProfileTypeToString(type, count) + " " + diveNumberOrDate(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
EditProfile::~EditProfile()
|
EditProfile::~EditProfile()
|
||||||
{
|
{
|
||||||
free_dive_dcs(&dc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditProfile::workToBeDone()
|
bool EditProfile::workToBeDone()
|
||||||
@ -924,8 +894,6 @@ void EditProfile::undo()
|
|||||||
if (!sdc)
|
if (!sdc)
|
||||||
return;
|
return;
|
||||||
std::swap(sdc->samples, dc.samples);
|
std::swap(sdc->samples, dc.samples);
|
||||||
std::swap(sdc->alloc_samples, dc.alloc_samples);
|
|
||||||
std::swap(sdc->sample, dc.sample);
|
|
||||||
std::swap(sdc->events, dc.events);
|
std::swap(sdc->events, dc.events);
|
||||||
std::swap(sdc->maxdepth, dc.maxdepth);
|
std::swap(sdc->maxdepth, dc.maxdepth);
|
||||||
std::swap(d->maxdepth, maxdepth);
|
std::swap(d->maxdepth, maxdepth);
|
||||||
@ -964,10 +932,10 @@ bool AddWeight::workToBeDone()
|
|||||||
void AddWeight::undo()
|
void AddWeight::undo()
|
||||||
{
|
{
|
||||||
for (dive *d: dives) {
|
for (dive *d: dives) {
|
||||||
if (d->weightsystems.nr <= 0)
|
if (d->weightsystems.empty())
|
||||||
continue;
|
continue;
|
||||||
remove_weightsystem(d, d->weightsystems.nr - 1);
|
d->weightsystems.pop_back();
|
||||||
emit diveListNotifier.weightRemoved(d, d->weightsystems.nr);
|
emit diveListNotifier.weightRemoved(d, d->weightsystems.size());
|
||||||
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
|
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -975,31 +943,26 @@ void AddWeight::undo()
|
|||||||
void AddWeight::redo()
|
void AddWeight::redo()
|
||||||
{
|
{
|
||||||
for (dive *d: dives) {
|
for (dive *d: dives) {
|
||||||
add_cloned_weightsystem(&d->weightsystems, empty_weightsystem);
|
d->weightsystems.emplace_back();
|
||||||
emit diveListNotifier.weightAdded(d, d->weightsystems.nr - 1);
|
emit diveListNotifier.weightAdded(d, d->weightsystems.size() - 1);
|
||||||
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
|
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_weightsystem_index(const struct dive *d, weightsystem_t ws)
|
static int find_weightsystem_index(const struct dive *d, const weightsystem_t &ws)
|
||||||
{
|
{
|
||||||
for (int idx = 0; idx < d->weightsystems.nr; ++idx) {
|
return index_of_if(d->weightsystems, [&ws](auto &ws2) { return same_weightsystem(ws2, ws); });
|
||||||
if (same_weightsystem(d->weightsystems.weightsystems[idx], ws))
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EditWeightBase::EditWeightBase(int index, bool currentDiveOnly) :
|
EditWeightBase::EditWeightBase(int index, bool currentDiveOnly) :
|
||||||
EditDivesBase(currentDiveOnly),
|
EditDivesBase(currentDiveOnly)
|
||||||
ws(empty_weightsystem)
|
|
||||||
{
|
{
|
||||||
// Get the old weightsystem, bail if index is invalid
|
// Get the old weightsystem, bail if index is invalid
|
||||||
if (!current || index < 0 || index >= current->weightsystems.nr) {
|
if (!current || index < 0 || static_cast<size_t>(index) >= current->weightsystems.size()) {
|
||||||
dives.clear();
|
dives.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ws = clone_weightsystem(current->weightsystems.weightsystems[index]);
|
ws = current->weightsystems[index];
|
||||||
|
|
||||||
// Deleting a weightsystem from multiple dives is semantically ill-defined.
|
// Deleting a weightsystem from multiple dives is semantically ill-defined.
|
||||||
// What we will do is trying to delete the same weightsystem if it exists.
|
// What we will do is trying to delete the same weightsystem if it exists.
|
||||||
@ -1025,7 +988,6 @@ EditWeightBase::EditWeightBase(int index, bool currentDiveOnly) :
|
|||||||
|
|
||||||
EditWeightBase::~EditWeightBase()
|
EditWeightBase::~EditWeightBase()
|
||||||
{
|
{
|
||||||
free_weightsystem(ws);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditWeightBase::workToBeDone()
|
bool EditWeightBase::workToBeDone()
|
||||||
@ -1047,7 +1009,7 @@ RemoveWeight::RemoveWeight(int index, bool currentDiveOnly) :
|
|||||||
void RemoveWeight::undo()
|
void RemoveWeight::undo()
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < dives.size(); ++i) {
|
for (size_t i = 0; i < dives.size(); ++i) {
|
||||||
add_to_weightsystem_table(&dives[i]->weightsystems, indices[i], clone_weightsystem(ws));
|
add_to_weightsystem_table(&dives[i]->weightsystems, indices[i], ws);
|
||||||
emit diveListNotifier.weightAdded(dives[i], indices[i]);
|
emit diveListNotifier.weightAdded(dives[i], indices[i]);
|
||||||
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
|
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
|
||||||
}
|
}
|
||||||
@ -1064,8 +1026,7 @@ void RemoveWeight::redo()
|
|||||||
|
|
||||||
// ***** Edit Weight *****
|
// ***** Edit Weight *****
|
||||||
EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) :
|
EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) :
|
||||||
EditWeightBase(index, currentDiveOnly),
|
EditWeightBase(index, currentDiveOnly)
|
||||||
new_ws(empty_weightsystem)
|
|
||||||
{
|
{
|
||||||
if (dives.empty())
|
if (dives.empty())
|
||||||
return;
|
return;
|
||||||
@ -1077,15 +1038,13 @@ EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) :
|
|||||||
setText(QStringLiteral("%1 [%2]").arg(Command::Base::tr("Edit weight (%n dive(s))", "", num_dives)).arg(getListOfDives(dives)));
|
setText(QStringLiteral("%1 [%2]").arg(Command::Base::tr("Edit weight (%n dive(s))", "", num_dives)).arg(getListOfDives(dives)));
|
||||||
|
|
||||||
// Try to untranslate the weightsystem name
|
// Try to untranslate the weightsystem name
|
||||||
new_ws = clone_weightsystem(wsIn);
|
new_ws = std::move(wsIn);
|
||||||
QString vString(new_ws.description);
|
QString vString = QString::fromStdString(new_ws.description);
|
||||||
for (int i = 0; i < MAX_WS_INFO && ws_info[i].name; ++i) {
|
auto it = std::find_if(ws_info_table.begin(), ws_info_table.end(),
|
||||||
if (gettextFromC::tr(ws_info[i].name) == vString) {
|
[&vString](const ws_info &info)
|
||||||
free_weightsystem(new_ws);
|
{ return gettextFromC::tr(info.name.c_str()) == vString; });
|
||||||
new_ws.description = copy_string(ws_info[i].name);
|
if (it != ws_info_table.end())
|
||||||
break;
|
new_ws.description = it->name;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If that doesn't change anything, do nothing
|
// If that doesn't change anything, do nothing
|
||||||
if (same_weightsystem(ws, new_ws)) {
|
if (same_weightsystem(ws, new_ws)) {
|
||||||
@ -1096,13 +1055,12 @@ EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) :
|
|||||||
|
|
||||||
EditWeight::~EditWeight()
|
EditWeight::~EditWeight()
|
||||||
{
|
{
|
||||||
free_weightsystem(new_ws);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditWeight::redo()
|
void EditWeight::redo()
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < dives.size(); ++i) {
|
for (size_t i = 0; i < dives.size(); ++i) {
|
||||||
add_weightsystem_description(&new_ws); // This updates the weightsystem info table
|
add_weightsystem_description(new_ws); // This updates the weightsystem info table
|
||||||
set_weightsystem(dives[i], indices[i], new_ws);
|
set_weightsystem(dives[i], indices[i], new_ws);
|
||||||
emit diveListNotifier.weightEdited(dives[i], indices[i]);
|
emit diveListNotifier.weightEdited(dives[i], indices[i]);
|
||||||
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
|
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
|
||||||
@ -1118,8 +1076,7 @@ void EditWeight::undo()
|
|||||||
|
|
||||||
// ***** Add Cylinder *****
|
// ***** Add Cylinder *****
|
||||||
AddCylinder::AddCylinder(bool currentDiveOnly) :
|
AddCylinder::AddCylinder(bool currentDiveOnly) :
|
||||||
EditDivesBase(currentDiveOnly),
|
EditDivesBase(currentDiveOnly)
|
||||||
cyl(empty_cylinder)
|
|
||||||
{
|
{
|
||||||
if (dives.empty())
|
if (dives.empty())
|
||||||
return;
|
return;
|
||||||
@ -1133,7 +1090,6 @@ AddCylinder::AddCylinder(bool currentDiveOnly) :
|
|||||||
|
|
||||||
AddCylinder::~AddCylinder()
|
AddCylinder::~AddCylinder()
|
||||||
{
|
{
|
||||||
free_cylinder(cyl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddCylinder::workToBeDone()
|
bool AddCylinder::workToBeDone()
|
||||||
@ -1157,7 +1113,7 @@ void AddCylinder::redo()
|
|||||||
for (dive *d: dives) {
|
for (dive *d: dives) {
|
||||||
int index = first_hidden_cylinder(d);
|
int index = first_hidden_cylinder(d);
|
||||||
indexes.push_back(index);
|
indexes.push_back(index);
|
||||||
add_cylinder(&d->cylinders, index, clone_cylinder(cyl));
|
add_cylinder(&d->cylinders, index, cyl);
|
||||||
update_cylinder_related_info(d);
|
update_cylinder_related_info(d);
|
||||||
emit diveListNotifier.cylinderAdded(d, index);
|
emit diveListNotifier.cylinderAdded(d, index);
|
||||||
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
|
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
|
||||||
@ -1166,14 +1122,14 @@ void AddCylinder::redo()
|
|||||||
|
|
||||||
static bool same_cylinder_type(const cylinder_t &cyl1, const cylinder_t &cyl2)
|
static bool same_cylinder_type(const cylinder_t &cyl1, const cylinder_t &cyl2)
|
||||||
{
|
{
|
||||||
return same_string(cyl1.type.description, cyl2.type.description) &&
|
return std::tie(cyl1.cylinder_use, cyl1.type.description) ==
|
||||||
cyl1.cylinder_use == cyl2.cylinder_use;
|
std::tie(cyl2.cylinder_use, cyl2.type.description);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool same_cylinder_size(const cylinder_t &cyl1, const cylinder_t &cyl2)
|
static bool same_cylinder_size(const cylinder_t &cyl1, const cylinder_t &cyl2)
|
||||||
{
|
{
|
||||||
return cyl1.type.size.mliter == cyl2.type.size.mliter &&
|
return std::tie(cyl1.type.size.mliter, cyl1.type.workingpressure.mbar) ==
|
||||||
cyl1.type.workingpressure.mbar == cyl2.type.workingpressure.mbar;
|
std::tie(cyl2.type.size.mliter, cyl2.type.workingpressure.mbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags for comparing cylinders
|
// Flags for comparing cylinders
|
||||||
@ -1186,7 +1142,7 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt
|
|||||||
EditDivesBase(currentDiveOnly)
|
EditDivesBase(currentDiveOnly)
|
||||||
{
|
{
|
||||||
// Get the old cylinder, bail if index is invalid
|
// Get the old cylinder, bail if index is invalid
|
||||||
if (!current || index < 0 || index >= current->cylinders.nr) {
|
if (!current || index < 0 || index >= static_cast<int>(current->cylinders.size())) {
|
||||||
dives.clear();
|
dives.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1198,12 +1154,12 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt
|
|||||||
cyl.reserve(dives.size());
|
cyl.reserve(dives.size());
|
||||||
|
|
||||||
for (dive *d: dives) {
|
for (dive *d: dives) {
|
||||||
if (index >= d->cylinders.nr)
|
if (index >= static_cast<int>(d->cylinders.size()))
|
||||||
continue;
|
continue;
|
||||||
if (nonProtectedOnly && is_cylinder_prot(d, index))
|
if (nonProtectedOnly && is_cylinder_prot(d, index))
|
||||||
continue;
|
continue;
|
||||||
// We checked that the cylinder exists above.
|
// We checked that the cylinder exists above.
|
||||||
const cylinder_t &cylinder = *get_cylinder(d, index);
|
const cylinder_t &cylinder = d->cylinders[index];
|
||||||
if (d != current &&
|
if (d != current &&
|
||||||
(!same_cylinder_size(orig, cylinder) || !same_cylinder_type(orig, cylinder))) {
|
(!same_cylinder_size(orig, cylinder) || !same_cylinder_type(orig, cylinder))) {
|
||||||
// when editing cylinders, we assume that the user wanted to edit the 'n-th' cylinder
|
// when editing cylinders, we assume that the user wanted to edit the 'n-th' cylinder
|
||||||
@ -1215,15 +1171,13 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt
|
|||||||
// that's silly as it's always the same value - but we need this vector of indices in the case where we add
|
// that's silly as it's always the same value - but we need this vector of indices in the case where we add
|
||||||
// a cylinder to several dives as the spot will potentially be different in different dives
|
// a cylinder to several dives as the spot will potentially be different in different dives
|
||||||
indexes.push_back(index);
|
indexes.push_back(index);
|
||||||
cyl.push_back(clone_cylinder(cylinder));
|
cyl.push_back(cylinder);
|
||||||
}
|
}
|
||||||
dives = std::move(divesNew);
|
dives = std::move(divesNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
EditCylinderBase::~EditCylinderBase()
|
EditCylinderBase::~EditCylinderBase()
|
||||||
{
|
{
|
||||||
for (cylinder_t c: cyl)
|
|
||||||
free_cylinder(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditCylinderBase::workToBeDone()
|
bool EditCylinderBase::workToBeDone()
|
||||||
@ -1244,9 +1198,9 @@ RemoveCylinder::RemoveCylinder(int index, bool currentDiveOnly) :
|
|||||||
void RemoveCylinder::undo()
|
void RemoveCylinder::undo()
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < dives.size(); ++i) {
|
for (size_t i = 0; i < dives.size(); ++i) {
|
||||||
std::vector<int> mapping = get_cylinder_map_for_add(dives[i]->cylinders.nr, indexes[i]);
|
std::vector<int> mapping = get_cylinder_map_for_add(dives[i]->cylinders.size(), indexes[i]);
|
||||||
add_cylinder(&dives[i]->cylinders, indexes[i], clone_cylinder(cyl[i]));
|
add_cylinder(&dives[i]->cylinders, indexes[i], cyl[i]);
|
||||||
cylinder_renumber(dives[i], &mapping[0]);
|
cylinder_renumber(*dives[i], &mapping[0]);
|
||||||
update_cylinder_related_info(dives[i]);
|
update_cylinder_related_info(dives[i]);
|
||||||
emit diveListNotifier.cylinderAdded(dives[i], indexes[i]);
|
emit diveListNotifier.cylinderAdded(dives[i], indexes[i]);
|
||||||
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
|
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
|
||||||
@ -1256,9 +1210,9 @@ void RemoveCylinder::undo()
|
|||||||
void RemoveCylinder::redo()
|
void RemoveCylinder::redo()
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < dives.size(); ++i) {
|
for (size_t i = 0; i < dives.size(); ++i) {
|
||||||
std::vector<int> mapping = get_cylinder_map_for_remove(dives[i]->cylinders.nr, indexes[i]);
|
std::vector<int> mapping = get_cylinder_map_for_remove(dives[i]->cylinders.size(), indexes[i]);
|
||||||
remove_cylinder(dives[i], indexes[i]);
|
remove_cylinder(dives[i], indexes[i]);
|
||||||
cylinder_renumber(dives[i], &mapping[0]);
|
cylinder_renumber(*dives[i], &mapping[0]);
|
||||||
update_cylinder_related_info(dives[i]);
|
update_cylinder_related_info(dives[i]);
|
||||||
emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]);
|
emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]);
|
||||||
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
|
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
|
||||||
@ -1291,15 +1245,12 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn,
|
|||||||
else
|
else
|
||||||
setText(Command::Base::tr("Edit cylinder (%n dive(s))", "", dives.size()));
|
setText(Command::Base::tr("Edit cylinder (%n dive(s))", "", dives.size()));
|
||||||
|
|
||||||
QString description = cylIn.type.description;
|
|
||||||
|
|
||||||
// The base class copied the cylinders for us, let's edit them
|
// The base class copied the cylinders for us, let's edit them
|
||||||
for (int i = 0; i < (int)indexes.size(); ++i) {
|
for (int i = 0; i < (int)indexes.size(); ++i) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case EditCylinderType::TYPE:
|
case EditCylinderType::TYPE:
|
||||||
free((void *)cyl[i].type.description);
|
|
||||||
cyl[i].type = cylIn.type;
|
cyl[i].type = cylIn.type;
|
||||||
cyl[i].type.description = copy_qstring(description);
|
cyl[i].type.description = cylIn.type.description;
|
||||||
cyl[i].cylinder_use = cylIn.cylinder_use;
|
cyl[i].cylinder_use = cylIn.cylinder_use;
|
||||||
break;
|
break;
|
||||||
case EditCylinderType::PRESSURE:
|
case EditCylinderType::PRESSURE:
|
||||||
@ -1310,7 +1261,7 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn,
|
|||||||
cyl[i].gasmix = cylIn.gasmix;
|
cyl[i].gasmix = cylIn.gasmix;
|
||||||
cyl[i].bestmix_o2 = cylIn.bestmix_o2;
|
cyl[i].bestmix_o2 = cylIn.bestmix_o2;
|
||||||
cyl[i].bestmix_he = cylIn.bestmix_he;
|
cyl[i].bestmix_he = cylIn.bestmix_he;
|
||||||
sanitize_gasmix(&cyl[i].gasmix);
|
sanitize_gasmix(cyl[i].gasmix);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1319,7 +1270,8 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn,
|
|||||||
void EditCylinder::redo()
|
void EditCylinder::redo()
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < dives.size(); ++i) {
|
for (size_t i = 0; i < dives.size(); ++i) {
|
||||||
set_tank_info_data(&tank_info_table, cyl[i].type.description, cyl[i].type.size, cyl[i].type.workingpressure);
|
const std::string &name = cyl[i].type.description;
|
||||||
|
set_tank_info_data(tank_info_table, name, cyl[i].type.size, cyl[i].type.workingpressure);
|
||||||
std::swap(*get_cylinder(dives[i], indexes[i]), cyl[i]);
|
std::swap(*get_cylinder(dives[i], indexes[i]), cyl[i]);
|
||||||
update_cylinder_related_info(dives[i]);
|
update_cylinder_related_info(dives[i]);
|
||||||
emit diveListNotifier.cylinderEdited(dives[i], indexes[i]);
|
emit diveListNotifier.cylinderEdited(dives[i], indexes[i]);
|
||||||
@ -1344,13 +1296,13 @@ EditSensors::EditSensors(int toCylinderIn, int fromCylinderIn, int dcNr)
|
|||||||
|
|
||||||
void EditSensors::mapSensors(int toCyl, int fromCyl)
|
void EditSensors::mapSensors(int toCyl, int fromCyl)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < dc->samples; ++i) {
|
for (auto &sample: dc->samples) {
|
||||||
for (int s = 0; s < MAX_SENSORS; ++s) {
|
for (int s = 0; s < MAX_SENSORS; ++s) {
|
||||||
if (dc->sample[i].pressure[s].mbar && dc->sample[i].sensor[s] == fromCyl)
|
if (sample.pressure[s].mbar && sample.sensor[s] == fromCyl)
|
||||||
dc->sample[i].sensor[s] = toCyl;
|
sample.sensor[s] = toCyl;
|
||||||
// In case the cylinder we are moving to has a sensor attached, move it to the other cylinder
|
// In case the cylinder we are moving to has a sensor attached, move it to the other cylinder
|
||||||
else if (dc->sample[i].pressure[s].mbar && dc->sample[i].sensor[s] == toCyl)
|
else if (sample.pressure[s].mbar && sample.sensor[s] == toCyl)
|
||||||
dc->sample[i].sensor[s] = fromCyl;
|
sample.sensor[s] = fromCyl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emit diveListNotifier.diveComputerEdited(dc);
|
emit diveListNotifier.diveComputerEdited(dc);
|
||||||
@ -1409,9 +1361,9 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s
|
|||||||
changedFields |= DiveField::ATM_PRESS;
|
changedFields |= DiveField::ATM_PRESS;
|
||||||
if (oldDive->dive_site != newDive->dive_site)
|
if (oldDive->dive_site != newDive->dive_site)
|
||||||
changedFields |= DiveField::DIVESITE;
|
changedFields |= DiveField::DIVESITE;
|
||||||
if (!same_string(oldDive->diveguide, newDive->diveguide))
|
if (oldDive->diveguide != newDive->diveguide)
|
||||||
changedFields |= DiveField::DIVEGUIDE;
|
changedFields |= DiveField::DIVEGUIDE;
|
||||||
if (!same_string(oldDive->buddy, newDive->buddy))
|
if (oldDive->buddy != newDive->buddy)
|
||||||
changedFields |= DiveField::BUDDY;
|
changedFields |= DiveField::BUDDY;
|
||||||
if (oldDive->rating != newDive->rating)
|
if (oldDive->rating != newDive->rating)
|
||||||
changedFields |= DiveField::RATING;
|
changedFields |= DiveField::RATING;
|
||||||
@ -1425,13 +1377,13 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s
|
|||||||
changedFields |= DiveField::SURGE;
|
changedFields |= DiveField::SURGE;
|
||||||
if (oldDive->chill != newDive->chill)
|
if (oldDive->chill != newDive->chill)
|
||||||
changedFields |= DiveField::CHILL;
|
changedFields |= DiveField::CHILL;
|
||||||
if (!same_string(oldDive->suit, newDive->suit))
|
if (oldDive->suit != newDive->suit)
|
||||||
changedFields |= DiveField::SUIT;
|
changedFields |= DiveField::SUIT;
|
||||||
if (taglist_get_tagstring(oldDive->tag_list) != taglist_get_tagstring(newDive->tag_list)) // This is cheating. Do we have a taglist comparison function?
|
if (taglist_get_tagstring(oldDive->tags) != taglist_get_tagstring(newDive->tags)) // This is cheating. Do we have a taglist comparison function?
|
||||||
changedFields |= DiveField::TAGS;
|
changedFields |= DiveField::TAGS;
|
||||||
if (oldDive->dc.divemode != newDive->dc.divemode)
|
if (oldDive->dcs[0].divemode != newDive->dcs[0].divemode)
|
||||||
changedFields |= DiveField::MODE;
|
changedFields |= DiveField::MODE;
|
||||||
if (!same_string(oldDive->notes, newDive->notes))
|
if (oldDive->notes != newDive->notes)
|
||||||
changedFields |= DiveField::NOTES;
|
changedFields |= DiveField::NOTES;
|
||||||
if (oldDive->salinity != newDive->salinity)
|
if (oldDive->salinity != newDive->salinity)
|
||||||
changedFields |= DiveField::SALINITY;
|
changedFields |= DiveField::SALINITY;
|
||||||
@ -1442,9 +1394,9 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s
|
|||||||
void EditDive::undo()
|
void EditDive::undo()
|
||||||
{
|
{
|
||||||
if (siteToRemove) {
|
if (siteToRemove) {
|
||||||
int idx = unregister_dive_site(siteToRemove);
|
auto res = divelog.sites.pull(siteToRemove);
|
||||||
siteToAdd.reset(siteToRemove);
|
siteToAdd = std::move(res.ptr);
|
||||||
emit diveListNotifier.diveSiteDeleted(siteToRemove, idx); // Inform frontend of removed dive site.
|
emit diveListNotifier.diveSiteDeleted(siteToRemove, res.idx); // Inform frontend of removed dive site.
|
||||||
}
|
}
|
||||||
|
|
||||||
exchangeDives();
|
exchangeDives();
|
||||||
@ -1454,9 +1406,9 @@ void EditDive::undo()
|
|||||||
void EditDive::redo()
|
void EditDive::redo()
|
||||||
{
|
{
|
||||||
if (siteToAdd) {
|
if (siteToAdd) {
|
||||||
siteToRemove = siteToAdd.get();
|
auto res = divelog.sites.register_site(std::move(siteToAdd)); // Return ownership to backend.
|
||||||
int idx = register_dive_site(siteToAdd.release()); // Return ownership to backend.
|
siteToRemove = res.ptr;
|
||||||
emit diveListNotifier.diveSiteAdded(siteToRemove, idx); // Inform frontend of new dive site.
|
emit diveListNotifier.diveSiteAdded(siteToRemove, res.idx); // Inform frontend of new dive site.
|
||||||
}
|
}
|
||||||
|
|
||||||
exchangeDives();
|
exchangeDives();
|
||||||
@ -1481,7 +1433,7 @@ void EditDive::exchangeDives()
|
|||||||
std::swap(*newDive, *oldDive);
|
std::swap(*newDive, *oldDive);
|
||||||
fulltext_register(oldDive);
|
fulltext_register(oldDive);
|
||||||
if (newDiveSite)
|
if (newDiveSite)
|
||||||
add_dive_to_dive_site(oldDive, newDiveSite);
|
newDiveSite->add_dive(oldDive);
|
||||||
newDiveSite = oldDiveSite; // remember the previous dive site
|
newDiveSite = oldDiveSite; // remember the previous dive site
|
||||||
invalidate_dive_cache(oldDive);
|
invalidate_dive_cache(oldDive);
|
||||||
|
|
||||||
@ -1489,12 +1441,12 @@ void EditDive::exchangeDives()
|
|||||||
QVector<dive *> dives = { oldDive };
|
QVector<dive *> dives = { oldDive };
|
||||||
timestamp_t delta = oldDive->when - newDive->when;
|
timestamp_t delta = oldDive->when - newDive->when;
|
||||||
if (delta != 0) {
|
if (delta != 0) {
|
||||||
sort_dive_table(divelog.dives);
|
divelog.dives.sort();
|
||||||
sort_trip_table(divelog.trips);
|
divelog.trips.sort();
|
||||||
if (newDive->divetrip != oldDive->divetrip)
|
if (newDive->divetrip != oldDive->divetrip)
|
||||||
qWarning("Command::EditDive::redo(): This command does not support moving between trips!");
|
qWarning("Command::EditDive::redo(): This command does not support moving between trips!");
|
||||||
if (oldDive->divetrip)
|
if (oldDive->divetrip)
|
||||||
sort_dive_table(&newDive->divetrip->dives); // Keep the trip-table in order
|
newDive->divetrip->sort_dives(); // Keep the trip-table in order
|
||||||
emit diveListNotifier.divesTimeChanged(delta, dives);
|
emit diveListNotifier.divesTimeChanged(delta, dives);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -88,7 +88,7 @@ private:
|
|||||||
|
|
||||||
// Automatically generate getter and setter in the case for string assignments.
|
// Automatically generate getter and setter in the case for string assignments.
|
||||||
// The third parameter is a pointer to a C-style string in the dive structure.
|
// The third parameter is a pointer to a C-style string in the dive structure.
|
||||||
template <DiveField::Flags ID, char *dive::*PTR>
|
template <DiveField::Flags ID, std::string dive::*PTR>
|
||||||
class EditStringSetter : public EditTemplate<QString, ID> {
|
class EditStringSetter : public EditTemplate<QString, ID> {
|
||||||
private:
|
private:
|
||||||
using EditTemplate<QString, ID>::EditTemplate;
|
using EditTemplate<QString, ID>::EditTemplate;
|
||||||
@ -208,7 +208,7 @@ public:
|
|||||||
// deriving from it and hooks into undo() and redo() to add / remove the dive site.
|
// deriving from it and hooks into undo() and redo() to add / remove the dive site.
|
||||||
class EditDiveSiteNew : public EditDiveSite {
|
class EditDiveSiteNew : public EditDiveSite {
|
||||||
public:
|
public:
|
||||||
OwningDiveSitePtr diveSiteToAdd;
|
std::unique_ptr<dive_site> diveSiteToAdd;
|
||||||
struct dive_site *diveSiteToRemove;
|
struct dive_site *diveSiteToRemove;
|
||||||
EditDiveSiteNew(const QString &newName, bool currentDiveOnly);
|
EditDiveSiteNew(const QString &newName, bool currentDiveOnly);
|
||||||
void undo() override;
|
void undo() override;
|
||||||
@ -289,19 +289,19 @@ public:
|
|||||||
struct PasteState {
|
struct PasteState {
|
||||||
dive *d;
|
dive *d;
|
||||||
dive_site *divesite;
|
dive_site *divesite;
|
||||||
QString notes;
|
std::string notes;
|
||||||
QString diveguide;
|
std::string diveguide;
|
||||||
QString buddy;
|
std::string buddy;
|
||||||
QString suit;
|
std::string suit;
|
||||||
int rating;
|
int rating;
|
||||||
int wavesize;
|
int wavesize;
|
||||||
int visibility;
|
int visibility;
|
||||||
int current;
|
int current;
|
||||||
int surge;
|
int surge;
|
||||||
int chill;
|
int chill;
|
||||||
tag_entry *tags;
|
tag_list tags;
|
||||||
struct cylinder_table cylinders;
|
cylinder_table cylinders;
|
||||||
struct weightsystem_table weightsystems;
|
weightsystem_table weightsystems;
|
||||||
int number;
|
int number;
|
||||||
timestamp_t when;
|
timestamp_t when;
|
||||||
|
|
||||||
@ -329,7 +329,7 @@ class ReplanDive : public Base {
|
|||||||
depth_t maxdepth, meandepth;
|
depth_t maxdepth, meandepth;
|
||||||
struct cylinder_table cylinders;
|
struct cylinder_table cylinders;
|
||||||
struct divecomputer dc;
|
struct divecomputer dc;
|
||||||
char *notes;
|
std::string notes;
|
||||||
pressure_t surface_pressure;
|
pressure_t surface_pressure;
|
||||||
duration_t duration;
|
duration_t duration;
|
||||||
int salinity;
|
int salinity;
|
||||||
@ -465,12 +465,12 @@ public:
|
|||||||
EditDive(dive *oldDive, dive *newDive, dive_site *createDs, dive_site *editDs, location_t dsLocation); // Takes ownership of newDive
|
EditDive(dive *oldDive, dive *newDive, dive_site *createDs, dive_site *editDs, location_t dsLocation); // Takes ownership of newDive
|
||||||
private:
|
private:
|
||||||
dive *oldDive; // Dive that is going to be overwritten
|
dive *oldDive; // Dive that is going to be overwritten
|
||||||
OwningDivePtr newDive; // New data
|
std::unique_ptr<dive> newDive; // New data
|
||||||
dive_site *newDiveSite;
|
dive_site *newDiveSite;
|
||||||
int changedFields;
|
int changedFields;
|
||||||
|
|
||||||
dive_site *siteToRemove;
|
dive_site *siteToRemove;
|
||||||
OwningDiveSitePtr siteToAdd;
|
std::unique_ptr<dive_site> siteToAdd;
|
||||||
|
|
||||||
dive_site *siteToEdit;
|
dive_site *siteToEdit;
|
||||||
location_t dsLocation;
|
location_t dsLocation;
|
||||||
|
|||||||
@ -41,13 +41,12 @@ void EditTripBase::redo()
|
|||||||
// ***** Location *****
|
// ***** Location *****
|
||||||
void EditTripLocation::set(dive_trip *t, const QString &s) const
|
void EditTripLocation::set(dive_trip *t, const QString &s) const
|
||||||
{
|
{
|
||||||
free(t->location);
|
t->location = s.toStdString();
|
||||||
t->location = copy_qstring(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EditTripLocation::data(dive_trip *t) const
|
QString EditTripLocation::data(dive_trip *t) const
|
||||||
{
|
{
|
||||||
return QString(t->location);
|
return QString::fromStdString(t->location);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EditTripLocation::fieldName() const
|
QString EditTripLocation::fieldName() const
|
||||||
@ -63,13 +62,12 @@ TripField EditTripLocation::fieldId() const
|
|||||||
// ***** Notes *****
|
// ***** Notes *****
|
||||||
void EditTripNotes::set(dive_trip *t, const QString &s) const
|
void EditTripNotes::set(dive_trip *t, const QString &s) const
|
||||||
{
|
{
|
||||||
free(t->notes);
|
t->notes = s.toStdString();
|
||||||
t->notes = copy_qstring(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EditTripNotes::data(dive_trip *t) const
|
QString EditTripNotes::data(dive_trip *t) const
|
||||||
{
|
{
|
||||||
return QString(t->notes);
|
return QString::fromStdString(t->notes);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EditTripNotes::fieldName() const
|
QString EditTripNotes::fieldName() const
|
||||||
|
|||||||
@ -30,7 +30,7 @@ protected:
|
|||||||
void redo() override;
|
void redo() override;
|
||||||
|
|
||||||
// Get and set functions to be overriden by sub-classes.
|
// Get and set functions to be overriden by sub-classes.
|
||||||
virtual void set(struct dive_trip *t, const QString &) const = 0;
|
virtual void set(dive_trip *t, const QString &) const = 0;
|
||||||
virtual QString data(struct dive_trip *t) const = 0;
|
virtual QString data(struct dive_trip *t) const = 0;
|
||||||
virtual QString fieldName() const = 0; // Name of the field, used to create the undo menu-entry
|
virtual QString fieldName() const = 0; // Name of the field, used to create the undo menu-entry
|
||||||
virtual TripField fieldId() const = 0;
|
virtual TripField fieldId() const = 0;
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "command_event.h"
|
#include "command_event.h"
|
||||||
#include "core/dive.h"
|
#include "core/dive.h"
|
||||||
#include "core/event.h"
|
|
||||||
#include "core/selection.h"
|
#include "core/selection.h"
|
||||||
#include "core/subsurface-qt/divelistnotifier.h"
|
#include "core/subsurface-qt/divelistnotifier.h"
|
||||||
#include "core/libdivecomputer.h"
|
#include "core/libdivecomputer.h"
|
||||||
@ -35,8 +34,8 @@ void EventBase::updateDive()
|
|||||||
setSelection({ d }, d, dcNr);
|
setSelection({ d }, d, dcNr);
|
||||||
}
|
}
|
||||||
|
|
||||||
AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr),
|
AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event ev) : EventBase(d, dcNr),
|
||||||
eventToAdd(ev)
|
ev(std::move(ev))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,32 +47,29 @@ bool AddEventBase::workToBeDone()
|
|||||||
void AddEventBase::redoit()
|
void AddEventBase::redoit()
|
||||||
{
|
{
|
||||||
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
||||||
eventToRemove = eventToAdd.get();
|
idx = add_event_to_dc(dc, ev); // return ownership to backend
|
||||||
add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddEventBase::undoit()
|
void AddEventBase::undoit()
|
||||||
{
|
{
|
||||||
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
||||||
remove_event_from_dc(dc, eventToRemove);
|
ev = remove_event_from_dc(dc, idx);
|
||||||
eventToAdd.reset(eventToRemove); // take ownership of event
|
|
||||||
eventToRemove = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AddEventBookmark::AddEventBookmark(struct dive *d, int dcNr, int seconds) :
|
AddEventBookmark::AddEventBookmark(struct dive *d, int dcNr, int seconds) :
|
||||||
AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_BOOKMARK, 0, 0, "bookmark"))
|
AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_BOOKMARK, 0, 0, "bookmark"))
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("Add bookmark"));
|
setText(Command::Base::tr("Add bookmark"));
|
||||||
}
|
}
|
||||||
|
|
||||||
AddEventDivemodeSwitch::AddEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode) :
|
AddEventDivemodeSwitch::AddEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode) :
|
||||||
AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_BOOKMARK, 0, divemode, QT_TRANSLATE_NOOP("gettextFromC", "modechange")))
|
AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_BOOKMARK, 0, divemode, QT_TRANSLATE_NOOP("gettextFromC", "modechange")))
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("Add dive mode switch to %1").arg(gettextFromC::tr(divemode_text_ui[divemode])));
|
setText(Command::Base::tr("Add dive mode switch to %1").arg(gettextFromC::tr(divemode_text_ui[divemode])));
|
||||||
}
|
}
|
||||||
|
|
||||||
AddEventSetpointChange::AddEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2) :
|
AddEventSetpointChange::AddEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2) :
|
||||||
AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_PO2, 0, pO2.mbar, QT_TRANSLATE_NOOP("gettextFromC", "SP change"))),
|
AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_PO2, 0, pO2.mbar, QT_TRANSLATE_NOOP("gettextFromC", "SP change"))),
|
||||||
divemode(CCR)
|
divemode(CCR)
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("Add set point change")); // TODO: format pO2 value in bar or psi.
|
setText(Command::Base::tr("Add set point change")); // TODO: format pO2 value in bar or psi.
|
||||||
@ -91,11 +87,11 @@ void AddEventSetpointChange::redoit()
|
|||||||
std::swap(get_dive_dc(d, dcNr)->divemode, divemode);
|
std::swap(get_dive_dc(d, dcNr)->divemode, divemode);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenameEvent::RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name) : EventBase(d, dcNr),
|
RenameEvent::RenameEvent(struct dive *d, int dcNr, int idx, const std::string name) : EventBase(d, dcNr),
|
||||||
eventToAdd(clone_event_rename(ev, name)),
|
idx(idx),
|
||||||
eventToRemove(ev)
|
name(std::move(name))
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("Rename bookmark to %1").arg(name));
|
setText(Command::Base::tr("Rename bookmark to %1").arg(name.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RenameEvent::workToBeDone()
|
bool RenameEvent::workToBeDone()
|
||||||
@ -106,24 +102,25 @@ bool RenameEvent::workToBeDone()
|
|||||||
void RenameEvent::redoit()
|
void RenameEvent::redoit()
|
||||||
{
|
{
|
||||||
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
||||||
swap_event(dc, eventToRemove, eventToAdd.get());
|
event *ev = get_event(dc, idx);
|
||||||
event *tmp = eventToRemove;
|
if (ev)
|
||||||
eventToRemove = eventToAdd.release();
|
std::swap(ev->name, name);
|
||||||
eventToAdd.reset(tmp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenameEvent::undoit()
|
void RenameEvent::undoit()
|
||||||
{
|
{
|
||||||
// Undo and redo do the same thing - they simply swap events
|
// Undo and redo do the same thing - they simply swap names
|
||||||
redoit();
|
redoit();
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveEvent::RemoveEvent(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr),
|
RemoveEvent::RemoveEvent(struct dive *d, int dcNr, int idx) : EventBase(d, dcNr),
|
||||||
eventToRemove(ev),
|
idx(idx), cylinder(-1)
|
||||||
cylinder(ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE ?
|
|
||||||
ev->gas.index : -1)
|
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("Remove %1 event").arg(ev->name));
|
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
||||||
|
event *ev = get_event(dc, idx);
|
||||||
|
if (ev && (ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE))
|
||||||
|
cylinder = ev->gas.index;
|
||||||
|
setText(Command::Base::tr("Remove %1 event").arg(ev->name.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RemoveEvent::workToBeDone()
|
bool RemoveEvent::workToBeDone()
|
||||||
@ -134,16 +131,13 @@ bool RemoveEvent::workToBeDone()
|
|||||||
void RemoveEvent::redoit()
|
void RemoveEvent::redoit()
|
||||||
{
|
{
|
||||||
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
||||||
remove_event_from_dc(dc, eventToRemove);
|
ev = remove_event_from_dc(dc, idx);
|
||||||
eventToAdd.reset(eventToRemove); // take ownership of event
|
|
||||||
eventToRemove = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveEvent::undoit()
|
void RemoveEvent::undoit()
|
||||||
{
|
{
|
||||||
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
||||||
eventToRemove = eventToAdd.get();
|
idx = add_event_to_dc(dc, std::move(ev));
|
||||||
add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveEvent::post() const
|
void RemoveEvent::post() const
|
||||||
@ -165,18 +159,18 @@ AddGasSwitch::AddGasSwitch(struct dive *d, int dcNr, int seconds, int tank) : Ev
|
|||||||
// There shouldn't be more than one gas change per time stamp. Just in case we'll
|
// There shouldn't be more than one gas change per time stamp. Just in case we'll
|
||||||
// support that anyway.
|
// support that anyway.
|
||||||
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
||||||
struct event *gasChangeEvent = dc->events;
|
|
||||||
while ((gasChangeEvent = get_next_event_mutable(gasChangeEvent, "gaschange")) != NULL) {
|
// Note that we remove events in reverse order so that the indexes don't change
|
||||||
if (gasChangeEvent->time.seconds == seconds) {
|
// meaning while removing. This should be an extremely rare case anyway.
|
||||||
eventsToRemove.push_back(gasChangeEvent);
|
for (int idx = static_cast<int>(dc->events.size()) - 1; idx > 0; --idx) {
|
||||||
int idx = gasChangeEvent->gas.index;
|
const event &ev = dc->events[idx];
|
||||||
if (std::find(cylinders.begin(), cylinders.end(), idx) == cylinders.end())
|
if (ev.time.seconds == seconds && ev.name == "gaschange")
|
||||||
cylinders.push_back(idx); // cylinders might have changed their status
|
eventsToRemove.push_back(idx);
|
||||||
}
|
if (std::find(cylinders.begin(), cylinders.end(), ev.gas.index) == cylinders.end())
|
||||||
gasChangeEvent = gasChangeEvent->next;
|
cylinders.push_back(ev.gas.index); // cylinders might have changed their status
|
||||||
}
|
}
|
||||||
|
|
||||||
eventsToAdd.emplace_back(create_gas_switch_event(d, dc, seconds, tank));
|
eventsToAdd.push_back(create_gas_switch_event(d, dc, seconds, tank));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddGasSwitch::workToBeDone()
|
bool AddGasSwitch::workToBeDone()
|
||||||
@ -186,20 +180,21 @@ bool AddGasSwitch::workToBeDone()
|
|||||||
|
|
||||||
void AddGasSwitch::redoit()
|
void AddGasSwitch::redoit()
|
||||||
{
|
{
|
||||||
std::vector<OwningEventPtr> newEventsToAdd;
|
std::vector<event> newEventsToAdd;
|
||||||
std::vector<event *> newEventsToRemove;
|
std::vector<int> newEventsToRemove;
|
||||||
newEventsToAdd.reserve(eventsToRemove.size());
|
newEventsToAdd.reserve(eventsToRemove.size());
|
||||||
newEventsToRemove.reserve(eventsToAdd.size());
|
newEventsToRemove.reserve(eventsToAdd.size());
|
||||||
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
struct divecomputer *dc = get_dive_dc(d, dcNr);
|
||||||
|
|
||||||
for (event *ev: eventsToRemove) {
|
for (int idx: eventsToRemove)
|
||||||
remove_event_from_dc(dc, ev);
|
newEventsToAdd.push_back(remove_event_from_dc(dc, idx));
|
||||||
newEventsToAdd.emplace_back(ev); // take ownership of event
|
|
||||||
}
|
for (auto &ev: eventsToAdd)
|
||||||
for (OwningEventPtr &ev: eventsToAdd) {
|
newEventsToRemove.push_back(add_event_to_dc(dc, std::move(ev)));
|
||||||
newEventsToRemove.push_back(ev.get());
|
|
||||||
add_event_to_dc(dc, ev.release()); // return ownership to backend
|
// Make sure that events are removed in reverse order
|
||||||
}
|
std::sort(newEventsToRemove.begin(), newEventsToRemove.end(), std::greater<int>());
|
||||||
|
|
||||||
eventsToAdd = std::move(newEventsToAdd);
|
eventsToAdd = std::move(newEventsToAdd);
|
||||||
eventsToRemove = std::move(newEventsToRemove);
|
eventsToRemove = std::move(newEventsToRemove);
|
||||||
|
|
||||||
|
|||||||
@ -6,15 +6,12 @@
|
|||||||
|
|
||||||
#include "command_base.h"
|
#include "command_base.h"
|
||||||
#include "core/divemode.h"
|
#include "core/divemode.h"
|
||||||
|
#include "core/event.h"
|
||||||
|
|
||||||
// We put everything in a namespace, so that we can shorten names without polluting the global namespace
|
// We put everything in a namespace, so that we can shorten names without polluting the global namespace
|
||||||
namespace Command {
|
namespace Command {
|
||||||
|
|
||||||
// Events are a strange thing: they contain there own description which means
|
// Pointers to events are not stable, so we always store indexes.
|
||||||
// that on changing the description a new object must be allocated. Moreover,
|
|
||||||
// it means that these objects can't be collected in a table.
|
|
||||||
// Therefore, the undo commands work on events as they do with dives: using
|
|
||||||
// owning pointers. See comments in command_base.h
|
|
||||||
|
|
||||||
class EventBase : public Base {
|
class EventBase : public Base {
|
||||||
protected:
|
protected:
|
||||||
@ -25,8 +22,7 @@ protected:
|
|||||||
virtual void undoit() = 0;
|
virtual void undoit() = 0;
|
||||||
|
|
||||||
// Note: we store dive and the divecomputer-number instead of a pointer to the divecomputer.
|
// Note: we store dive and the divecomputer-number instead of a pointer to the divecomputer.
|
||||||
// Since one divecomputer is integrated into the dive structure, pointers to divecomputers
|
// Pointers to divecomputers are not stable.
|
||||||
// are probably not stable.
|
|
||||||
struct dive *d;
|
struct dive *d;
|
||||||
int dcNr;
|
int dcNr;
|
||||||
private:
|
private:
|
||||||
@ -35,15 +31,15 @@ private:
|
|||||||
|
|
||||||
class AddEventBase : public EventBase {
|
class AddEventBase : public EventBase {
|
||||||
public:
|
public:
|
||||||
AddEventBase(struct dive *d, int dcNr, struct event *ev); // Takes ownership of event!
|
AddEventBase(struct dive *d, int dcNr, struct event ev); // Takes ownership of event!
|
||||||
protected:
|
protected:
|
||||||
void undoit() override;
|
void undoit() override;
|
||||||
void redoit() override;
|
void redoit() override;
|
||||||
private:
|
private:
|
||||||
bool workToBeDone() override;
|
bool workToBeDone() override;
|
||||||
|
|
||||||
OwningEventPtr eventToAdd; // for redo
|
struct event ev; // for redo
|
||||||
event *eventToRemove; // for undo
|
int idx; // for undo
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddEventBookmark : public AddEventBase {
|
class AddEventBookmark : public AddEventBase {
|
||||||
@ -67,28 +63,28 @@ private:
|
|||||||
|
|
||||||
class RenameEvent : public EventBase {
|
class RenameEvent : public EventBase {
|
||||||
public:
|
public:
|
||||||
RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name);
|
RenameEvent(struct dive *d, int dcNr, int idx, const std::string name);
|
||||||
private:
|
private:
|
||||||
bool workToBeDone() override;
|
bool workToBeDone() override;
|
||||||
void undoit() override;
|
void undoit() override;
|
||||||
void redoit() override;
|
void redoit() override;
|
||||||
|
|
||||||
OwningEventPtr eventToAdd; // for undo and redo
|
int idx; // for undo and redo
|
||||||
event *eventToRemove; // for undo and redo
|
std::string name; // for undo and redo
|
||||||
};
|
};
|
||||||
|
|
||||||
class RemoveEvent : public EventBase {
|
class RemoveEvent : public EventBase {
|
||||||
public:
|
public:
|
||||||
RemoveEvent(struct dive *d, int dcNr, struct event *ev);
|
RemoveEvent(struct dive *d, int dcNr, int idx);
|
||||||
private:
|
private:
|
||||||
bool workToBeDone() override;
|
bool workToBeDone() override;
|
||||||
void undoit() override;
|
void undoit() override;
|
||||||
void redoit() override;
|
void redoit() override;
|
||||||
void post() const; // Called to fix up dives should a gas-change have happened.
|
void post() const; // Called to fix up dives should a gas-change have happened.
|
||||||
|
|
||||||
OwningEventPtr eventToAdd; // for undo
|
event ev; // for undo
|
||||||
event *eventToRemove; // for redo
|
int idx; // for redo
|
||||||
int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch.
|
int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch.
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddGasSwitch : public EventBase {
|
class AddGasSwitch : public EventBase {
|
||||||
@ -100,8 +96,8 @@ private:
|
|||||||
void redoit() override;
|
void redoit() override;
|
||||||
|
|
||||||
std::vector<int> cylinders; // cylinders that are modified
|
std::vector<int> cylinders; // cylinders that are modified
|
||||||
std::vector<OwningEventPtr> eventsToAdd;
|
std::vector<event> eventsToAdd;
|
||||||
std::vector<event *> eventsToRemove;
|
std::vector<int> eventsToRemove;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Command
|
} // namespace Command
|
||||||
|
|||||||
@ -1,23 +1,26 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
#include "command_filter.h"
|
#include "command_filter.h"
|
||||||
|
#include "core/divelog.h"
|
||||||
#include "core/filterpreset.h"
|
#include "core/filterpreset.h"
|
||||||
|
#include "core/filterpresettable.h"
|
||||||
#include "core/subsurface-qt/divelistnotifier.h"
|
#include "core/subsurface-qt/divelistnotifier.h"
|
||||||
|
|
||||||
namespace Command {
|
namespace Command {
|
||||||
|
|
||||||
static int createFilterPreset(const std::string &name, const FilterData &data)
|
static int createFilterPreset(const std::string &name, const FilterData &data)
|
||||||
{
|
{
|
||||||
int index = filter_preset_add(name, data);
|
int index = divelog.filter_presets.add(name, data);
|
||||||
emit diveListNotifier.filterPresetAdded(index);
|
emit diveListNotifier.filterPresetAdded(index);
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<std::string, FilterData> removeFilterPreset(int index)
|
static std::pair<std::string, FilterData> removeFilterPreset(int index)
|
||||||
{
|
{
|
||||||
std::string oldName = filter_preset_name(index);
|
const filter_preset &preset = divelog.filter_presets[index];
|
||||||
FilterData oldData = filter_preset_get(index);
|
std::string oldName = preset.name;
|
||||||
filter_preset_delete(index);
|
FilterData oldData = preset.data;
|
||||||
|
divelog.filter_presets.remove(index);
|
||||||
emit diveListNotifier.filterPresetRemoved(index);
|
emit diveListNotifier.filterPresetRemoved(index);
|
||||||
return { oldName, oldData };
|
return { oldName, oldData };
|
||||||
}
|
}
|
||||||
@ -46,7 +49,8 @@ void CreateFilterPreset::undo()
|
|||||||
|
|
||||||
RemoveFilterPreset::RemoveFilterPreset(int indexIn) : index(indexIn)
|
RemoveFilterPreset::RemoveFilterPreset(int indexIn) : index(indexIn)
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("Delete filter preset %1").arg(QString(filter_preset_name(index).c_str())));
|
const std::string &name = divelog.filter_presets[index].name;
|
||||||
|
setText(Command::Base::tr("Delete filter preset %1").arg(QString::fromStdString(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RemoveFilterPreset::workToBeDone()
|
bool RemoveFilterPreset::workToBeDone()
|
||||||
@ -68,7 +72,8 @@ void RemoveFilterPreset::undo()
|
|||||||
EditFilterPreset::EditFilterPreset(int indexIn, const FilterData &dataIn) :
|
EditFilterPreset::EditFilterPreset(int indexIn, const FilterData &dataIn) :
|
||||||
index(indexIn), data(dataIn)
|
index(indexIn), data(dataIn)
|
||||||
{
|
{
|
||||||
setText(Command::Base::tr("Edit filter preset %1").arg(QString(filter_preset_name(index).c_str())));
|
const std::string &name = divelog.filter_presets[index].name;
|
||||||
|
setText(Command::Base::tr("Edit filter preset %1").arg(QString::fromStdString(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditFilterPreset::workToBeDone()
|
bool EditFilterPreset::workToBeDone()
|
||||||
@ -78,9 +83,8 @@ bool EditFilterPreset::workToBeDone()
|
|||||||
|
|
||||||
void EditFilterPreset::redo()
|
void EditFilterPreset::redo()
|
||||||
{
|
{
|
||||||
FilterData oldData = filter_preset_get(index);
|
filter_preset &preset = divelog.filter_presets[index];
|
||||||
filter_preset_set(index, data);
|
std::swap(data, preset.data);
|
||||||
data = std::move(oldData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditFilterPreset::undo()
|
void EditFilterPreset::undo()
|
||||||
|
|||||||
@ -2,15 +2,16 @@
|
|||||||
|
|
||||||
#include "command_pictures.h"
|
#include "command_pictures.h"
|
||||||
#include "core/errorhelper.h"
|
#include "core/errorhelper.h"
|
||||||
|
#include "core/range.h"
|
||||||
#include "core/subsurface-qt/divelistnotifier.h"
|
#include "core/subsurface-qt/divelistnotifier.h"
|
||||||
#include "qt-models/divelocationmodel.h"
|
#include "qt-models/divelocationmodel.h"
|
||||||
|
|
||||||
namespace Command {
|
namespace Command {
|
||||||
|
|
||||||
static picture *dive_get_picture(const dive *d, const QString &fn)
|
static picture *dive_get_picture(dive *d, const QString &fn)
|
||||||
{
|
{
|
||||||
int idx = get_picture_idx(&d->pictures, qPrintable(fn));
|
int idx = get_picture_idx(d->pictures, fn.toStdString());
|
||||||
return idx < 0 ? nullptr : &d->pictures.pictures[idx];
|
return idx < 0 ? nullptr : &d->pictures[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
SetPictureOffset::SetPictureOffset(dive *dIn, const QString &filenameIn, offset_t offsetIn) :
|
SetPictureOffset::SetPictureOffset(dive *dIn, const QString &filenameIn, offset_t offsetIn) :
|
||||||
@ -33,7 +34,7 @@ void SetPictureOffset::redo()
|
|||||||
|
|
||||||
// Instead of trying to be smart, let's simply resort the picture table.
|
// Instead of trying to be smart, let's simply resort the picture table.
|
||||||
// If someone complains about speed, do our usual "smart" thing.
|
// If someone complains about speed, do our usual "smart" thing.
|
||||||
sort_picture_table(&d->pictures);
|
std::sort(d->pictures.begin(), d->pictures.end());
|
||||||
emit diveListNotifier.pictureOffsetChanged(d, filename, newOffset);
|
emit diveListNotifier.pictureOffsetChanged(d, filename, newOffset);
|
||||||
invalidate_dive_cache(d);
|
invalidate_dive_cache(d);
|
||||||
}
|
}
|
||||||
@ -55,10 +56,9 @@ static PictureListForDeletion filterPictureListForDeletion(const PictureListForD
|
|||||||
PictureListForDeletion res;
|
PictureListForDeletion res;
|
||||||
res.d = p.d;
|
res.d = p.d;
|
||||||
res.filenames.reserve(p.filenames.size());
|
res.filenames.reserve(p.filenames.size());
|
||||||
for (int i = 0; i < p.d->pictures.nr; ++i) {
|
for (auto &pic: p.d->pictures) {
|
||||||
std::string fn = p.d->pictures.pictures[i].filename;
|
if (range_contains(p.filenames, pic.filename))
|
||||||
if (std::find(p.filenames.begin(), p.filenames.end(), fn) != p.filenames.end())
|
res.filenames.push_back(pic.filename);
|
||||||
res.filenames.push_back(fn);
|
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -72,14 +72,14 @@ static std::vector<PictureListForAddition> removePictures(std::vector<PictureLis
|
|||||||
PictureListForAddition toAdd;
|
PictureListForAddition toAdd;
|
||||||
toAdd.d = list.d;
|
toAdd.d = list.d;
|
||||||
for (const std::string &fn: list.filenames) {
|
for (const std::string &fn: list.filenames) {
|
||||||
int idx = get_picture_idx(&list.d->pictures, fn.c_str());
|
int idx = get_picture_idx(list.d->pictures, fn);
|
||||||
if (idx < 0) {
|
if (idx < 0) {
|
||||||
report_info("removePictures(): picture disappeared!");
|
report_info("removePictures(): picture disappeared!");
|
||||||
continue; // Huh? We made sure that this can't happen by filtering out non-existent pictures.
|
continue; // Huh? We made sure that this can't happen by filtering out non-existent pictures.
|
||||||
}
|
}
|
||||||
filenames.push_back(QString::fromStdString(fn));
|
filenames.push_back(QString::fromStdString(fn));
|
||||||
toAdd.pics.emplace_back(list.d->pictures.pictures[idx]);
|
toAdd.pics.emplace_back(list.d->pictures[idx]);
|
||||||
remove_from_picture_table(&list.d->pictures, idx);
|
list.d->pictures.erase(list.d->pictures.begin() + idx);
|
||||||
}
|
}
|
||||||
if (!toAdd.pics.empty())
|
if (!toAdd.pics.empty())
|
||||||
res.push_back(toAdd);
|
res.push_back(toAdd);
|
||||||
@ -98,17 +98,17 @@ static std::vector<PictureListForDeletion> addPictures(std::vector<PictureListFo
|
|||||||
// happen, as we checked that before.
|
// happen, as we checked that before.
|
||||||
std::vector<PictureListForDeletion> res;
|
std::vector<PictureListForDeletion> res;
|
||||||
for (const PictureListForAddition &list: picturesToAdd) {
|
for (const PictureListForAddition &list: picturesToAdd) {
|
||||||
QVector<PictureObj> picsForSignal;
|
QVector<picture> picsForSignal;
|
||||||
PictureListForDeletion toRemove;
|
PictureListForDeletion toRemove;
|
||||||
toRemove.d = list.d;
|
toRemove.d = list.d;
|
||||||
for (const PictureObj &pic: list.pics) {
|
for (const picture &pic: list.pics) {
|
||||||
int idx = get_picture_idx(&list.d->pictures, pic.filename.c_str()); // This should *not* already exist!
|
int idx = get_picture_idx(list.d->pictures, pic.filename); // This should *not* already exist!
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
report_info("addPictures(): picture disappeared!");
|
report_info("addPictures(): picture disappeared!");
|
||||||
continue; // Huh? We made sure that this can't happen by filtering out existing pictures.
|
continue; // Huh? We made sure that this can't happen by filtering out existing pictures.
|
||||||
}
|
}
|
||||||
picsForSignal.push_back(pic);
|
picsForSignal.push_back(pic);
|
||||||
add_picture(&list.d->pictures, pic.toCore());
|
add_picture(list.d->pictures, pic);
|
||||||
toRemove.filenames.push_back(pic.filename);
|
toRemove.filenames.push_back(pic.filename);
|
||||||
}
|
}
|
||||||
if (!toRemove.filenames.empty())
|
if (!toRemove.filenames.empty())
|
||||||
@ -164,16 +164,15 @@ AddPictures::AddPictures(const std::vector<PictureListForAddition> &pictures) :
|
|||||||
std::sort(p.pics.begin(), p.pics.end());
|
std::sort(p.pics.begin(), p.pics.end());
|
||||||
|
|
||||||
// Find a picture with a location
|
// Find a picture with a location
|
||||||
auto it = std::find_if(p.pics.begin(), p.pics.end(), [](const PictureObj &p) { return has_location(&p.location); });
|
auto it = std::find_if(p.pics.begin(), p.pics.end(), [](const picture &p) { return has_location(&p.location); });
|
||||||
if (it != p.pics.end()) {
|
if (it != p.pics.end()) {
|
||||||
// There is a dive with a location, we might want to modify the dive accordingly.
|
// There is a dive with a location, we might want to modify the dive accordingly.
|
||||||
struct dive_site *ds = p.d->dive_site;
|
struct dive_site *ds = p.d->dive_site;
|
||||||
if (!ds) {
|
if (!ds) {
|
||||||
// This dive doesn't yet have a dive site -> add a new dive site.
|
// This dive doesn't yet have a dive site -> add a new dive site.
|
||||||
QString name = Command::Base::tr("unnamed dive site");
|
QString name = Command::Base::tr("unnamed dive site");
|
||||||
dive_site *ds = alloc_dive_site_with_gps(qPrintable(name), &it->location);
|
sitesToAdd.push_back(std::make_unique<dive_site>(qPrintable(name), it->location));
|
||||||
sitesToAdd.emplace_back(ds);
|
sitesToSet.push_back({ p.d, sitesToAdd.back().get() });
|
||||||
sitesToSet.push_back({ p.d, ds });
|
|
||||||
} else if (!dive_site_has_gps_location(ds)) {
|
} else if (!dive_site_has_gps_location(ds)) {
|
||||||
// This dive has a dive site, but without coordinates. Let's add them.
|
// This dive has a dive site, but without coordinates. Let's add them.
|
||||||
sitesToEdit.push_back({ ds, it->location });
|
sitesToEdit.push_back({ ds, it->location });
|
||||||
@ -201,7 +200,7 @@ void AddPictures::swapDiveSites()
|
|||||||
unregister_dive_from_dive_site(entry.d); // the dive-site pointer in the dive is now NULL
|
unregister_dive_from_dive_site(entry.d); // the dive-site pointer in the dive is now NULL
|
||||||
std::swap(ds, entry.ds);
|
std::swap(ds, entry.ds);
|
||||||
if (ds)
|
if (ds)
|
||||||
add_dive_to_dive_site(entry.d, ds);
|
ds->add_dive(entry.d);
|
||||||
emit diveListNotifier.divesChanged(QVector<dive *>{ entry.d }, DiveField::DIVESITE);
|
emit diveListNotifier.divesChanged(QVector<dive *>{ entry.d }, DiveField::DIVESITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,9 +217,9 @@ void AddPictures::undo()
|
|||||||
|
|
||||||
// Remove dive sites
|
// Remove dive sites
|
||||||
for (dive_site *siteToRemove: sitesToRemove) {
|
for (dive_site *siteToRemove: sitesToRemove) {
|
||||||
int idx = unregister_dive_site(siteToRemove);
|
auto res = divelog.sites.pull(siteToRemove);
|
||||||
sitesToAdd.emplace_back(siteToRemove);
|
sitesToAdd.push_back(std::move(res.ptr));
|
||||||
emit diveListNotifier.diveSiteDeleted(siteToRemove, idx); // Inform frontend of removed dive site.
|
emit diveListNotifier.diveSiteDeleted(siteToRemove, res.idx); // Inform frontend of removed dive site.
|
||||||
}
|
}
|
||||||
sitesToRemove.clear();
|
sitesToRemove.clear();
|
||||||
}
|
}
|
||||||
@ -228,10 +227,10 @@ void AddPictures::undo()
|
|||||||
void AddPictures::redo()
|
void AddPictures::redo()
|
||||||
{
|
{
|
||||||
// Add dive sites
|
// Add dive sites
|
||||||
for (OwningDiveSitePtr &siteToAdd: sitesToAdd) {
|
for (std::unique_ptr<dive_site> &siteToAdd: sitesToAdd) {
|
||||||
sitesToRemove.push_back(siteToAdd.get());
|
auto res = divelog.sites.register_site(std::move(siteToAdd)); // Return ownership to backend.
|
||||||
int idx = register_dive_site(siteToAdd.release()); // Return ownership to backend.
|
sitesToRemove.push_back(res.ptr);
|
||||||
emit diveListNotifier.diveSiteAdded(sitesToRemove.back(), idx); // Inform frontend of new dive site.
|
emit diveListNotifier.diveSiteAdded(sitesToRemove.back(), res.idx); // Inform frontend of new dive site.
|
||||||
}
|
}
|
||||||
sitesToAdd.clear();
|
sitesToAdd.clear();
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,7 @@ private:
|
|||||||
location_t location;
|
location_t location;
|
||||||
};
|
};
|
||||||
std::vector<PictureListForAddition> picturesToAdd; // for redo
|
std::vector<PictureListForAddition> picturesToAdd; // for redo
|
||||||
std::vector<OwningDiveSitePtr> sitesToAdd; //for redo
|
std::vector<std::unique_ptr<dive_site>> sitesToAdd; //for redo
|
||||||
std::vector<PictureListForDeletion> picturesToRemove; // for undo
|
std::vector<PictureListForDeletion> picturesToRemove; // for undo
|
||||||
std::vector<dive_site *> sitesToRemove; // for undo
|
std::vector<dive_site *> sitesToRemove; // for undo
|
||||||
std::vector<DiveSiteEntry> sitesToSet; // for redo and undo
|
std::vector<DiveSiteEntry> sitesToSet; // for redo and undo
|
||||||
|
|||||||
@ -17,7 +17,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(FTDISUPPORT)
|
if(FTDISUPPORT)
|
||||||
set(SERIAL_FTDI serial_ftdi.c)
|
set(SERIAL_FTDI serial_ftdi.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BTSUPPORT)
|
if(BTSUPPORT)
|
||||||
@ -61,29 +61,29 @@ set(SUBSURFACE_CORE_LIB_SRCS
|
|||||||
devicedetails.h
|
devicedetails.h
|
||||||
dive.cpp
|
dive.cpp
|
||||||
dive.h
|
dive.h
|
||||||
divecomputer.c
|
divecomputer.cpp
|
||||||
divecomputer.h
|
divecomputer.h
|
||||||
dive.h
|
dive.h
|
||||||
divefilter.cpp
|
divefilter.cpp
|
||||||
divefilter.h
|
divefilter.h
|
||||||
divelist.c
|
divelist.cpp
|
||||||
divelist.h
|
divelist.h
|
||||||
divelog.cpp
|
divelog.cpp
|
||||||
divelog.h
|
divelog.h
|
||||||
divelogexportlogic.cpp
|
divelogexportlogic.cpp
|
||||||
divelogexportlogic.h
|
divelogexportlogic.h
|
||||||
divesite-helper.cpp
|
divesite.cpp
|
||||||
divesite.c
|
|
||||||
divesite.h
|
divesite.h
|
||||||
|
divesitetable.h
|
||||||
divesitehelpers.cpp
|
divesitehelpers.cpp
|
||||||
divesitehelpers.h
|
divesitehelpers.h
|
||||||
downloadfromdcthread.cpp
|
downloadfromdcthread.cpp
|
||||||
downloadfromdcthread.h
|
downloadfromdcthread.h
|
||||||
event.c
|
event.cpp
|
||||||
event.h
|
event.h
|
||||||
eventtype.cpp
|
eventtype.cpp
|
||||||
eventtype.h
|
eventtype.h
|
||||||
equipment.c
|
equipment.cpp
|
||||||
equipment.h
|
equipment.h
|
||||||
errorhelper.cpp
|
errorhelper.cpp
|
||||||
exif.cpp
|
exif.cpp
|
||||||
@ -95,14 +95,16 @@ set(SUBSURFACE_CORE_LIB_SRCS
|
|||||||
filterconstraint.h
|
filterconstraint.h
|
||||||
filterpreset.cpp
|
filterpreset.cpp
|
||||||
filterpreset.h
|
filterpreset.h
|
||||||
|
filterpresettable.cpp
|
||||||
|
filterpresettable.h
|
||||||
format.cpp
|
format.cpp
|
||||||
format.h
|
format.h
|
||||||
fulltext.cpp
|
fulltext.cpp
|
||||||
fulltext.h
|
fulltext.h
|
||||||
gas.c
|
gas.cpp
|
||||||
gas.h
|
gas.h
|
||||||
gas-model.c
|
gas-model.cpp
|
||||||
gaspressures.c
|
gaspressures.cpp
|
||||||
gaspressures.h
|
gaspressures.h
|
||||||
gettext.h
|
gettext.h
|
||||||
gettextfromc.cpp
|
gettextfromc.cpp
|
||||||
@ -131,21 +133,18 @@ set(SUBSURFACE_CORE_LIB_SRCS
|
|||||||
metadata.h
|
metadata.h
|
||||||
metrics.cpp
|
metrics.cpp
|
||||||
metrics.h
|
metrics.h
|
||||||
ostctools.c
|
ostctools.cpp
|
||||||
owning_ptrs.h
|
|
||||||
parse-gpx.cpp
|
parse-gpx.cpp
|
||||||
parse-xml.cpp
|
parse-xml.cpp
|
||||||
parse.cpp
|
parse.cpp
|
||||||
parse.h
|
parse.h
|
||||||
picture.c
|
picture.cpp
|
||||||
picture.h
|
picture.h
|
||||||
pictureobj.cpp
|
|
||||||
pictureobj.h
|
|
||||||
planner.cpp
|
planner.cpp
|
||||||
planner.h
|
planner.h
|
||||||
plannernotes.cpp
|
plannernotes.cpp
|
||||||
pref.h
|
pref.h
|
||||||
pref.c
|
pref.cpp
|
||||||
profile.cpp
|
profile.cpp
|
||||||
profile.h
|
profile.h
|
||||||
qt-gui.h
|
qt-gui.h
|
||||||
@ -158,18 +157,17 @@ set(SUBSURFACE_CORE_LIB_SRCS
|
|||||||
save-git.cpp
|
save-git.cpp
|
||||||
save-html.cpp
|
save-html.cpp
|
||||||
save-html.h
|
save-html.h
|
||||||
save-profiledata.c
|
save-profiledata.cpp
|
||||||
save-xml.cpp
|
save-xml.cpp
|
||||||
selection.cpp
|
selection.cpp
|
||||||
selection.h
|
selection.h
|
||||||
sha1.c
|
sha1.cpp
|
||||||
sha1.h
|
sha1.h
|
||||||
ssrf.h
|
statistics.cpp
|
||||||
statistics.c
|
|
||||||
statistics.h
|
statistics.h
|
||||||
string-format.h
|
string-format.h
|
||||||
string-format.cpp
|
string-format.cpp
|
||||||
strtod.c
|
strtod.cpp
|
||||||
subsurface-float.h
|
subsurface-float.h
|
||||||
subsurface-string.cpp
|
subsurface-string.cpp
|
||||||
subsurface-string.h
|
subsurface-string.h
|
||||||
@ -179,23 +177,23 @@ set(SUBSURFACE_CORE_LIB_SRCS
|
|||||||
subsurfacesysinfo.h
|
subsurfacesysinfo.h
|
||||||
tag.cpp
|
tag.cpp
|
||||||
tag.h
|
tag.h
|
||||||
taxonomy.c
|
taxonomy.cpp
|
||||||
taxonomy.h
|
taxonomy.h
|
||||||
time.cpp
|
time.cpp
|
||||||
timer.c
|
trip.cpp
|
||||||
timer.h
|
|
||||||
trip.c
|
|
||||||
trip.h
|
trip.h
|
||||||
|
triptable.cpp
|
||||||
|
triptable.h
|
||||||
uemis-downloader.cpp
|
uemis-downloader.cpp
|
||||||
uemis.c
|
uemis.cpp
|
||||||
uemis.h
|
uemis.h
|
||||||
units.h
|
units.h
|
||||||
units.c
|
units.cpp
|
||||||
uploadDiveShare.cpp
|
uploadDiveShare.cpp
|
||||||
uploadDiveShare.h
|
uploadDiveShare.h
|
||||||
uploadDiveLogsDE.cpp
|
uploadDiveLogsDE.cpp
|
||||||
uploadDiveLogsDE.h
|
uploadDiveLogsDE.h
|
||||||
version.c
|
version.cpp
|
||||||
version.h
|
version.h
|
||||||
videoframeextractor.cpp
|
videoframeextractor.cpp
|
||||||
videoframeextractor.h
|
videoframeextractor.h
|
||||||
|
|||||||
@ -41,14 +41,12 @@ static std::string make_default_filename()
|
|||||||
return system_default_path() + "/subsurface.xml";
|
return system_default_path() + "/subsurface.xml";
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
const char android_system_divelist_default_font[] = "Roboto";
|
const char android_system_divelist_default_font[] = "Roboto";
|
||||||
const char *system_divelist_default_font = android_system_divelist_default_font;
|
const char *system_divelist_default_font = android_system_divelist_default_font;
|
||||||
double system_divelist_default_font_size = -1;
|
double system_divelist_default_font_size = -1;
|
||||||
|
|
||||||
int get_usb_fd(uint16_t idVendor, uint16_t idProduct);
|
int get_usb_fd(uint16_t idVendor, uint16_t idProduct);
|
||||||
void subsurface_OS_pref_setup(void)
|
void subsurface_OS_pref_setup()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,13 +56,13 @@ bool subsurface_ignore_font(const char *font)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *system_default_directory(void)
|
const char *system_default_directory()
|
||||||
{
|
{
|
||||||
static const std::string path = system_default_path();
|
static const std::string path = system_default_path();
|
||||||
return path.c_str();
|
return path.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *system_default_filename(void)
|
const char *system_default_filename()
|
||||||
{
|
{
|
||||||
static const std::string fn = make_default_filename();
|
static const std::string fn = make_default_filename();
|
||||||
return fn.c_str();
|
return fn.c_str();
|
||||||
@ -158,12 +156,10 @@ int get_usb_fd(uint16_t idVendor, uint16_t idProduct)
|
|||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_setUsbDevice(JNIEnv *env,
|
Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_setUsbDevice(JNIEnv *,
|
||||||
jobject obj,
|
jobject,
|
||||||
jobject javaUsbDevice)
|
jobject javaUsbDevice)
|
||||||
{
|
{
|
||||||
Q_UNUSED (obj)
|
|
||||||
Q_UNUSED (env)
|
|
||||||
QAndroidJniObject usbDevice(javaUsbDevice);
|
QAndroidJniObject usbDevice(javaUsbDevice);
|
||||||
if (usbDevice.isValid()) {
|
if (usbDevice.isValid()) {
|
||||||
android_usb_serial_device_descriptor descriptor = getDescriptor(usbDevice);
|
android_usb_serial_device_descriptor descriptor = getDescriptor(usbDevice);
|
||||||
@ -177,12 +173,10 @@ Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_setUsbDevice(JNIEnv *
|
|||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_restartDownload(JNIEnv *env,
|
Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_restartDownload(JNIEnv *,
|
||||||
jobject obj,
|
jobject,
|
||||||
jobject javaUsbDevice)
|
jobject javaUsbDevice)
|
||||||
{
|
{
|
||||||
Q_UNUSED (obj)
|
|
||||||
Q_UNUSED (env)
|
|
||||||
QAndroidJniObject usbDevice(javaUsbDevice);
|
QAndroidJniObject usbDevice(javaUsbDevice);
|
||||||
if (usbDevice.isValid()) {
|
if (usbDevice.isValid()) {
|
||||||
android_usb_serial_device_descriptor descriptor = getDescriptor(usbDevice);
|
android_usb_serial_device_descriptor descriptor = getDescriptor(usbDevice);
|
||||||
@ -237,12 +231,12 @@ int subsurface_zip_close(struct zip *zip)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* win32 console */
|
/* win32 console */
|
||||||
void subsurface_console_init(void)
|
void subsurface_console_init()
|
||||||
{
|
{
|
||||||
/* NOP */
|
/* NOP */
|
||||||
}
|
}
|
||||||
|
|
||||||
void subsurface_console_exit(void)
|
void subsurface_console_exit()
|
||||||
{
|
{
|
||||||
/* NOP */
|
/* NOP */
|
||||||
}
|
}
|
||||||
@ -251,7 +245,6 @@ bool subsurface_user_is_root()
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* called from QML manager */
|
/* called from QML manager */
|
||||||
void checkPendingIntents()
|
void checkPendingIntents()
|
||||||
|
|||||||
@ -200,7 +200,7 @@ void CheckCloudConnection::gotContinent(QNetworkReply *reply)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// helper to be used from C code
|
// helper to be used from C code
|
||||||
extern "C" bool canReachCloudServer(struct git_info *info)
|
bool canReachCloudServer(struct git_info *info)
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
qWarning() << "Cloud storage: checking connection to cloud server" << info->url.c_str();
|
qWarning() << "Cloud storage: checking connection to cloud server" << info->url.c_str();
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "ssrf.h"
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -445,7 +444,7 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
|
|||||||
unsigned int ndl = 0;
|
unsigned int ndl = 0;
|
||||||
unsigned int in_deco = 0, deco_ceiling = 0, deco_time = 0;
|
unsigned int in_deco = 0, deco_ceiling = 0, deco_time = 0;
|
||||||
|
|
||||||
struct divecomputer *dc = &dive->dc;
|
struct divecomputer *dc = &dive->dcs[0];
|
||||||
struct sample *sample;
|
struct sample *sample;
|
||||||
|
|
||||||
// Initialize stat variables
|
// Initialize stat variables
|
||||||
@ -495,10 +494,6 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
|
|||||||
while (offset + config.sample_size < size) {
|
while (offset + config.sample_size < size) {
|
||||||
s = samples + offset;
|
s = samples + offset;
|
||||||
|
|
||||||
// Start with an empty sample
|
|
||||||
sample = prepare_sample(dc);
|
|
||||||
sample->time.seconds = sample_cnt * profile_period;
|
|
||||||
|
|
||||||
// Check for event
|
// Check for event
|
||||||
if (s[0] & 0x80) {
|
if (s[0] & 0x80) {
|
||||||
cochran_dive_event(dc, s, sample_cnt * profile_period, &in_deco, &deco_ceiling, &deco_time);
|
cochran_dive_event(dc, s, sample_cnt * profile_period, &in_deco, &deco_ceiling, &deco_time);
|
||||||
@ -506,6 +501,10 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start with an empty sample
|
||||||
|
sample = prepare_sample(dc);
|
||||||
|
sample->time.seconds = sample_cnt * profile_period;
|
||||||
|
|
||||||
// Depth is in every sample
|
// Depth is in every sample
|
||||||
depth_sample = (double)(s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
|
depth_sample = (double)(s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
|
||||||
depth += depth_sample;
|
depth += depth_sample;
|
||||||
@ -592,8 +591,6 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
|
|||||||
sample->sensor[0] = 0;
|
sample->sensor[0] = 0;
|
||||||
sample->pressure[0].mbar = lrint(psi * PSI / 100);
|
sample->pressure[0].mbar = lrint(psi * PSI / 100);
|
||||||
|
|
||||||
finish_sample(dc);
|
|
||||||
|
|
||||||
offset += config.sample_size;
|
offset += config.sample_size;
|
||||||
sample_cnt++;
|
sample_cnt++;
|
||||||
}
|
}
|
||||||
@ -604,13 +601,11 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
|
|||||||
|
|
||||||
static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
|
static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
|
||||||
const unsigned char *in, unsigned size,
|
const unsigned char *in, unsigned size,
|
||||||
struct dive_table *table)
|
struct dive_table &table)
|
||||||
{
|
{
|
||||||
unsigned char *buf = (unsigned char *)malloc(size);
|
unsigned char *buf = (unsigned char *)malloc(size);
|
||||||
struct dive *dive;
|
|
||||||
struct divecomputer *dc;
|
struct divecomputer *dc;
|
||||||
struct tm tm = {0};
|
struct tm tm = {0};
|
||||||
uint32_t csum[5];
|
|
||||||
|
|
||||||
double max_depth, avg_depth, min_temp;
|
double max_depth, avg_depth, min_temp;
|
||||||
unsigned int duration = 0, corrupt_dive = 0;
|
unsigned int duration = 0, corrupt_dive = 0;
|
||||||
@ -668,8 +663,8 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
|
|||||||
puts("\nSample Data\n");
|
puts("\nSample Data\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dive = alloc_dive();
|
auto dive = std::make_unique<struct dive>();
|
||||||
dc = &dive->dc;
|
dc = &dive->dcs[0];
|
||||||
|
|
||||||
unsigned char *log = (buf + 0x4914);
|
unsigned char *log = (buf + 0x4914);
|
||||||
|
|
||||||
@ -677,24 +672,22 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
|
|||||||
case TYPE_GEMINI:
|
case TYPE_GEMINI:
|
||||||
case TYPE_COMMANDER:
|
case TYPE_COMMANDER:
|
||||||
if (config.type == TYPE_GEMINI) {
|
if (config.type == TYPE_GEMINI) {
|
||||||
cylinder_t cyl = empty_cylinder;
|
|
||||||
dc->model = "Gemini";
|
dc->model = "Gemini";
|
||||||
dc->deviceid = buf[0x18c] * 256 + buf[0x18d]; // serial no
|
dc->deviceid = buf[0x18c] * 256 + buf[0x18d]; // serial no
|
||||||
fill_default_cylinder(dive, &cyl);
|
cylinder_t cyl = default_cylinder(dive.get());
|
||||||
cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256
|
cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256
|
||||||
+ log[CMD_O2_PERCENT + 1]) * 10;
|
+ log[CMD_O2_PERCENT + 1]) * 10;
|
||||||
cyl.gasmix.he.permille = 0;
|
cyl.gasmix.he.permille = 0;
|
||||||
add_cylinder(&dive->cylinders, 0, cyl);
|
add_cylinder(&dive->cylinders, 0, std::move(cyl));
|
||||||
} else {
|
} else {
|
||||||
dc->model = "Commander";
|
dc->model = "Commander";
|
||||||
dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
|
dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
|
||||||
for (g = 0; g < 2; g++) {
|
for (g = 0; g < 2; g++) {
|
||||||
cylinder_t cyl = empty_cylinder;
|
cylinder_t cyl = default_cylinder(dive.get());
|
||||||
fill_default_cylinder(dive, &cyl);
|
|
||||||
cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256
|
cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256
|
||||||
+ log[CMD_O2_PERCENT + g * 2 + 1]) * 10;
|
+ log[CMD_O2_PERCENT + g * 2 + 1]) * 10;
|
||||||
cyl.gasmix.he.permille = 0;
|
cyl.gasmix.he.permille = 0;
|
||||||
add_cylinder(&dive->cylinders, g, cyl);
|
add_cylinder(&dive->cylinders, g, std::move(cyl));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -719,8 +712,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
|
|||||||
* (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588) * 1000);
|
* (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588) * 1000);
|
||||||
dc->salinity = 10000 + 150 * log[CMD_WATER_CONDUCTIVITY];
|
dc->salinity = 10000 + 150 * log[CMD_WATER_CONDUCTIVITY];
|
||||||
|
|
||||||
SHA1(log + CMD_NUMBER, 2, (unsigned char *)csum);
|
dc->diveid = SHA1_uint32(log + CMD_NUMBER, 2);
|
||||||
dc->diveid = csum[0];
|
|
||||||
|
|
||||||
if (log[CMD_MAX_DEPTH] == 0xff && log[CMD_MAX_DEPTH + 1] == 0xff)
|
if (log[CMD_MAX_DEPTH] == 0xff && log[CMD_MAX_DEPTH + 1] == 0xff)
|
||||||
corrupt_dive = 1;
|
corrupt_dive = 1;
|
||||||
@ -733,15 +725,14 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
|
|||||||
dc->model = "EMC";
|
dc->model = "EMC";
|
||||||
dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
|
dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
|
||||||
for (g = 0; g < 4; g++) {
|
for (g = 0; g < 4; g++) {
|
||||||
cylinder_t cyl = empty_cylinder;
|
cylinder_t cyl = default_cylinder(dive.get());
|
||||||
fill_default_cylinder(dive, &cyl);
|
|
||||||
cyl.gasmix.o2.permille =
|
cyl.gasmix.o2.permille =
|
||||||
(log[EMC_O2_PERCENT + g * 2] / 256
|
(log[EMC_O2_PERCENT + g * 2] / 256
|
||||||
+ log[EMC_O2_PERCENT + g * 2 + 1]) * 10;
|
+ log[EMC_O2_PERCENT + g * 2 + 1]) * 10;
|
||||||
cyl.gasmix.he.permille =
|
cyl.gasmix.he.permille =
|
||||||
(log[EMC_HE_PERCENT + g * 2] / 256
|
(log[EMC_HE_PERCENT + g * 2] / 256
|
||||||
+ log[EMC_HE_PERCENT + g * 2 + 1]) * 10;
|
+ log[EMC_HE_PERCENT + g * 2 + 1]) * 10;
|
||||||
add_cylinder(&dive->cylinders, g, cyl);
|
add_cylinder(&dive->cylinders, g, std::move(cyl));
|
||||||
}
|
}
|
||||||
|
|
||||||
tm.tm_year = log[EMC_YEAR];
|
tm.tm_year = log[EMC_YEAR];
|
||||||
@ -765,8 +756,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
|
|||||||
* (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588) * 1000);
|
* (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588) * 1000);
|
||||||
dc->salinity = 10000 + 150 * (log[EMC_WATER_CONDUCTIVITY] & 0x3);
|
dc->salinity = 10000 + 150 * (log[EMC_WATER_CONDUCTIVITY] & 0x3);
|
||||||
|
|
||||||
SHA1(log + EMC_NUMBER, 2, (unsigned char *)csum);
|
dc->diveid = SHA1_uint32(log + EMC_NUMBER, 2);
|
||||||
dc->diveid = csum[0];
|
|
||||||
|
|
||||||
if (log[EMC_MAX_DEPTH] == 0xff && log[EMC_MAX_DEPTH + 1] == 0xff)
|
if (log[EMC_MAX_DEPTH] == 0xff && log[EMC_MAX_DEPTH + 1] == 0xff)
|
||||||
corrupt_dive = 1;
|
corrupt_dive = 1;
|
||||||
@ -782,7 +772,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
|
|||||||
if (sample_pre_offset < sample_end_offset && sample_end_offset != 0xffffffff)
|
if (sample_pre_offset < sample_end_offset && sample_end_offset != 0xffffffff)
|
||||||
sample_size = sample_end_offset - sample_pre_offset;
|
sample_size = sample_end_offset - sample_pre_offset;
|
||||||
|
|
||||||
cochran_parse_samples(dive, buf + 0x4914, buf + 0x4914
|
cochran_parse_samples(dive.get(), buf + 0x4914, buf + 0x4914
|
||||||
+ config.logbook_size, sample_size,
|
+ config.logbook_size, sample_size,
|
||||||
&duration, &max_depth, &avg_depth, &min_temp);
|
&duration, &max_depth, &avg_depth, &min_temp);
|
||||||
|
|
||||||
@ -794,7 +784,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
|
|||||||
dc->duration.seconds = duration;
|
dc->duration.seconds = duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
record_dive_to_table(dive, table);
|
table.record_dive(std::move(dive));
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,7 +81,7 @@ static inline QColor makeColor(double r, double g, double b, double a = 1.0)
|
|||||||
#define VELOCITY_COLORS_START_IDX VELO_STABLE
|
#define VELOCITY_COLORS_START_IDX VELO_STABLE
|
||||||
#define VELOCITY_COLORS 5
|
#define VELOCITY_COLORS 5
|
||||||
|
|
||||||
typedef enum {
|
enum color_index_t {
|
||||||
/* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */
|
/* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */
|
||||||
SAC_1,
|
SAC_1,
|
||||||
SAC_2,
|
SAC_2,
|
||||||
@ -145,7 +145,7 @@ typedef enum {
|
|||||||
CALC_CEILING_DEEP,
|
CALC_CEILING_DEEP,
|
||||||
TISSUE_PERCENTAGE,
|
TISSUE_PERCENTAGE,
|
||||||
DURATION_LINE
|
DURATION_LINE
|
||||||
} color_index_t;
|
};
|
||||||
|
|
||||||
QColor getColor(const color_index_t i, bool isGrayscale = false);
|
QColor getColor(const color_index_t i, bool isGrayscale = false);
|
||||||
QColor getSacColor(int sac, int diveSac);
|
QColor getSacColor(int sac, int diveSac);
|
||||||
|
|||||||
@ -68,16 +68,14 @@ static QString writeGasDetails(gas g)
|
|||||||
bool ConfigureDiveComputer::saveXMLBackup(const QString &fileName, const DeviceDetails &details, device_data_t *data)
|
bool ConfigureDiveComputer::saveXMLBackup(const QString &fileName, const DeviceDetails &details, device_data_t *data)
|
||||||
{
|
{
|
||||||
QString xml = "";
|
QString xml = "";
|
||||||
QString vendor = data->vendor;
|
|
||||||
QString product = data->product;
|
|
||||||
QXmlStreamWriter writer(&xml);
|
QXmlStreamWriter writer(&xml);
|
||||||
writer.setAutoFormatting(true);
|
writer.setAutoFormatting(true);
|
||||||
|
|
||||||
writer.writeStartDocument();
|
writer.writeStartDocument();
|
||||||
writer.writeStartElement("DiveComputerSettingsBackup");
|
writer.writeStartElement("DiveComputerSettingsBackup");
|
||||||
writer.writeStartElement("DiveComputer");
|
writer.writeStartElement("DiveComputer");
|
||||||
writer.writeTextElement("Vendor", vendor);
|
writer.writeTextElement("Vendor", QString::fromStdString(data->vendor));
|
||||||
writer.writeTextElement("Product", product);
|
writer.writeTextElement("Product", QString::fromStdString(data->product));
|
||||||
writer.writeEndElement();
|
writer.writeEndElement();
|
||||||
writer.writeStartElement("Settings");
|
writer.writeStartElement("Settings");
|
||||||
writer.writeTextElement("CustomText", details.customText);
|
writer.writeTextElement("CustomText", details.customText);
|
||||||
|
|||||||
@ -15,11 +15,11 @@
|
|||||||
#include "units.h"
|
#include "units.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
#include "format.h"
|
||||||
#include "divesite.h"
|
#include "divesite.h"
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
#include "divelog.h"
|
#include "divelog.h"
|
||||||
#include "errorhelper.h"
|
#include "errorhelper.h"
|
||||||
#include "ssrf.h"
|
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
|
|
||||||
static unsigned int two_bytes_to_int(unsigned char x, unsigned char y)
|
static unsigned int two_bytes_to_int(unsigned char x, unsigned char y)
|
||||||
@ -104,21 +104,23 @@ static int read_file_header(unsigned char *buffer)
|
|||||||
* Returns libdc's equivalent model number (also from g_models) or zero if
|
* Returns libdc's equivalent model number (also from g_models) or zero if
|
||||||
* this a manual dive.
|
* this a manual dive.
|
||||||
*/
|
*/
|
||||||
static int dtrak_prepare_data(int model, device_data_t *dev_data)
|
static int dtrak_prepare_data(int model, device_data_t &dev_data)
|
||||||
{
|
{
|
||||||
dc_descriptor_t *d = NULL;
|
dc_descriptor_t *d = NULL;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
while (model != g_models[i].model_num && g_models[i].model_num != 0xEE)
|
while (model != g_models[i].model_num && g_models[i].model_num != 0xEE)
|
||||||
i++;
|
i++;
|
||||||
dev_data->model = copy_string(g_models[i].name);
|
dev_data.model = g_models[i].name;
|
||||||
dev_data->vendor = (const char *)malloc(strlen(g_models[i].name) + 1);
|
dev_data.vendor = (const char *)malloc(strlen(g_models[i].name) + 1);
|
||||||
sscanf(g_models[i].name, "%[A-Za-z] ", (char *)dev_data->vendor);
|
dev_data.vendor.clear();
|
||||||
dev_data->product = copy_string(strchr(g_models[i].name, ' ') + 1);
|
for (const char *s = g_models[i].name; isalpha(*s); ++s)
|
||||||
|
dev_data.vendor += *s;
|
||||||
|
dev_data.product = strchr(g_models[i].name, ' ') + 1;
|
||||||
|
|
||||||
d = get_descriptor(g_models[i].type, g_models[i].libdc_num);
|
d = get_descriptor(g_models[i].type, g_models[i].libdc_num);
|
||||||
if (d)
|
if (d)
|
||||||
dev_data->descriptor = d;
|
dev_data.descriptor = d;
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
return g_models[i].libdc_num;
|
return g_models[i].libdc_num;
|
||||||
@ -129,14 +131,11 @@ static int dtrak_prepare_data(int model, device_data_t *dev_data)
|
|||||||
* Just get the first in the user's list for given size.
|
* Just get the first in the user's list for given size.
|
||||||
* Reaching the end of the list means there is no tank of this size.
|
* Reaching the end of the list means there is no tank of this size.
|
||||||
*/
|
*/
|
||||||
static const char *cyl_type_by_size(int size)
|
static std::string cyl_type_by_size(int size)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < tank_info_table.nr; ++i) {
|
auto it = std::find_if(tank_info_table.begin(), tank_info_table.end(),
|
||||||
const struct tank_info *ti = &tank_info_table.infos[i];
|
[size] (const tank_info &info) { return info.ml == size; });
|
||||||
if (ti->ml == size)
|
return it != tank_info_table.end() ? it->name : std::string();
|
||||||
return ti->name;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -161,21 +160,19 @@ static dc_status_t dt_libdc_buffer(unsigned char *ptr, int prf_length, int dc_mo
|
|||||||
static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct divelog *log, char *maxbuf)
|
static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct divelog *log, char *maxbuf)
|
||||||
{
|
{
|
||||||
int rc, profile_length, libdc_model;
|
int rc, profile_length, libdc_model;
|
||||||
char *tmp_notes_str = NULL;
|
|
||||||
unsigned char *tmp_string1 = NULL,
|
unsigned char *tmp_string1 = NULL,
|
||||||
*locality = NULL,
|
*locality = NULL,
|
||||||
*dive_point = NULL,
|
*dive_point = NULL,
|
||||||
*compl_buffer,
|
*compl_buffer,
|
||||||
*membuf = runner;
|
*membuf = runner;
|
||||||
char buffer[1024];
|
|
||||||
unsigned char tmp_1byte;
|
unsigned char tmp_1byte;
|
||||||
unsigned int tmp_2bytes;
|
unsigned int tmp_2bytes;
|
||||||
unsigned long tmp_4bytes;
|
unsigned long tmp_4bytes;
|
||||||
struct dive_site *ds;
|
std::string tmp_notes_str;
|
||||||
char is_nitrox = 0, is_O2 = 0, is_SCR = 0;
|
char is_nitrox = 0, is_O2 = 0, is_SCR = 0;
|
||||||
|
|
||||||
device_data_t *devdata = (device_data_t *)calloc(1, sizeof(device_data_t));
|
device_data_t devdata;
|
||||||
devdata->log = log;
|
devdata.log = log;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse byte to byte till next dive entry
|
* Parse byte to byte till next dive entry
|
||||||
@ -195,7 +192,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
* Next, Time in minutes since 00:00
|
* Next, Time in minutes since 00:00
|
||||||
*/
|
*/
|
||||||
read_bytes(2);
|
read_bytes(2);
|
||||||
dt_dive->dc.when = dt_dive->when = (timestamp_t)date_time_to_ssrfc(tmp_4bytes, tmp_2bytes);
|
dt_dive->dcs[0].when = dt_dive->when = (timestamp_t)date_time_to_ssrfc(tmp_4bytes, tmp_2bytes);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now, Locality, 1st byte is long of string, rest is string
|
* Now, Locality, 1st byte is long of string, rest is string
|
||||||
@ -213,11 +210,13 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
* Subsurface only have a location variable, so we have to merge DTrak's
|
* Subsurface only have a location variable, so we have to merge DTrak's
|
||||||
* Locality and Dive points.
|
* Locality and Dive points.
|
||||||
*/
|
*/
|
||||||
snprintf(buffer, sizeof(buffer), "%s, %s", locality, dive_point);
|
{
|
||||||
ds = get_dive_site_by_name(buffer, log->sites);
|
std::string buffer2 = std::string((char *)locality) + " " + (char *)dive_point;
|
||||||
if (!ds)
|
struct dive_site *ds = log->sites.get_by_name(buffer2);
|
||||||
ds = create_dive_site(buffer, log->sites);
|
if (!ds)
|
||||||
add_dive_to_dive_site(dt_dive, ds);
|
ds = log->sites.create(buffer2);
|
||||||
|
ds->add_dive(dt_dive);
|
||||||
|
}
|
||||||
free(locality);
|
free(locality);
|
||||||
locality = NULL;
|
locality = NULL;
|
||||||
free(dive_point);
|
free(dive_point);
|
||||||
@ -241,19 +240,19 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
read_bytes(1);
|
read_bytes(1);
|
||||||
switch (tmp_1byte) {
|
switch (tmp_1byte) {
|
||||||
case 1:
|
case 1:
|
||||||
dt_dive->dc.surface_pressure.mbar = 1013;
|
dt_dive->dcs[0].surface_pressure.mbar = 1013;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
dt_dive->dc.surface_pressure.mbar = 932;
|
dt_dive->dcs[0].surface_pressure.mbar = 932;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
dt_dive->dc.surface_pressure.mbar = 828;
|
dt_dive->dcs[0].surface_pressure.mbar = 828;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
dt_dive->dc.surface_pressure.mbar = 735;
|
dt_dive->dcs[0].surface_pressure.mbar = 735;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dt_dive->dc.surface_pressure.mbar = 1013;
|
dt_dive->dcs[0].surface_pressure.mbar = 1013;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -261,32 +260,32 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
*/
|
*/
|
||||||
read_bytes(2);
|
read_bytes(2);
|
||||||
if (tmp_2bytes != 0x7FFF)
|
if (tmp_2bytes != 0x7FFF)
|
||||||
dt_dive->dc.surfacetime.seconds = (uint32_t) tmp_2bytes * 60;
|
dt_dive->dcs[0].surfacetime.seconds = (uint32_t) tmp_2bytes * 60;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Weather, values table, 0 to 6
|
* Weather, values table, 0 to 6
|
||||||
* Subsurface don't have this record but we can use tags
|
* Subsurface don't have this record but we can use tags
|
||||||
*/
|
*/
|
||||||
dt_dive->tag_list = NULL;
|
dt_dive->tags.clear();
|
||||||
read_bytes(1);
|
read_bytes(1);
|
||||||
switch (tmp_1byte) {
|
switch (tmp_1byte) {
|
||||||
case 1:
|
case 1:
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "clear")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "clear"));
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "misty")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "misty"));
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "fog")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "fog"));
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "rain")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "rain"));
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "storm")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "storm"));
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "snow")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "snow"));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// unknown, do nothing
|
// unknown, do nothing
|
||||||
@ -298,7 +297,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
*/
|
*/
|
||||||
read_bytes(2);
|
read_bytes(2);
|
||||||
if (tmp_2bytes != 0x7FFF)
|
if (tmp_2bytes != 0x7FFF)
|
||||||
dt_dive->dc.airtemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
|
dt_dive->dcs[0].airtemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dive suit, values table, 0 to 6
|
* Dive suit, values table, 0 to 6
|
||||||
@ -306,22 +305,22 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
read_bytes(1);
|
read_bytes(1);
|
||||||
switch (tmp_1byte) {
|
switch (tmp_1byte) {
|
||||||
case 1:
|
case 1:
|
||||||
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "No suit"));
|
dt_dive->suit = "No suit";
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Shorty"));
|
dt_dive->suit = "Shorty";
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Combi"));
|
dt_dive->suit = "Combi";
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Wet suit"));
|
dt_dive->suit = "Wet suit";
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Semidry suit"));
|
dt_dive->suit = "Semidry suit";
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Dry suit"));
|
dt_dive->suit = "Dry suit";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// unknown, do nothing
|
// unknown, do nothing
|
||||||
@ -335,14 +334,14 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
*/
|
*/
|
||||||
read_bytes(2);
|
read_bytes(2);
|
||||||
if (tmp_2bytes != 0x7FFF) {
|
if (tmp_2bytes != 0x7FFF) {
|
||||||
cylinder_t cyl = empty_cylinder;
|
cylinder_t cyl;
|
||||||
cyl.type.size.mliter = tmp_2bytes * 10;
|
cyl.type.size.mliter = tmp_2bytes * 10;
|
||||||
cyl.type.description = cyl_type_by_size(tmp_2bytes * 10);
|
cyl.type.description = cyl_type_by_size(tmp_2bytes * 10);
|
||||||
cyl.start.mbar = 200000;
|
cyl.start.mbar = 200000;
|
||||||
cyl.gasmix.he.permille = 0;
|
cyl.gasmix.he.permille = 0;
|
||||||
cyl.gasmix.o2.permille = 210;
|
cyl.gasmix.o2.permille = 210;
|
||||||
cyl.manually_added = true;
|
cyl.manually_added = true;
|
||||||
add_cloned_cylinder(&dt_dive->cylinders, cyl);
|
dt_dive->cylinders.push_back(std::move(cyl));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -350,14 +349,14 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
*/
|
*/
|
||||||
read_bytes(2);
|
read_bytes(2);
|
||||||
if (tmp_2bytes != 0x7FFF)
|
if (tmp_2bytes != 0x7FFF)
|
||||||
dt_dive->maxdepth.mm = dt_dive->dc.maxdepth.mm = (int32_t)tmp_2bytes * 10;
|
dt_dive->maxdepth.mm = dt_dive->dcs[0].maxdepth.mm = (int32_t)tmp_2bytes * 10;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dive time in minutes.
|
* Dive time in minutes.
|
||||||
*/
|
*/
|
||||||
read_bytes(2);
|
read_bytes(2);
|
||||||
if (tmp_2bytes != 0x7FFF)
|
if (tmp_2bytes != 0x7FFF)
|
||||||
dt_dive->duration.seconds = dt_dive->dc.duration.seconds = (uint32_t)tmp_2bytes * 60;
|
dt_dive->duration.seconds = dt_dive->dcs[0].duration.seconds = (uint32_t)tmp_2bytes * 60;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Minimum water temperature in C*100. If unknown, set it to 0K which
|
* Minimum water temperature in C*100. If unknown, set it to 0K which
|
||||||
@ -365,7 +364,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
*/
|
*/
|
||||||
read_bytes(2);
|
read_bytes(2);
|
||||||
if (tmp_2bytes != 0x7fff)
|
if (tmp_2bytes != 0x7fff)
|
||||||
dt_dive->watertemp.mkelvin = dt_dive->dc.watertemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
|
dt_dive->watertemp.mkelvin = dt_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
|
||||||
else
|
else
|
||||||
dt_dive->watertemp.mkelvin = 0;
|
dt_dive->watertemp.mkelvin = 0;
|
||||||
|
|
||||||
@ -373,7 +372,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
* Air used in bar*100.
|
* Air used in bar*100.
|
||||||
*/
|
*/
|
||||||
read_bytes(2);
|
read_bytes(2);
|
||||||
if (tmp_2bytes != 0x7FFF && dt_dive->cylinders.nr > 0)
|
if (tmp_2bytes != 0x7FFF && dt_dive->cylinders.size() > 0)
|
||||||
get_cylinder(dt_dive, 0)->gas_used.mliter = lrint(get_cylinder(dt_dive, 0)->type.size.mliter * (tmp_2bytes / 100.0));
|
get_cylinder(dt_dive, 0)->gas_used.mliter = lrint(get_cylinder(dt_dive, 0)->type.size.mliter * (tmp_2bytes / 100.0));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -382,30 +381,30 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
*/
|
*/
|
||||||
read_bytes(1);
|
read_bytes(1);
|
||||||
if (bit_set(tmp_1byte, 2))
|
if (bit_set(tmp_1byte, 2))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "no stop")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "no stop"));
|
||||||
if (bit_set(tmp_1byte, 3))
|
if (bit_set(tmp_1byte, 3))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "deco")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "deco"));
|
||||||
if (bit_set(tmp_1byte, 4))
|
if (bit_set(tmp_1byte, 4))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "single ascent")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "single ascent"));
|
||||||
if (bit_set(tmp_1byte, 5))
|
if (bit_set(tmp_1byte, 5))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "multiple ascent")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "multiple ascent"));
|
||||||
if (bit_set(tmp_1byte, 6))
|
if (bit_set(tmp_1byte, 6))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "fresh water")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "fresh water"));
|
||||||
if (bit_set(tmp_1byte, 7))
|
if (bit_set(tmp_1byte, 7))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "salt water")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "salt water"));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dive Type 2 - Bit table, use tags again
|
* Dive Type 2 - Bit table, use tags again
|
||||||
*/
|
*/
|
||||||
read_bytes(1);
|
read_bytes(1);
|
||||||
if (bit_set(tmp_1byte, 0)) {
|
if (bit_set(tmp_1byte, 0)) {
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup("nitrox"));
|
taglist_add_tag(dt_dive->tags, "nitrox");
|
||||||
is_nitrox = 1;
|
is_nitrox = 1;
|
||||||
}
|
}
|
||||||
if (bit_set(tmp_1byte, 1)) {
|
if (bit_set(tmp_1byte, 1)) {
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup("rebreather"));
|
taglist_add_tag(dt_dive->tags, "rebreather");
|
||||||
is_SCR = 1;
|
is_SCR = 1;
|
||||||
dt_dive->dc.divemode = PSCR;
|
dt_dive->dcs[0].divemode = PSCR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -413,36 +412,36 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
*/
|
*/
|
||||||
read_bytes(1);
|
read_bytes(1);
|
||||||
if (bit_set(tmp_1byte, 0))
|
if (bit_set(tmp_1byte, 0))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "sight seeing")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "sight seeing"));
|
||||||
if (bit_set(tmp_1byte, 1))
|
if (bit_set(tmp_1byte, 1))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "club dive")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "club dive"));
|
||||||
if (bit_set(tmp_1byte, 2))
|
if (bit_set(tmp_1byte, 2))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "instructor")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "instructor"));
|
||||||
if (bit_set(tmp_1byte, 3))
|
if (bit_set(tmp_1byte, 3))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "instruction")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "instruction"));
|
||||||
if (bit_set(tmp_1byte, 4))
|
if (bit_set(tmp_1byte, 4))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "night")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "night"));
|
||||||
if (bit_set(tmp_1byte, 5))
|
if (bit_set(tmp_1byte, 5))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "cave")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "cave"));
|
||||||
if (bit_set(tmp_1byte, 6))
|
if (bit_set(tmp_1byte, 6))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "ice")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "ice"));
|
||||||
if (bit_set(tmp_1byte, 7))
|
if (bit_set(tmp_1byte, 7))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "search")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "search"));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dive Activity 2 - Bit table, use tags again
|
* Dive Activity 2 - Bit table, use tags again
|
||||||
*/
|
*/
|
||||||
read_bytes(1);
|
read_bytes(1);
|
||||||
if (bit_set(tmp_1byte, 0))
|
if (bit_set(tmp_1byte, 0))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "wreck")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "wreck"));
|
||||||
if (bit_set(tmp_1byte, 1))
|
if (bit_set(tmp_1byte, 1))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "river")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "river"));
|
||||||
if (bit_set(tmp_1byte, 2))
|
if (bit_set(tmp_1byte, 2))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "drift")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "drift"));
|
||||||
if (bit_set(tmp_1byte, 3))
|
if (bit_set(tmp_1byte, 3))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "photo")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "photo"));
|
||||||
if (bit_set(tmp_1byte, 4))
|
if (bit_set(tmp_1byte, 4))
|
||||||
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "other")));
|
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "other"));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Other activities - String 1st byte = long
|
* Other activities - String 1st byte = long
|
||||||
@ -451,10 +450,9 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
read_bytes(1);
|
read_bytes(1);
|
||||||
if (tmp_1byte != 0) {
|
if (tmp_1byte != 0) {
|
||||||
read_string(tmp_string1);
|
read_string(tmp_string1);
|
||||||
snprintf(buffer, sizeof(buffer), "%s: %s\n",
|
tmp_notes_str= format_string_std("%s: %s\n",
|
||||||
QT_TRANSLATE_NOOP("gettextFromC", "Other activities"),
|
translate("gettextFromC", "Other activities"),
|
||||||
tmp_string1);
|
tmp_string1);
|
||||||
tmp_notes_str = strdup(buffer);
|
|
||||||
free(tmp_string1);
|
free(tmp_string1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,7 +462,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
read_bytes(1);
|
read_bytes(1);
|
||||||
if (tmp_1byte != 0) {
|
if (tmp_1byte != 0) {
|
||||||
read_string(tmp_string1);
|
read_string(tmp_string1);
|
||||||
dt_dive->buddy = strdup((char *)tmp_string1);
|
dt_dive->buddy = (const char *)tmp_string1;
|
||||||
free(tmp_string1);
|
free(tmp_string1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,15 +472,12 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
read_bytes(1);
|
read_bytes(1);
|
||||||
if (tmp_1byte != 0) {
|
if (tmp_1byte != 0) {
|
||||||
read_string(tmp_string1);
|
read_string(tmp_string1);
|
||||||
int len = snprintf(buffer, sizeof(buffer), "%s%s:\n%s",
|
dt_dive->notes = format_string_std("%s%s:\n%s",
|
||||||
tmp_notes_str ? tmp_notes_str : "",
|
tmp_notes_str.c_str(),
|
||||||
QT_TRANSLATE_NOOP("gettextFromC", "Datatrak/Wlog notes"),
|
translate("gettextFromC", "Datatrak/Wlog notes"),
|
||||||
tmp_string1);
|
tmp_string1);
|
||||||
dt_dive->notes = (char *)calloc((len +1), 1);
|
|
||||||
memcpy(dt_dive->notes, buffer, len);
|
|
||||||
free(tmp_string1);
|
free(tmp_string1);
|
||||||
}
|
}
|
||||||
free(tmp_notes_str);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alarms 1 and Alarms2 - Bit tables - Not in Subsurface, we use the profile
|
* Alarms 1 and Alarms2 - Bit tables - Not in Subsurface, we use the profile
|
||||||
@ -520,7 +515,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
libdc_model = dtrak_prepare_data(tmp_1byte, devdata);
|
libdc_model = dtrak_prepare_data(tmp_1byte, devdata);
|
||||||
if (!libdc_model)
|
if (!libdc_model)
|
||||||
report_error(translate("gettextFromC", "[Warning] Manual dive # %d\n"), dt_dive->number);
|
report_error(translate("gettextFromC", "[Warning] Manual dive # %d\n"), dt_dive->number);
|
||||||
dt_dive->dc.model = copy_string(devdata->model);
|
dt_dive->dcs[0].model = devdata.model;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Air usage, unknown use. Probably allows or deny manually entering gas
|
* Air usage, unknown use. Probably allows or deny manually entering gas
|
||||||
@ -543,16 +538,16 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
compl_buffer = (unsigned char *) calloc(18 + profile_length, 1);
|
compl_buffer = (unsigned char *) calloc(18 + profile_length, 1);
|
||||||
rc = dt_libdc_buffer(membuf, profile_length, libdc_model, compl_buffer);
|
rc = dt_libdc_buffer(membuf, profile_length, libdc_model, compl_buffer);
|
||||||
if (rc == DC_STATUS_SUCCESS) {
|
if (rc == DC_STATUS_SUCCESS) {
|
||||||
libdc_buffer_parser(dt_dive, devdata, compl_buffer, profile_length + 18);
|
libdc_buffer_parser(dt_dive, &devdata, compl_buffer, profile_length + 18);
|
||||||
} else {
|
} else {
|
||||||
report_error(translate("gettextFromC", "[Error] Out of memory for dive %d. Abort parsing."), dt_dive->number);
|
report_error(translate("gettextFromC", "[Error] Out of memory for dive %d. Abort parsing."), dt_dive->number);
|
||||||
free(compl_buffer);
|
free(compl_buffer);
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
if (is_nitrox && dt_dive->cylinders.nr > 0)
|
if (is_nitrox && dt_dive->cylinders.size() > 0)
|
||||||
get_cylinder(dt_dive, 0)->gasmix.o2.permille =
|
get_cylinder(dt_dive, 0)->gasmix.o2.permille =
|
||||||
lrint(membuf[23] & 0x0F ? 20.0 + 2 * (membuf[23] & 0x0F) : 21.0) * 10;
|
lrint(membuf[23] & 0x0F ? 20.0 + 2 * (membuf[23] & 0x0F) : 21.0) * 10;
|
||||||
if (is_O2 && dt_dive->cylinders.nr > 0)
|
if (is_O2 && dt_dive->cylinders.size() > 0)
|
||||||
get_cylinder(dt_dive, 0)->gasmix.o2.permille = membuf[23] * 10;
|
get_cylinder(dt_dive, 0)->gasmix.o2.permille = membuf[23] * 10;
|
||||||
free(compl_buffer);
|
free(compl_buffer);
|
||||||
}
|
}
|
||||||
@ -562,19 +557,16 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
|
|||||||
* Initialize some dive data not supported by Datatrak/WLog
|
* Initialize some dive data not supported by Datatrak/WLog
|
||||||
*/
|
*/
|
||||||
if (!libdc_model)
|
if (!libdc_model)
|
||||||
dt_dive->dc.deviceid = 0;
|
dt_dive->dcs[0].deviceid = 0;
|
||||||
else
|
else
|
||||||
dt_dive->dc.deviceid = 0xffffffff;
|
dt_dive->dcs[0].deviceid = 0xffffffff;
|
||||||
dt_dive->dc.next = NULL;
|
if (!is_SCR && dt_dive->cylinders.size() > 0) {
|
||||||
if (!is_SCR && dt_dive->cylinders.nr > 0) {
|
|
||||||
get_cylinder(dt_dive, 0)->end.mbar = get_cylinder(dt_dive, 0)->start.mbar -
|
get_cylinder(dt_dive, 0)->end.mbar = get_cylinder(dt_dive, 0)->start.mbar -
|
||||||
((get_cylinder(dt_dive, 0)->gas_used.mliter / get_cylinder(dt_dive, 0)->type.size.mliter) * 1000);
|
((get_cylinder(dt_dive, 0)->gas_used.mliter / get_cylinder(dt_dive, 0)->type.size.mliter) * 1000);
|
||||||
}
|
}
|
||||||
free(devdata);
|
|
||||||
return (char *)membuf;
|
return (char *)membuf;
|
||||||
bail:
|
bail:
|
||||||
free(locality);
|
free(locality);
|
||||||
free(devdata);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -607,7 +599,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco
|
|||||||
pos_viz = offset + 258,
|
pos_viz = offset + 258,
|
||||||
pos_tank_init = offset + 266,
|
pos_tank_init = offset + 266,
|
||||||
pos_suit = offset + 268;
|
pos_suit = offset + 268;
|
||||||
char *wlog_notes = NULL, *wlog_suit = NULL, *buffer = NULL;
|
char *wlog_notes = NULL, *wlog_suit = NULL;
|
||||||
unsigned char *runner = (unsigned char *) wl_mem.data();
|
unsigned char *runner = (unsigned char *) wl_mem.data();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -619,24 +611,16 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco
|
|||||||
(void)memcpy(wlog_notes_temp, runner + offset, NOTES_LENGTH);
|
(void)memcpy(wlog_notes_temp, runner + offset, NOTES_LENGTH);
|
||||||
wlog_notes = to_utf8((unsigned char *) wlog_notes_temp);
|
wlog_notes = to_utf8((unsigned char *) wlog_notes_temp);
|
||||||
}
|
}
|
||||||
if (dt_dive->notes && wlog_notes) {
|
if (wlog_notes)
|
||||||
buffer = (char *)calloc (strlen(dt_dive->notes) + strlen(wlog_notes) + 1, 1);
|
dt_dive->notes += wlog_notes;
|
||||||
sprintf(buffer, "%s%s", dt_dive->notes, wlog_notes);
|
|
||||||
free(dt_dive->notes);
|
|
||||||
dt_dive->notes = copy_string(buffer);
|
|
||||||
} else if (wlog_notes) {
|
|
||||||
dt_dive->notes = copy_string(wlog_notes);
|
|
||||||
}
|
|
||||||
free(buffer);
|
|
||||||
free(wlog_notes);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Weight in Kg * 100
|
* Weight in Kg * 100
|
||||||
*/
|
*/
|
||||||
tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]);
|
tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]);
|
||||||
if (tmp != 0x7fff) {
|
if (tmp != 0x7fff) {
|
||||||
weightsystem_t ws = { {tmp * 10}, QT_TRANSLATE_NOOP("gettextFromC", "unknown"), false };
|
weightsystem_t ws = { {tmp * 10}, translate("gettextFromC", "unknown"), false };
|
||||||
add_cloned_weightsystem(&dt_dive->weightsystems, ws);
|
dt_dive->weightsystems.push_back(std::move(ws));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -670,7 +654,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco
|
|||||||
wlog_suit = to_utf8((unsigned char *) wlog_suit_temp);
|
wlog_suit = to_utf8((unsigned char *) wlog_suit_temp);
|
||||||
}
|
}
|
||||||
if (wlog_suit)
|
if (wlog_suit)
|
||||||
dt_dive->suit = copy_string(wlog_suit);
|
dt_dive->suit = wlog_suit;
|
||||||
free(wlog_suit);
|
free(wlog_suit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -703,25 +687,24 @@ int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log)
|
|||||||
runner = mem.data();
|
runner = mem.data();
|
||||||
JUMP(runner, 12);
|
JUMP(runner, 12);
|
||||||
|
|
||||||
// Secuential parsing. Abort if received NULL from dt_dive_parser.
|
// Sequential parsing. Abort if received NULL from dt_dive_parser.
|
||||||
while ((i < numdives) && (runner < maxbuf)) {
|
while ((i < numdives) && (runner < maxbuf)) {
|
||||||
struct dive *ptdive = alloc_dive();
|
auto ptdive = std::make_unique<dive>();
|
||||||
|
|
||||||
runner = dt_dive_parser((unsigned char *)runner, ptdive, log, maxbuf);
|
runner = dt_dive_parser((unsigned char *)runner, ptdive.get(), log, maxbuf);
|
||||||
if (!wl_mem.empty())
|
if (!wl_mem.empty())
|
||||||
wlog_compl_parser(wl_mem, ptdive, i);
|
wlog_compl_parser(wl_mem, ptdive.get(), i);
|
||||||
if (runner == NULL) {
|
if (runner == NULL) {
|
||||||
report_error("%s", translate("gettextFromC", "Error: no dive"));
|
report_error("%s", translate("gettextFromC", "Error: no dive"));
|
||||||
free(ptdive);
|
|
||||||
rc = 1;
|
rc = 1;
|
||||||
goto out;
|
goto out;
|
||||||
} else {
|
} else {
|
||||||
record_dive_to_table(ptdive, log->dives);
|
log->dives.record_dive(std::move(ptdive));
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
sort_dive_table(log->dives);
|
log->dives.sort();
|
||||||
return rc;
|
return rc;
|
||||||
bail:
|
bail:
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@ -21,7 +21,6 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include "deco.h"
|
#include "deco.h"
|
||||||
#include "ssrf.h"
|
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
#include "gas.h"
|
#include "gas.h"
|
||||||
#include "subsurface-string.h"
|
#include "subsurface-string.h"
|
||||||
@ -216,7 +215,7 @@ static double vpmb_tolerated_ambient_pressure(struct deco_state *ds, double refe
|
|||||||
return ds->tissue_n2_sat[ci] + ds->tissue_he_sat[ci] + vpmb_config.other_gases_pressure - total_gradient;
|
return ds->tissue_n2_sat[ci] + ds->tissue_he_sat[ci] + vpmb_config.other_gases_pressure - total_gradient;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" double tissue_tolerance_calc(struct deco_state *ds, const struct dive *dive, double pressure, bool in_planner)
|
double tissue_tolerance_calc(struct deco_state *ds, const struct dive *dive, double pressure, bool in_planner)
|
||||||
{
|
{
|
||||||
int ci = -1;
|
int ci = -1;
|
||||||
double ret_tolerance_limit_ambient_pressure = 0.0;
|
double ret_tolerance_limit_ambient_pressure = 0.0;
|
||||||
@ -323,7 +322,7 @@ static double calc_surface_phase(double surface_pressure, double he_pressure, do
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void vpmb_start_gradient(struct deco_state *ds)
|
void vpmb_start_gradient(struct deco_state *ds)
|
||||||
{
|
{
|
||||||
int ci;
|
int ci;
|
||||||
|
|
||||||
@ -333,7 +332,7 @@ extern "C" void vpmb_start_gradient(struct deco_state *ds)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void vpmb_next_gradient(struct deco_state *ds, double deco_time, double surface_pressure, bool in_planner)
|
void vpmb_next_gradient(struct deco_state *ds, double deco_time, double surface_pressure, bool in_planner)
|
||||||
{
|
{
|
||||||
int ci;
|
int ci;
|
||||||
double n2_b, n2_c;
|
double n2_b, n2_c;
|
||||||
@ -379,7 +378,7 @@ static double solve_cubic(double A, double B, double C)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" void nuclear_regeneration(struct deco_state *ds, double time)
|
void nuclear_regeneration(struct deco_state *ds, double time)
|
||||||
{
|
{
|
||||||
time /= 60.0;
|
time /= 60.0;
|
||||||
int ci;
|
int ci;
|
||||||
@ -411,7 +410,7 @@ static double calc_inner_pressure(double crit_radius, double onset_tension, doub
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculates the crushing pressure in the given moment. Updates crushing_onset_tension and critical radius if needed
|
// Calculates the crushing pressure in the given moment. Updates crushing_onset_tension and critical radius if needed
|
||||||
extern "C" void calc_crushing_pressure(struct deco_state *ds, double pressure)
|
void calc_crushing_pressure(struct deco_state *ds, double pressure)
|
||||||
{
|
{
|
||||||
int ci;
|
int ci;
|
||||||
double gradient;
|
double gradient;
|
||||||
@ -443,12 +442,11 @@ extern "C" void calc_crushing_pressure(struct deco_state *ds, double pressure)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* add period_in_seconds at the given pressure and gas to the deco calculation */
|
/* add period_in_seconds at the given pressure and gas to the deco calculation */
|
||||||
extern "C" void add_segment(struct deco_state *ds, double pressure, struct gasmix gasmix, int period_in_seconds, int ccpo2, enum divemode_t divemode, int, bool in_planner)
|
void add_segment(struct deco_state *ds, double pressure, struct gasmix gasmix, int period_in_seconds, int ccpo2, enum divemode_t divemode, int, bool in_planner)
|
||||||
{
|
{
|
||||||
int ci;
|
int ci;
|
||||||
struct gas_pressures pressures;
|
|
||||||
bool icd = false;
|
bool icd = false;
|
||||||
fill_pressures(&pressures, pressure - ((in_planner && (decoMode(true) == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE),
|
gas_pressures pressures = fill_pressures(pressure - ((in_planner && (decoMode(true) == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE),
|
||||||
gasmix, (double) ccpo2 / 1000.0, divemode);
|
gasmix, (double) ccpo2 / 1000.0, divemode);
|
||||||
|
|
||||||
for (ci = 0; ci < 16; ci++) {
|
for (ci = 0; ci < 16; ci++) {
|
||||||
@ -476,7 +474,7 @@ extern "C" void add_segment(struct deco_state *ds, double pressure, struct gasmi
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if DECO_CALC_DEBUG
|
#if DECO_CALC_DEBUG
|
||||||
extern "C" void dump_tissues(struct deco_state *ds)
|
void dump_tissues(struct deco_state *ds)
|
||||||
{
|
{
|
||||||
int ci;
|
int ci;
|
||||||
printf("N2 tissues:");
|
printf("N2 tissues:");
|
||||||
@ -489,7 +487,7 @@ extern "C" void dump_tissues(struct deco_state *ds)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern "C" void clear_vpmb_state(struct deco_state *ds)
|
void clear_vpmb_state(struct deco_state *ds)
|
||||||
{
|
{
|
||||||
int ci;
|
int ci;
|
||||||
for (ci = 0; ci < 16; ci++) {
|
for (ci = 0; ci < 16; ci++) {
|
||||||
@ -501,11 +499,11 @@ extern "C" void clear_vpmb_state(struct deco_state *ds)
|
|||||||
ds->max_bottom_ceiling_pressure.mbar = 0;
|
ds->max_bottom_ceiling_pressure.mbar = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void clear_deco(struct deco_state *ds, double surface_pressure, bool in_planner)
|
void clear_deco(struct deco_state *ds, double surface_pressure, bool in_planner)
|
||||||
{
|
{
|
||||||
int ci;
|
int ci;
|
||||||
|
|
||||||
memset(ds, 0, sizeof(*ds));
|
*ds = deco_state();
|
||||||
clear_vpmb_state(ds);
|
clear_vpmb_state(ds);
|
||||||
for (ci = 0; ci < 16; ci++) {
|
for (ci = 0; ci < 16; ci++) {
|
||||||
ds->tissue_n2_sat[ci] = (surface_pressure - ((in_planner && (decoMode(true) == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE)) * N2_IN_AIR / 1000;
|
ds->tissue_n2_sat[ci] = (surface_pressure - ((in_planner && (decoMode(true) == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE)) * N2_IN_AIR / 1000;
|
||||||
@ -545,7 +543,7 @@ void deco_state_cache::restore(struct deco_state *target, bool keep_vpmb_state)
|
|||||||
*target = *data;
|
*target = *data;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int deco_allowed_depth(double tissues_tolerance, double surface_pressure, const struct dive *dive, bool smooth)
|
int deco_allowed_depth(double tissues_tolerance, double surface_pressure, const struct dive *dive, bool smooth)
|
||||||
{
|
{
|
||||||
int depth;
|
int depth;
|
||||||
double pressure_delta;
|
double pressure_delta;
|
||||||
@ -564,7 +562,7 @@ extern "C" int deco_allowed_depth(double tissues_tolerance, double surface_press
|
|||||||
return depth;
|
return depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void set_gf(short gflow, short gfhigh)
|
void set_gf(short gflow, short gfhigh)
|
||||||
{
|
{
|
||||||
if (gflow != -1)
|
if (gflow != -1)
|
||||||
buehlmann_config.gf_low = (double)gflow / 100.0;
|
buehlmann_config.gf_low = (double)gflow / 100.0;
|
||||||
@ -572,7 +570,7 @@ extern "C" void set_gf(short gflow, short gfhigh)
|
|||||||
buehlmann_config.gf_high = (double)gfhigh / 100.0;
|
buehlmann_config.gf_high = (double)gfhigh / 100.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void set_vpmb_conservatism(short conservatism)
|
void set_vpmb_conservatism(short conservatism)
|
||||||
{
|
{
|
||||||
if (conservatism < 0)
|
if (conservatism < 0)
|
||||||
vpmb_config.conservatism = 0;
|
vpmb_config.conservatism = 0;
|
||||||
@ -582,7 +580,7 @@ extern "C" void set_vpmb_conservatism(short conservatism)
|
|||||||
vpmb_config.conservatism = conservatism;
|
vpmb_config.conservatism = conservatism;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" double get_gf(struct deco_state *ds, double ambpressure_bar, const struct dive *dive)
|
double get_gf(struct deco_state *ds, double ambpressure_bar, const struct dive *dive)
|
||||||
{
|
{
|
||||||
double surface_pressure_bar = get_surface_pressure_in_mbar(dive, true) / 1000.0;
|
double surface_pressure_bar = get_surface_pressure_in_mbar(dive, true) / 1000.0;
|
||||||
double gf_low = buehlmann_config.gf_low;
|
double gf_low = buehlmann_config.gf_low;
|
||||||
@ -596,7 +594,7 @@ extern "C" double get_gf(struct deco_state *ds, double ambpressure_bar, const st
|
|||||||
return gf;
|
return gf;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" double regressiona(const struct deco_state *ds)
|
double regressiona(const struct deco_state *ds)
|
||||||
{
|
{
|
||||||
if (ds->sum1 > 1) {
|
if (ds->sum1 > 1) {
|
||||||
double avxy = ds->sumxy / ds->sum1;
|
double avxy = ds->sumxy / ds->sum1;
|
||||||
@ -609,7 +607,7 @@ extern "C" double regressiona(const struct deco_state *ds)
|
|||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" double regressionb(const struct deco_state *ds)
|
double regressionb(const struct deco_state *ds)
|
||||||
{
|
{
|
||||||
if (ds->sum1)
|
if (ds->sum1)
|
||||||
return ds->sumy / ds->sum1 - ds->sumx * regressiona(ds) / ds->sum1;
|
return ds->sumy / ds->sum1 - ds->sumx * regressiona(ds) / ds->sum1;
|
||||||
@ -617,14 +615,14 @@ extern "C" double regressionb(const struct deco_state *ds)
|
|||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void reset_regression(struct deco_state *ds)
|
void reset_regression(struct deco_state *ds)
|
||||||
{
|
{
|
||||||
ds->sum1 = 0;
|
ds->sum1 = 0;
|
||||||
ds->sumxx = ds->sumx = 0L;
|
ds->sumxx = ds->sumx = 0L;
|
||||||
ds->sumy = ds->sumxy = 0.0;
|
ds->sumy = ds->sumxy = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void update_regression(struct deco_state *ds, const struct dive *dive)
|
void update_regression(struct deco_state *ds, const struct dive *dive)
|
||||||
{
|
{
|
||||||
if (!ds->plot_depth)
|
if (!ds->plot_depth)
|
||||||
return;
|
return;
|
||||||
|
|||||||
61
core/deco.h
61
core/deco.h
@ -5,46 +5,43 @@
|
|||||||
#include "units.h"
|
#include "units.h"
|
||||||
#include "gas.h"
|
#include "gas.h"
|
||||||
#include "divemode.h"
|
#include "divemode.h"
|
||||||
|
#include <memory>
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct dive;
|
struct dive;
|
||||||
struct divecomputer;
|
struct divecomputer;
|
||||||
struct decostop;
|
struct decostop;
|
||||||
|
|
||||||
struct deco_state {
|
struct deco_state {
|
||||||
double tissue_n2_sat[16];
|
double tissue_n2_sat[16] = {};
|
||||||
double tissue_he_sat[16];
|
double tissue_he_sat[16] = {};
|
||||||
double tolerated_by_tissue[16];
|
double tolerated_by_tissue[16] = {};
|
||||||
double tissue_inertgas_saturation[16];
|
double tissue_inertgas_saturation[16] = {};
|
||||||
double buehlmann_inertgas_a[16];
|
double buehlmann_inertgas_a[16] = {};
|
||||||
double buehlmann_inertgas_b[16];
|
double buehlmann_inertgas_b[16] = {};
|
||||||
|
|
||||||
double max_n2_crushing_pressure[16];
|
double max_n2_crushing_pressure[16] = {};
|
||||||
double max_he_crushing_pressure[16];
|
double max_he_crushing_pressure[16] = {};
|
||||||
|
|
||||||
double crushing_onset_tension[16]; // total inert gas tension in the t* moment
|
double crushing_onset_tension[16] = {}; // total inert gas tension in the t* moment
|
||||||
double n2_regen_radius[16]; // rs
|
double n2_regen_radius[16] = {}; // rs
|
||||||
double he_regen_radius[16];
|
double he_regen_radius[16] = {};
|
||||||
double max_ambient_pressure; // last moment we were descending
|
double max_ambient_pressure = 0.0; // last moment we were descending
|
||||||
|
|
||||||
double bottom_n2_gradient[16];
|
double bottom_n2_gradient[16] = {};
|
||||||
double bottom_he_gradient[16];
|
double bottom_he_gradient[16] = {};
|
||||||
|
|
||||||
double initial_n2_gradient[16];
|
double initial_n2_gradient[16] = {};
|
||||||
double initial_he_gradient[16];
|
double initial_he_gradient[16] = {};
|
||||||
pressure_t first_ceiling_pressure;
|
pressure_t first_ceiling_pressure;
|
||||||
pressure_t max_bottom_ceiling_pressure;
|
pressure_t max_bottom_ceiling_pressure;
|
||||||
int ci_pointing_to_guiding_tissue;
|
int ci_pointing_to_guiding_tissue = 0;
|
||||||
double gf_low_pressure_this_dive;
|
double gf_low_pressure_this_dive = 0.0;
|
||||||
int deco_time;
|
int deco_time = 0;
|
||||||
bool icd_warning;
|
bool icd_warning = false;
|
||||||
int sum1;
|
int sum1 = 0;
|
||||||
long sumx, sumxx;
|
long sumx = 0, sumxx = 0;
|
||||||
double sumy, sumxy;
|
double sumy = 0, sumxy = 0;
|
||||||
int plot_depth;
|
int plot_depth = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const double buehlmann_N2_t_halflife[];
|
extern const double buehlmann_N2_t_halflife[];
|
||||||
@ -70,12 +67,6 @@ extern double regressionb(const struct deco_state *ds);
|
|||||||
extern void reset_regression(struct deco_state *ds);
|
extern void reset_regression(struct deco_state *ds);
|
||||||
extern void update_regression(struct deco_state *ds, const struct dive *dive);
|
extern void update_regression(struct deco_state *ds, const struct dive *dive);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
|
|
||||||
// C++ only functions
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
struct deco_state_cache {
|
struct deco_state_cache {
|
||||||
// Test if there is cached data
|
// Test if there is cached data
|
||||||
operator bool () {
|
operator bool () {
|
||||||
@ -87,6 +78,4 @@ private:
|
|||||||
std::unique_ptr<deco_state> data;
|
std::unique_ptr<deco_state> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // DECO_H
|
#endif // DECO_H
|
||||||
|
|||||||
219
core/device.cpp
219
core/device.cpp
@ -8,7 +8,7 @@
|
|||||||
#include "selection.h"
|
#include "selection.h"
|
||||||
#include "core/settings/qPrefDiveComputer.h"
|
#include "core/settings/qPrefDiveComputer.h"
|
||||||
|
|
||||||
struct fingerprint_table fingerprint_table;
|
fingerprint_table fingerprints;
|
||||||
|
|
||||||
static bool same_device(const device &dev1, const device &dev2)
|
static bool same_device(const device &dev1, const device &dev2)
|
||||||
{
|
{
|
||||||
@ -18,42 +18,35 @@ static bool same_device(const device &dev1, const device &dev2)
|
|||||||
|
|
||||||
bool device::operator<(const device &a) const
|
bool device::operator<(const device &a) const
|
||||||
{
|
{
|
||||||
int diff;
|
return std::tie(model, serialNumber) < std::tie(a.model, a.serialNumber);
|
||||||
|
|
||||||
diff = model.compare(a.model);
|
|
||||||
if (diff)
|
|
||||||
return diff < 0;
|
|
||||||
|
|
||||||
return serialNumber < a.serialNumber;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" const struct device *get_device_for_dc(const struct device_table *table, const struct divecomputer *dc)
|
const struct device *get_device_for_dc(const device_table &table, const struct divecomputer *dc)
|
||||||
{
|
{
|
||||||
if (!dc->model || !dc->serial)
|
if (dc->model.empty() || dc->serial.empty())
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
const std::vector<device> &dcs = table->devices;
|
|
||||||
device dev { dc->model, dc->serial };
|
device dev { dc->model, dc->serial };
|
||||||
auto it = std::lower_bound(dcs.begin(), dcs.end(), dev);
|
auto it = std::lower_bound(table.begin(), table.end(), dev);
|
||||||
return it != dcs.end() && same_device(*it, dev) ? &*it : NULL;
|
return it != table.end() && same_device(*it, dev) ? &*it : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int get_or_add_device_for_dc(struct device_table *table, const struct divecomputer *dc)
|
int get_or_add_device_for_dc(device_table &table, const struct divecomputer *dc)
|
||||||
{
|
{
|
||||||
if (!dc->model || !dc->serial)
|
if (dc->model.empty() || dc->serial.empty())
|
||||||
return -1;
|
return -1;
|
||||||
const struct device *dev = get_device_for_dc(table, dc);
|
const struct device *dev = get_device_for_dc(table, dc);
|
||||||
if (dev) {
|
if (dev) {
|
||||||
auto it = std::lower_bound(table->devices.begin(), table->devices.end(), *dev);
|
auto it = std::lower_bound(table.begin(), table.end(), *dev);
|
||||||
return it - table->devices.begin();
|
return it - table.begin();
|
||||||
}
|
}
|
||||||
return create_device_node(table, dc->model, dc->serial, "");
|
return create_device_node(table, dc->model, dc->serial, std::string());
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" bool device_exists(const struct device_table *device_table, const struct device *dev)
|
bool device_exists(const device_table &table, const struct device &dev)
|
||||||
{
|
{
|
||||||
auto it = std::lower_bound(device_table->devices.begin(), device_table->devices.end(), *dev);
|
auto it = std::lower_bound(table.begin(), table.end(), dev);
|
||||||
return it != device_table->devices.end() && same_device(*it, *dev);
|
return it != table.end() && same_device(*it, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void device::showchanges(const std::string &n) const
|
void device::showchanges(const std::string &n) const
|
||||||
@ -66,7 +59,7 @@ void device::showchanges(const std::string &n) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int addDC(std::vector<device> &dcs, const std::string &m, const std::string &s, const std::string &n)
|
int create_device_node(device_table &dcs, const std::string &m, const std::string &s, const std::string &n)
|
||||||
{
|
{
|
||||||
if (m.empty() || s.empty())
|
if (m.empty() || s.empty())
|
||||||
return -1;
|
return -1;
|
||||||
@ -86,220 +79,122 @@ static int addDC(std::vector<device> &dcs, const std::string &m, const std::stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int create_device_node(struct device_table *device_table, const char *model, const char *serial, const char *nickname)
|
int add_to_device_table(device_table &device_table, const struct device &dev)
|
||||||
{
|
{
|
||||||
return addDC(device_table->devices, model ?: "", serial ?: "", nickname ?: "");
|
return create_device_node(device_table, dev.model, dev.serialNumber, dev.nickName);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int add_to_device_table(struct device_table *device_table, const struct device *dev)
|
int remove_device(device_table &table, const struct device &dev)
|
||||||
{
|
{
|
||||||
return create_device_node(device_table, dev->model.c_str(), dev->serialNumber.c_str(), dev->nickName.c_str());
|
auto it = std::lower_bound(table.begin(), table.end(), dev);
|
||||||
}
|
if (it != table.end() && same_device(*it, dev)) {
|
||||||
|
int idx = it - table.begin();
|
||||||
extern "C" int remove_device(struct device_table *device_table, const struct device *dev)
|
table.erase(it);
|
||||||
{
|
|
||||||
auto it = std::lower_bound(device_table->devices.begin(), device_table->devices.end(), *dev);
|
|
||||||
if (it != device_table->devices.end() && same_device(*it, *dev)) {
|
|
||||||
int idx = it - device_table->devices.begin();
|
|
||||||
device_table->devices.erase(it);
|
|
||||||
return idx;
|
return idx;
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void remove_from_device_table(struct device_table *device_table, int idx)
|
void remove_from_device_table(device_table &table, int idx)
|
||||||
{
|
{
|
||||||
if (idx < 0 || idx >= (int)device_table->devices.size())
|
if (idx < 0 || idx >= (int)table.size())
|
||||||
return;
|
return;
|
||||||
device_table->devices.erase(device_table->devices.begin() + idx);
|
table.erase(table.begin() + idx);
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void clear_device_table(struct device_table *device_table)
|
|
||||||
{
|
|
||||||
device_table->devices.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns whether the given device is used by a selected dive. */
|
/* Returns whether the given device is used by a selected dive. */
|
||||||
extern "C" bool device_used_by_selected_dive(const struct device *dev)
|
bool device_used_by_selected_dive(const struct device &dev)
|
||||||
{
|
{
|
||||||
for (dive *d: getDiveSelection()) {
|
for (dive *d: getDiveSelection()) {
|
||||||
struct divecomputer *dc;
|
for (auto &dc: d->dcs) {
|
||||||
for_each_dc (d, dc) {
|
if (dc.deviceid == dev.deviceId)
|
||||||
if (dc->deviceid == dev->deviceId)
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int is_default_dive_computer_device(const char *name)
|
int is_default_dive_computer_device(const char *name)
|
||||||
{
|
{
|
||||||
return qPrefDiveComputer::device() == name;
|
return qPrefDiveComputer::device() == name;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *get_dc_nickname(const struct divecomputer *dc)
|
std::string get_dc_nickname(const struct divecomputer *dc)
|
||||||
{
|
{
|
||||||
const device *existNode = get_device_for_dc(divelog.devices, dc);
|
const device *existNode = get_device_for_dc(divelog.devices, dc);
|
||||||
|
|
||||||
if (existNode && !existNode->nickName.empty())
|
if (existNode && !existNode->nickName.empty())
|
||||||
return existNode->nickName.c_str();
|
return existNode->nickName;
|
||||||
else
|
else
|
||||||
return dc->model;
|
return dc->model;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int nr_devices(const struct device_table *table)
|
|
||||||
{
|
|
||||||
return (int)table->devices.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" const struct device *get_device(const struct device_table *table, int i)
|
|
||||||
{
|
|
||||||
if (i < 0 || i > nr_devices(table))
|
|
||||||
return NULL;
|
|
||||||
return &table->devices[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" struct device *get_device_mutable(struct device_table *table, int i)
|
|
||||||
{
|
|
||||||
if (i < 0 || i > nr_devices(table))
|
|
||||||
return NULL;
|
|
||||||
return &table->devices[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" const char *device_get_model(const struct device *dev)
|
|
||||||
{
|
|
||||||
return dev ? dev->model.c_str() : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" const char *device_get_serial(const struct device *dev)
|
|
||||||
{
|
|
||||||
return dev ? dev->serialNumber.c_str() : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" const char *device_get_nickname(const struct device *dev)
|
|
||||||
{
|
|
||||||
return dev ? dev->nickName.c_str() : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" struct device_table *alloc_device_table()
|
|
||||||
{
|
|
||||||
return new struct device_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void free_device_table(struct device_table *devices)
|
|
||||||
{
|
|
||||||
delete devices;
|
|
||||||
}
|
|
||||||
|
|
||||||
// managing fingerprint data
|
// managing fingerprint data
|
||||||
bool fingerprint_record::operator<(const fingerprint_record &a) const
|
bool fingerprint_record::operator<(const fingerprint_record &a) const
|
||||||
{
|
{
|
||||||
if (model == a.model)
|
return std::tie(model, serial) < std::tie(a.model, a.serial);
|
||||||
return serial < a.serial;
|
|
||||||
return model < a.model;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// annoyingly, the Cressi Edy doesn't support a serial number (it's always 0), but still uses fingerprints
|
// annoyingly, the Cressi Edy doesn't support a serial number (it's always 0), but still uses fingerprints
|
||||||
// so we can't bail on the serial number being 0
|
// so we can't bail on the serial number being 0
|
||||||
extern "C" unsigned int get_fingerprint_data(const struct fingerprint_table *table, uint32_t model, uint32_t serial, const unsigned char **fp_out)
|
std::pair<int, const unsigned char *> get_fingerprint_data(const fingerprint_table &table, uint32_t model, uint32_t serial)
|
||||||
{
|
{
|
||||||
if (model == 0 || fp_out == nullptr)
|
if (model == 0)
|
||||||
return 0;
|
return { 0, nullptr };
|
||||||
struct fingerprint_record fpr = { model, serial };
|
struct fingerprint_record fpr = { model, serial };
|
||||||
auto it = std::lower_bound(table->fingerprints.begin(), table->fingerprints.end(), fpr);
|
auto it = std::lower_bound(table.begin(), table.end(), fpr);
|
||||||
if (it != table->fingerprints.end() && it->model == model && it->serial == serial) {
|
if (it != table.end() && it->model == model && it->serial == serial) {
|
||||||
// std::lower_bound gets us the first element that isn't smaller than what we are looking
|
// std::lower_bound gets us the first element that isn't smaller than what we are looking
|
||||||
// for - so if one is found, we still need to check for equality
|
// for - so if one is found, we still need to check for equality
|
||||||
if (has_dive(it->fdeviceid, it->fdiveid)) {
|
if (has_dive(it->fdeviceid, it->fdiveid))
|
||||||
*fp_out = it->raw_data;
|
return { it->fsize, it->raw_data.get() };
|
||||||
return it->fsize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return { 0, nullptr };
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void create_fingerprint_node(struct fingerprint_table *table, uint32_t model, uint32_t serial,
|
void create_fingerprint_node(fingerprint_table &table, uint32_t model, uint32_t serial,
|
||||||
const unsigned char *raw_data_in, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid)
|
const unsigned char *raw_data_in, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid)
|
||||||
{
|
{
|
||||||
// since raw data can contain \0 we copy this manually, not as string
|
// since raw data can contain \0 we copy this manually, not as string
|
||||||
unsigned char *raw_data = (unsigned char *)malloc(fsize);
|
auto raw_data = std::make_unique<unsigned char []>(fsize);
|
||||||
if (!raw_data)
|
std::copy(raw_data_in, raw_data_in + fsize, raw_data.get());
|
||||||
return;
|
|
||||||
memcpy(raw_data, raw_data_in, fsize);
|
|
||||||
|
|
||||||
struct fingerprint_record fpr = { model, serial, raw_data, fsize, fdeviceid, fdiveid };
|
struct fingerprint_record fpr = { model, serial, std::move(raw_data), fsize, fdeviceid, fdiveid };
|
||||||
auto it = std::lower_bound(table->fingerprints.begin(), table->fingerprints.end(), fpr);
|
auto it = std::lower_bound(table.begin(), table.end(), fpr);
|
||||||
if (it != table->fingerprints.end() && it->model == model && it->serial == serial) {
|
if (it != table.end() && it->model == model && it->serial == serial) {
|
||||||
// std::lower_bound gets us the first element that isn't smaller than what we are looking
|
// std::lower_bound gets us the first element that isn't smaller than what we are looking
|
||||||
// for - so if one is found, we still need to check for equality - and then we
|
// for - so if one is found, we still need to check for equality - and then we
|
||||||
// can update the existing entry; first we free the memory for the stored raw data
|
// can update the existing entry; first we free the memory for the stored raw data
|
||||||
free(it->raw_data);
|
|
||||||
it->fdeviceid = fdeviceid;
|
it->fdeviceid = fdeviceid;
|
||||||
it->fdiveid = fdiveid;
|
it->fdiveid = fdiveid;
|
||||||
it->raw_data = raw_data;
|
it->raw_data = std::move(fpr.raw_data);
|
||||||
it->fsize = fsize;
|
it->fsize = fsize;
|
||||||
} else {
|
} else {
|
||||||
// insert a new one
|
// insert a new one
|
||||||
table->fingerprints.insert(it, fpr);
|
table.insert(it, std::move(fpr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void create_fingerprint_node_from_hex(struct fingerprint_table *table, uint32_t model, uint32_t serial,
|
void create_fingerprint_node_from_hex(fingerprint_table &table, uint32_t model, uint32_t serial,
|
||||||
const char *hex_data, uint32_t fdeviceid, uint32_t fdiveid)
|
const std::string &hex_data, uint32_t fdeviceid, uint32_t fdiveid)
|
||||||
{
|
{
|
||||||
QByteArray raw = QByteArray::fromHex(hex_data);
|
QByteArray raw = QByteArray::fromHex(hex_data.c_str());
|
||||||
create_fingerprint_node(table, model, serial,
|
create_fingerprint_node(table, model, serial,
|
||||||
(const unsigned char *)raw.constData(), raw.size(), fdeviceid, fdiveid);
|
(const unsigned char *)raw.constData(), raw.size(), fdeviceid, fdiveid);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int nr_fingerprints(struct fingerprint_table *table)
|
|
||||||
{
|
|
||||||
return table->fingerprints.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" uint32_t fp_get_model(struct fingerprint_table *table, unsigned int i)
|
|
||||||
{
|
|
||||||
if (!table || i >= table->fingerprints.size())
|
|
||||||
return 0;
|
|
||||||
return table->fingerprints[i].model;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" uint32_t fp_get_serial(struct fingerprint_table *table, unsigned int i)
|
|
||||||
{
|
|
||||||
if (!table || i >= table->fingerprints.size())
|
|
||||||
return 0;
|
|
||||||
return table->fingerprints[i].serial;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" uint32_t fp_get_deviceid(struct fingerprint_table *table, unsigned int i)
|
|
||||||
{
|
|
||||||
if (!table || i >= table->fingerprints.size())
|
|
||||||
return 0;
|
|
||||||
return table->fingerprints[i].fdeviceid;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" uint32_t fp_get_diveid(struct fingerprint_table *table, unsigned int i)
|
|
||||||
{
|
|
||||||
if (!table || i >= table->fingerprints.size())
|
|
||||||
return 0;
|
|
||||||
return table->fingerprints[i].fdiveid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char to_hex_digit(unsigned char d)
|
static char to_hex_digit(unsigned char d)
|
||||||
{
|
{
|
||||||
return d <= 9 ? d + '0' : d - 10 + 'a';
|
return d <= 9 ? d + '0' : d - 10 + 'a';
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fp_get_data(struct fingerprint_table *table, unsigned int i)
|
std::string fingerprint_record::get_data() const
|
||||||
{
|
{
|
||||||
if (!table || i >= table->fingerprints.size())
|
std::string res(fsize * 2, ' ');
|
||||||
return std::string();
|
for (unsigned int i = 0; i < fsize; ++i) {
|
||||||
struct fingerprint_record *fpr = &table->fingerprints[i];
|
res[2 * i] = to_hex_digit((raw_data[i] >> 4) & 0xf);
|
||||||
std::string res(fpr->fsize * 2, ' ');
|
res[2 * i + 1] = to_hex_digit(raw_data[i] & 0xf);
|
||||||
for (unsigned int i = 0; i < fpr->fsize; ++i) {
|
|
||||||
res[2 * i] = to_hex_digit((fpr->raw_data[i] >> 4) & 0xf);
|
|
||||||
res[2 * i + 1] = to_hex_digit(fpr->raw_data[i] & 0xf);
|
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
112
core/device.h
112
core/device.h
@ -3,73 +3,13 @@
|
|||||||
#define DEVICE_H
|
#define DEVICE_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <memory>
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct divecomputer;
|
|
||||||
struct device;
|
|
||||||
struct device_table;
|
|
||||||
struct dive_table;
|
|
||||||
|
|
||||||
// global device table
|
|
||||||
extern struct fingerprint_table fingerprint_table;
|
|
||||||
|
|
||||||
extern int create_device_node(struct device_table *table, const char *model, const char *serial, const char *nickname);
|
|
||||||
extern int nr_devices(const struct device_table *table);
|
|
||||||
extern const struct device *get_device(const struct device_table *table, int i);
|
|
||||||
extern struct device *get_device_mutable(struct device_table *table, int i);
|
|
||||||
extern void clear_device_table(struct device_table *table);
|
|
||||||
const char *get_dc_nickname(const struct divecomputer *dc);
|
|
||||||
extern bool device_used_by_selected_dive(const struct device *dev);
|
|
||||||
|
|
||||||
extern const struct device *get_device_for_dc(const struct device_table *table, const struct divecomputer *dc);
|
|
||||||
extern int get_or_add_device_for_dc(struct device_table *table, const struct divecomputer *dc);
|
|
||||||
extern bool device_exists(const struct device_table *table, const struct device *dev);
|
|
||||||
extern int add_to_device_table(struct device_table *table, const struct device *dev); // returns index
|
|
||||||
extern int remove_device(struct device_table *table, const struct device *dev); // returns index or -1 if not found
|
|
||||||
extern void remove_from_device_table(struct device_table *table, int idx);
|
|
||||||
|
|
||||||
// struct device accessors for C-code. The returned strings are not stable!
|
|
||||||
const char *device_get_model(const struct device *dev);
|
|
||||||
const char *device_get_serial(const struct device *dev);
|
|
||||||
const char *device_get_nickname(const struct device *dev);
|
|
||||||
|
|
||||||
// for C code that needs to alloc/free a device table. (Let's try to get rid of those)
|
|
||||||
extern struct device_table *alloc_device_table();
|
|
||||||
extern void free_device_table(struct device_table *devices);
|
|
||||||
|
|
||||||
// create fingerprint entry - raw data remains owned by caller
|
|
||||||
extern void create_fingerprint_node(struct fingerprint_table *table, uint32_t model, uint32_t serial,
|
|
||||||
const unsigned char *raw_data, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid);
|
|
||||||
extern void create_fingerprint_node_from_hex(struct fingerprint_table *table, uint32_t model, uint32_t serial,
|
|
||||||
const char *hex_data, uint32_t fdeviceid, uint32_t fdiveid);
|
|
||||||
// look up the fingerprint for model/serial - returns the number of bytes in the fingerprint; memory owned by the table
|
|
||||||
extern unsigned int get_fingerprint_data(const struct fingerprint_table *table, uint32_t model, uint32_t serial, const unsigned char **fp_out);
|
|
||||||
|
|
||||||
// access the fingerprint data from C
|
|
||||||
extern int nr_fingerprints(struct fingerprint_table *table);
|
|
||||||
extern uint32_t fp_get_model(struct fingerprint_table *table, unsigned int i);
|
|
||||||
extern uint32_t fp_get_serial(struct fingerprint_table *table, unsigned int i);
|
|
||||||
extern uint32_t fp_get_deviceid(struct fingerprint_table *table, unsigned int i);
|
|
||||||
extern uint32_t fp_get_diveid(struct fingerprint_table *table, unsigned int i);
|
|
||||||
|
|
||||||
extern int is_default_dive_computer_device(const char *);
|
|
||||||
|
|
||||||
typedef void (*device_callback_t)(const char *name, void *userdata);
|
|
||||||
|
|
||||||
extern int enumerate_devices(device_callback_t callback, void *userdata, unsigned int transport);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Functions and global variables that are only available to C++ code
|
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
struct divecomputer;
|
||||||
|
struct dive_table;
|
||||||
|
|
||||||
struct device {
|
struct device {
|
||||||
bool operator<(const device &a) const;
|
bool operator<(const device &a) const;
|
||||||
void showchanges(const std::string &n) const;
|
void showchanges(const std::string &n) const;
|
||||||
@ -79,28 +19,48 @@ struct device {
|
|||||||
uint32_t deviceId; // Always the string hash of the serialNumber
|
uint32_t deviceId; // Always the string hash of the serialNumber
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using device_table = std::vector<device>;
|
||||||
|
|
||||||
|
extern int create_device_node(device_table &table, const std::string &model, const std::string &serial, const std::string &nickname);
|
||||||
|
std::string get_dc_nickname(const struct divecomputer *dc);
|
||||||
|
extern bool device_used_by_selected_dive(const struct device &dev);
|
||||||
|
|
||||||
|
extern const struct device *get_device_for_dc(const device_table &table, const struct divecomputer *dc);
|
||||||
|
extern int get_or_add_device_for_dc(device_table &table, const struct divecomputer *dc);
|
||||||
|
extern bool device_exists(const device_table &table, const struct device &dev);
|
||||||
|
extern int add_to_device_table(device_table &table, const struct device &dev); // returns index
|
||||||
|
extern int remove_device(device_table &table, const struct device &dev); // returns index or -1 if not found
|
||||||
|
extern void remove_from_device_table(device_table &table, int idx);
|
||||||
|
|
||||||
struct fingerprint_record {
|
struct fingerprint_record {
|
||||||
bool operator<(const fingerprint_record &a) const;
|
bool operator<(const fingerprint_record &a) const;
|
||||||
uint32_t model; // model and libdivecomputer serial number to
|
uint32_t model; // model and libdivecomputer serial number to
|
||||||
uint32_t serial; // look up the fingerprint
|
uint32_t serial; // look up the fingerprint
|
||||||
unsigned char *raw_data; // fingerprint data as provided by libdivecomputer
|
std::unique_ptr<unsigned char[]> raw_data; // fingerprint data as provided by libdivecomputer
|
||||||
unsigned int fsize; // size of raw fingerprint data
|
unsigned int fsize; // size of raw fingerprint data
|
||||||
unsigned int fdeviceid; // corresponding deviceid
|
unsigned int fdeviceid; // corresponding deviceid
|
||||||
unsigned int fdiveid; // corresponding diveid
|
unsigned int fdiveid; // corresponding diveid
|
||||||
|
std::string get_data() const; // As hex-string
|
||||||
};
|
};
|
||||||
|
|
||||||
struct device_table {
|
using fingerprint_table = std::vector<fingerprint_record>;
|
||||||
// Keep the dive computers in a vector sorted by (model, serial)
|
|
||||||
std::vector<device> devices;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct fingerprint_table {
|
// global device table
|
||||||
// Keep the fingerprint records in a vector sorted by (model, serial) - these are uint32_t here
|
extern fingerprint_table fingerprints;
|
||||||
std::vector<fingerprint_record> fingerprints;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string fp_get_data(struct fingerprint_table *table, unsigned int i);
|
// create fingerprint entry - raw data remains owned by caller
|
||||||
|
extern void create_fingerprint_node(fingerprint_table &table, uint32_t model, uint32_t serial,
|
||||||
|
const unsigned char *raw_data, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid);
|
||||||
|
extern void create_fingerprint_node_from_hex(fingerprint_table &table, uint32_t model, uint32_t serial,
|
||||||
|
const std::string &hex_data, uint32_t fdeviceid, uint32_t fdiveid);
|
||||||
|
// look up the fingerprint for model/serial - returns the number of bytes in the fingerprint; memory owned by the table
|
||||||
|
extern std::pair <int, const unsigned char *> get_fingerprint_data(const fingerprint_table &table, uint32_t model, uint32_t serial);
|
||||||
|
|
||||||
|
extern int is_default_dive_computer_device(const char *);
|
||||||
|
|
||||||
|
typedef void (*device_callback_t)(const char *name, void *userdata);
|
||||||
|
|
||||||
|
extern int enumerate_devices(device_callback_t callback, void *userdata, unsigned int transport);
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // DEVICE_H
|
#endif // DEVICE_H
|
||||||
|
|||||||
2211
core/dive.cpp
2211
core/dive.cpp
File diff suppressed because it is too large
Load Diff
186
core/dive.h
186
core/dive.h
@ -7,14 +7,13 @@
|
|||||||
#include "divemode.h"
|
#include "divemode.h"
|
||||||
#include "divecomputer.h"
|
#include "divecomputer.h"
|
||||||
#include "equipment.h"
|
#include "equipment.h"
|
||||||
#include "picture.h"
|
#include "picture.h" // TODO: remove
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <array>
|
||||||
#include <stdlib.h>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
#ifdef __cplusplus
|
#include <vector>
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern int last_xml_version;
|
extern int last_xml_version;
|
||||||
|
|
||||||
@ -22,44 +21,72 @@ extern const char *divemode_text_ui[];
|
|||||||
extern const char *divemode_text[];
|
extern const char *divemode_text[];
|
||||||
|
|
||||||
struct dive_site;
|
struct dive_site;
|
||||||
struct dive_site_table;
|
|
||||||
struct dive_table;
|
struct dive_table;
|
||||||
struct dive_trip;
|
struct dive_trip;
|
||||||
struct full_text_cache;
|
struct full_text_cache;
|
||||||
struct event;
|
struct event;
|
||||||
struct trip_table;
|
struct trip_table;
|
||||||
|
|
||||||
|
/* A unique_ptr that will not be copied if the parent class is copied.
|
||||||
|
* This is used to keep a pointer to the fulltext cache and avoid
|
||||||
|
* having it copied when the dive is copied, since the new dive is
|
||||||
|
* not (yet) registered in the fulltext system. Quite hackish.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct non_copying_unique_ptr : public std::unique_ptr<T> {
|
||||||
|
using std::unique_ptr<T>::unique_ptr;
|
||||||
|
using std::unique_ptr<T>::operator=;
|
||||||
|
non_copying_unique_ptr(const non_copying_unique_ptr<T> &) { }
|
||||||
|
void operator=(const non_copying_unique_ptr<T> &) { }
|
||||||
|
};
|
||||||
|
|
||||||
struct dive {
|
struct dive {
|
||||||
struct dive_trip *divetrip;
|
struct dive_trip *divetrip = nullptr;
|
||||||
timestamp_t when;
|
timestamp_t when = 0;
|
||||||
struct dive_site *dive_site;
|
struct dive_site *dive_site = nullptr;
|
||||||
char *notes;
|
std::string notes;
|
||||||
char *diveguide, *buddy;
|
std::string diveguide, buddy;
|
||||||
struct cylinder_table cylinders;
|
std::string suit;
|
||||||
struct weightsystem_table weightsystems;
|
cylinder_table cylinders;
|
||||||
char *suit;
|
weightsystem_table weightsystems;
|
||||||
int number;
|
int number = 0;
|
||||||
int rating;
|
int rating = 0;
|
||||||
int wavesize, current, visibility, surge, chill; /* 0 - 5 star ratings */
|
int wavesize = 0, current = 0, visibility = 0, surge = 0, chill = 0; /* 0 - 5 star ratings */
|
||||||
int sac, otu, cns, maxcns;
|
int sac = 0, otu = 0, cns = 0, maxcns = 0;
|
||||||
|
|
||||||
/* Calculated based on dive computer data */
|
/* Calculated based on dive computer data */
|
||||||
temperature_t mintemp, maxtemp, watertemp, airtemp;
|
temperature_t mintemp, maxtemp, watertemp, airtemp;
|
||||||
depth_t maxdepth, meandepth;
|
depth_t maxdepth, meandepth;
|
||||||
pressure_t surface_pressure;
|
pressure_t surface_pressure;
|
||||||
duration_t duration;
|
duration_t duration;
|
||||||
int salinity; // kg per 10000 l
|
int salinity = 0; // kg per 10000 l
|
||||||
int user_salinity; // water density reflecting a user-specified type
|
int user_salinity = 0; // water density reflecting a user-specified type
|
||||||
|
|
||||||
|
tag_list tags;
|
||||||
|
std::vector<divecomputer> dcs; // Attn: pointers to divecomputers are not stable!
|
||||||
|
int id = 0; // unique ID for this dive
|
||||||
|
picture_table pictures;
|
||||||
|
unsigned char git_id[20] = {};
|
||||||
|
bool notrip = false; /* Don't autogroup this dive to a trip */
|
||||||
|
bool selected = false;
|
||||||
|
bool hidden_by_filter = false;
|
||||||
|
non_copying_unique_ptr<full_text_cache> full_text; /* word cache for full text search */
|
||||||
|
bool invalid = false;
|
||||||
|
|
||||||
|
dive();
|
||||||
|
~dive();
|
||||||
|
dive(const dive &);
|
||||||
|
dive(dive &&);
|
||||||
|
dive &operator=(const dive &);
|
||||||
|
|
||||||
|
timestamp_t endtime() const; /* maximum over divecomputers (with samples) */
|
||||||
|
duration_t totaltime() const; /* maximum over divecomputers (with samples) */
|
||||||
|
temperature_t dc_airtemp() const; /* average over divecomputers */
|
||||||
|
temperature_t dc_watertemp() const; /* average over divecomputers */
|
||||||
|
|
||||||
|
bool is_planned() const;
|
||||||
|
bool is_logged() const;
|
||||||
|
|
||||||
struct tag_entry *tag_list;
|
|
||||||
struct divecomputer dc;
|
|
||||||
int id; // unique ID for this dive
|
|
||||||
struct picture_table pictures;
|
|
||||||
unsigned char git_id[20];
|
|
||||||
bool notrip; /* Don't autogroup this dive to a trip */
|
|
||||||
bool selected;
|
|
||||||
bool hidden_by_filter;
|
|
||||||
struct full_text_cache *full_text; /* word cache for full text search */
|
|
||||||
bool invalid;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* For the top-level list: an entry is either a dive or a trip */
|
/* For the top-level list: an entry is either a dive or a trip */
|
||||||
@ -72,8 +99,8 @@ extern void invalidate_dive_cache(struct dive *dive);
|
|||||||
extern bool dive_cache_is_valid(const struct dive *dive);
|
extern bool dive_cache_is_valid(const struct dive *dive);
|
||||||
|
|
||||||
extern int get_cylinder_idx_by_use(const struct dive *dive, enum cylinderuse cylinder_use_type);
|
extern int get_cylinder_idx_by_use(const struct dive *dive, enum cylinderuse cylinder_use_type);
|
||||||
extern void cylinder_renumber(struct dive *dive, int mapping[]);
|
extern void cylinder_renumber(struct dive &dive, int mapping[]);
|
||||||
extern int same_gasmix_cylinder(const cylinder_t *cyl, int cylid, const struct dive *dive, bool check_unused);
|
extern int same_gasmix_cylinder(const cylinder_t &cyl, int cylid, const struct dive *dive, bool check_unused);
|
||||||
|
|
||||||
/* when selectively copying dive information, which parts should be copied? */
|
/* when selectively copying dive information, which parts should be copied? */
|
||||||
struct dive_components {
|
struct dive_components {
|
||||||
@ -112,39 +139,19 @@ extern depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct div
|
|||||||
extern depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int roundto);
|
extern depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int roundto);
|
||||||
|
|
||||||
extern struct dive *get_dive(int nr);
|
extern struct dive *get_dive(int nr);
|
||||||
extern struct dive *get_dive_from_table(int nr, const struct dive_table *dt);
|
|
||||||
extern struct dive_site *get_dive_site_for_dive(const struct dive *dive);
|
extern struct dive_site *get_dive_site_for_dive(const struct dive *dive);
|
||||||
extern const char *get_dive_country(const struct dive *dive);
|
extern std::string get_dive_country(const struct dive *dive);
|
||||||
extern const char *get_dive_location(const struct dive *dive);
|
extern std::string get_dive_location(const struct dive *dive);
|
||||||
extern unsigned int number_of_computers(const struct dive *dive);
|
extern unsigned int number_of_computers(const struct dive *dive);
|
||||||
extern struct divecomputer *get_dive_dc(struct dive *dive, int nr);
|
extern struct divecomputer *get_dive_dc(struct dive *dive, int nr);
|
||||||
extern const struct divecomputer *get_dive_dc_const(const struct dive *dive, int nr);
|
extern const struct divecomputer *get_dive_dc(const struct dive *dive, int nr);
|
||||||
extern timestamp_t dive_endtime(const struct dive *dive);
|
|
||||||
|
|
||||||
extern void set_git_prefs(const char *prefs);
|
extern void set_git_prefs(const char *prefs);
|
||||||
|
|
||||||
extern struct dive *make_first_dc(const struct dive *d, int dc_number);
|
extern std::unique_ptr<dive> clone_make_first_dc(const struct dive &d, int dc_number);
|
||||||
extern struct dive *clone_delete_divecomputer(const struct dive *d, int dc_number);
|
extern std::unique_ptr<dive> clone_delete_divecomputer(const struct dive &d, int dc_number);
|
||||||
void split_divecomputer(const struct dive *src, int num, struct dive **out1, struct dive **out2);
|
extern std::array<std::unique_ptr<dive>, 2> split_divecomputer(const struct dive &src, int num);
|
||||||
|
|
||||||
/*
|
|
||||||
* Iterate over each dive, with the first parameter being the index
|
|
||||||
* iterator variable, and the second one being the dive one.
|
|
||||||
*
|
|
||||||
* I don't think anybody really wants the index, and we could make
|
|
||||||
* it local to the for-loop, but that would make us requires C99.
|
|
||||||
*/
|
|
||||||
#define for_each_dive(_i, _x) \
|
|
||||||
for ((_i) = 0; ((_x) = get_dive(_i)) != NULL; (_i)++)
|
|
||||||
|
|
||||||
#define for_each_dc(_dive, _dc) \
|
|
||||||
for (_dc = &_dive->dc; _dc; _dc = _dc->next)
|
|
||||||
|
|
||||||
#define for_each_relevant_dc(_dive, _dc) \
|
|
||||||
for (_dc = &_dive->dc; _dc; _dc = _dc->next) if (!is_logged(_dive) || !is_dc_planner(_dc))
|
|
||||||
|
|
||||||
extern struct dive *get_dive_by_uniq_id(int id);
|
|
||||||
extern int get_idx_by_uniq_id(int id);
|
|
||||||
extern bool dive_site_has_gps_location(const struct dive_site *ds);
|
extern bool dive_site_has_gps_location(const struct dive_site *ds);
|
||||||
extern int dive_has_gps_location(const struct dive *dive);
|
extern int dive_has_gps_location(const struct dive *dive);
|
||||||
extern location_t dive_get_gps_location(const struct dive *d);
|
extern location_t dive_get_gps_location(const struct dive *d);
|
||||||
@ -153,51 +160,53 @@ extern bool time_during_dive_with_offset(const struct dive *dive, timestamp_t wh
|
|||||||
|
|
||||||
extern int save_dives(const char *filename);
|
extern int save_dives(const char *filename);
|
||||||
extern int save_dives_logic(const char *filename, bool select_only, bool anonymize);
|
extern int save_dives_logic(const char *filename, bool select_only, bool anonymize);
|
||||||
extern int save_dive(FILE *f, struct dive *dive, bool anonymize);
|
extern int save_dive(FILE *f, const struct dive &dive, bool anonymize);
|
||||||
extern int export_dives_xslt(const char *filename, bool selected, const int units, const char *export_xslt, bool anonymize);
|
extern int export_dives_xslt(const char *filename, bool selected, const int units, const char *export_xslt, bool anonymize);
|
||||||
|
|
||||||
extern int save_dive_sites_logic(const char *filename, const struct dive_site *sites[], int nr_sites, bool anonymize);
|
extern int save_dive_sites_logic(const char *filename, const struct dive_site *sites[], int nr_sites, bool anonymize);
|
||||||
|
|
||||||
struct membuffer;
|
struct membuffer;
|
||||||
extern void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize);
|
extern void save_one_dive_to_mb(struct membuffer *b, const struct dive &dive, bool anonymize);
|
||||||
|
|
||||||
extern void subsurface_console_init(void);
|
extern void subsurface_console_init();
|
||||||
extern void subsurface_console_exit(void);
|
extern void subsurface_console_exit();
|
||||||
extern bool subsurface_user_is_root(void);
|
extern bool subsurface_user_is_root();
|
||||||
|
|
||||||
extern struct dive *alloc_dive(void);
|
|
||||||
extern void free_dive(struct dive *);
|
|
||||||
extern void record_dive_to_table(struct dive *dive, struct dive_table *table);
|
|
||||||
extern void clear_dive(struct dive *dive);
|
extern void clear_dive(struct dive *dive);
|
||||||
extern void copy_dive(const struct dive *s, struct dive *d);
|
extern void copy_dive(const struct dive *s, struct dive *d);
|
||||||
extern void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear);
|
extern void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear);
|
||||||
extern struct dive *move_dive(struct dive *s);
|
extern struct std::unique_ptr<dive> move_dive(struct dive *s);
|
||||||
|
|
||||||
extern int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc);
|
extern int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc);
|
||||||
|
|
||||||
extern bool dive_less_than(const struct dive *a, const struct dive *b);
|
extern bool dive_less_than(const struct dive &a, const struct dive &b);
|
||||||
|
extern bool dive_less_than_ptr(const struct dive *a, const struct dive *b);
|
||||||
extern bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b);
|
extern bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b);
|
||||||
extern struct dive *fixup_dive(struct dive *dive);
|
extern struct dive *fixup_dive(struct dive *dive);
|
||||||
extern pressure_t calculate_surface_pressure(const struct dive *dive);
|
extern pressure_t calculate_surface_pressure(const struct dive *dive);
|
||||||
extern pressure_t un_fixup_surface_pressure(const struct dive *d);
|
extern pressure_t un_fixup_surface_pressure(const struct dive *d);
|
||||||
extern int get_dive_salinity(const struct dive *dive);
|
extern int get_dive_salinity(const struct dive *dive);
|
||||||
extern int dive_getUniqID();
|
extern int dive_getUniqID();
|
||||||
extern int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2);
|
extern std::array<std::unique_ptr<dive>, 2> split_dive(const struct dive &dive);
|
||||||
extern int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2);
|
extern std::array<std::unique_ptr<dive>, 2> split_dive_at_time(const struct dive &dive, duration_t time);
|
||||||
extern struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, bool prefer_downloaded, struct dive_trip **trip, struct dive_site **site);
|
|
||||||
extern struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded);
|
struct merge_result {
|
||||||
|
std::unique_ptr<struct dive> dive;
|
||||||
|
dive_trip *trip;
|
||||||
|
dive_site *site;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern merge_result merge_dives(const struct dive &a, const struct dive &b, int offset, bool prefer_downloaded);
|
||||||
|
extern std::unique_ptr<dive> try_to_merge(const struct dive &a, const struct dive &b, bool prefer_downloaded);
|
||||||
extern void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int time);
|
extern void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int time);
|
||||||
extern void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only);
|
extern void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only);
|
||||||
extern bool is_cylinder_used(const struct dive *dive, int idx);
|
extern bool is_cylinder_used(const struct dive *dive, int idx);
|
||||||
extern bool is_cylinder_prot(const struct dive *dive, int idx);
|
extern bool is_cylinder_prot(const struct dive *dive, int idx);
|
||||||
extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx);
|
extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx);
|
||||||
extern struct event *create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx);
|
extern struct event create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx);
|
||||||
extern void update_event_name(struct dive *d, int dc_number, struct event *event, const char *name);
|
|
||||||
extern void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration);
|
extern void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration);
|
||||||
extern int get_cylinder_index(const struct dive *dive, const struct event *ev);
|
extern int get_cylinder_index(const struct dive *dive, const struct event &ev);
|
||||||
extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event *ev);
|
extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event &ev);
|
||||||
extern int nr_cylinders(const struct dive *dive);
|
|
||||||
extern int nr_weightsystems(const struct dive *dive);
|
|
||||||
extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id);
|
extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id);
|
||||||
|
|
||||||
/* UI related protopypes */
|
/* UI related protopypes */
|
||||||
@ -206,32 +215,17 @@ extern void invalidate_dive_cache(struct dive *dc);
|
|||||||
|
|
||||||
extern int total_weight(const struct dive *);
|
extern int total_weight(const struct dive *);
|
||||||
|
|
||||||
extern bool is_planned(const struct dive *dive);
|
|
||||||
extern bool is_logged(const struct dive *dive);
|
|
||||||
|
|
||||||
/* Get gasmixes at increasing timestamps.
|
|
||||||
* In "evp", pass a pointer to a "struct event *" which is NULL-initialized on first invocation.
|
|
||||||
* On subsequent calls, pass the same "evp" and the "gasmix" from previous calls.
|
|
||||||
*/
|
|
||||||
extern struct gasmix get_gasmix(const struct dive *dive, const struct divecomputer *dc, int time, const struct event **evp, struct gasmix gasmix);
|
|
||||||
|
|
||||||
/* Get gasmix at a given time */
|
/* Get gasmix at a given time */
|
||||||
extern struct gasmix get_gasmix_at_time(const struct dive *dive, const struct divecomputer *dc, duration_t time);
|
extern struct gasmix get_gasmix_at_time(const struct dive &dive, const struct divecomputer &dc, duration_t time);
|
||||||
|
|
||||||
extern void update_setpoint_events(const struct dive *dive, struct divecomputer *dc);
|
extern void update_setpoint_events(const struct dive *dive, struct divecomputer *dc);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make pointers to dive and dive_trip "Qt metatypes" so that they can be passed through
|
/* Make pointers to dive and dive_trip "Qt metatypes" so that they can be passed through
|
||||||
* QVariants and through QML.
|
* QVariants and through QML.
|
||||||
*/
|
*/
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <string>
|
|
||||||
Q_DECLARE_METATYPE(struct dive *);
|
Q_DECLARE_METATYPE(struct dive *);
|
||||||
|
|
||||||
extern std::string existing_filename;
|
extern std::string existing_filename;
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // DIVE_H
|
#endif // DIVE_H
|
||||||
|
|||||||
@ -1,570 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
|
|
||||||
#include "divecomputer.h"
|
|
||||||
#include "event.h"
|
|
||||||
#include "extradata.h"
|
|
||||||
#include "pref.h"
|
|
||||||
#include "sample.h"
|
|
||||||
#include "structured_list.h"
|
|
||||||
#include "subsurface-string.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Good fake dive profiles are hard.
|
|
||||||
*
|
|
||||||
* "depthtime" is the integral of the dive depth over
|
|
||||||
* time ("area" of the dive profile). We want that
|
|
||||||
* area to match the average depth (avg_d*max_t).
|
|
||||||
*
|
|
||||||
* To do that, we generate a 6-point profile:
|
|
||||||
*
|
|
||||||
* (0, 0)
|
|
||||||
* (t1, max_d)
|
|
||||||
* (t2, max_d)
|
|
||||||
* (t3, d)
|
|
||||||
* (t4, d)
|
|
||||||
* (max_t, 0)
|
|
||||||
*
|
|
||||||
* with the same ascent/descent rates between the
|
|
||||||
* different depths.
|
|
||||||
*
|
|
||||||
* NOTE: avg_d, max_d and max_t are given constants.
|
|
||||||
* The rest we can/should play around with to get a
|
|
||||||
* good-looking profile.
|
|
||||||
*
|
|
||||||
* That six-point profile gives a total area of:
|
|
||||||
*
|
|
||||||
* (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3)
|
|
||||||
*
|
|
||||||
* And the "same ascent/descent rates" requirement
|
|
||||||
* gives us (time per depth must be same):
|
|
||||||
*
|
|
||||||
* t1 / max_d = (t3-t2) / (max_d-d)
|
|
||||||
* t1 / max_d = (max_t-t4) / d
|
|
||||||
*
|
|
||||||
* We also obviously require:
|
|
||||||
*
|
|
||||||
* 0 <= t1 <= t2 <= t3 <= t4 <= max_t
|
|
||||||
*
|
|
||||||
* Let us call 'd_frac = d / max_d', and we get:
|
|
||||||
*
|
|
||||||
* Total area must match average depth-time:
|
|
||||||
*
|
|
||||||
* (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3) = avg_d*max_t
|
|
||||||
* max_d*(max_t-t1-(1-d_frac)*(t4-t3)) = avg_d*max_t
|
|
||||||
* max_t-t1-(1-d_frac)*(t4-t3) = avg_d*max_t/max_d
|
|
||||||
* t1+(1-d_frac)*(t4-t3) = max_t*(1-avg_d/max_d)
|
|
||||||
*
|
|
||||||
* and descent slope must match ascent slopes:
|
|
||||||
*
|
|
||||||
* t1 / max_d = (t3-t2) / (max_d*(1-d_frac))
|
|
||||||
* t1 = (t3-t2)/(1-d_frac)
|
|
||||||
*
|
|
||||||
* and
|
|
||||||
*
|
|
||||||
* t1 / max_d = (max_t-t4) / (max_d*d_frac)
|
|
||||||
* t1 = (max_t-t4)/d_frac
|
|
||||||
*
|
|
||||||
* In general, we have more free variables than we have constraints,
|
|
||||||
* but we can aim for certain basics, like a good ascent slope.
|
|
||||||
*/
|
|
||||||
static int fill_samples(struct sample *s, int max_d, int avg_d, int max_t, double slope, double d_frac)
|
|
||||||
{
|
|
||||||
double t_frac = max_t * (1 - avg_d / (double)max_d);
|
|
||||||
int t1 = lrint(max_d / slope);
|
|
||||||
int t4 = lrint(max_t - t1 * d_frac);
|
|
||||||
int t3 = lrint(t4 - (t_frac - t1) / (1 - d_frac));
|
|
||||||
int t2 = lrint(t3 - t1 * (1 - d_frac));
|
|
||||||
|
|
||||||
if (t1 < 0 || t1 > t2 || t2 > t3 || t3 > t4 || t4 > max_t)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
s[1].time.seconds = t1;
|
|
||||||
s[1].depth.mm = max_d;
|
|
||||||
s[2].time.seconds = t2;
|
|
||||||
s[2].depth.mm = max_d;
|
|
||||||
s[3].time.seconds = t3;
|
|
||||||
s[3].depth.mm = lrint(max_d * d_frac);
|
|
||||||
s[4].time.seconds = t4;
|
|
||||||
s[4].depth.mm = lrint(max_d * d_frac);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we have no average depth; instead of making up a random average depth
|
|
||||||
* we should assume either a PADI rectangular profile (for short and/or
|
|
||||||
* shallow dives) or more reasonably a six point profile with a 3 minute
|
|
||||||
* safety stop at 5m */
|
|
||||||
static void fill_samples_no_avg(struct sample *s, int max_d, int max_t, double slope)
|
|
||||||
{
|
|
||||||
// shallow or short dives are just trapecoids based on the given slope
|
|
||||||
if (max_d < 10000 || max_t < 600) {
|
|
||||||
s[1].time.seconds = lrint(max_d / slope);
|
|
||||||
s[1].depth.mm = max_d;
|
|
||||||
s[2].time.seconds = max_t - lrint(max_d / slope);
|
|
||||||
s[2].depth.mm = max_d;
|
|
||||||
} else {
|
|
||||||
s[1].time.seconds = lrint(max_d / slope);
|
|
||||||
s[1].depth.mm = max_d;
|
|
||||||
s[2].time.seconds = max_t - lrint(max_d / slope) - 180;
|
|
||||||
s[2].depth.mm = max_d;
|
|
||||||
s[3].time.seconds = max_t - lrint(5000 / slope) - 180;
|
|
||||||
s[3].depth.mm = 5000;
|
|
||||||
s[4].time.seconds = max_t - lrint(5000 / slope);
|
|
||||||
s[4].depth.mm = 5000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void fake_dc(struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
alloc_samples(dc, 6);
|
|
||||||
struct sample *fake = dc->sample;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
dc->samples = 6;
|
|
||||||
|
|
||||||
/* The dive has no samples, so create a few fake ones */
|
|
||||||
int max_t = dc->duration.seconds;
|
|
||||||
int max_d = dc->maxdepth.mm;
|
|
||||||
int avg_d = dc->meandepth.mm;
|
|
||||||
|
|
||||||
memset(fake, 0, 6 * sizeof(struct sample));
|
|
||||||
fake[5].time.seconds = max_t;
|
|
||||||
for (i = 0; i < 6; i++) {
|
|
||||||
fake[i].bearing.degrees = -1;
|
|
||||||
fake[i].ndl.seconds = -1;
|
|
||||||
}
|
|
||||||
if (!max_t || !max_d) {
|
|
||||||
dc->samples = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set last manually entered time to the total dive length */
|
|
||||||
dc->last_manual_time = dc->duration;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We want to fake the profile so that the average
|
|
||||||
* depth ends up correct. However, in the absence of
|
|
||||||
* a reasonable average, let's just make something
|
|
||||||
* up. Note that 'avg_d == max_d' is _not_ a reasonable
|
|
||||||
* average.
|
|
||||||
* We explicitly treat avg_d == 0 differently */
|
|
||||||
if (avg_d == 0) {
|
|
||||||
/* we try for a sane slope, but bow to the insanity of
|
|
||||||
* the user supplied data */
|
|
||||||
fill_samples_no_avg(fake, max_d, max_t, MAX(2.0 * max_d / max_t, (double)prefs.ascratelast6m));
|
|
||||||
if (fake[3].time.seconds == 0) { // just a 4 point profile
|
|
||||||
dc->samples = 4;
|
|
||||||
fake[3].time.seconds = max_t;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (avg_d < max_d / 10 || avg_d >= max_d) {
|
|
||||||
avg_d = (max_d + 10000) / 3;
|
|
||||||
if (avg_d > max_d)
|
|
||||||
avg_d = max_d * 2 / 3;
|
|
||||||
}
|
|
||||||
if (!avg_d)
|
|
||||||
avg_d = 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ok, first we try a basic profile with a specific ascent
|
|
||||||
* rate (5 meters per minute) and d_frac (1/3).
|
|
||||||
*/
|
|
||||||
if (fill_samples(fake, max_d, avg_d, max_t, (double)prefs.ascratelast6m, 0.33))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ok, assume that didn't work because we cannot make the
|
|
||||||
* average come out right because it was a quick deep dive
|
|
||||||
* followed by a much shallower region
|
|
||||||
*/
|
|
||||||
if (fill_samples(fake, max_d, avg_d, max_t, 10000.0 / 60, 0.10))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Uhhuh. That didn't work. We'd need to find a good combination that
|
|
||||||
* satisfies our constraints. Currently, we don't, we just give insane
|
|
||||||
* slopes.
|
|
||||||
*/
|
|
||||||
if (fill_samples(fake, max_d, avg_d, max_t, 10000.0, 0.01))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Even that didn't work? Give up, there's something wrong */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find the divemode at time 'time' (in seconds) into the dive. Sequentially step through the divemode-change events,
|
|
||||||
* saving the dive mode for each event. When the events occur AFTER 'time' seconds, the last stored divemode
|
|
||||||
* is returned. This function is self-tracking, relying on setting the event pointer 'evp' so that, in each iteration
|
|
||||||
* that calls this function, the search does not have to begin at the first event of the dive */
|
|
||||||
enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode)
|
|
||||||
{
|
|
||||||
const struct event *ev = *evp;
|
|
||||||
if (dc) {
|
|
||||||
if (*divemode == UNDEF_COMP_TYPE) {
|
|
||||||
*divemode = dc->divemode;
|
|
||||||
ev = get_next_event(dc->events, "modechange");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ev = NULL;
|
|
||||||
}
|
|
||||||
while (ev && ev->time.seconds < time) {
|
|
||||||
*divemode = (enum divemode_t) ev->value;
|
|
||||||
ev = get_next_event(ev->next, "modechange");
|
|
||||||
}
|
|
||||||
*evp = ev;
|
|
||||||
return *divemode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* helper function to make it easier to work with our structures
|
|
||||||
* we don't interpolate here, just use the value from the last sample up to that time */
|
|
||||||
int get_depth_at_time(const struct divecomputer *dc, unsigned int time)
|
|
||||||
{
|
|
||||||
int depth = 0;
|
|
||||||
if (dc && dc->sample)
|
|
||||||
for (int i = 0; i < dc->samples; i++) {
|
|
||||||
if (dc->sample[i].time.seconds > time)
|
|
||||||
break;
|
|
||||||
depth = dc->sample[i].depth.mm;
|
|
||||||
}
|
|
||||||
return depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* The first divecomputer is embedded in the dive structure. Free its data but not
|
|
||||||
* the structure itself. For all remainding dcs in the list, free data *and* structures. */
|
|
||||||
void free_dive_dcs(struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
free_dc_contents(dc);
|
|
||||||
STRUCTURED_LIST_FREE(struct divecomputer, dc->next, free_dc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* make room for num samples; if not enough space is available, the sample
|
|
||||||
* array is reallocated and the existing samples are copied. */
|
|
||||||
void alloc_samples(struct divecomputer *dc, int num)
|
|
||||||
{
|
|
||||||
if (num > dc->alloc_samples) {
|
|
||||||
dc->alloc_samples = (num * 3) / 2 + 10;
|
|
||||||
dc->sample = realloc(dc->sample, dc->alloc_samples * sizeof(struct sample));
|
|
||||||
if (!dc->sample)
|
|
||||||
dc->samples = dc->alloc_samples = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_samples(struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
if (dc) {
|
|
||||||
free(dc->sample);
|
|
||||||
dc->sample = 0;
|
|
||||||
dc->samples = 0;
|
|
||||||
dc->alloc_samples = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sample *prepare_sample(struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
if (dc) {
|
|
||||||
int nr = dc->samples;
|
|
||||||
struct sample *sample;
|
|
||||||
alloc_samples(dc, nr + 1);
|
|
||||||
if (!dc->sample)
|
|
||||||
return NULL;
|
|
||||||
sample = dc->sample + nr;
|
|
||||||
memset(sample, 0, sizeof(*sample));
|
|
||||||
|
|
||||||
// Copy the sensor numbers - but not the pressure values
|
|
||||||
// from the previous sample if any.
|
|
||||||
if (nr) {
|
|
||||||
for (int idx = 0; idx < MAX_SENSORS; idx++)
|
|
||||||
sample->sensor[idx] = sample[-1].sensor[idx];
|
|
||||||
}
|
|
||||||
// Init some values with -1
|
|
||||||
sample->bearing.degrees = -1;
|
|
||||||
sample->ndl.seconds = -1;
|
|
||||||
|
|
||||||
return sample;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void finish_sample(struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
dc->samples++;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
struct sample *p = prepare_sample(dc);
|
|
||||||
|
|
||||||
if (p) {
|
|
||||||
*p = *sample;
|
|
||||||
p->time.seconds = time;
|
|
||||||
finish_sample(dc);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate how long we were actually under water, and the average
|
|
||||||
* depth while under water.
|
|
||||||
*
|
|
||||||
* This ignores any surface time in the middle of the dive.
|
|
||||||
*/
|
|
||||||
void fixup_dc_duration(struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
int duration, i;
|
|
||||||
int lasttime, lastdepth, depthtime;
|
|
||||||
|
|
||||||
duration = 0;
|
|
||||||
lasttime = 0;
|
|
||||||
lastdepth = 0;
|
|
||||||
depthtime = 0;
|
|
||||||
for (i = 0; i < dc->samples; i++) {
|
|
||||||
struct sample *sample = dc->sample + i;
|
|
||||||
int time = sample->time.seconds;
|
|
||||||
int depth = sample->depth.mm;
|
|
||||||
|
|
||||||
/* We ignore segments at the surface */
|
|
||||||
if (depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) {
|
|
||||||
duration += time - lasttime;
|
|
||||||
depthtime += (time - lasttime) * (depth + lastdepth) / 2;
|
|
||||||
}
|
|
||||||
lastdepth = depth;
|
|
||||||
lasttime = time;
|
|
||||||
}
|
|
||||||
if (duration) {
|
|
||||||
dc->duration.seconds = duration;
|
|
||||||
dc->meandepth.mm = (depthtime + duration / 2) / duration;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* What do the dive computers say the water temperature is?
|
|
||||||
* (not in the samples, but as dc property for dcs that support that)
|
|
||||||
*/
|
|
||||||
unsigned int dc_watertemp(const struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
int sum = 0, nr = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (dc->watertemp.mkelvin) {
|
|
||||||
sum += dc->watertemp.mkelvin;
|
|
||||||
nr++;
|
|
||||||
}
|
|
||||||
} while ((dc = dc->next) != NULL);
|
|
||||||
if (!nr)
|
|
||||||
return 0;
|
|
||||||
return (sum + nr / 2) / nr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* What do the dive computers say the air temperature is?
|
|
||||||
*/
|
|
||||||
unsigned int dc_airtemp(const struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
int sum = 0, nr = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (dc->airtemp.mkelvin) {
|
|
||||||
sum += dc->airtemp.mkelvin;
|
|
||||||
nr++;
|
|
||||||
}
|
|
||||||
} while ((dc = dc->next) != NULL);
|
|
||||||
if (!nr)
|
|
||||||
return 0;
|
|
||||||
return (sum + nr / 2) / nr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copies all events in this dive computer */
|
|
||||||
void copy_events(const struct divecomputer *s, struct divecomputer *d)
|
|
||||||
{
|
|
||||||
const struct event *ev;
|
|
||||||
struct event **pev;
|
|
||||||
if (!s || !d)
|
|
||||||
return;
|
|
||||||
ev = s->events;
|
|
||||||
pev = &d->events;
|
|
||||||
while (ev != NULL) {
|
|
||||||
struct event *new_ev = clone_event(ev);
|
|
||||||
*pev = new_ev;
|
|
||||||
pev = &new_ev->next;
|
|
||||||
ev = ev->next;
|
|
||||||
}
|
|
||||||
*pev = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void copy_samples(const struct divecomputer *s, struct divecomputer *d)
|
|
||||||
{
|
|
||||||
/* instead of carefully copying them one by one and calling add_sample
|
|
||||||
* over and over again, let's just copy the whole blob */
|
|
||||||
if (!s || !d)
|
|
||||||
return;
|
|
||||||
int nr = s->samples;
|
|
||||||
d->samples = nr;
|
|
||||||
d->alloc_samples = nr;
|
|
||||||
// We expect to be able to read the memory in the other end of the pointer
|
|
||||||
// if its a valid pointer, so don't expect malloc() to return NULL for
|
|
||||||
// zero-sized malloc, do it ourselves.
|
|
||||||
d->sample = NULL;
|
|
||||||
|
|
||||||
if(!nr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
d->sample = malloc(nr * sizeof(struct sample));
|
|
||||||
if (d->sample)
|
|
||||||
memcpy(d->sample, s->sample, nr * sizeof(struct sample));
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_event_to_dc(struct divecomputer *dc, struct event *ev)
|
|
||||||
{
|
|
||||||
struct event **p;
|
|
||||||
|
|
||||||
p = &dc->events;
|
|
||||||
|
|
||||||
/* insert in the sorted list of events */
|
|
||||||
while (*p && (*p)->time.seconds <= ev->time.seconds)
|
|
||||||
p = &(*p)->next;
|
|
||||||
ev->next = *p;
|
|
||||||
*p = ev;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name)
|
|
||||||
{
|
|
||||||
struct event *ev = create_event(time, type, flags, value, name);
|
|
||||||
|
|
||||||
if (!ev)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
add_event_to_dc(dc, ev);
|
|
||||||
|
|
||||||
return ev;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Substitutes an event in a divecomputer for another. No reordering is performed! */
|
|
||||||
void swap_event(struct divecomputer *dc, struct event *from, struct event *to)
|
|
||||||
{
|
|
||||||
for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) {
|
|
||||||
if (*ep == from) {
|
|
||||||
to->next = from->next;
|
|
||||||
*ep = to;
|
|
||||||
from->next = NULL; // For good measure.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove given event from dive computer. Does *not* free the event. */
|
|
||||||
void remove_event_from_dc(struct divecomputer *dc, struct event *event)
|
|
||||||
{
|
|
||||||
for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) {
|
|
||||||
if (*ep == event) {
|
|
||||||
*ep = event->next;
|
|
||||||
event->next = NULL; // For good measure.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_extra_data(struct divecomputer *dc, const char *key, const char *value)
|
|
||||||
{
|
|
||||||
struct extra_data **ed = &dc->extra_data;
|
|
||||||
|
|
||||||
if (!strcasecmp(key, "Serial")) {
|
|
||||||
dc->deviceid = calculate_string_hash(value);
|
|
||||||
dc->serial = strdup(value);
|
|
||||||
}
|
|
||||||
if (!strcmp(key, "FW Version")) {
|
|
||||||
dc->fw_version = strdup(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (*ed)
|
|
||||||
ed = &(*ed)->next;
|
|
||||||
*ed = malloc(sizeof(struct extra_data));
|
|
||||||
if (*ed) {
|
|
||||||
(*ed)->key = strdup(key);
|
|
||||||
(*ed)->value = strdup(value);
|
|
||||||
(*ed)->next = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Match two dive computer entries against each other, and
|
|
||||||
* tell if it's the same dive. Return 0 if "don't know",
|
|
||||||
* positive for "same dive" and negative for "definitely
|
|
||||||
* not the same dive"
|
|
||||||
*/
|
|
||||||
int match_one_dc(const struct divecomputer *a, const struct divecomputer *b)
|
|
||||||
{
|
|
||||||
/* Not same model? Don't know if matching.. */
|
|
||||||
if (!a->model || !b->model)
|
|
||||||
return 0;
|
|
||||||
if (strcasecmp(a->model, b->model))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Different device ID's? Don't know */
|
|
||||||
if (a->deviceid != b->deviceid)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Do we have dive IDs? */
|
|
||||||
if (!a->diveid || !b->diveid)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If they have different dive ID's on the same
|
|
||||||
* dive computer, that's a definite "same or not"
|
|
||||||
*/
|
|
||||||
return a->diveid == b->diveid && a->when == b->when ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_extra_data(struct extra_data *ed)
|
|
||||||
{
|
|
||||||
free((void *)ed->key);
|
|
||||||
free((void *)ed->value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_dc_contents(struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
free(dc->sample);
|
|
||||||
free((void *)dc->model);
|
|
||||||
free((void *)dc->serial);
|
|
||||||
free((void *)dc->fw_version);
|
|
||||||
free_events(dc->events);
|
|
||||||
STRUCTURED_LIST_FREE(struct extra_data, dc->extra_data, free_extra_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_dc(struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
free_dc_contents(dc);
|
|
||||||
free(dc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *planner_dc_name = "planned dive";
|
|
||||||
|
|
||||||
bool is_dc_planner(const struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
return dc && same_string(dc->model, planner_dc_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void make_planner_dc(struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
free((void *)dc->model);
|
|
||||||
dc->model = strdup(planner_dc_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *manual_dc_name = "manually added dive";
|
|
||||||
|
|
||||||
bool is_dc_manually_added_dive(const struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
return dc && same_string(dc->model, manual_dc_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void make_manually_added_dive_dc(struct divecomputer *dc)
|
|
||||||
{
|
|
||||||
free((void *)dc->model);
|
|
||||||
dc->model = strdup(manual_dc_name);
|
|
||||||
}
|
|
||||||
399
core/divecomputer.cpp
Normal file
399
core/divecomputer.cpp
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
#include "divecomputer.h"
|
||||||
|
#include "errorhelper.h"
|
||||||
|
#include "event.h"
|
||||||
|
#include "extradata.h"
|
||||||
|
#include "pref.h"
|
||||||
|
#include "sample.h"
|
||||||
|
#include "structured_list.h"
|
||||||
|
#include "subsurface-string.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
divecomputer::divecomputer() = default;
|
||||||
|
divecomputer::~divecomputer() = default;
|
||||||
|
divecomputer::divecomputer(const divecomputer &) = default;
|
||||||
|
divecomputer::divecomputer(divecomputer &&) = default;
|
||||||
|
divecomputer &divecomputer::operator=(const divecomputer &) = default;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Good fake dive profiles are hard.
|
||||||
|
*
|
||||||
|
* "depthtime" is the integral of the dive depth over
|
||||||
|
* time ("area" of the dive profile). We want that
|
||||||
|
* area to match the average depth (avg_d*max_t).
|
||||||
|
*
|
||||||
|
* To do that, we generate a 6-point profile:
|
||||||
|
*
|
||||||
|
* (0, 0)
|
||||||
|
* (t1, max_d)
|
||||||
|
* (t2, max_d)
|
||||||
|
* (t3, d)
|
||||||
|
* (t4, d)
|
||||||
|
* (max_t, 0)
|
||||||
|
*
|
||||||
|
* with the same ascent/descent rates between the
|
||||||
|
* different depths.
|
||||||
|
*
|
||||||
|
* NOTE: avg_d, max_d and max_t are given constants.
|
||||||
|
* The rest we can/should play around with to get a
|
||||||
|
* good-looking profile.
|
||||||
|
*
|
||||||
|
* That six-point profile gives a total area of:
|
||||||
|
*
|
||||||
|
* (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3)
|
||||||
|
*
|
||||||
|
* And the "same ascent/descent rates" requirement
|
||||||
|
* gives us (time per depth must be same):
|
||||||
|
*
|
||||||
|
* t1 / max_d = (t3-t2) / (max_d-d)
|
||||||
|
* t1 / max_d = (max_t-t4) / d
|
||||||
|
*
|
||||||
|
* We also obviously require:
|
||||||
|
*
|
||||||
|
* 0 <= t1 <= t2 <= t3 <= t4 <= max_t
|
||||||
|
*
|
||||||
|
* Let us call 'd_frac = d / max_d', and we get:
|
||||||
|
*
|
||||||
|
* Total area must match average depth-time:
|
||||||
|
*
|
||||||
|
* (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3) = avg_d*max_t
|
||||||
|
* max_d*(max_t-t1-(1-d_frac)*(t4-t3)) = avg_d*max_t
|
||||||
|
* max_t-t1-(1-d_frac)*(t4-t3) = avg_d*max_t/max_d
|
||||||
|
* t1+(1-d_frac)*(t4-t3) = max_t*(1-avg_d/max_d)
|
||||||
|
*
|
||||||
|
* and descent slope must match ascent slopes:
|
||||||
|
*
|
||||||
|
* t1 / max_d = (t3-t2) / (max_d*(1-d_frac))
|
||||||
|
* t1 = (t3-t2)/(1-d_frac)
|
||||||
|
*
|
||||||
|
* and
|
||||||
|
*
|
||||||
|
* t1 / max_d = (max_t-t4) / (max_d*d_frac)
|
||||||
|
* t1 = (max_t-t4)/d_frac
|
||||||
|
*
|
||||||
|
* In general, we have more free variables than we have constraints,
|
||||||
|
* but we can aim for certain basics, like a good ascent slope.
|
||||||
|
*/
|
||||||
|
static int fill_samples(std::vector<sample> &s, int max_d, int avg_d, int max_t, double slope, double d_frac)
|
||||||
|
{
|
||||||
|
double t_frac = max_t * (1 - avg_d / (double)max_d);
|
||||||
|
int t1 = lrint(max_d / slope);
|
||||||
|
int t4 = lrint(max_t - t1 * d_frac);
|
||||||
|
int t3 = lrint(t4 - (t_frac - t1) / (1 - d_frac));
|
||||||
|
int t2 = lrint(t3 - t1 * (1 - d_frac));
|
||||||
|
|
||||||
|
if (t1 < 0 || t1 > t2 || t2 > t3 || t3 > t4 || t4 > max_t)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
s[1].time.seconds = t1;
|
||||||
|
s[1].depth.mm = max_d;
|
||||||
|
s[2].time.seconds = t2;
|
||||||
|
s[2].depth.mm = max_d;
|
||||||
|
s[3].time.seconds = t3;
|
||||||
|
s[3].depth.mm = lrint(max_d * d_frac);
|
||||||
|
s[4].time.seconds = t4;
|
||||||
|
s[4].depth.mm = lrint(max_d * d_frac);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we have no average depth; instead of making up a random average depth
|
||||||
|
* we should assume either a PADI rectangular profile (for short and/or
|
||||||
|
* shallow dives) or more reasonably a six point profile with a 3 minute
|
||||||
|
* safety stop at 5m */
|
||||||
|
static void fill_samples_no_avg(std::vector<sample> &s, int max_d, int max_t, double slope)
|
||||||
|
{
|
||||||
|
// shallow or short dives are just trapecoids based on the given slope
|
||||||
|
if (max_d < 10000 || max_t < 600) {
|
||||||
|
s[1].time.seconds = lrint(max_d / slope);
|
||||||
|
s[1].depth.mm = max_d;
|
||||||
|
s[2].time.seconds = max_t - lrint(max_d / slope);
|
||||||
|
s[2].depth.mm = max_d;
|
||||||
|
} else {
|
||||||
|
s[1].time.seconds = lrint(max_d / slope);
|
||||||
|
s[1].depth.mm = max_d;
|
||||||
|
s[2].time.seconds = max_t - lrint(max_d / slope) - 180;
|
||||||
|
s[2].depth.mm = max_d;
|
||||||
|
s[3].time.seconds = max_t - lrint(5000 / slope) - 180;
|
||||||
|
s[3].depth.mm = 5000;
|
||||||
|
s[4].time.seconds = max_t - lrint(5000 / slope);
|
||||||
|
s[4].depth.mm = 5000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fake_dc(struct divecomputer *dc)
|
||||||
|
{
|
||||||
|
/* The dive has no samples, so create a few fake ones */
|
||||||
|
int max_t = dc->duration.seconds;
|
||||||
|
int max_d = dc->maxdepth.mm;
|
||||||
|
int avg_d = dc->meandepth.mm;
|
||||||
|
|
||||||
|
if (!max_t || !max_d) {
|
||||||
|
dc->samples.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<struct sample> &fake = dc->samples;
|
||||||
|
fake.resize(6);
|
||||||
|
|
||||||
|
fake[5].time.seconds = max_t;
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
fake[i].bearing.degrees = -1;
|
||||||
|
fake[i].ndl.seconds = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set last manually entered time to the total dive length */
|
||||||
|
dc->last_manual_time = dc->duration;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We want to fake the profile so that the average
|
||||||
|
* depth ends up correct. However, in the absence of
|
||||||
|
* a reasonable average, let's just make something
|
||||||
|
* up. Note that 'avg_d == max_d' is _not_ a reasonable
|
||||||
|
* average.
|
||||||
|
* We explicitly treat avg_d == 0 differently */
|
||||||
|
if (avg_d == 0) {
|
||||||
|
/* we try for a sane slope, but bow to the insanity of
|
||||||
|
* the user supplied data */
|
||||||
|
fill_samples_no_avg(fake, max_d, max_t, std::max(2.0 * max_d / max_t, (double)prefs.ascratelast6m));
|
||||||
|
if (fake[3].time.seconds == 0) { // just a 4 point profile
|
||||||
|
dc->samples.resize(4);
|
||||||
|
fake[3].time.seconds = max_t;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (avg_d < max_d / 10 || avg_d >= max_d) {
|
||||||
|
avg_d = (max_d + 10000) / 3;
|
||||||
|
if (avg_d > max_d)
|
||||||
|
avg_d = max_d * 2 / 3;
|
||||||
|
}
|
||||||
|
if (!avg_d)
|
||||||
|
avg_d = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ok, first we try a basic profile with a specific ascent
|
||||||
|
* rate (5 meters per minute) and d_frac (1/3).
|
||||||
|
*/
|
||||||
|
if (fill_samples(fake, max_d, avg_d, max_t, (double)prefs.ascratelast6m, 0.33))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ok, assume that didn't work because we cannot make the
|
||||||
|
* average come out right because it was a quick deep dive
|
||||||
|
* followed by a much shallower region
|
||||||
|
*/
|
||||||
|
if (fill_samples(fake, max_d, avg_d, max_t, 10000.0 / 60, 0.10))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Uhhuh. That didn't work. We'd need to find a good combination that
|
||||||
|
* satisfies our constraints. Currently, we don't, we just give insane
|
||||||
|
* slopes.
|
||||||
|
*/
|
||||||
|
if (fill_samples(fake, max_d, avg_d, max_t, 10000.0, 0.01))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Even that didn't work? Give up, there's something wrong */
|
||||||
|
}
|
||||||
|
|
||||||
|
divemode_loop::divemode_loop(const struct divecomputer &dc) :
|
||||||
|
dc(dc), last(dc.divemode), loop("modechange")
|
||||||
|
{
|
||||||
|
/* on first invocation, get first event (if any) */
|
||||||
|
ev = loop.next(dc);
|
||||||
|
}
|
||||||
|
|
||||||
|
divemode_t divemode_loop::next(int time)
|
||||||
|
{
|
||||||
|
while (ev && ev->time.seconds <= time) {
|
||||||
|
last = static_cast<divemode_t>(ev->value);
|
||||||
|
ev = loop.next(dc);
|
||||||
|
}
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper function to make it easier to work with our structures
|
||||||
|
* we don't interpolate here, just use the value from the last sample up to that time */
|
||||||
|
int get_depth_at_time(const struct divecomputer *dc, unsigned int time)
|
||||||
|
{
|
||||||
|
int depth = 0;
|
||||||
|
if (dc) {
|
||||||
|
for (const auto &sample: dc->samples) {
|
||||||
|
if (sample.time.seconds > (int)time)
|
||||||
|
break;
|
||||||
|
depth = sample.depth.mm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sample *prepare_sample(struct divecomputer *dc)
|
||||||
|
{
|
||||||
|
if (dc) {
|
||||||
|
dc->samples.emplace_back();
|
||||||
|
auto &sample = dc->samples.back();
|
||||||
|
|
||||||
|
// Copy the sensor numbers - but not the pressure values
|
||||||
|
// from the previous sample if any.
|
||||||
|
if (dc->samples.size() >= 2) {
|
||||||
|
auto &prev = dc->samples[dc->samples.size() - 2];
|
||||||
|
for (int idx = 0; idx < MAX_SENSORS; idx++)
|
||||||
|
sample.sensor[idx] = prev.sensor[idx];
|
||||||
|
}
|
||||||
|
// Init some values with -1
|
||||||
|
sample.bearing.degrees = -1;
|
||||||
|
sample.ndl.seconds = -1;
|
||||||
|
|
||||||
|
return &sample;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append_sample(const struct sample &sample, struct divecomputer *dc)
|
||||||
|
{
|
||||||
|
dc->samples.push_back(sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate how long we were actually under water, and the average
|
||||||
|
* depth while under water.
|
||||||
|
*
|
||||||
|
* This ignores any surface time in the middle of the dive.
|
||||||
|
*/
|
||||||
|
void fixup_dc_duration(struct divecomputer &dc)
|
||||||
|
{
|
||||||
|
int duration = 0;
|
||||||
|
int lasttime = 0, lastdepth = 0, depthtime = 0;
|
||||||
|
|
||||||
|
for (const auto &sample: dc.samples) {
|
||||||
|
int time = sample.time.seconds;
|
||||||
|
int depth = sample.depth.mm;
|
||||||
|
|
||||||
|
/* We ignore segments at the surface */
|
||||||
|
if (depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) {
|
||||||
|
duration += time - lasttime;
|
||||||
|
depthtime += (time - lasttime) * (depth + lastdepth) / 2;
|
||||||
|
}
|
||||||
|
lastdepth = depth;
|
||||||
|
lasttime = time;
|
||||||
|
}
|
||||||
|
if (duration) {
|
||||||
|
dc.duration.seconds = duration;
|
||||||
|
dc.meandepth.mm = (depthtime + duration / 2) / duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool operator<(const event &ev1, const event &ev2)
|
||||||
|
{
|
||||||
|
if (ev1.time.seconds < ev2.time.seconds)
|
||||||
|
return -1;
|
||||||
|
if (ev1.time.seconds > ev2.time.seconds)
|
||||||
|
return 1;
|
||||||
|
return ev1.name < ev2.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
int add_event_to_dc(struct divecomputer *dc, struct event ev)
|
||||||
|
{
|
||||||
|
// Do a binary search for insertion point
|
||||||
|
auto it = std::lower_bound(dc->events.begin(), dc->events.end(), ev);
|
||||||
|
int idx = it - dc->events.begin();
|
||||||
|
dc->events.insert(it, ev);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name)
|
||||||
|
{
|
||||||
|
struct event ev(time, type, flags, value, name);
|
||||||
|
int idx = add_event_to_dc(dc, std::move(ev));
|
||||||
|
|
||||||
|
return &dc->events[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove given event from dive computer. Returns the removed event. */
|
||||||
|
struct event remove_event_from_dc(struct divecomputer *dc, int idx)
|
||||||
|
{
|
||||||
|
if (idx < 0 || static_cast<size_t>(idx) > dc->events.size()) {
|
||||||
|
report_info("removing invalid event %d", idx);
|
||||||
|
return event();
|
||||||
|
}
|
||||||
|
event res = std::move(dc->events[idx]);
|
||||||
|
dc->events.erase(dc->events.begin() + idx);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct event *get_event(struct divecomputer *dc, int idx)
|
||||||
|
{
|
||||||
|
if (idx < 0 || static_cast<size_t>(idx) > dc->events.size()) {
|
||||||
|
report_info("accessing invalid event %d", idx);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &dc->events[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value)
|
||||||
|
{
|
||||||
|
if (key == "Serial") {
|
||||||
|
dc->deviceid = calculate_string_hash(value.c_str());
|
||||||
|
dc->serial = value;
|
||||||
|
}
|
||||||
|
if (key == "FW Version")
|
||||||
|
dc->fw_version = value;
|
||||||
|
|
||||||
|
dc->extra_data.push_back(extra_data { key, value });
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Match two dive computer entries against each other, and
|
||||||
|
* tell if it's the same dive. Return 0 if "don't know",
|
||||||
|
* positive for "same dive" and negative for "definitely
|
||||||
|
* not the same dive"
|
||||||
|
*/
|
||||||
|
int match_one_dc(const struct divecomputer &a, const struct divecomputer &b)
|
||||||
|
{
|
||||||
|
/* Not same model? Don't know if matching.. */
|
||||||
|
if (a.model.empty() || b.model.empty())
|
||||||
|
return 0;
|
||||||
|
if (strcasecmp(a.model.c_str(), b.model.c_str()))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Different device ID's? Don't know */
|
||||||
|
if (a.deviceid != b.deviceid)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Do we have dive IDs? */
|
||||||
|
if (!a.diveid || !b.diveid)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If they have different dive ID's on the same
|
||||||
|
* dive computer, that's a definite "same or not"
|
||||||
|
*/
|
||||||
|
return a.diveid == b.diveid && a.when == b.when ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *planner_dc_name = "planned dive";
|
||||||
|
|
||||||
|
bool is_dc_planner(const struct divecomputer *dc)
|
||||||
|
{
|
||||||
|
return dc->model == planner_dc_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_planner_dc(struct divecomputer *dc)
|
||||||
|
{
|
||||||
|
dc->model = planner_dc_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *manual_dc_name = "manually added dive";
|
||||||
|
|
||||||
|
bool is_dc_manually_added_dive(const struct divecomputer *dc)
|
||||||
|
{
|
||||||
|
return dc->model == manual_dc_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_manually_added_dive_dc(struct divecomputer *dc)
|
||||||
|
{
|
||||||
|
dc->model = manual_dc_name;
|
||||||
|
}
|
||||||
@ -4,12 +4,11 @@
|
|||||||
|
|
||||||
#include "divemode.h"
|
#include "divemode.h"
|
||||||
#include "units.h"
|
#include "units.h"
|
||||||
|
#include <string>
|
||||||
#ifdef __cplusplus
|
#include <vector>
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct extra_data;
|
struct extra_data;
|
||||||
|
struct event;
|
||||||
struct sample;
|
struct sample;
|
||||||
|
|
||||||
/* Is this header the correct place? */
|
/* Is this header the correct place? */
|
||||||
@ -29,44 +28,40 @@ struct sample;
|
|||||||
* A deviceid or diveid of zero is assumed to be "no ID".
|
* A deviceid or diveid of zero is assumed to be "no ID".
|
||||||
*/
|
*/
|
||||||
struct divecomputer {
|
struct divecomputer {
|
||||||
timestamp_t when;
|
timestamp_t when = 0;
|
||||||
duration_t duration, surfacetime, last_manual_time;
|
duration_t duration, surfacetime, last_manual_time;
|
||||||
depth_t maxdepth, meandepth;
|
depth_t maxdepth, meandepth;
|
||||||
temperature_t airtemp, watertemp;
|
temperature_t airtemp, watertemp;
|
||||||
pressure_t surface_pressure;
|
pressure_t surface_pressure;
|
||||||
enum divemode_t divemode; // dive computer type: OC(default) or CCR
|
enum divemode_t divemode = OC; // dive computer type: OC(default) or CCR
|
||||||
uint8_t no_o2sensors; // rebreathers: number of O2 sensors used
|
uint8_t no_o2sensors = 0; // rebreathers: number of O2 sensors used
|
||||||
int salinity; // kg per 10000 l
|
int salinity = 0; // kg per 10000 l
|
||||||
const char *model, *serial, *fw_version;
|
std::string model, serial, fw_version;
|
||||||
uint32_t deviceid, diveid;
|
uint32_t deviceid = 0, diveid = 0;
|
||||||
int samples, alloc_samples;
|
// Note: ve store samples, events and extra_data in std::vector<>s.
|
||||||
struct sample *sample;
|
// This means that pointers to these items are *not* stable.
|
||||||
struct event *events;
|
std::vector<struct sample> samples;
|
||||||
struct extra_data *extra_data;
|
std::vector<struct event> events;
|
||||||
struct divecomputer *next;
|
std::vector<struct extra_data> extra_data;
|
||||||
|
|
||||||
|
divecomputer();
|
||||||
|
~divecomputer();
|
||||||
|
divecomputer(const divecomputer &);
|
||||||
|
divecomputer(divecomputer &&);
|
||||||
|
divecomputer &operator=(const divecomputer &);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void fake_dc(struct divecomputer *dc);
|
extern void fake_dc(struct divecomputer *dc);
|
||||||
extern void free_dc(struct divecomputer *dc);
|
|
||||||
extern void free_dc_contents(struct divecomputer *dc);
|
extern void free_dc_contents(struct divecomputer *dc);
|
||||||
extern enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode);
|
|
||||||
extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time);
|
extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time);
|
||||||
extern void free_dive_dcs(struct divecomputer *dc);
|
|
||||||
extern void alloc_samples(struct divecomputer *dc, int num);
|
|
||||||
extern void free_samples(struct divecomputer *dc);
|
|
||||||
extern struct sample *prepare_sample(struct divecomputer *dc);
|
extern struct sample *prepare_sample(struct divecomputer *dc);
|
||||||
extern void finish_sample(struct divecomputer *dc);
|
extern void append_sample(const struct sample &sample, struct divecomputer *dc);
|
||||||
extern struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc);
|
extern void fixup_dc_duration(struct divecomputer &dc);
|
||||||
extern void fixup_dc_duration(struct divecomputer *dc);
|
extern int add_event_to_dc(struct divecomputer *dc, struct event ev); // event structure is consumed, returns index of inserted event
|
||||||
extern unsigned int dc_airtemp(const struct divecomputer *dc);
|
extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name);
|
||||||
extern unsigned int dc_watertemp(const struct divecomputer *dc);
|
extern struct event remove_event_from_dc(struct divecomputer *dc, int idx);
|
||||||
extern void copy_events(const struct divecomputer *s, struct divecomputer *d);
|
struct event *get_event(struct divecomputer *dc, int idx);
|
||||||
extern void swap_event(struct divecomputer *dc, struct event *from, struct event *to);
|
extern void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value);
|
||||||
extern void copy_samples(const struct divecomputer *s, struct divecomputer *d);
|
|
||||||
extern void add_event_to_dc(struct divecomputer *dc, struct event *ev);
|
|
||||||
extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name);
|
|
||||||
extern void remove_event_from_dc(struct divecomputer *dc, struct event *event);
|
|
||||||
extern void add_extra_data(struct divecomputer *dc, const char *key, const char *value);
|
|
||||||
extern uint32_t calculate_string_hash(const char *str);
|
extern uint32_t calculate_string_hash(const char *str);
|
||||||
extern bool is_dc_planner(const struct divecomputer *dc);
|
extern bool is_dc_planner(const struct divecomputer *dc);
|
||||||
extern void make_planner_dc(struct divecomputer *dc);
|
extern void make_planner_dc(struct divecomputer *dc);
|
||||||
@ -75,10 +70,6 @@ extern bool is_dc_manually_added_dive(const struct divecomputer *dc);
|
|||||||
extern void make_manually_added_dive_dc(struct divecomputer *dc);
|
extern void make_manually_added_dive_dc(struct divecomputer *dc);
|
||||||
|
|
||||||
/* Check if two dive computer entries are the exact same dive (-1=no/0=maybe/1=yes) */
|
/* Check if two dive computer entries are the exact same dive (-1=no/0=maybe/1=yes) */
|
||||||
extern int match_one_dc(const struct divecomputer *a, const struct divecomputer *b);
|
extern int match_one_dc(const struct divecomputer &a, const struct divecomputer &b);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "divelog.h"
|
#include "divelog.h"
|
||||||
#include "gettextfromc.h"
|
#include "gettextfromc.h"
|
||||||
#include "qthelper.h"
|
#include "qthelper.h"
|
||||||
|
#include "range.h"
|
||||||
#include "selection.h"
|
#include "selection.h"
|
||||||
#include "subsurface-qt/divelistnotifier.h"
|
#include "subsurface-qt/divelistnotifier.h"
|
||||||
#if !defined(SUBSURFACE_MOBILE) && !defined(SUBSURFACE_DOWNLOADER)
|
#if !defined(SUBSURFACE_MOBILE) && !defined(SUBSURFACE_DOWNLOADER)
|
||||||
@ -61,7 +62,7 @@ ShownChange DiveFilter::update(const QVector<dive *> &dives) const
|
|||||||
std::vector<dive *> removeFromSelection;
|
std::vector<dive *> removeFromSelection;
|
||||||
for (dive *d: dives) {
|
for (dive *d: dives) {
|
||||||
// There are three modes: divesite, fulltext, normal
|
// There are three modes: divesite, fulltext, normal
|
||||||
bool newStatus = doDS ? dive_sites.contains(d->dive_site) :
|
bool newStatus = doDS ? range_contains(dive_sites, d->dive_site) :
|
||||||
doFullText ? fulltext_dive_matches(d, filterData.fullText, filterData.fulltextStringMode) && showDive(d) :
|
doFullText ? fulltext_dive_matches(d, filterData.fullText, filterData.fulltextStringMode) && showDive(d) :
|
||||||
showDive(d);
|
showDive(d);
|
||||||
updateDiveStatus(d, newStatus, res, removeFromSelection);
|
updateDiveStatus(d, newStatus, res, removeFromSelection);
|
||||||
@ -73,10 +74,8 @@ ShownChange DiveFilter::update(const QVector<dive *> &dives) const
|
|||||||
|
|
||||||
void DiveFilter::reset()
|
void DiveFilter::reset()
|
||||||
{
|
{
|
||||||
int i;
|
shown_dives = static_cast<int>(divelog.dives.size());
|
||||||
dive *d;
|
for (auto &d: divelog.dives)
|
||||||
shown_dives = divelog.dives->nr;
|
|
||||||
for_each_dive(i, d)
|
|
||||||
d->hidden_by_filter = false;
|
d->hidden_by_filter = false;
|
||||||
updateAll();
|
updateAll();
|
||||||
}
|
}
|
||||||
@ -84,26 +83,24 @@ void DiveFilter::reset()
|
|||||||
ShownChange DiveFilter::updateAll() const
|
ShownChange DiveFilter::updateAll() const
|
||||||
{
|
{
|
||||||
ShownChange res;
|
ShownChange res;
|
||||||
int i;
|
|
||||||
dive *d;
|
|
||||||
std::vector<dive *> selection = getDiveSelection();
|
std::vector<dive *> selection = getDiveSelection();
|
||||||
std::vector<dive *> removeFromSelection;
|
std::vector<dive *> removeFromSelection;
|
||||||
// There are three modes: divesite, fulltext, normal
|
// There are three modes: divesite, fulltext, normal
|
||||||
if (diveSiteMode()) {
|
if (diveSiteMode()) {
|
||||||
for_each_dive(i, d) {
|
for (auto &d: divelog.dives) {
|
||||||
bool newStatus = dive_sites.contains(d->dive_site);
|
bool newStatus = range_contains(dive_sites, d->dive_site);
|
||||||
updateDiveStatus(d, newStatus, res, removeFromSelection);
|
updateDiveStatus(d.get(), newStatus, res, removeFromSelection);
|
||||||
}
|
}
|
||||||
} else if (filterData.fullText.doit()) {
|
} else if (filterData.fullText.doit()) {
|
||||||
FullTextResult ft = fulltext_find_dives(filterData.fullText, filterData.fulltextStringMode);
|
FullTextResult ft = fulltext_find_dives(filterData.fullText, filterData.fulltextStringMode);
|
||||||
for_each_dive(i, d) {
|
for (auto &d: divelog.dives) {
|
||||||
bool newStatus = ft.dive_matches(d) && showDive(d);
|
bool newStatus = ft.dive_matches(d.get()) && showDive(d.get());
|
||||||
updateDiveStatus(d, newStatus, res, removeFromSelection);
|
updateDiveStatus(d.get(), newStatus, res, removeFromSelection);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for_each_dive(i, d) {
|
for (auto &d: divelog.dives) {
|
||||||
bool newStatus = showDive(d);
|
bool newStatus = showDive(d.get());
|
||||||
updateDiveStatus(d, newStatus, res, removeFromSelection);
|
updateDiveStatus(d.get(), newStatus, res, removeFromSelection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateSelection(selection, std::vector<dive *>(), removeFromSelection);
|
updateSelection(selection, std::vector<dive *>(), removeFromSelection);
|
||||||
@ -142,7 +139,7 @@ bool DiveFilter::showDive(const struct dive *d) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(SUBSURFACE_MOBILE) && !defined(SUBSURFACE_DOWNLOADER)
|
#if !defined(SUBSURFACE_MOBILE) && !defined(SUBSURFACE_DOWNLOADER)
|
||||||
void DiveFilter::startFilterDiveSites(QVector<dive_site *> ds)
|
void DiveFilter::startFilterDiveSites(std::vector<dive_site *> ds)
|
||||||
{
|
{
|
||||||
if (++diveSiteRefCount > 1) {
|
if (++diveSiteRefCount > 1) {
|
||||||
setFilterDiveSite(std::move(ds));
|
setFilterDiveSite(std::move(ds));
|
||||||
@ -169,7 +166,7 @@ void DiveFilter::stopFilterDiveSites()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiveFilter::setFilterDiveSite(QVector<dive_site *> ds)
|
void DiveFilter::setFilterDiveSite(std::vector<dive_site *> ds)
|
||||||
{
|
{
|
||||||
// If the filter didn't change, return early to avoid a full
|
// If the filter didn't change, return early to avoid a full
|
||||||
// map reload. For a well-defined comparison, sort the vector first.
|
// map reload. For a well-defined comparison, sort the vector first.
|
||||||
@ -185,7 +182,7 @@ void DiveFilter::setFilterDiveSite(QVector<dive_site *> ds)
|
|||||||
MainWindow::instance()->diveList->expandAll();
|
MainWindow::instance()->diveList->expandAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QVector<dive_site *> &DiveFilter::filteredDiveSites() const
|
const std::vector<dive_site *> &DiveFilter::filteredDiveSites() const
|
||||||
{
|
{
|
||||||
return dive_sites;
|
return dive_sites;
|
||||||
}
|
}
|
||||||
@ -203,7 +200,7 @@ bool DiveFilter::diveSiteMode() const
|
|||||||
|
|
||||||
QString DiveFilter::shownText() const
|
QString DiveFilter::shownText() const
|
||||||
{
|
{
|
||||||
int num = divelog.dives->nr;
|
size_t num = divelog.dives.size();
|
||||||
if (diveSiteMode() || filterData.validFilter())
|
if (diveSiteMode() || filterData.validFilter())
|
||||||
return gettextFromC::tr("%L1/%L2 shown").arg(shown_dives).arg(num);
|
return gettextFromC::tr("%L1/%L2 shown").arg(shown_dives).arg(num);
|
||||||
else
|
else
|
||||||
@ -229,11 +226,9 @@ std::vector<dive *> DiveFilter::visibleDives() const
|
|||||||
std::vector<dive *> res;
|
std::vector<dive *> res;
|
||||||
res.reserve(shown_dives);
|
res.reserve(shown_dives);
|
||||||
|
|
||||||
int i;
|
for (auto &d: divelog.dives) {
|
||||||
dive *d;
|
|
||||||
for_each_dive(i, d) {
|
|
||||||
if (!d->hidden_by_filter)
|
if (!d->hidden_by_filter)
|
||||||
res.push_back(d);
|
res.push_back(d.get());
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,9 +45,9 @@ public:
|
|||||||
bool diveSiteMode() const; // returns true if we're filtering on dive site (on mobile always returns false)
|
bool diveSiteMode() const; // returns true if we're filtering on dive site (on mobile always returns false)
|
||||||
std::vector<dive *> visibleDives() const;
|
std::vector<dive *> visibleDives() const;
|
||||||
#ifndef SUBSURFACE_MOBILE
|
#ifndef SUBSURFACE_MOBILE
|
||||||
const QVector<dive_site *> &filteredDiveSites() const;
|
const std::vector<dive_site *> &filteredDiveSites() const;
|
||||||
void startFilterDiveSites(QVector<dive_site *> ds);
|
void startFilterDiveSites(std::vector<dive_site *> ds);
|
||||||
void setFilterDiveSite(QVector<dive_site *> ds);
|
void setFilterDiveSite(std::vector<dive_site *> ds);
|
||||||
void stopFilterDiveSites();
|
void stopFilterDiveSites();
|
||||||
#endif
|
#endif
|
||||||
void setFilter(const FilterData &data);
|
void setFilter(const FilterData &data);
|
||||||
@ -62,7 +62,7 @@ private:
|
|||||||
void updateDiveStatus(dive *d, bool newStatus, ShownChange &change,
|
void updateDiveStatus(dive *d, bool newStatus, ShownChange &change,
|
||||||
std::vector<dive *> &removeFromSelection) const;
|
std::vector<dive *> &removeFromSelection) const;
|
||||||
|
|
||||||
QVector<dive_site *> dive_sites;
|
std::vector<dive_site *> dive_sites;
|
||||||
FilterData filterData;
|
FilterData filterData;
|
||||||
mutable int shown_dives;
|
mutable int shown_dives;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -2,29 +2,29 @@
|
|||||||
#ifndef DIVELIST_H
|
#ifndef DIVELIST_H
|
||||||
#define DIVELIST_H
|
#define DIVELIST_H
|
||||||
|
|
||||||
|
#include "triptable.h"
|
||||||
|
#include "divesitetable.h"
|
||||||
#include "units.h"
|
#include "units.h"
|
||||||
|
#include <memory>
|
||||||
#ifdef __cplusplus
|
#include <vector>
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct dive;
|
struct dive;
|
||||||
struct divelog;
|
struct divelog;
|
||||||
struct trip_table;
|
struct device;
|
||||||
struct dive_site_table;
|
|
||||||
struct device_table;
|
|
||||||
struct deco_state;
|
struct deco_state;
|
||||||
|
|
||||||
struct dive_table {
|
int comp_dives(const struct dive &a, const struct dive &b);
|
||||||
int nr, allocated;
|
int comp_dives_ptr(const struct dive *a, const struct dive *b);
|
||||||
struct dive **dives;
|
|
||||||
|
struct dive_table : public sorted_owning_table<dive, &comp_dives> {
|
||||||
|
dive *get_by_uniq_id(int id) const;
|
||||||
|
void record_dive(std::unique_ptr<dive> d); // call fixup_dive() before adding dive to table.
|
||||||
|
std::unique_ptr<dive> unregister_dive(int idx);
|
||||||
};
|
};
|
||||||
static const struct dive_table empty_dive_table = { 0, 0, (struct dive **)0 };
|
|
||||||
|
|
||||||
/* this is used for both git and xml format */
|
/* this is used for both git and xml format */
|
||||||
#define DATAFORMAT_VERSION 3
|
#define DATAFORMAT_VERSION 3
|
||||||
|
|
||||||
extern void sort_dive_table(struct dive_table *table);
|
|
||||||
extern void update_cylinder_related_info(struct dive *);
|
extern void update_cylinder_related_info(struct dive *);
|
||||||
extern int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner);
|
extern int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner);
|
||||||
|
|
||||||
@ -35,37 +35,29 @@ extern void process_loaded_dives();
|
|||||||
#define IMPORT_IS_DOWNLOADED (1 << 1)
|
#define IMPORT_IS_DOWNLOADED (1 << 1)
|
||||||
#define IMPORT_MERGE_ALL_TRIPS (1 << 2)
|
#define IMPORT_MERGE_ALL_TRIPS (1 << 2)
|
||||||
#define IMPORT_ADD_TO_NEW_TRIP (1 << 3)
|
#define IMPORT_ADD_TO_NEW_TRIP (1 << 3)
|
||||||
extern void add_imported_dives(struct divelog *log, int flags);
|
extern void add_imported_dives(struct divelog &log, int flags);
|
||||||
extern void process_imported_dives(struct divelog *import_log, int flags,
|
|
||||||
struct dive_table *dives_to_add, struct dive_table *dives_to_remove,
|
struct process_imported_dives_result {
|
||||||
struct trip_table *trips_to_add, struct dive_site_table *sites_to_add,
|
dive_table dives_to_add;
|
||||||
struct device_table *devices_to_add);
|
std::vector<dive *> dives_to_remove;
|
||||||
|
trip_table trips_to_add;
|
||||||
|
dive_site_table sites_to_add;
|
||||||
|
std::vector<device> devices_to_add;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern process_imported_dives_result process_imported_dives(struct divelog &import_log, int flags);
|
||||||
|
|
||||||
extern int dive_table_get_insertion_index(struct dive_table *table, struct dive *dive);
|
|
||||||
extern void add_to_dive_table(struct dive_table *table, int idx, struct dive *dive);
|
|
||||||
extern void insert_dive(struct dive_table *table, struct dive *d);
|
|
||||||
extern void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2low_p);
|
extern void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2low_p);
|
||||||
extern int get_divenr(const struct dive *dive);
|
|
||||||
extern int remove_dive(const struct dive *dive, struct dive_table *table);
|
|
||||||
extern int get_dive_nr_at_idx(int idx);
|
extern int get_dive_nr_at_idx(int idx);
|
||||||
extern timestamp_t get_surface_interval(timestamp_t when);
|
extern timestamp_t get_surface_interval(timestamp_t when);
|
||||||
extern void delete_dive_from_table(struct dive_table *table, int idx);
|
|
||||||
extern struct dive *find_next_visible_dive(timestamp_t when);
|
extern struct dive *find_next_visible_dive(timestamp_t when);
|
||||||
|
|
||||||
extern int comp_dives(const struct dive *a, const struct dive *b);
|
|
||||||
|
|
||||||
int get_min_datafile_version();
|
int get_min_datafile_version();
|
||||||
void reset_min_datafile_version();
|
|
||||||
void report_datafile_version(int version);
|
void report_datafile_version(int version);
|
||||||
int get_dive_id_closest_to(timestamp_t when);
|
int get_dive_id_closest_to(timestamp_t when);
|
||||||
void clear_dive_file_data();
|
void clear_dive_file_data();
|
||||||
void clear_dive_table(struct dive_table *table);
|
void clear_dive_table(struct dive_table *table);
|
||||||
void move_dive_table(struct dive_table *src, struct dive_table *dst);
|
struct dive *register_dive(std::unique_ptr<dive> d);
|
||||||
struct dive *unregister_dive(int idx);
|
|
||||||
extern bool has_dive(unsigned int deviceid, unsigned int diveid);
|
extern bool has_dive(unsigned int deviceid, unsigned int diveid);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // DIVELIST_H
|
#endif // DIVELIST_H
|
||||||
|
|||||||
137
core/divelog.cpp
137
core/divelog.cpp
@ -3,96 +3,89 @@
|
|||||||
#include "divelist.h"
|
#include "divelist.h"
|
||||||
#include "divesite.h"
|
#include "divesite.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
|
#include "dive.h"
|
||||||
#include "errorhelper.h"
|
#include "errorhelper.h"
|
||||||
#include "filterpreset.h"
|
#include "filterpreset.h"
|
||||||
|
#include "filterpresettable.h"
|
||||||
#include "trip.h"
|
#include "trip.h"
|
||||||
|
|
||||||
struct divelog divelog;
|
struct divelog divelog;
|
||||||
|
|
||||||
// We can't use smart pointers, since this is used from C
|
divelog::divelog() = default;
|
||||||
// and it would be bold to presume that std::unique_ptr<>
|
divelog::~divelog() = default;
|
||||||
// and a plain pointer have the same memory layout.
|
divelog::divelog(divelog &&) = default;
|
||||||
divelog::divelog() :
|
struct divelog &divelog::operator=(divelog &&) = default;
|
||||||
dives(new dive_table),
|
|
||||||
trips(new trip_table),
|
|
||||||
sites(new dive_site_table),
|
|
||||||
devices(new device_table),
|
|
||||||
filter_presets(new filter_preset_table),
|
|
||||||
autogroup(false)
|
|
||||||
{
|
|
||||||
*dives = empty_dive_table;
|
|
||||||
*trips = empty_trip_table;
|
|
||||||
*sites = empty_dive_site_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
divelog::~divelog()
|
|
||||||
{
|
|
||||||
clear_dive_table(dives);
|
|
||||||
clear_trip_table(trips);
|
|
||||||
clear_dive_site_table(sites);
|
|
||||||
delete dives;
|
|
||||||
delete trips;
|
|
||||||
delete sites;
|
|
||||||
delete devices;
|
|
||||||
delete filter_presets;
|
|
||||||
}
|
|
||||||
|
|
||||||
divelog::divelog(divelog &&log) :
|
|
||||||
dives(new dive_table),
|
|
||||||
trips(new trip_table),
|
|
||||||
sites(new dive_site_table),
|
|
||||||
devices(new device_table),
|
|
||||||
filter_presets(new filter_preset_table)
|
|
||||||
{
|
|
||||||
*dives = empty_dive_table;
|
|
||||||
*trips = empty_trip_table;
|
|
||||||
*sites = empty_dive_site_table;
|
|
||||||
move_dive_table(log.dives, dives);
|
|
||||||
move_trip_table(log.trips, trips);
|
|
||||||
move_dive_site_table(log.sites, sites);
|
|
||||||
*devices = std::move(*log.devices);
|
|
||||||
*filter_presets = std::move(*log.filter_presets);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct divelog &divelog::operator=(divelog &&log)
|
|
||||||
{
|
|
||||||
move_dive_table(log.dives, dives);
|
|
||||||
move_trip_table(log.trips, trips);
|
|
||||||
move_dive_site_table(log.sites, sites);
|
|
||||||
*devices = std::move(*log.devices);
|
|
||||||
*filter_presets = std::move(*log.filter_presets);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this implements the mechanics of removing the dive from the
|
/* this implements the mechanics of removing the dive from the
|
||||||
* dive log and the trip, but doesn't deal with updating dive trips, etc */
|
* dive log and the trip, but doesn't deal with updating dive trips, etc */
|
||||||
void delete_single_dive(struct divelog *log, int idx)
|
void divelog::delete_single_dive(int idx)
|
||||||
{
|
{
|
||||||
if (idx < 0 || idx > log->dives->nr) {
|
if (idx < 0 || static_cast<size_t>(idx) >= dives.size()) {
|
||||||
report_info("Warning: deleting unexisting dive with index %d", idx);
|
report_info("Warning: deleting non-existing dive with index %d", idx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
struct dive *dive = log->dives->dives[idx];
|
struct dive *dive = dives[idx].get();
|
||||||
remove_dive_from_trip(dive, log->trips);
|
struct dive_trip *trip = unregister_dive_from_trip(dive);
|
||||||
|
|
||||||
|
// Deleting a dive may change the order of trips!
|
||||||
|
if (trip)
|
||||||
|
trips.sort();
|
||||||
|
|
||||||
|
if (trip && trip->dives.empty())
|
||||||
|
trips.pull(trip);
|
||||||
unregister_dive_from_dive_site(dive);
|
unregister_dive_from_dive_site(dive);
|
||||||
delete_dive_from_table(log->dives, idx);
|
dives.erase(dives.begin() + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void divelog::delete_multiple_dives(const std::vector<dive *> &dives_to_delete)
|
||||||
|
{
|
||||||
|
bool trips_changed = false;
|
||||||
|
|
||||||
|
for (dive *d: dives_to_delete) {
|
||||||
|
// Delete dive from trip and delete trip if empty
|
||||||
|
struct dive_trip *trip = unregister_dive_from_trip(d);
|
||||||
|
if (trip && trip->dives.empty()) {
|
||||||
|
trips_changed = true;
|
||||||
|
trips.pull(trip);
|
||||||
|
}
|
||||||
|
|
||||||
|
unregister_dive_from_dive_site(d);
|
||||||
|
dives.pull(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deleting a dive may change the order of trips!
|
||||||
|
if (trips_changed)
|
||||||
|
trips.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
void divelog::clear()
|
void divelog::clear()
|
||||||
{
|
{
|
||||||
while (dives->nr > 0)
|
dives.clear();
|
||||||
delete_single_dive(this, dives->nr - 1);
|
sites.clear();
|
||||||
while (sites->nr)
|
trips.clear();
|
||||||
delete_dive_site(get_dive_site(0, sites), sites);
|
devices.clear();
|
||||||
if (trips->nr != 0) {
|
filter_presets.clear();
|
||||||
report_info("Warning: trip table not empty in divelog::clear()!");
|
|
||||||
trips->nr = 0;
|
|
||||||
}
|
|
||||||
clear_device_table(devices);
|
|
||||||
filter_presets->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void clear_divelog(struct divelog *log)
|
/* check if we have a trip right before / after this dive */
|
||||||
|
bool divelog::is_trip_before_after(const struct dive *dive, bool before) const
|
||||||
{
|
{
|
||||||
log->clear();
|
auto it = std::find_if(dives.begin(), dives.end(),
|
||||||
|
[dive](auto &d) { return d.get() == dive; });
|
||||||
|
if (it == dives.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (before) {
|
||||||
|
do {
|
||||||
|
if (it == dives.begin())
|
||||||
|
return false;
|
||||||
|
--it;
|
||||||
|
} while ((*it)->invalid);
|
||||||
|
return (*it)->divetrip != nullptr;
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
while (it != dives.end() && (*it)->invalid)
|
||||||
|
++it;
|
||||||
|
return it != dives.end() && (*it)->divetrip != nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,43 +1,36 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
// A structure that contains all the values we save in a divelog file
|
// A structure that contains all the data we store in divelog files
|
||||||
#ifndef DIVELOG_H
|
#ifndef DIVELOG_H
|
||||||
#define DIVELOG_H
|
#define DIVELOG_H
|
||||||
|
|
||||||
struct dive_table;
|
#include "divelist.h"
|
||||||
struct trip_table;
|
#include "divesitetable.h"
|
||||||
struct dive_site_table;
|
#include "filterpresettable.h"
|
||||||
struct device_table;
|
#include "triptable.h"
|
||||||
struct filter_preset_table;
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <vector>
|
||||||
|
|
||||||
|
struct device;
|
||||||
|
|
||||||
struct divelog {
|
struct divelog {
|
||||||
struct dive_table *dives;
|
dive_table dives;
|
||||||
struct trip_table *trips;
|
trip_table trips;
|
||||||
struct dive_site_table *sites;
|
dive_site_table sites;
|
||||||
struct device_table *devices;
|
std::vector<device> devices;
|
||||||
struct filter_preset_table *filter_presets;
|
filter_preset_table filter_presets;
|
||||||
bool autogroup;
|
bool autogroup = false;
|
||||||
#ifdef __cplusplus
|
|
||||||
void clear();
|
|
||||||
divelog();
|
divelog();
|
||||||
~divelog();
|
~divelog();
|
||||||
divelog(divelog &&log); // move constructor (argument is consumed).
|
divelog(divelog &&); // move constructor (argument is consumed).
|
||||||
divelog &operator=(divelog &&log); // move assignment (argument is consumed).
|
divelog &operator=(divelog &&); // move assignment (argument is consumed).
|
||||||
#endif
|
|
||||||
|
void delete_single_dive(int idx);
|
||||||
|
void delete_multiple_dives(const std::vector<dive *> &dives);
|
||||||
|
void clear();
|
||||||
|
bool is_trip_before_after(const struct dive *dive, bool before) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct divelog divelog;
|
extern struct divelog divelog;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void clear_divelog(struct divelog *);
|
|
||||||
extern void delete_single_dive(struct divelog *, int idx);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -75,45 +75,42 @@ static void exportHTMLstatistics(const QString filename, struct htmlExportSettin
|
|||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
file.open(QIODevice::WriteOnly | QIODevice::Text);
|
file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||||
QTextStream out(&file);
|
QTextStream out(&file);
|
||||||
stats_summary_auto_free stats;
|
|
||||||
|
|
||||||
stats_t total_stats;
|
stats_t total_stats;
|
||||||
|
|
||||||
calculate_stats_summary(&stats, hes.selectedOnly);
|
stats_summary stats = calculate_stats_summary(hes.selectedOnly);
|
||||||
total_stats.selection_size = 0;
|
total_stats.selection_size = 0;
|
||||||
total_stats.total_time.seconds = 0;
|
total_stats.total_time.seconds = 0;
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
out << "divestat=[";
|
out << "divestat=[";
|
||||||
if (hes.yearlyStatistics) {
|
if (hes.yearlyStatistics) {
|
||||||
while (stats.stats_yearly != NULL && stats.stats_yearly[i].period) {
|
for (const auto &s: stats.stats_yearly) {
|
||||||
out << "{";
|
out << "{";
|
||||||
out << "\"YEAR\":\"" << stats.stats_yearly[i].period << "\",";
|
out << "\"YEAR\":\"" << s.period << "\",";
|
||||||
out << "\"DIVES\":\"" << stats.stats_yearly[i].selection_size << "\",";
|
out << "\"DIVES\":\"" << s.selection_size << "\",";
|
||||||
out << "\"TOTAL_TIME\":\"" << get_dive_duration_string(stats.stats_yearly[i].total_time.seconds,
|
out << "\"TOTAL_TIME\":\"" << get_dive_duration_string(s.total_time.seconds,
|
||||||
gettextFromC::tr("h"), gettextFromC::tr("min"), gettextFromC::tr("sec"), " ") << "\",";
|
gettextFromC::tr("h"), gettextFromC::tr("min"), gettextFromC::tr("sec"), " ") << "\",";
|
||||||
out << "\"AVERAGE_TIME\":\"" << formatMinutes(stats.stats_yearly[i].total_time.seconds / stats.stats_yearly[i].selection_size) << "\",";
|
out << "\"AVERAGE_TIME\":\"" << formatMinutes(s.total_time.seconds / s.selection_size) << "\",";
|
||||||
out << "\"SHORTEST_TIME\":\"" << formatMinutes(stats.stats_yearly[i].shortest_time.seconds) << "\",";
|
out << "\"SHORTEST_TIME\":\"" << formatMinutes(s.shortest_time.seconds) << "\",";
|
||||||
out << "\"LONGEST_TIME\":\"" << formatMinutes(stats.stats_yearly[i].longest_time.seconds) << "\",";
|
out << "\"LONGEST_TIME\":\"" << formatMinutes(s.longest_time.seconds) << "\",";
|
||||||
out << "\"AVG_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].avg_depth) << "\",";
|
out << "\"AVG_DEPTH\":\"" << get_depth_string(s.avg_depth) << "\",";
|
||||||
out << "\"MIN_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].min_depth) << "\",";
|
out << "\"MIN_DEPTH\":\"" << get_depth_string(s.min_depth) << "\",";
|
||||||
out << "\"MAX_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].max_depth) << "\",";
|
out << "\"MAX_DEPTH\":\"" << get_depth_string(s.max_depth) << "\",";
|
||||||
out << "\"AVG_SAC\":\"" << get_volume_string(stats.stats_yearly[i].avg_sac) << "\",";
|
out << "\"AVG_SAC\":\"" << get_volume_string(s.avg_sac) << "\",";
|
||||||
out << "\"MIN_SAC\":\"" << get_volume_string(stats.stats_yearly[i].min_sac) << "\",";
|
out << "\"MIN_SAC\":\"" << get_volume_string(s.min_sac) << "\",";
|
||||||
out << "\"MAX_SAC\":\"" << get_volume_string(stats.stats_yearly[i].max_sac) << "\",";
|
out << "\"MAX_SAC\":\"" << get_volume_string(s.max_sac) << "\",";
|
||||||
if (stats.stats_yearly[i].combined_count) {
|
if (s.combined_count) {
|
||||||
temperature_t avg_temp;
|
temperature_t avg_temp;
|
||||||
avg_temp.mkelvin = stats.stats_yearly[i].combined_temp.mkelvin / stats.stats_yearly[i].combined_count;
|
avg_temp.mkelvin = s.combined_temp.mkelvin / s.combined_count;
|
||||||
out << "\"AVG_TEMP\":\"" << get_temperature_string(avg_temp) << "\",";
|
out << "\"AVG_TEMP\":\"" << get_temperature_string(avg_temp) << "\",";
|
||||||
} else {
|
} else {
|
||||||
out << "\"AVG_TEMP\":\"0.0\",";
|
out << "\"AVG_TEMP\":\"0.0\",";
|
||||||
}
|
}
|
||||||
out << "\"MIN_TEMP\":\"" << (stats.stats_yearly[i].min_temp.mkelvin == 0 ? 0 : get_temperature_string(stats.stats_yearly[i].min_temp)) << "\",";
|
out << "\"MIN_TEMP\":\"" << (s.min_temp.mkelvin == 0 ? 0 : get_temperature_string(s.min_temp)) << "\",";
|
||||||
out << "\"MAX_TEMP\":\"" << (stats.stats_yearly[i].max_temp.mkelvin == 0 ? 0 : get_temperature_string(stats.stats_yearly[i].max_temp)) << "\",";
|
out << "\"MAX_TEMP\":\"" << (s.max_temp.mkelvin == 0 ? 0 : get_temperature_string(s.max_temp)) << "\",";
|
||||||
out << "},";
|
out << "},";
|
||||||
total_stats.selection_size += stats.stats_yearly[i].selection_size;
|
total_stats.selection_size += s.selection_size;
|
||||||
total_stats.total_time.seconds += stats.stats_yearly[i].total_time.seconds;
|
total_stats.total_time.seconds += s.total_time.seconds;
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
exportHTMLstatisticsTotal(out, &total_stats);
|
exportHTMLstatisticsTotal(out, &total_stats);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,53 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
#include "divesite.h"
|
|
||||||
#include "pref.h"
|
|
||||||
#include "gettextfromc.h"
|
|
||||||
|
|
||||||
QString constructLocationTags(struct taxonomy_data *taxonomy, bool for_maintab)
|
|
||||||
{
|
|
||||||
QString locationTag;
|
|
||||||
|
|
||||||
if (!taxonomy->nr)
|
|
||||||
return locationTag;
|
|
||||||
|
|
||||||
/* Check if the user set any of the 3 geocoding categories */
|
|
||||||
bool prefs_set = false;
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
if (prefs.geocoding.category[i] != TC_NONE)
|
|
||||||
prefs_set = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!prefs_set && !for_maintab) {
|
|
||||||
locationTag = QString("<small><small>") + gettextFromC::tr("No dive site layout categories set in preferences!") +
|
|
||||||
QString("</small></small>");
|
|
||||||
return locationTag;
|
|
||||||
}
|
|
||||||
else if (!prefs_set)
|
|
||||||
return locationTag;
|
|
||||||
|
|
||||||
if (for_maintab)
|
|
||||||
locationTag = QString("<small><small>(") + gettextFromC::tr("Tags") + QString(": ");
|
|
||||||
else
|
|
||||||
locationTag = QString("<small><small>");
|
|
||||||
QString connector;
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
if (prefs.geocoding.category[i] == TC_NONE)
|
|
||||||
continue;
|
|
||||||
for (int j = 0; j < taxonomy->nr; j++) {
|
|
||||||
if (taxonomy->category[j].category == prefs.geocoding.category[i]) {
|
|
||||||
QString tag = taxonomy->category[j].value;
|
|
||||||
if (!tag.isEmpty()) {
|
|
||||||
locationTag += connector + tag;
|
|
||||||
connector = " / ";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (for_maintab)
|
|
||||||
locationTag += ")</small></small>";
|
|
||||||
else
|
|
||||||
locationTag += "</small></small>";
|
|
||||||
return locationTag;
|
|
||||||
}
|
|
||||||
401
core/divesite.c
401
core/divesite.c
@ -1,401 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
/* divesite.c */
|
|
||||||
#include "divesite.h"
|
|
||||||
#include "dive.h"
|
|
||||||
#include "divelist.h"
|
|
||||||
#include "divelog.h"
|
|
||||||
#include "errorhelper.h"
|
|
||||||
#include "membuffer.h"
|
|
||||||
#include "subsurface-string.h"
|
|
||||||
#include "table.h"
|
|
||||||
#include "sha1.h"
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
const struct dive_site *d;
|
|
||||||
// tempting as it may be, don't die when called with ds=NULL
|
|
||||||
if (ds)
|
|
||||||
for_each_dive_site(i, d, ds_table) {
|
|
||||||
if (d == ds)
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: keep table sorted by UUID and do a binary search?
|
|
||||||
struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct dive_site *ds;
|
|
||||||
for_each_dive_site (i, ds, ds_table)
|
|
||||||
if (ds->uuid == uuid)
|
|
||||||
return get_dive_site(i, ds_table);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* there could be multiple sites of the same name - return the first one */
|
|
||||||
struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct dive_site *ds;
|
|
||||||
for_each_dive_site (i, ds, ds_table) {
|
|
||||||
if (same_string(ds->name, name))
|
|
||||||
return ds;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* there could be multiple sites at the same GPS fix - return the first one */
|
|
||||||
struct dive_site *get_dive_site_by_gps(const location_t *loc, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct dive_site *ds;
|
|
||||||
for_each_dive_site (i, ds, ds_table) {
|
|
||||||
if (same_location(loc, &ds->location))
|
|
||||||
return ds;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* to avoid a bug where we have two dive sites with different name and the same GPS coordinates
|
|
||||||
* and first get the gps coordinates (reading a V2 file) and happen to get back "the other" name,
|
|
||||||
* this function allows us to verify if a very specific name/GPS combination already exists */
|
|
||||||
struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location_t *loc, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct dive_site *ds;
|
|
||||||
for_each_dive_site (i, ds, ds_table) {
|
|
||||||
if (same_location(loc, &ds->location) && same_string(ds->name, name))
|
|
||||||
return ds;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the distance in meters between two coordinates.
|
|
||||||
unsigned int get_distance(const location_t *loc1, const location_t *loc2)
|
|
||||||
{
|
|
||||||
double lat1_r = udeg_to_radians(loc1->lat.udeg);
|
|
||||||
double lat2_r = udeg_to_radians(loc2->lat.udeg);
|
|
||||||
double lat_d_r = udeg_to_radians(loc2->lat.udeg - loc1->lat.udeg);
|
|
||||||
double lon_d_r = udeg_to_radians(loc2->lon.udeg - loc1->lon.udeg);
|
|
||||||
|
|
||||||
double a = sin(lat_d_r/2) * sin(lat_d_r/2) +
|
|
||||||
cos(lat1_r) * cos(lat2_r) * sin(lon_d_r/2) * sin(lon_d_r/2);
|
|
||||||
if (a < 0.0) a = 0.0;
|
|
||||||
if (a > 1.0) a = 1.0;
|
|
||||||
double c = 2 * atan2(sqrt(a), sqrt(1.0 - a));
|
|
||||||
|
|
||||||
// Earth radius in metres
|
|
||||||
return lrint(6371000 * c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* find the closest one, no more than distance meters away - if more than one at same distance, pick the first */
|
|
||||||
struct dive_site *get_dive_site_by_gps_proximity(const location_t *loc, int distance, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct dive_site *ds, *res = NULL;
|
|
||||||
unsigned int cur_distance, min_distance = distance;
|
|
||||||
for_each_dive_site (i, ds, ds_table) {
|
|
||||||
if (dive_site_has_gps_location(ds) &&
|
|
||||||
(cur_distance = get_distance(&ds->location, loc)) < min_distance) {
|
|
||||||
min_distance = cur_distance;
|
|
||||||
res = ds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int register_dive_site(struct dive_site *ds)
|
|
||||||
{
|
|
||||||
return add_dive_site_to_table(ds, divelog.sites);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int compare_sites(const struct dive_site *a, const struct dive_site *b)
|
|
||||||
{
|
|
||||||
return a->uuid > b->uuid ? 1 : a->uuid == b->uuid ? 0 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int site_less_than(const struct dive_site *a, const struct dive_site *b)
|
|
||||||
{
|
|
||||||
return compare_sites(a, b) < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static MAKE_GROW_TABLE(dive_site_table, struct dive_site *, dive_sites)
|
|
||||||
static MAKE_GET_INSERTION_INDEX(dive_site_table, struct dive_site *, dive_sites, site_less_than)
|
|
||||||
static MAKE_ADD_TO(dive_site_table, struct dive_site *, dive_sites)
|
|
||||||
static MAKE_REMOVE_FROM(dive_site_table, dive_sites)
|
|
||||||
static MAKE_GET_IDX(dive_site_table, struct dive_site *, dive_sites)
|
|
||||||
MAKE_SORT(dive_site_table, struct dive_site *, dive_sites, compare_sites)
|
|
||||||
static MAKE_REMOVE(dive_site_table, struct dive_site *, dive_site)
|
|
||||||
MAKE_CLEAR_TABLE(dive_site_table, dive_sites, dive_site)
|
|
||||||
MAKE_MOVE_TABLE(dive_site_table, dive_sites)
|
|
||||||
|
|
||||||
int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
/* If the site doesn't yet have an UUID, create a new one.
|
|
||||||
* Make this deterministic for testing. */
|
|
||||||
if (!ds->uuid) {
|
|
||||||
SHA_CTX ctx;
|
|
||||||
uint32_t csum[5];
|
|
||||||
|
|
||||||
SHA1_Init(&ctx);
|
|
||||||
if (ds->name)
|
|
||||||
SHA1_Update(&ctx, ds->name, strlen(ds->name));
|
|
||||||
if (ds->description)
|
|
||||||
SHA1_Update(&ctx, ds->description, strlen(ds->description));
|
|
||||||
if (ds->notes)
|
|
||||||
SHA1_Update(&ctx, ds->notes, strlen(ds->notes));
|
|
||||||
SHA1_Final((unsigned char *)csum, &ctx);
|
|
||||||
ds->uuid = csum[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Take care to never have the same uuid twice. This could happen on
|
|
||||||
* reimport of a log where the dive sites have diverged */
|
|
||||||
while (ds->uuid == 0 || get_dive_site_by_uuid(ds->uuid, ds_table) != NULL)
|
|
||||||
++ds->uuid;
|
|
||||||
|
|
||||||
int idx = dive_site_table_get_insertion_index(ds_table, ds);
|
|
||||||
add_to_dive_site_table(ds_table, idx, ds);
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dive_site *alloc_dive_site()
|
|
||||||
{
|
|
||||||
struct dive_site *ds;
|
|
||||||
ds = calloc(1, sizeof(*ds));
|
|
||||||
if (!ds)
|
|
||||||
exit(1);
|
|
||||||
return ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dive_site *alloc_dive_site_with_name(const char *name)
|
|
||||||
{
|
|
||||||
struct dive_site *ds = alloc_dive_site();
|
|
||||||
ds->name = copy_string(name);
|
|
||||||
return ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dive_site *alloc_dive_site_with_gps(const char *name, const location_t *loc)
|
|
||||||
{
|
|
||||||
struct dive_site *ds = alloc_dive_site_with_name(name);
|
|
||||||
ds->location = *loc;
|
|
||||||
|
|
||||||
return ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* when parsing, dive sites are identified by uuid */
|
|
||||||
struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
struct dive_site *ds;
|
|
||||||
|
|
||||||
if (uuid && (ds = get_dive_site_by_uuid(uuid, ds_table)) != NULL)
|
|
||||||
return ds;
|
|
||||||
|
|
||||||
ds = alloc_dive_site();
|
|
||||||
ds->uuid = uuid;
|
|
||||||
|
|
||||||
add_dive_site_to_table(ds, ds_table);
|
|
||||||
|
|
||||||
return ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nr_of_dives_at_dive_site(struct dive_site *ds)
|
|
||||||
{
|
|
||||||
return ds->dives.nr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_dive_site_selected(struct dive_site *ds)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ds->dives.nr; i++) {
|
|
||||||
if (ds->dives.dives[i]->selected)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_dive_site(struct dive_site *ds)
|
|
||||||
{
|
|
||||||
if (ds) {
|
|
||||||
free(ds->name);
|
|
||||||
free(ds->notes);
|
|
||||||
free(ds->description);
|
|
||||||
free(ds->dives.dives);
|
|
||||||
free_taxonomy(&ds->taxonomy);
|
|
||||||
free(ds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int unregister_dive_site(struct dive_site *ds)
|
|
||||||
{
|
|
||||||
return remove_dive_site(ds, divelog.sites);
|
|
||||||
}
|
|
||||||
|
|
||||||
void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
if (!ds)
|
|
||||||
return;
|
|
||||||
remove_dive_site(ds, ds_table);
|
|
||||||
free_dive_site(ds);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate a new site and add it to the table */
|
|
||||||
struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
struct dive_site *ds = alloc_dive_site_with_name(name);
|
|
||||||
add_dive_site_to_table(ds, ds_table);
|
|
||||||
return ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* same as before, but with GPS data */
|
|
||||||
struct dive_site *create_dive_site_with_gps(const char *name, const location_t *loc, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
struct dive_site *ds = alloc_dive_site_with_gps(name, loc);
|
|
||||||
add_dive_site_to_table(ds, ds_table);
|
|
||||||
return ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if all fields are empty, the dive site is pointless */
|
|
||||||
bool dive_site_is_empty(struct dive_site *ds)
|
|
||||||
{
|
|
||||||
return !ds ||
|
|
||||||
(empty_string(ds->name) &&
|
|
||||||
empty_string(ds->description) &&
|
|
||||||
empty_string(ds->notes) &&
|
|
||||||
!has_location(&ds->location));
|
|
||||||
}
|
|
||||||
|
|
||||||
void copy_dive_site(struct dive_site *orig, struct dive_site *copy)
|
|
||||||
{
|
|
||||||
free(copy->name);
|
|
||||||
free(copy->notes);
|
|
||||||
free(copy->description);
|
|
||||||
|
|
||||||
copy->location = orig->location;
|
|
||||||
copy->name = copy_string(orig->name);
|
|
||||||
copy->notes = copy_string(orig->notes);
|
|
||||||
copy->description = copy_string(orig->description);
|
|
||||||
copy_taxonomy(&orig->taxonomy, ©->taxonomy);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void merge_string(char **a, char **b)
|
|
||||||
{
|
|
||||||
char *s1 = *a, *s2 = *b;
|
|
||||||
|
|
||||||
if (!s2)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (same_string(s1, s2))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!s1) {
|
|
||||||
*a = strdup(s2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
*a = format_string("(%s) or (%s)", s1, s2);
|
|
||||||
free(s1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Used to check on import if two dive sites are equivalent.
|
|
||||||
* Since currently no merging is performed, be very conservative
|
|
||||||
* and only consider equal dive sites that are exactly the same.
|
|
||||||
* Taxonomy is not compared, as no taxonomy is generated on
|
|
||||||
* import.
|
|
||||||
*/
|
|
||||||
static bool same_dive_site(const struct dive_site *a, const struct dive_site *b)
|
|
||||||
{
|
|
||||||
return same_string(a->name, b->name)
|
|
||||||
&& same_location(&a->location, &b->location)
|
|
||||||
&& same_string(a->description, b->description)
|
|
||||||
&& same_string(a->notes, b->notes);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dive_site *get_same_dive_site(const struct dive_site *site)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct dive_site *ds;
|
|
||||||
for_each_dive_site (i, ds, divelog.sites)
|
|
||||||
if (same_dive_site(ds, site))
|
|
||||||
return ds;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void merge_dive_site(struct dive_site *a, struct dive_site *b)
|
|
||||||
{
|
|
||||||
if (!has_location(&a->location)) a->location = b->location;
|
|
||||||
merge_string(&a->name, &b->name);
|
|
||||||
merge_string(&a->notes, &b->notes);
|
|
||||||
merge_string(&a->description, &b->description);
|
|
||||||
|
|
||||||
if (!a->taxonomy.category) {
|
|
||||||
a->taxonomy = b->taxonomy;
|
|
||||||
memset(&b->taxonomy, 0, sizeof(b->taxonomy));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dive_site *find_or_create_dive_site_with_name(const char *name, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct dive_site *ds;
|
|
||||||
for_each_dive_site(i,ds, ds_table) {
|
|
||||||
if (same_string(name, ds->name))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (ds)
|
|
||||||
return ds;
|
|
||||||
return create_dive_site(name, ds_table);
|
|
||||||
}
|
|
||||||
|
|
||||||
void purge_empty_dive_sites(struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
struct dive *d;
|
|
||||||
struct dive_site *ds;
|
|
||||||
|
|
||||||
for (i = 0; i < ds_table->nr; i++) {
|
|
||||||
ds = get_dive_site(i, ds_table);
|
|
||||||
if (!dive_site_is_empty(ds))
|
|
||||||
continue;
|
|
||||||
for_each_dive(j, d) {
|
|
||||||
if (d->dive_site == ds)
|
|
||||||
unregister_dive_from_dive_site(d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_dive_to_dive_site(struct dive *d, struct dive_site *ds)
|
|
||||||
{
|
|
||||||
int idx;
|
|
||||||
if (!d) {
|
|
||||||
report_info("Warning: add_dive_to_dive_site called with NULL dive");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!ds) {
|
|
||||||
report_info("Warning: add_dive_to_dive_site called with NULL dive site");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (d->dive_site == ds)
|
|
||||||
return;
|
|
||||||
if (d->dive_site) {
|
|
||||||
report_info("Warning: adding dive that already belongs to a dive site to a different site");
|
|
||||||
unregister_dive_from_dive_site(d);
|
|
||||||
}
|
|
||||||
idx = dive_table_get_insertion_index(&ds->dives, d);
|
|
||||||
add_to_dive_table(&ds->dives, idx, d);
|
|
||||||
d->dive_site = ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dive_site *unregister_dive_from_dive_site(struct dive *d)
|
|
||||||
{
|
|
||||||
struct dive_site *ds = d->dive_site;
|
|
||||||
if (!ds)
|
|
||||||
return NULL;
|
|
||||||
remove_dive(d, &ds->dives);
|
|
||||||
d->dive_site = NULL;
|
|
||||||
return ds;
|
|
||||||
}
|
|
||||||
257
core/divesite.cpp
Normal file
257
core/divesite.cpp
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/* divesite.c */
|
||||||
|
#include "divesite.h"
|
||||||
|
#include "dive.h"
|
||||||
|
#include "divelist.h"
|
||||||
|
#include "errorhelper.h"
|
||||||
|
#include "format.h"
|
||||||
|
#include "membuffer.h"
|
||||||
|
#include "subsurface-string.h"
|
||||||
|
#include "sha1.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2)
|
||||||
|
{
|
||||||
|
if (ds1.uuid == ds2.uuid)
|
||||||
|
return 0;
|
||||||
|
return ds1.uuid < ds2.uuid ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PRED>
|
||||||
|
dive_site *get_by_predicate(const dive_site_table &ds_table, PRED pred)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(ds_table.begin(), ds_table.end(), pred);
|
||||||
|
return it != ds_table.end() ? it->get() : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dive_site *dive_site_table::get_by_uuid(uint32_t uuid) const
|
||||||
|
{
|
||||||
|
// The table is sorted by uuid
|
||||||
|
auto it = std::lower_bound(begin(), end(), uuid,
|
||||||
|
[] (const auto &ds, auto uuid) { return ds->uuid < uuid; });
|
||||||
|
return it != end() && (*it)->uuid == uuid ? it->get() : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* there could be multiple sites of the same name - return the first one */
|
||||||
|
dive_site *dive_site_table::get_by_name(const std::string &name) const
|
||||||
|
{
|
||||||
|
return get_by_predicate(*this, [&name](const auto &ds) { return ds->name == name; });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* there could be multiple sites at the same GPS fix - return the first one */
|
||||||
|
dive_site *dive_site_table::get_by_gps(const location_t *loc) const
|
||||||
|
{
|
||||||
|
return get_by_predicate(*this, [loc](const auto &ds) { return ds->location == *loc; });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* to avoid a bug where we have two dive sites with different name and the same GPS coordinates
|
||||||
|
* and first get the gps coordinates (reading a V2 file) and happen to get back "the other" name,
|
||||||
|
* this function allows us to verify if a very specific name/GPS combination already exists */
|
||||||
|
dive_site *dive_site_table::get_by_gps_and_name(const std::string &name, const location_t loc) const
|
||||||
|
{
|
||||||
|
return get_by_predicate(*this, [&name, loc](const auto &ds) { return ds->location == loc &&
|
||||||
|
ds->name == name; });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the closest one, no more than distance meters away - if more than one at same distance, pick the first */
|
||||||
|
dive_site *dive_site_table::get_by_gps_proximity(location_t loc, int distance) const
|
||||||
|
{
|
||||||
|
struct dive_site *res = nullptr;
|
||||||
|
unsigned int cur_distance, min_distance = distance;
|
||||||
|
for (const auto &ds: *this) {
|
||||||
|
if (dive_site_has_gps_location(ds.get()) &&
|
||||||
|
(cur_distance = get_distance(ds->location, loc)) < min_distance) {
|
||||||
|
min_distance = cur_distance;
|
||||||
|
res = ds.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
dive_site_table::put_result dive_site_table::register_site(std::unique_ptr<dive_site> ds)
|
||||||
|
{
|
||||||
|
/* If the site doesn't yet have an UUID, create a new one.
|
||||||
|
* Make this deterministic for testing. */
|
||||||
|
if (!ds->uuid) {
|
||||||
|
SHA1 sha;
|
||||||
|
if (!ds->name.empty())
|
||||||
|
sha.update(ds->name);
|
||||||
|
if (!ds->description.empty())
|
||||||
|
sha.update(ds->description);
|
||||||
|
if (!ds->notes.empty())
|
||||||
|
sha.update(ds->notes);
|
||||||
|
ds->uuid = sha.hash_uint32();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take care to never have the same uuid twice. This could happen on
|
||||||
|
* reimport of a log where the dive sites have diverged */
|
||||||
|
while (ds->uuid == 0 || get_by_uuid(ds->uuid) != NULL)
|
||||||
|
++ds->uuid;
|
||||||
|
|
||||||
|
return put(std::move(ds));
|
||||||
|
}
|
||||||
|
|
||||||
|
dive_site::dive_site()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
dive_site::dive_site(const std::string &name) : name(name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
dive_site::dive_site(const std::string &name, const location_t loc) : name(name), location(loc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
dive_site::dive_site(uint32_t uuid) : uuid(uuid)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
dive_site::~dive_site()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* when parsing, dive sites are identified by uuid */
|
||||||
|
dive_site *dive_site_table::alloc_or_get(uint32_t uuid)
|
||||||
|
{
|
||||||
|
struct dive_site *ds;
|
||||||
|
|
||||||
|
if (uuid && (ds = get_by_uuid(uuid)) != NULL)
|
||||||
|
return ds;
|
||||||
|
|
||||||
|
return register_site(std::make_unique<dive_site>(uuid)).ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t dive_site::nr_of_dives() const
|
||||||
|
{
|
||||||
|
return dives.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dive_site::is_selected() const
|
||||||
|
{
|
||||||
|
return any_of(dives.begin(), dives.end(),
|
||||||
|
[](dive *dive) { return dive->selected; });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate a new site and add it to the table */
|
||||||
|
dive_site *dive_site_table::create(const std::string &name)
|
||||||
|
{
|
||||||
|
return register_site(std::make_unique<dive_site>(name)).ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* same as before, but with GPS data */
|
||||||
|
dive_site *dive_site_table::create(const std::string &name, const location_t loc)
|
||||||
|
{
|
||||||
|
return register_site(std::make_unique<dive_site>(name, loc)).ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if all fields are empty, the dive site is pointless */
|
||||||
|
bool dive_site::is_empty() const
|
||||||
|
{
|
||||||
|
return name.empty() &&
|
||||||
|
description.empty() &&
|
||||||
|
notes.empty() &&
|
||||||
|
!has_location(&location);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void merge_string(std::string &a, const std::string &b)
|
||||||
|
{
|
||||||
|
if (b.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (a == b)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (a.empty()) {
|
||||||
|
a = b;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = format_string_std("(%s) or (%s)", a.c_str(), b.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used to check on import if two dive sites are equivalent.
|
||||||
|
* Since currently no merging is performed, be very conservative
|
||||||
|
* and only consider equal dive sites that are exactly the same.
|
||||||
|
* Taxonomy is not compared, as no taxonomy is generated on
|
||||||
|
* import.
|
||||||
|
*/
|
||||||
|
static bool same(const struct dive_site &a, const struct dive_site &b)
|
||||||
|
{
|
||||||
|
return a.name == b.name
|
||||||
|
&& a.location == b.location
|
||||||
|
&& a.description == b.description
|
||||||
|
&& a.notes == b.notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
dive_site *dive_site_table::get_same(const struct dive_site &site) const
|
||||||
|
{
|
||||||
|
return get_by_predicate(*this, [site](const auto &ds) { return same(*ds, site); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void dive_site::merge(dive_site &b)
|
||||||
|
{
|
||||||
|
if (!has_location(&location)) location = b.location;
|
||||||
|
merge_string(name, b.name);
|
||||||
|
merge_string(notes, b.notes);
|
||||||
|
merge_string(description, b.description);
|
||||||
|
|
||||||
|
if (taxonomy.empty())
|
||||||
|
taxonomy = std::move(b.taxonomy);
|
||||||
|
}
|
||||||
|
|
||||||
|
dive_site *dive_site_table::find_or_create(const std::string &name)
|
||||||
|
{
|
||||||
|
struct dive_site *ds = get_by_name(name);
|
||||||
|
if (ds)
|
||||||
|
return ds;
|
||||||
|
return create(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dive_site_table::purge_empty()
|
||||||
|
{
|
||||||
|
for (const auto &ds: *this) {
|
||||||
|
if (!ds->is_empty())
|
||||||
|
continue;
|
||||||
|
while (!ds->dives.empty()) {
|
||||||
|
struct dive *d = ds->dives.back();
|
||||||
|
if (d->dive_site != ds.get()) {
|
||||||
|
report_info("Warning: dive %d registered to wrong dive site in %s", d->number, __func__);
|
||||||
|
ds->dives.pop_back();
|
||||||
|
} else {
|
||||||
|
unregister_dive_from_dive_site(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dive_site::add_dive(struct dive *d)
|
||||||
|
{
|
||||||
|
if (!d) {
|
||||||
|
report_info("Warning: dive_site::add_dive() called with NULL dive");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (d->dive_site == this)
|
||||||
|
return;
|
||||||
|
if (d->dive_site) {
|
||||||
|
report_info("Warning: adding dive that already belongs to a dive site to a different site");
|
||||||
|
unregister_dive_from_dive_site(d);
|
||||||
|
}
|
||||||
|
dives.push_back(d);
|
||||||
|
d->dive_site = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dive_site *unregister_dive_from_dive_site(struct dive *d)
|
||||||
|
{
|
||||||
|
struct dive_site *ds = d->dive_site;
|
||||||
|
if (!ds)
|
||||||
|
return nullptr;
|
||||||
|
auto it = std::find(ds->dives.begin(), ds->dives.end(), d);
|
||||||
|
if (it != ds->dives.end())
|
||||||
|
ds->dives.erase(it);
|
||||||
|
else
|
||||||
|
report_info("Warning: dive not found in divesite table, even though it should be registered there.");
|
||||||
|
d->dive_site = nullptr;
|
||||||
|
return ds;
|
||||||
|
}
|
||||||
@ -2,88 +2,39 @@
|
|||||||
#ifndef DIVESITE_H
|
#ifndef DIVESITE_H
|
||||||
#define DIVESITE_H
|
#define DIVESITE_H
|
||||||
|
|
||||||
#include "units.h"
|
|
||||||
#include "taxonomy.h"
|
|
||||||
#include "divelist.h"
|
#include "divelist.h"
|
||||||
|
#include "taxonomy.h"
|
||||||
|
#include "units.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#include <QString>
|
|
||||||
#include <QObject>
|
|
||||||
extern "C" {
|
|
||||||
#else
|
|
||||||
#include <stdbool.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct dive_site
|
struct dive_site
|
||||||
{
|
{
|
||||||
uint32_t uuid;
|
uint32_t uuid = 0;
|
||||||
char *name;
|
std::string name;
|
||||||
struct dive_table dives;
|
std::vector<dive *> dives;
|
||||||
location_t location;
|
location_t location;
|
||||||
char *description;
|
std::string description;
|
||||||
char *notes;
|
std::string notes;
|
||||||
struct taxonomy_data taxonomy;
|
taxonomy_data taxonomy;
|
||||||
|
|
||||||
|
dive_site();
|
||||||
|
dive_site(const std::string &name);
|
||||||
|
dive_site(const std::string &name, const location_t loc);
|
||||||
|
dive_site(uint32_t uuid);
|
||||||
|
~dive_site();
|
||||||
|
|
||||||
|
size_t nr_of_dives() const;
|
||||||
|
bool is_selected() const;
|
||||||
|
bool is_empty() const;
|
||||||
|
void merge(struct dive_site &b); // Note: b is consumed
|
||||||
|
void add_dive(struct dive *d);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct dive_site_table {
|
|
||||||
int nr, allocated;
|
|
||||||
struct dive_site **dive_sites;
|
|
||||||
} dive_site_table_t;
|
|
||||||
|
|
||||||
static const dive_site_table_t empty_dive_site_table = { 0, 0, (struct dive_site **)0 };
|
|
||||||
|
|
||||||
static inline struct dive_site *get_dive_site(int nr, struct dive_site_table *ds_table)
|
|
||||||
{
|
|
||||||
if (nr >= ds_table->nr || nr < 0)
|
|
||||||
return NULL;
|
|
||||||
return ds_table->dive_sites[nr];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* iterate over each dive site */
|
|
||||||
#define for_each_dive_site(_i, _x, _ds_table) \
|
|
||||||
for ((_i) = 0; ((_x) = get_dive_site(_i, _ds_table)) != NULL; (_i)++)
|
|
||||||
|
|
||||||
int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_table);
|
|
||||||
struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *ds_table);
|
|
||||||
void sort_dive_site_table(struct dive_site_table *ds_table);
|
|
||||||
int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table);
|
|
||||||
struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table);
|
|
||||||
struct dive_site *alloc_dive_site();
|
|
||||||
struct dive_site *alloc_dive_site_with_name(const char *name);
|
|
||||||
struct dive_site *alloc_dive_site_with_gps(const char *name, const location_t *loc);
|
|
||||||
int nr_of_dives_at_dive_site(struct dive_site *ds);
|
|
||||||
bool is_dive_site_selected(struct dive_site *ds);
|
|
||||||
void free_dive_site(struct dive_site *ds);
|
|
||||||
int unregister_dive_site(struct dive_site *ds);
|
|
||||||
int register_dive_site(struct dive_site *ds);
|
|
||||||
void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table);
|
|
||||||
struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_table);
|
|
||||||
struct dive_site *create_dive_site_with_gps(const char *name, const location_t *, struct dive_site_table *ds_table);
|
|
||||||
struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table *ds_table);
|
|
||||||
struct dive_site *get_dive_site_by_gps(const location_t *, struct dive_site_table *ds_table);
|
|
||||||
struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location_t *, struct dive_site_table *ds_table);
|
|
||||||
struct dive_site *get_dive_site_by_gps_proximity(const location_t *, int distance, struct dive_site_table *ds_table);
|
|
||||||
struct dive_site *get_same_dive_site(const struct dive_site *);
|
|
||||||
bool dive_site_is_empty(struct dive_site *ds);
|
|
||||||
void copy_dive_site_taxonomy(struct dive_site *orig, struct dive_site *copy);
|
|
||||||
void copy_dive_site(struct dive_site *orig, struct dive_site *copy);
|
|
||||||
void merge_dive_site(struct dive_site *a, struct dive_site *b);
|
|
||||||
unsigned int get_distance(const location_t *loc1, const location_t *loc2);
|
|
||||||
struct dive_site *find_or_create_dive_site_with_name(const char *name, struct dive_site_table *ds_table);
|
|
||||||
void purge_empty_dive_sites(struct dive_site_table *ds_table);
|
|
||||||
void clear_dive_site_table(struct dive_site_table *ds_table);
|
|
||||||
void move_dive_site_table(struct dive_site_table *src, struct dive_site_table *dst);
|
|
||||||
void add_dive_to_dive_site(struct dive *d, struct dive_site *ds);
|
|
||||||
struct dive_site *unregister_dive_from_dive_site(struct dive *d);
|
struct dive_site *unregister_dive_from_dive_site(struct dive *d);
|
||||||
|
int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2);
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
QString constructLocationTags(struct taxonomy_data *taxonomy, bool for_maintab);
|
|
||||||
|
|
||||||
/* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */
|
/* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */
|
||||||
|
#include <QObject>
|
||||||
Q_DECLARE_METATYPE(dive_site *);
|
Q_DECLARE_METATYPE(dive_site *);
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // DIVESITE_H
|
#endif // DIVESITE_H
|
||||||
|
|||||||
@ -84,14 +84,14 @@ taxonomy_data reverseGeoLookup(degrees_t latitude, degrees_t longitude)
|
|||||||
|
|
||||||
QString url;
|
QString url;
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
taxonomy_data taxonomy = { 0, 0 };
|
taxonomy_data taxonomy;
|
||||||
|
|
||||||
// check the oceans API to figure out the body of water
|
// check the oceans API to figure out the body of water
|
||||||
url = geonamesOceanURL.arg(getUiLanguage().section(QRegularExpression("[-_ ]"), 0, 0)).arg(latitude.udeg / 1000000.0).arg(longitude.udeg / 1000000.0);
|
url = geonamesOceanURL.arg(getUiLanguage().section(QRegularExpression("[-_ ]"), 0, 0)).arg(latitude.udeg / 1000000.0).arg(longitude.udeg / 1000000.0);
|
||||||
obj = doAsyncRESTGetRequest(url, 5000); // 5 secs. timeout
|
obj = doAsyncRESTGetRequest(url, 5000); // 5 secs. timeout
|
||||||
QVariantMap oceanName = obj.value("ocean").toVariant().toMap();
|
QVariantMap oceanName = obj.value("ocean").toVariant().toMap();
|
||||||
if (oceanName["name"].isValid())
|
if (oceanName["name"].isValid())
|
||||||
taxonomy_set_category(&taxonomy, TC_OCEAN, qPrintable(oceanName["name"].toString()), taxonomy_origin::GEOCODED);
|
taxonomy_set_category(taxonomy, TC_OCEAN, oceanName["name"].toString().toStdString(), taxonomy_origin::GEOCODED);
|
||||||
|
|
||||||
// check the findNearbyPlaces API from geonames - that should give us country, state, city
|
// check the findNearbyPlaces API from geonames - that should give us country, state, city
|
||||||
url = geonamesNearbyPlaceNameURL.arg(getUiLanguage().section(QRegularExpression("[-_ ]"), 0, 0)).arg(latitude.udeg / 1000000.0).arg(longitude.udeg / 1000000.0);
|
url = geonamesNearbyPlaceNameURL.arg(getUiLanguage().section(QRegularExpression("[-_ ]"), 0, 0)).arg(latitude.udeg / 1000000.0).arg(longitude.udeg / 1000000.0);
|
||||||
@ -110,16 +110,16 @@ taxonomy_data reverseGeoLookup(degrees_t latitude, degrees_t longitude)
|
|||||||
for (int idx = TC_COUNTRY; idx < TC_NR_CATEGORIES; idx++) {
|
for (int idx = TC_COUNTRY; idx < TC_NR_CATEGORIES; idx++) {
|
||||||
if (firstData[taxonomy_api_names[idx]].isValid()) {
|
if (firstData[taxonomy_api_names[idx]].isValid()) {
|
||||||
QString value = firstData[taxonomy_api_names[idx]].toString();
|
QString value = firstData[taxonomy_api_names[idx]].toString();
|
||||||
taxonomy_set_category(&taxonomy, (taxonomy_category)idx, qPrintable(value), taxonomy_origin::GEOCODED);
|
taxonomy_set_category(taxonomy, (taxonomy_category)idx, value.toStdString(), taxonomy_origin::GEOCODED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const char *l3 = taxonomy_get_value(&taxonomy, TC_ADMIN_L3);
|
std::string l3 = taxonomy_get_value(taxonomy, TC_ADMIN_L3);
|
||||||
const char *lt = taxonomy_get_value(&taxonomy, TC_LOCALNAME);
|
std::string lt = taxonomy_get_value(taxonomy, TC_LOCALNAME);
|
||||||
if (empty_string(l3) && !empty_string(lt)) {
|
if (!l3.empty() && !lt.empty()) {
|
||||||
// basically this means we did get a local name (what we call town), but just like most places
|
// basically this means we did get a local name (what we call town), but just like most places
|
||||||
// we didn't get an adminName_3 - which in some regions is the actual city that town belongs to,
|
// we didn't get an adminName_3 - which in some regions is the actual city that town belongs to,
|
||||||
// then we copy the town into the city
|
// then we copy the town into the city
|
||||||
taxonomy_set_category(&taxonomy, TC_ADMIN_L3, lt, taxonomy_origin::GEOCOPIED);
|
taxonomy_set_category(taxonomy, TC_ADMIN_L3, lt, taxonomy_origin::GEOCOPIED);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
report_error("geonames.org did not provide reverse lookup information");
|
report_error("geonames.org did not provide reverse lookup information");
|
||||||
|
|||||||
27
core/divesitetable.h
Normal file
27
core/divesitetable.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#ifndef DIVESITETABLE_H
|
||||||
|
#define DIVESITETABLE_H
|
||||||
|
|
||||||
|
#include "owning_table.h"
|
||||||
|
#include "units.h"
|
||||||
|
|
||||||
|
struct dive_site;
|
||||||
|
int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2);
|
||||||
|
|
||||||
|
class dive_site_table : public sorted_owning_table<dive_site, &divesite_comp_uuid> {
|
||||||
|
public:
|
||||||
|
put_result register_site(std::unique_ptr<dive_site> site); // Creates or changes UUID if duplicate
|
||||||
|
dive_site *get_by_uuid(uint32_t uuid) const;
|
||||||
|
dive_site *alloc_or_get(uint32_t uuid);
|
||||||
|
dive_site *create(const std::string &name);
|
||||||
|
dive_site *create(const std::string &name, const location_t);
|
||||||
|
dive_site *find_or_create(const std::string &name);
|
||||||
|
dive_site *get_by_name(const std::string &name) const;
|
||||||
|
dive_site *get_by_gps(const location_t *) const;
|
||||||
|
dive_site *get_by_gps_and_name(const std::string &name, const location_t) const;
|
||||||
|
dive_site *get_by_gps_proximity(location_t, int distance) const;
|
||||||
|
dive_site *get_same(const struct dive_site &) const;
|
||||||
|
void purge_empty();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DIVESITETABLE_H
|
||||||
@ -1,8 +1,10 @@
|
|||||||
#include "downloadfromdcthread.h"
|
#include "downloadfromdcthread.h"
|
||||||
#include "core/errorhelper.h"
|
#include "core/errorhelper.h"
|
||||||
|
#include "core/format.h"
|
||||||
#include "core/libdivecomputer.h"
|
#include "core/libdivecomputer.h"
|
||||||
#include "core/qthelper.h"
|
#include "core/qthelper.h"
|
||||||
#include "core/range.h"
|
#include "core/range.h"
|
||||||
|
#include "core/uemis.h"
|
||||||
#include "core/settings/qPrefDiveComputer.h"
|
#include "core/settings/qPrefDiveComputer.h"
|
||||||
#include "core/divelist.h"
|
#include "core/divelist.h"
|
||||||
#if defined(Q_OS_ANDROID)
|
#if defined(Q_OS_ANDROID)
|
||||||
@ -15,16 +17,6 @@ static QHash<QString, QStringList> mobileProductList; // BT, BLE or FTDI support
|
|||||||
QMap<QString, dc_descriptor_t *> descriptorLookup;
|
QMap<QString, dc_descriptor_t *> descriptorLookup;
|
||||||
ConnectionListModel connectionListModel;
|
ConnectionListModel connectionListModel;
|
||||||
|
|
||||||
static QString str_error(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
const QString str = QString::vasprintf(fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void updateRememberedDCs()
|
static void updateRememberedDCs()
|
||||||
{
|
{
|
||||||
QString current = qPrefDiveComputer::vendor() + " - " + qPrefDiveComputer::product() + " - " + qPrefDiveComputer::device();
|
QString current = qPrefDiveComputer::vendor() + " - " + qPrefDiveComputer::product() + " - " + qPrefDiveComputer::device();
|
||||||
@ -89,7 +81,7 @@ void DownloadThread::run()
|
|||||||
auto internalData = m_data->internalData();
|
auto internalData = m_data->internalData();
|
||||||
internalData->descriptor = descriptorLookup[m_data->vendor().toLower() + m_data->product().toLower()];
|
internalData->descriptor = descriptorLookup[m_data->vendor().toLower() + m_data->product().toLower()];
|
||||||
internalData->log = &log;
|
internalData->log = &log;
|
||||||
internalData->btname = strdup(m_data->devBluetoothName().toUtf8());
|
internalData->btname = m_data->devBluetoothName().toStdString();
|
||||||
if (!internalData->descriptor) {
|
if (!internalData->descriptor) {
|
||||||
report_info("No download possible when DC type is unknown");
|
report_info("No download possible when DC type is unknown");
|
||||||
return;
|
return;
|
||||||
@ -105,27 +97,28 @@ void DownloadThread::run()
|
|||||||
|
|
||||||
report_info("Starting download from %s", qPrintable(getTransportString(transports)));
|
report_info("Starting download from %s", qPrintable(getTransportString(transports)));
|
||||||
report_info("downloading %s dives", internalData->force_download ? "all" : "only new");
|
report_info("downloading %s dives", internalData->force_download ? "all" : "only new");
|
||||||
clear_divelog(&log);
|
log.clear();
|
||||||
|
|
||||||
Q_ASSERT(internalData->log != nullptr);
|
Q_ASSERT(internalData->log != nullptr);
|
||||||
const char *errorText;
|
std::string errorText;
|
||||||
import_thread_cancelled = false;
|
import_thread_cancelled = false;
|
||||||
error.clear();
|
error.clear();
|
||||||
if (!strcmp(internalData->vendor, "Uemis"))
|
if (internalData->vendor == "Uemis")
|
||||||
errorText = do_uemis_import(internalData);
|
errorText = do_uemis_import(internalData);
|
||||||
else
|
else
|
||||||
errorText = do_libdivecomputer_import(internalData);
|
errorText = do_libdivecomputer_import(internalData);
|
||||||
if (errorText) {
|
if (!errorText.empty()) {
|
||||||
error = str_error(errorText, internalData->devname, internalData->vendor, internalData->product);
|
error = format_string_std(errorText.c_str(), internalData->devname.c_str(),
|
||||||
report_info("Finishing download thread: %s", qPrintable(error));
|
internalData->vendor.c_str(), internalData->product.c_str());
|
||||||
|
report_info("Finishing download thread: %s", error.c_str());
|
||||||
} else {
|
} else {
|
||||||
if (!log.dives->nr)
|
if (log.dives.empty())
|
||||||
error = tr("No new dives downloaded from dive computer");
|
error = tr("No new dives downloaded from dive computer").toStdString();
|
||||||
report_info("Finishing download thread: %d dives downloaded", log.dives->nr);
|
report_info("Finishing download thread: %d dives downloaded", static_cast<int>(log.dives.size()));
|
||||||
}
|
}
|
||||||
qPrefDiveComputer::set_vendor(internalData->vendor);
|
qPrefDiveComputer::set_vendor(internalData->vendor.c_str());
|
||||||
qPrefDiveComputer::set_product(internalData->product);
|
qPrefDiveComputer::set_product(internalData->product.c_str());
|
||||||
qPrefDiveComputer::set_device(internalData->devname);
|
qPrefDiveComputer::set_device(internalData->devname.c_str());
|
||||||
qPrefDiveComputer::set_device_name(m_data->devBluetoothName());
|
qPrefDiveComputer::set_device_name(m_data->devBluetoothName());
|
||||||
|
|
||||||
updateRememberedDCs();
|
updateRememberedDCs();
|
||||||
@ -209,7 +202,6 @@ void show_computer_list()
|
|||||||
|
|
||||||
DCDeviceData::DCDeviceData()
|
DCDeviceData::DCDeviceData()
|
||||||
{
|
{
|
||||||
memset(&data, 0, sizeof(data));
|
|
||||||
data.log = nullptr;
|
data.log = nullptr;
|
||||||
data.diveid = 0;
|
data.diveid = 0;
|
||||||
#if defined(BT_SUPPORT)
|
#if defined(BT_SUPPORT)
|
||||||
@ -241,9 +233,8 @@ QStringList DCDeviceData::getProductListFromVendor(const QString &vendor)
|
|||||||
return productList[vendor];
|
return productList[vendor];
|
||||||
}
|
}
|
||||||
|
|
||||||
int DCDeviceData::getMatchingAddress(const QString &vendor, const QString &product)
|
int DCDeviceData::getMatchingAddress(const QString &, const QString &product)
|
||||||
{
|
{
|
||||||
Q_UNUSED(vendor)
|
|
||||||
return connectionListModel.indexOf(product);
|
return connectionListModel.indexOf(product);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,17 +245,17 @@ DCDeviceData *DownloadThread::data()
|
|||||||
|
|
||||||
QString DCDeviceData::vendor() const
|
QString DCDeviceData::vendor() const
|
||||||
{
|
{
|
||||||
return data.vendor;
|
return QString::fromStdString(data.vendor);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DCDeviceData::product() const
|
QString DCDeviceData::product() const
|
||||||
{
|
{
|
||||||
return data.product;
|
return QString::fromStdString(data.product);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DCDeviceData::devName() const
|
QString DCDeviceData::devName() const
|
||||||
{
|
{
|
||||||
return data.devname;
|
return QString::fromStdString(data.devname);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DCDeviceData::devBluetoothName() const
|
QString DCDeviceData::devBluetoothName() const
|
||||||
@ -299,12 +290,12 @@ bool DCDeviceData::syncTime() const
|
|||||||
|
|
||||||
void DCDeviceData::setVendor(const QString &vendor)
|
void DCDeviceData::setVendor(const QString &vendor)
|
||||||
{
|
{
|
||||||
data.vendor = copy_qstring(vendor);
|
data.vendor = vendor.toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCDeviceData::setProduct(const QString &product)
|
void DCDeviceData::setProduct(const QString &product)
|
||||||
{
|
{
|
||||||
data.product = copy_qstring(product);
|
data.product = product.toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCDeviceData::setDevName(const QString &devName)
|
void DCDeviceData::setDevName(const QString &devName)
|
||||||
@ -321,11 +312,11 @@ void DCDeviceData::setDevName(const QString &devName)
|
|||||||
QString back = devName.mid(idx1 + 1, idx2 - idx1 - 1);
|
QString back = devName.mid(idx1 + 1, idx2 - idx1 - 1);
|
||||||
QString newDevName = back.indexOf(':') >= 0 ? back : front;
|
QString newDevName = back.indexOf(':') >= 0 ? back : front;
|
||||||
qWarning() << "Found invalid bluetooth device" << devName << "corrected to" << newDevName << ".";
|
qWarning() << "Found invalid bluetooth device" << devName << "corrected to" << newDevName << ".";
|
||||||
data.devname = copy_qstring(newDevName);
|
data.devname = newDevName.toStdString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.devname = copy_qstring(devName);
|
data.devname = devName.toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID)
|
#if defined(Q_OS_ANDROID)
|
||||||
|
|||||||
@ -74,7 +74,7 @@ public:
|
|||||||
void run() override;
|
void run() override;
|
||||||
|
|
||||||
DCDeviceData *data();
|
DCDeviceData *data();
|
||||||
QString error;
|
std::string error;
|
||||||
struct divelog log;
|
struct divelog log;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
591
core/equipment.c
591
core/equipment.c
@ -1,591 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
#ifdef __clang__
|
|
||||||
// Clang has a bug on zero-initialization of C structs.
|
|
||||||
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* equipment.c */
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include "equipment.h"
|
|
||||||
#include "gettext.h"
|
|
||||||
#include "dive.h"
|
|
||||||
#include "divelist.h"
|
|
||||||
#include "divelog.h"
|
|
||||||
#include "errorhelper.h"
|
|
||||||
#include "pref.h"
|
|
||||||
#include "subsurface-string.h"
|
|
||||||
#include "table.h"
|
|
||||||
|
|
||||||
/* Warning: this has strange semantics for C-code! Not the weightsystem object
|
|
||||||
* is freed, but the data it references. The object itself is passed in by value.
|
|
||||||
* This is due to the fact how the table macros work.
|
|
||||||
*/
|
|
||||||
void free_weightsystem(weightsystem_t ws)
|
|
||||||
{
|
|
||||||
free((void *)ws.description);
|
|
||||||
ws.description = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_cylinder(cylinder_t c)
|
|
||||||
{
|
|
||||||
free((void *)c.type.description);
|
|
||||||
c.type.description = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d)
|
|
||||||
{
|
|
||||||
clear_weightsystem_table(d);
|
|
||||||
for (int i = 0; i < s->nr; i++)
|
|
||||||
add_cloned_weightsystem(d, s->weightsystems[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void copy_cylinders(const struct cylinder_table *s, struct cylinder_table *d)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
clear_cylinder_table(d);
|
|
||||||
for (i = 0; i < s->nr; i++)
|
|
||||||
add_cloned_cylinder(d, s->cylinders[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_tank_info(struct tank_info info)
|
|
||||||
{
|
|
||||||
free((void *)info.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* weightsystem table functions */
|
|
||||||
//static MAKE_GET_IDX(weightsystem_table, weightsystem_t, weightsystems)
|
|
||||||
static MAKE_GROW_TABLE(weightsystem_table, weightsystem_t, weightsystems)
|
|
||||||
//static MAKE_GET_INSERTION_INDEX(weightsystem_table, weightsystem_t, weightsystems, weightsystem_less_than)
|
|
||||||
MAKE_ADD_TO(weightsystem_table, weightsystem_t, weightsystems)
|
|
||||||
static MAKE_REMOVE_FROM(weightsystem_table, weightsystems)
|
|
||||||
//MAKE_SORT(weightsystem_table, weightsystem_t, weightsystems, comp_weightsystems)
|
|
||||||
//MAKE_REMOVE(weightsystem_table, weightsystem_t, weightsystem)
|
|
||||||
MAKE_CLEAR_TABLE(weightsystem_table, weightsystems, weightsystem)
|
|
||||||
|
|
||||||
/* cylinder table functions */
|
|
||||||
//static MAKE_GET_IDX(cylinder_table, cylinder_t, cylinders)
|
|
||||||
static MAKE_GROW_TABLE(cylinder_table, cylinder_t, cylinders)
|
|
||||||
//static MAKE_GET_INSERTION_INDEX(cylinder_table, cylinder_t, cylinders, cylinder_less_than)
|
|
||||||
static MAKE_ADD_TO(cylinder_table, cylinder_t, cylinders)
|
|
||||||
static MAKE_REMOVE_FROM(cylinder_table, cylinders)
|
|
||||||
//MAKE_SORT(cylinder_table, cylinder_t, cylinders, comp_cylinders)
|
|
||||||
//MAKE_REMOVE(cylinder_table, cylinder_t, cylinder)
|
|
||||||
MAKE_CLEAR_TABLE(cylinder_table, cylinders, cylinder)
|
|
||||||
|
|
||||||
/* tank_info table functions */
|
|
||||||
static MAKE_GROW_TABLE(tank_info_table, tank_info_t, infos)
|
|
||||||
static MAKE_ADD_TO(tank_info_table, tank_info_t, infos)
|
|
||||||
//static MAKE_REMOVE_FROM(tank_info_table, infos)
|
|
||||||
MAKE_CLEAR_TABLE(tank_info_table, infos, tank_info)
|
|
||||||
|
|
||||||
const char *cylinderuse_text[NUM_GAS_USE] = {
|
|
||||||
QT_TRANSLATE_NOOP("gettextFromC", "OC-gas"), QT_TRANSLATE_NOOP("gettextFromC", "diluent"), QT_TRANSLATE_NOOP("gettextFromC", "oxygen"), QT_TRANSLATE_NOOP("gettextFromC", "not used")
|
|
||||||
};
|
|
||||||
|
|
||||||
enum cylinderuse cylinderuse_from_text(const char *text)
|
|
||||||
{
|
|
||||||
for (enum cylinderuse i = 0; i < NUM_GAS_USE; i++) {
|
|
||||||
if (same_string(text, cylinderuse_text[i]) || same_string(text, translate("gettextFromC", cylinderuse_text[i])))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return (enum cylinderuse)-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a metric or an imperial tank info structure. Copies the passed-in string. */
|
|
||||||
void add_tank_info_metric(struct tank_info_table *table, const char *name, int ml, int bar)
|
|
||||||
{
|
|
||||||
struct tank_info info = { strdup(name), .ml = ml, .bar = bar };
|
|
||||||
add_to_tank_info_table(table, table->nr, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_tank_info_imperial(struct tank_info_table *table, const char *name, int cuft, int psi)
|
|
||||||
{
|
|
||||||
struct tank_info info = { strdup(name), .cuft = cuft, .psi = psi };
|
|
||||||
add_to_tank_info_table(table, table->nr, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct tank_info *get_tank_info(struct tank_info_table *table, const char *name)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < table->nr; ++i) {
|
|
||||||
if (same_string(table->infos[i].name, name))
|
|
||||||
return &table->infos[i];
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void set_tank_info_data(struct tank_info_table *table, const char *name, volume_t size, pressure_t working_pressure)
|
|
||||||
{
|
|
||||||
struct tank_info *info = get_tank_info(table, name);
|
|
||||||
if (info) {
|
|
||||||
if (info->ml != 0 || info->bar != 0) {
|
|
||||||
info->bar = working_pressure.mbar / 1000;
|
|
||||||
info->ml = size.mliter;
|
|
||||||
} else {
|
|
||||||
info->psi = lrint(to_PSI(working_pressure));
|
|
||||||
info->cuft = lrint(ml_to_cuft(size.mliter) * mbar_to_atm(working_pressure.mbar));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Metric is a better choice as the volume is independent of the working pressure
|
|
||||||
add_tank_info_metric(table, name, size.mliter, working_pressure.mbar / 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void extract_tank_info(const struct tank_info *info, volume_t *size, pressure_t *working_pressure)
|
|
||||||
{
|
|
||||||
working_pressure->mbar = info->bar != 0 ? info->bar * 1000 : psi_to_mbar(info->psi);
|
|
||||||
if (info->ml != 0)
|
|
||||||
size->mliter = info->ml;
|
|
||||||
else if (working_pressure->mbar != 0)
|
|
||||||
size->mliter = lrint(cuft_to_l(info->cuft) * 1000 / mbar_to_atm(working_pressure->mbar));
|
|
||||||
}
|
|
||||||
|
|
||||||
extern bool get_tank_info_data(struct tank_info_table *table, const char *name, volume_t *size, pressure_t *working_pressure)
|
|
||||||
{
|
|
||||||
struct tank_info *info = get_tank_info(table, name);
|
|
||||||
if (info) {
|
|
||||||
extract_tank_info(info, size, working_pressure);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* placeholders for a few functions that we need to redesign for the Qt UI */
|
|
||||||
void add_cylinder_description(const cylinder_type_t *type)
|
|
||||||
{
|
|
||||||
const char *desc = type->description;
|
|
||||||
if (empty_string(desc))
|
|
||||||
return;
|
|
||||||
for (int i = 0; i < tank_info_table.nr; i++) {
|
|
||||||
if (strcmp(tank_info_table.infos[i].name, desc) == 0)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
add_tank_info_metric(&tank_info_table, desc, type->size.mliter,
|
|
||||||
type->workingpressure.mbar / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_weightsystem_description(const weightsystem_t *weightsystem)
|
|
||||||
{
|
|
||||||
const char *desc;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
desc = weightsystem->description;
|
|
||||||
if (!desc)
|
|
||||||
return;
|
|
||||||
for (i = 0; i < MAX_WS_INFO && ws_info[i].name != NULL; i++) {
|
|
||||||
if (same_string(ws_info[i].name, desc)) {
|
|
||||||
ws_info[i].grams = weightsystem->weight.grams;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i < MAX_WS_INFO) {
|
|
||||||
// FIXME: leaked on exit
|
|
||||||
ws_info[i].name = strdup(desc);
|
|
||||||
ws_info[i].grams = weightsystem->weight.grams;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ws_info_t *get_weightsystem_description(const char *name)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < MAX_WS_INFO && ws_info[i].name != NULL; i++) {
|
|
||||||
// Also finds translated names (TODO: should only consider non-user items).
|
|
||||||
if (same_string(ws_info[i].name, name) ||
|
|
||||||
same_string(translate("gettextFromC", ws_info[i].name), name))
|
|
||||||
return &ws_info[i];
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
weightsystem_t clone_weightsystem(weightsystem_t ws)
|
|
||||||
{
|
|
||||||
weightsystem_t res = { ws.weight, copy_string(ws.description), ws.auto_filled };
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a clone of a weightsystem to the end of a weightsystem table.
|
|
||||||
* Cloned means that the description-string is copied. */
|
|
||||||
void add_cloned_weightsystem(struct weightsystem_table *t, weightsystem_t ws)
|
|
||||||
{
|
|
||||||
add_to_weightsystem_table(t, t->nr, clone_weightsystem(ws));
|
|
||||||
}
|
|
||||||
|
|
||||||
cylinder_t clone_cylinder(cylinder_t cyl)
|
|
||||||
{
|
|
||||||
cylinder_t res = cyl;
|
|
||||||
res.type.description = copy_string(res.type.description);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl)
|
|
||||||
{
|
|
||||||
add_to_cylinder_table(t, idx, cyl);
|
|
||||||
/* FIXME: This is a horrible hack: we make sure that at the end of
|
|
||||||
* every single cylinder table there is an empty cylinder that can
|
|
||||||
* be used by the planner as "surface air" cylinder. Fix this.
|
|
||||||
*/
|
|
||||||
add_to_cylinder_table(t, t->nr, empty_cylinder);
|
|
||||||
t->nr--;
|
|
||||||
t->cylinders[t->nr].cylinder_use = NOT_USED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a clone of a cylinder to the end of a cylinder table.
|
|
||||||
* Cloned means that the description-string is copied. */
|
|
||||||
void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl)
|
|
||||||
{
|
|
||||||
add_cylinder(t, t->nr, clone_cylinder(cyl));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool same_weightsystem(weightsystem_t w1, weightsystem_t w2)
|
|
||||||
{
|
|
||||||
return w1.weight.grams == w2.weight.grams &&
|
|
||||||
same_string(w1.description, w2.description);
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_gas_string(struct gasmix gasmix, char *text, int len)
|
|
||||||
{
|
|
||||||
if (gasmix_is_air(gasmix))
|
|
||||||
snprintf(text, len, "%s", translate("gettextFromC", "air"));
|
|
||||||
else if (get_he(gasmix) == 0 && get_o2(gasmix) < 1000)
|
|
||||||
snprintf(text, len, translate("gettextFromC", "EAN%d"), (get_o2(gasmix) + 5) / 10);
|
|
||||||
else if (get_he(gasmix) == 0 && get_o2(gasmix) == 1000)
|
|
||||||
snprintf(text, len, "%s", translate("gettextFromC", "oxygen"));
|
|
||||||
else
|
|
||||||
snprintf(text, len, "(%d/%d)", (get_o2(gasmix) + 5) / 10, (get_he(gasmix) + 5) / 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns a static char buffer - only good for immediate use by printf etc */
|
|
||||||
const char *gasname(struct gasmix gasmix)
|
|
||||||
{
|
|
||||||
static char gas[64];
|
|
||||||
get_gas_string(gasmix, gas, sizeof(gas));
|
|
||||||
return gas;
|
|
||||||
}
|
|
||||||
|
|
||||||
int gas_volume(const cylinder_t *cyl, pressure_t p)
|
|
||||||
{
|
|
||||||
double bar = p.mbar / 1000.0;
|
|
||||||
double z_factor = gas_compressibility_factor(cyl->gasmix, bar);
|
|
||||||
return lrint(cyl->type.size.mliter * bar_to_atm(bar) / z_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table *cylinders)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int best = -1, score = INT_MAX;
|
|
||||||
|
|
||||||
for (i = 0; i < cylinders->nr; i++) {
|
|
||||||
const cylinder_t *match;
|
|
||||||
int distance;
|
|
||||||
|
|
||||||
match = cylinders->cylinders + i;
|
|
||||||
distance = gasmix_distance(mix, match->gasmix);
|
|
||||||
if (distance >= score)
|
|
||||||
continue;
|
|
||||||
best = i;
|
|
||||||
score = distance;
|
|
||||||
}
|
|
||||||
return best;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We hardcode the most common standard cylinders,
|
|
||||||
* we should pick up any other names from the dive
|
|
||||||
* logs directly.
|
|
||||||
*/
|
|
||||||
static void add_default_tank_infos(struct tank_info_table *table)
|
|
||||||
{
|
|
||||||
/* Size-only metric cylinders */
|
|
||||||
add_tank_info_metric(table, "10.0ℓ", 10000, 0);
|
|
||||||
add_tank_info_metric(table, "11.1ℓ", 11100, 0);
|
|
||||||
|
|
||||||
/* Most common AL cylinders */
|
|
||||||
add_tank_info_imperial(table, "AL40", 40, 3000);
|
|
||||||
add_tank_info_imperial(table, "AL50", 50, 3000);
|
|
||||||
add_tank_info_imperial(table, "AL63", 63, 3000);
|
|
||||||
add_tank_info_imperial(table, "AL72", 72, 3000);
|
|
||||||
add_tank_info_imperial(table, "AL80", 80, 3000);
|
|
||||||
add_tank_info_imperial(table, "AL100", 100, 3300);
|
|
||||||
|
|
||||||
/* Metric AL cylinders */
|
|
||||||
add_tank_info_metric(table, "ALU7", 7000, 200);
|
|
||||||
|
|
||||||
/* Somewhat common LP steel cylinders */
|
|
||||||
add_tank_info_imperial(table, "LP85", 85, 2640);
|
|
||||||
add_tank_info_imperial(table, "LP95", 95, 2640);
|
|
||||||
add_tank_info_imperial(table, "LP108", 108, 2640);
|
|
||||||
add_tank_info_imperial(table, "LP121", 121, 2640);
|
|
||||||
|
|
||||||
/* Somewhat common HP steel cylinders */
|
|
||||||
add_tank_info_imperial(table, "HP65", 65, 3442);
|
|
||||||
add_tank_info_imperial(table, "HP80", 80, 3442);
|
|
||||||
add_tank_info_imperial(table, "HP100", 100, 3442);
|
|
||||||
add_tank_info_imperial(table, "HP117", 117, 3442);
|
|
||||||
add_tank_info_imperial(table, "HP119", 119, 3442);
|
|
||||||
add_tank_info_imperial(table, "HP130", 130, 3442);
|
|
||||||
|
|
||||||
/* Common European steel cylinders */
|
|
||||||
add_tank_info_metric(table, "3ℓ 232 bar", 3000, 232);
|
|
||||||
add_tank_info_metric(table, "3ℓ 300 bar", 3000, 300);
|
|
||||||
add_tank_info_metric(table, "10ℓ 200 bar", 10000, 200);
|
|
||||||
add_tank_info_metric(table, "10ℓ 232 bar", 10000, 232);
|
|
||||||
add_tank_info_metric(table, "10ℓ 300 bar", 10000, 300);
|
|
||||||
add_tank_info_metric(table, "12ℓ 200 bar", 12000, 200);
|
|
||||||
add_tank_info_metric(table, "12ℓ 232 bar", 12000, 232);
|
|
||||||
add_tank_info_metric(table, "12ℓ 300 bar", 12000, 300);
|
|
||||||
add_tank_info_metric(table, "15ℓ 200 bar", 15000, 200);
|
|
||||||
add_tank_info_metric(table, "15ℓ 232 bar", 15000, 232);
|
|
||||||
add_tank_info_metric(table, "D7 300 bar", 14000, 300);
|
|
||||||
add_tank_info_metric(table, "D8.5 232 bar", 17000, 232);
|
|
||||||
add_tank_info_metric(table, "D12 232 bar", 24000, 232);
|
|
||||||
add_tank_info_metric(table, "D13 232 bar", 26000, 232);
|
|
||||||
add_tank_info_metric(table, "D15 232 bar", 30000, 232);
|
|
||||||
add_tank_info_metric(table, "D16 232 bar", 32000, 232);
|
|
||||||
add_tank_info_metric(table, "D18 232 bar", 36000, 232);
|
|
||||||
add_tank_info_metric(table, "D20 232 bar", 40000, 232);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct tank_info_table tank_info_table;
|
|
||||||
void reset_tank_info_table(struct tank_info_table *table)
|
|
||||||
{
|
|
||||||
clear_tank_info_table(table);
|
|
||||||
if (prefs.display_default_tank_infos)
|
|
||||||
add_default_tank_infos(table);
|
|
||||||
|
|
||||||
/* Add cylinders from dive list */
|
|
||||||
for (int i = 0; i < divelog.dives->nr; ++i) {
|
|
||||||
const struct dive *dive = divelog.dives->dives[i];
|
|
||||||
for (int j = 0; j < dive->cylinders.nr; j++) {
|
|
||||||
const cylinder_t *cyl = get_cylinder(dive, j);
|
|
||||||
add_cylinder_description(&cyl->type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We hardcode the most common weight system types
|
|
||||||
* This is a bit odd as the weight system types don't usually encode weight
|
|
||||||
*/
|
|
||||||
struct ws_info_t ws_info[MAX_WS_INFO] = {
|
|
||||||
{ QT_TRANSLATE_NOOP("gettextFromC", "integrated"), 0 },
|
|
||||||
{ QT_TRANSLATE_NOOP("gettextFromC", "belt"), 0 },
|
|
||||||
{ QT_TRANSLATE_NOOP("gettextFromC", "ankle"), 0 },
|
|
||||||
{ QT_TRANSLATE_NOOP("gettextFromC", "backplate"), 0 },
|
|
||||||
{ QT_TRANSLATE_NOOP("gettextFromC", "clip-on"), 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
void remove_cylinder(struct dive *dive, int idx)
|
|
||||||
{
|
|
||||||
remove_from_cylinder_table(&dive->cylinders, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_weightsystem(struct dive *dive, int idx)
|
|
||||||
{
|
|
||||||
remove_from_weightsystem_table(&dive->weightsystems, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ws is cloned.
|
|
||||||
void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws)
|
|
||||||
{
|
|
||||||
if (idx < 0 || idx >= dive->weightsystems.nr)
|
|
||||||
return;
|
|
||||||
free_weightsystem(dive->weightsystems.weightsystems[idx]);
|
|
||||||
dive->weightsystems.weightsystems[idx] = clone_weightsystem(ws);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* when planning a dive we need to make sure that all cylinders have a sane depth assigned
|
|
||||||
* and if we are tracking gas consumption the pressures need to be reset to start = end = workingpressure */
|
|
||||||
void reset_cylinders(struct dive *dive, bool track_gas)
|
|
||||||
{
|
|
||||||
pressure_t decopo2 = {.mbar = prefs.decopo2};
|
|
||||||
|
|
||||||
for (int i = 0; i < dive->cylinders.nr; i++) {
|
|
||||||
cylinder_t *cyl = get_cylinder(dive, i);
|
|
||||||
if (cyl->depth.mm == 0) /* if the gas doesn't give a mod, calculate based on prefs */
|
|
||||||
cyl->depth = gas_mod(cyl->gasmix, decopo2, dive, M_OR_FT(3,10));
|
|
||||||
if (track_gas)
|
|
||||||
cyl->start.mbar = cyl->end.mbar = cyl->type.workingpressure.mbar;
|
|
||||||
cyl->gas_used.mliter = 0;
|
|
||||||
cyl->deco_gas_used.mliter = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void copy_cylinder_type(const cylinder_t *s, cylinder_t *d)
|
|
||||||
{
|
|
||||||
free_cylinder(*d);
|
|
||||||
d->type = s->type;
|
|
||||||
d->type.description = s->type.description ? strdup(s->type.description) : NULL;
|
|
||||||
d->gasmix = s->gasmix;
|
|
||||||
d->depth = s->depth;
|
|
||||||
d->cylinder_use = s->cylinder_use;
|
|
||||||
d->manually_added = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy the equipment data part of the cylinders but keep pressures */
|
|
||||||
void copy_cylinder_types(const struct dive *s, struct dive *d)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
if (!s || !d)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (i = 0; i < s->cylinders.nr && i < d->cylinders.nr; i++)
|
|
||||||
copy_cylinder_type(get_cylinder(s, i), get_cylinder(d, i));
|
|
||||||
|
|
||||||
for ( ; i < s->cylinders.nr; i++)
|
|
||||||
add_cloned_cylinder(&d->cylinders, *get_cylinder(s, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
cylinder_t *add_empty_cylinder(struct cylinder_table *t)
|
|
||||||
{
|
|
||||||
cylinder_t cyl = empty_cylinder;
|
|
||||||
cyl.type.description = strdup("");
|
|
||||||
add_cylinder(t, t->nr, cyl);
|
|
||||||
return &t->cylinders[t->nr - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* access to cylinders is controlled by two functions:
|
|
||||||
* - get_cylinder() returns the cylinder of a dive and supposes that
|
|
||||||
* the cylinder with the given index exists. If it doesn't, an error
|
|
||||||
* message is printed and NULL returned.
|
|
||||||
* - get_or_create_cylinder() creates an empty cylinder if it doesn't exist.
|
|
||||||
* Multiple cylinders might be created if the index is bigger than the
|
|
||||||
* number of existing cylinders
|
|
||||||
*/
|
|
||||||
cylinder_t *get_cylinder(const struct dive *d, int idx)
|
|
||||||
{
|
|
||||||
/* FIXME: The planner uses a dummy cylinder one past the official number of cylinders
|
|
||||||
* in the table to mark no-cylinder surface interavals. This is horrendous. Fix ASAP. */
|
|
||||||
// if (idx < 0 || idx >= d->cylinders.nr) {
|
|
||||||
if (idx < 0 || idx >= d->cylinders.nr + 1 || idx >= d->cylinders.allocated) {
|
|
||||||
report_info("Warning: accessing invalid cylinder %d (%d existing)", idx, d->cylinders.nr);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return &d->cylinders.cylinders[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
cylinder_t *get_or_create_cylinder(struct dive *d, int idx)
|
|
||||||
{
|
|
||||||
if (idx < 0) {
|
|
||||||
report_info("Warning: accessing invalid cylinder %d", idx);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
while (idx >= d->cylinders.nr)
|
|
||||||
add_empty_cylinder(&d->cylinders);
|
|
||||||
return &d->cylinders.cylinders[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if a default cylinder is set, use that */
|
|
||||||
void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl)
|
|
||||||
{
|
|
||||||
const char *cyl_name = prefs.default_cylinder;
|
|
||||||
pressure_t pO2 = {.mbar = lrint(prefs.modpO2 * 1000.0)};
|
|
||||||
|
|
||||||
if (!cyl_name)
|
|
||||||
return;
|
|
||||||
for (int i = 0; i < tank_info_table.nr; ++i) {
|
|
||||||
struct tank_info *ti = &tank_info_table.infos[i];
|
|
||||||
if (strcmp(ti->name, cyl_name) == 0) {
|
|
||||||
cyl->type.description = strdup(ti->name);
|
|
||||||
if (ti->ml) {
|
|
||||||
cyl->type.size.mliter = ti->ml;
|
|
||||||
cyl->type.workingpressure.mbar = ti->bar * 1000;
|
|
||||||
} else {
|
|
||||||
cyl->type.workingpressure.mbar = psi_to_mbar(ti->psi);
|
|
||||||
if (ti->psi)
|
|
||||||
cyl->type.size.mliter = lrint(cuft_to_l(ti->cuft) * 1000 / bar_to_atm(psi_to_bar(ti->psi)));
|
|
||||||
}
|
|
||||||
// MOD of air
|
|
||||||
cyl->depth = gas_mod(cyl->gasmix, pO2, dive, 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cylinder_t create_new_cylinder(const struct dive *d)
|
|
||||||
{
|
|
||||||
cylinder_t cyl = empty_cylinder;
|
|
||||||
fill_default_cylinder(d, &cyl);
|
|
||||||
cyl.start = cyl.type.workingpressure;
|
|
||||||
cyl.cylinder_use = OC_GAS;
|
|
||||||
return cyl;
|
|
||||||
}
|
|
||||||
|
|
||||||
cylinder_t create_new_manual_cylinder(const struct dive *d)
|
|
||||||
{
|
|
||||||
cylinder_t cyl = create_new_cylinder(d);
|
|
||||||
cyl.manually_added = true;
|
|
||||||
return cyl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_default_cylinder(struct dive *d)
|
|
||||||
{
|
|
||||||
// Only add if there are no cylinders yet
|
|
||||||
if (d->cylinders.nr > 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
cylinder_t cyl;
|
|
||||||
if (!empty_string(prefs.default_cylinder)) {
|
|
||||||
cyl = create_new_cylinder(d);
|
|
||||||
} else {
|
|
||||||
cyl = empty_cylinder;
|
|
||||||
// roughly an AL80
|
|
||||||
cyl.type.description = strdup(translate("gettextFromC", "unknown"));
|
|
||||||
cyl.type.size.mliter = 11100;
|
|
||||||
cyl.type.workingpressure.mbar = 207000;
|
|
||||||
}
|
|
||||||
add_cylinder(&d->cylinders, 0, cyl);
|
|
||||||
reset_cylinders(d, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool show_cylinder(const struct dive *d, int i)
|
|
||||||
{
|
|
||||||
if (is_cylinder_used(d, i))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const cylinder_t *cyl = &d->cylinders.cylinders[i];
|
|
||||||
if (cyl->start.mbar || cyl->sample_start.mbar ||
|
|
||||||
cyl->end.mbar || cyl->sample_end.mbar)
|
|
||||||
return true;
|
|
||||||
if (cyl->manually_added)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The cylinder has some data, but none of it is very interesting,
|
|
||||||
* it has no pressures and no gas switches. Do we want to show it?
|
|
||||||
*/
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The unused cylinders at the end of the cylinder list are hidden. */
|
|
||||||
int first_hidden_cylinder(const struct dive *d)
|
|
||||||
{
|
|
||||||
int res = d->cylinders.nr;
|
|
||||||
while (res > 0 && !show_cylinder(d, res - 1))
|
|
||||||
--res;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_CYL
|
|
||||||
void dump_cylinders(struct dive *dive, bool verbose)
|
|
||||||
{
|
|
||||||
printf("Cylinder list:\n");
|
|
||||||
for (int i = 0; i < dive->cylinders; i++) {
|
|
||||||
cylinder_t *cyl = get_cylinder(dive, i);
|
|
||||||
|
|
||||||
printf("%02d: Type %s, %3.1fl, %3.0fbar\n", i, cyl->type.description, cyl->type.size.mliter / 1000.0, cyl->type.workingpressure.mbar / 1000.0);
|
|
||||||
printf(" Gasmix O2 %2.0f%% He %2.0f%%\n", cyl->gasmix.o2.permille / 10.0, cyl->gasmix.he.permille / 10.0);
|
|
||||||
printf(" Pressure Start %3.0fbar End %3.0fbar Sample start %3.0fbar Sample end %3.0fbar\n", cyl->start.mbar / 1000.0, cyl->end.mbar / 1000.0, cyl->sample_start.mbar / 1000.0, cyl->sample_end.mbar / 1000.0);
|
|
||||||
if (verbose) {
|
|
||||||
printf(" Depth %3.0fm\n", cyl->depth.mm / 1000.0);
|
|
||||||
printf(" Added %s\n", (cyl->manually_added ? "manually" : ""));
|
|
||||||
printf(" Gas used Bottom %5.0fl Deco %5.0fl\n", cyl->gas_used.mliter / 1000.0, cyl->deco_gas_used.mliter / 1000.0);
|
|
||||||
printf(" Use %d\n", cyl->cylinder_use);
|
|
||||||
printf(" Bestmix %s %s\n", (cyl->bestmix_o2 ? "O2" : " "), (cyl->bestmix_he ? "He" : " "));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
516
core/equipment.cpp
Normal file
516
core/equipment.cpp
Normal file
@ -0,0 +1,516 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#ifdef __clang__
|
||||||
|
// Clang has a bug on zero-initialization of C structs.
|
||||||
|
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* equipment.cpp */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include "equipment.h"
|
||||||
|
#include "gettext.h"
|
||||||
|
#include "dive.h"
|
||||||
|
#include "divelist.h"
|
||||||
|
#include "divelog.h"
|
||||||
|
#include "errorhelper.h"
|
||||||
|
#include "pref.h"
|
||||||
|
#include "range.h"
|
||||||
|
#include "subsurface-string.h"
|
||||||
|
|
||||||
|
cylinder_t::cylinder_t() = default;
|
||||||
|
cylinder_t::~cylinder_t() = default;
|
||||||
|
|
||||||
|
static cylinder_t make_surface_air_cylinder()
|
||||||
|
{
|
||||||
|
cylinder_t res;
|
||||||
|
res.cylinder_use = NOT_USED;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
static const cylinder_t surface_air_cylinder = make_surface_air_cylinder();
|
||||||
|
|
||||||
|
static void warn_index(size_t i, size_t max)
|
||||||
|
{
|
||||||
|
if (i >= max + 1) {
|
||||||
|
report_info("Warning: accessing invalid cylinder %lu (%lu existing)",
|
||||||
|
static_cast<unsigned long>(i), static_cast<unsigned long>(max));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cylinder_t &cylinder_table::operator[](size_t i)
|
||||||
|
{
|
||||||
|
warn_index(i, size());
|
||||||
|
return i < size() ? std::vector<cylinder_t>::operator[](i)
|
||||||
|
: const_cast<cylinder_t &>(surface_air_cylinder);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cylinder_t &cylinder_table::operator[](size_t i) const
|
||||||
|
{
|
||||||
|
warn_index(i, size());
|
||||||
|
return i < size() ? std::vector<cylinder_t>::operator[](i)
|
||||||
|
: surface_air_cylinder;
|
||||||
|
}
|
||||||
|
|
||||||
|
weightsystem_t::weightsystem_t() = default;
|
||||||
|
weightsystem_t::~weightsystem_t() = default;
|
||||||
|
weightsystem_t::weightsystem_t(weight_t w, std::string desc, bool auto_filled)
|
||||||
|
: weight(w), description(std::move(desc)), auto_filled(auto_filled)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cylinderuse_text[NUM_GAS_USE] = {
|
||||||
|
QT_TRANSLATE_NOOP("gettextFromC", "OC-gas"), QT_TRANSLATE_NOOP("gettextFromC", "diluent"), QT_TRANSLATE_NOOP("gettextFromC", "oxygen"), QT_TRANSLATE_NOOP("gettextFromC", "not used")
|
||||||
|
};
|
||||||
|
|
||||||
|
enum cylinderuse cylinderuse_from_text(const char *text)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < static_cast<int>(NUM_GAS_USE); i++) {
|
||||||
|
if (same_string(text, cylinderuse_text[i]) || same_string(text, translate("gettextFromC", cylinderuse_text[i])))
|
||||||
|
return static_cast<enum cylinderuse>(i);
|
||||||
|
}
|
||||||
|
return static_cast<enum cylinderuse>(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a metric or an imperial tank info structure. Copies the passed-in string. */
|
||||||
|
static void add_tank_info_metric(std::vector<tank_info> &table, const std::string &name, int ml, int bar)
|
||||||
|
{
|
||||||
|
tank_info_table.push_back(tank_info{ name, .ml = ml, .bar = bar });
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_tank_info_imperial(std::vector<tank_info> &table, const std::string &name, int cuft, int psi)
|
||||||
|
{
|
||||||
|
tank_info_table.push_back(tank_info{ name, .cuft = cuft, .psi = psi });
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tank_info *get_tank_info(std::vector<tank_info> &table, const std::string &name)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(table.begin(), table.end(), [&name](const tank_info &info)
|
||||||
|
{ return info.name == name; });
|
||||||
|
return it != table.end() ? &*it : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_tank_info_data(std::vector<tank_info> &table, const std::string &name, volume_t size, pressure_t working_pressure)
|
||||||
|
{
|
||||||
|
struct tank_info *info = get_tank_info(table, name);
|
||||||
|
if (info) {
|
||||||
|
if (info->ml != 0 || info->bar != 0) {
|
||||||
|
info->bar = working_pressure.mbar / 1000;
|
||||||
|
info->ml = size.mliter;
|
||||||
|
} else {
|
||||||
|
info->psi = lrint(to_PSI(working_pressure));
|
||||||
|
info->cuft = lrint(ml_to_cuft(size.mliter) * mbar_to_atm(working_pressure.mbar));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Metric is a better choice as the volume is independent of the working pressure
|
||||||
|
add_tank_info_metric(table, name, size.mliter, working_pressure.mbar / 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<volume_t, pressure_t> extract_tank_info(const struct tank_info &info)
|
||||||
|
{
|
||||||
|
pressure_t working_pressure {
|
||||||
|
static_cast<int32_t>(info.bar != 0 ? info.bar * 1000 : psi_to_mbar(info.psi))
|
||||||
|
};
|
||||||
|
volume_t size { 0 };
|
||||||
|
if (info.ml != 0)
|
||||||
|
size.mliter = info.ml;
|
||||||
|
else if (working_pressure.mbar != 0)
|
||||||
|
size.mliter = lrint(cuft_to_l(info.cuft) * 1000 / mbar_to_atm(working_pressure.mbar));
|
||||||
|
return std::make_pair(size, working_pressure);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<volume_t, pressure_t> get_tank_info_data(const std::vector<tank_info> &table, const std::string &name)
|
||||||
|
{
|
||||||
|
// Here, we would need a const version of get_tank_info().
|
||||||
|
auto it = std::find_if(table.begin(), table.end(), [&name](const tank_info &info)
|
||||||
|
{ return info.name == name; });
|
||||||
|
return it != table.end() ? extract_tank_info(*it)
|
||||||
|
: std::make_pair(volume_t{0}, pressure_t{0});
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_cylinder_description(const cylinder_type_t &type)
|
||||||
|
{
|
||||||
|
const std::string &desc = type.description;
|
||||||
|
if (desc.empty())
|
||||||
|
return;
|
||||||
|
if (std::any_of(tank_info_table.begin(), tank_info_table.end(),
|
||||||
|
[&desc](const tank_info &info) { return info.name == desc; }))
|
||||||
|
return;
|
||||||
|
add_tank_info_metric(tank_info_table, desc, type.size.mliter,
|
||||||
|
type.workingpressure.mbar / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_weightsystem_description(const weightsystem_t &weightsystem)
|
||||||
|
{
|
||||||
|
if (weightsystem.description.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto it = std::find_if(ws_info_table.begin(), ws_info_table.end(),
|
||||||
|
[&weightsystem](const ws_info &info)
|
||||||
|
{ return info.name == weightsystem.description; });
|
||||||
|
if (it != ws_info_table.end()) {
|
||||||
|
it->weight = weightsystem.weight;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ws_info_table.push_back(ws_info { std::string(weightsystem.description), weightsystem.weight });
|
||||||
|
}
|
||||||
|
|
||||||
|
weight_t get_weightsystem_weight(const std::string &name)
|
||||||
|
{
|
||||||
|
// Also finds translated names (TODO: should only consider non-user items).
|
||||||
|
auto it = std::find_if(ws_info_table.begin(), ws_info_table.end(),
|
||||||
|
[&name](const ws_info &info)
|
||||||
|
{ return info.name == name || translate("gettextFromC", info.name.c_str()) == name; });
|
||||||
|
return it != ws_info_table.end() ? it->weight : weight_t();
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl)
|
||||||
|
{
|
||||||
|
t->insert(t->begin() + idx, std::move(cyl));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool same_weightsystem(weightsystem_t w1, weightsystem_t w2)
|
||||||
|
{
|
||||||
|
return w1.weight.grams == w2.weight.grams &&
|
||||||
|
w1.description == w2.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_gas_string(struct gasmix gasmix, char *text, int len)
|
||||||
|
{
|
||||||
|
if (gasmix_is_air(gasmix))
|
||||||
|
snprintf(text, len, "%s", translate("gettextFromC", "air"));
|
||||||
|
else if (get_he(gasmix) == 0 && get_o2(gasmix) < 1000)
|
||||||
|
snprintf(text, len, translate("gettextFromC", "EAN%d"), (get_o2(gasmix) + 5) / 10);
|
||||||
|
else if (get_he(gasmix) == 0 && get_o2(gasmix) == 1000)
|
||||||
|
snprintf(text, len, "%s", translate("gettextFromC", "oxygen"));
|
||||||
|
else
|
||||||
|
snprintf(text, len, "(%d/%d)", (get_o2(gasmix) + 5) / 10, (get_he(gasmix) + 5) / 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a static char buffer - only good for immediate use by printf etc */
|
||||||
|
const char *gasname(struct gasmix gasmix)
|
||||||
|
{
|
||||||
|
static char gas[64];
|
||||||
|
get_gas_string(gasmix, gas, sizeof(gas));
|
||||||
|
return gas;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gas_volume(const cylinder_t *cyl, pressure_t p)
|
||||||
|
{
|
||||||
|
double bar = p.mbar / 1000.0;
|
||||||
|
double z_factor = gas_compressibility_factor(cyl->gasmix, bar);
|
||||||
|
return lrint(cyl->type.size.mliter * bar_to_atm(bar) / z_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders)
|
||||||
|
{
|
||||||
|
int best = -1, score = INT_MAX;
|
||||||
|
|
||||||
|
for (auto [i, match]: enumerated_range(cylinders)) {
|
||||||
|
int distance = gasmix_distance(mix, match.gasmix);
|
||||||
|
if (distance >= score)
|
||||||
|
continue;
|
||||||
|
best = i;
|
||||||
|
score = distance;
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We hardcode the most common standard cylinders,
|
||||||
|
* we should pick up any other names from the dive
|
||||||
|
* logs directly.
|
||||||
|
*/
|
||||||
|
static void add_default_tank_infos(std::vector<tank_info> &table)
|
||||||
|
{
|
||||||
|
/* Size-only metric cylinders */
|
||||||
|
add_tank_info_metric(table, "10.0ℓ", 10000, 0);
|
||||||
|
add_tank_info_metric(table, "11.1ℓ", 11100, 0);
|
||||||
|
|
||||||
|
/* Most common AL cylinders */
|
||||||
|
add_tank_info_imperial(table, "AL40", 40, 3000);
|
||||||
|
add_tank_info_imperial(table, "AL50", 50, 3000);
|
||||||
|
add_tank_info_imperial(table, "AL63", 63, 3000);
|
||||||
|
add_tank_info_imperial(table, "AL72", 72, 3000);
|
||||||
|
add_tank_info_imperial(table, "AL80", 80, 3000);
|
||||||
|
add_tank_info_imperial(table, "AL100", 100, 3300);
|
||||||
|
|
||||||
|
/* Metric AL cylinders */
|
||||||
|
add_tank_info_metric(table, "ALU7", 7000, 200);
|
||||||
|
|
||||||
|
/* Somewhat common LP steel cylinders */
|
||||||
|
add_tank_info_imperial(table, "LP85", 85, 2640);
|
||||||
|
add_tank_info_imperial(table, "LP95", 95, 2640);
|
||||||
|
add_tank_info_imperial(table, "LP108", 108, 2640);
|
||||||
|
add_tank_info_imperial(table, "LP121", 121, 2640);
|
||||||
|
|
||||||
|
/* Somewhat common HP steel cylinders */
|
||||||
|
add_tank_info_imperial(table, "HP65", 65, 3442);
|
||||||
|
add_tank_info_imperial(table, "HP80", 80, 3442);
|
||||||
|
add_tank_info_imperial(table, "HP100", 100, 3442);
|
||||||
|
add_tank_info_imperial(table, "HP117", 117, 3442);
|
||||||
|
add_tank_info_imperial(table, "HP119", 119, 3442);
|
||||||
|
add_tank_info_imperial(table, "HP130", 130, 3442);
|
||||||
|
|
||||||
|
/* Common European steel cylinders */
|
||||||
|
add_tank_info_metric(table, "3ℓ 232 bar", 3000, 232);
|
||||||
|
add_tank_info_metric(table, "3ℓ 300 bar", 3000, 300);
|
||||||
|
add_tank_info_metric(table, "10ℓ 200 bar", 10000, 200);
|
||||||
|
add_tank_info_metric(table, "10ℓ 232 bar", 10000, 232);
|
||||||
|
add_tank_info_metric(table, "10ℓ 300 bar", 10000, 300);
|
||||||
|
add_tank_info_metric(table, "12ℓ 200 bar", 12000, 200);
|
||||||
|
add_tank_info_metric(table, "12ℓ 232 bar", 12000, 232);
|
||||||
|
add_tank_info_metric(table, "12ℓ 300 bar", 12000, 300);
|
||||||
|
add_tank_info_metric(table, "15ℓ 200 bar", 15000, 200);
|
||||||
|
add_tank_info_metric(table, "15ℓ 232 bar", 15000, 232);
|
||||||
|
add_tank_info_metric(table, "D7 300 bar", 14000, 300);
|
||||||
|
add_tank_info_metric(table, "D8.5 232 bar", 17000, 232);
|
||||||
|
add_tank_info_metric(table, "D12 232 bar", 24000, 232);
|
||||||
|
add_tank_info_metric(table, "D13 232 bar", 26000, 232);
|
||||||
|
add_tank_info_metric(table, "D15 232 bar", 30000, 232);
|
||||||
|
add_tank_info_metric(table, "D16 232 bar", 32000, 232);
|
||||||
|
add_tank_info_metric(table, "D18 232 bar", 36000, 232);
|
||||||
|
add_tank_info_metric(table, "D20 232 bar", 40000, 232);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<tank_info> tank_info_table;
|
||||||
|
void reset_tank_info_table(std::vector<tank_info> &table)
|
||||||
|
{
|
||||||
|
table.clear();
|
||||||
|
if (prefs.display_default_tank_infos)
|
||||||
|
add_default_tank_infos(table);
|
||||||
|
|
||||||
|
/* Add cylinders from dive list */
|
||||||
|
for (auto &dive: divelog.dives) {
|
||||||
|
for (auto &cyl: dive->cylinders)
|
||||||
|
add_cylinder_description(cyl.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We hardcode the most common weight system types
|
||||||
|
* This is a bit odd as the weight system types don't usually encode weight
|
||||||
|
*/
|
||||||
|
struct std::vector<ws_info> ws_info_table = {
|
||||||
|
{ QT_TRANSLATE_NOOP("gettextFromC", "integrated"), weight_t() },
|
||||||
|
{ QT_TRANSLATE_NOOP("gettextFromC", "belt"), weight_t() },
|
||||||
|
{ QT_TRANSLATE_NOOP("gettextFromC", "ankle"), weight_t() },
|
||||||
|
{ QT_TRANSLATE_NOOP("gettextFromC", "backplate"), weight_t() },
|
||||||
|
{ QT_TRANSLATE_NOOP("gettextFromC", "clip-on"), weight_t() },
|
||||||
|
};
|
||||||
|
|
||||||
|
void remove_cylinder(struct dive *dive, int idx)
|
||||||
|
{
|
||||||
|
dive->cylinders.erase(dive->cylinders.begin() + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_weightsystem(struct dive *dive, int idx)
|
||||||
|
{
|
||||||
|
dive->weightsystems.erase(dive->weightsystems.begin() + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_to_weightsystem_table(weightsystem_table *table, int idx, weightsystem_t ws)
|
||||||
|
{
|
||||||
|
idx = std::clamp(idx, 0, static_cast<int>(table->size()));
|
||||||
|
table->insert(table->begin() + idx, std::move(ws));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws)
|
||||||
|
{
|
||||||
|
if (idx < 0 || static_cast<size_t>(idx) >= dive->weightsystems.size())
|
||||||
|
return;
|
||||||
|
dive->weightsystems[idx] = std::move(ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* when planning a dive we need to make sure that all cylinders have a sane depth assigned
|
||||||
|
* and if we are tracking gas consumption the pressures need to be reset to start = end = workingpressure */
|
||||||
|
void reset_cylinders(struct dive *dive, bool track_gas)
|
||||||
|
{
|
||||||
|
pressure_t decopo2 = {.mbar = prefs.decopo2};
|
||||||
|
|
||||||
|
for (cylinder_t &cyl: dive->cylinders) {
|
||||||
|
if (cyl.depth.mm == 0) /* if the gas doesn't give a mod, calculate based on prefs */
|
||||||
|
cyl.depth = gas_mod(cyl.gasmix, decopo2, dive, M_OR_FT(3,10));
|
||||||
|
if (track_gas)
|
||||||
|
cyl.start.mbar = cyl.end.mbar = cyl.type.workingpressure.mbar;
|
||||||
|
cyl.gas_used.mliter = 0;
|
||||||
|
cyl.deco_gas_used.mliter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy_cylinder_type(const cylinder_t &s, cylinder_t &d)
|
||||||
|
{
|
||||||
|
d.type = s.type;
|
||||||
|
d.gasmix = s.gasmix;
|
||||||
|
d.depth = s.depth;
|
||||||
|
d.cylinder_use = s.cylinder_use;
|
||||||
|
d.manually_added = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy the equipment data part of the cylinders but keep pressures */
|
||||||
|
void copy_cylinder_types(const struct dive *s, struct dive *d)
|
||||||
|
{
|
||||||
|
if (!s || !d)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < s->cylinders.size() && i < d->cylinders.size(); i++)
|
||||||
|
copy_cylinder_type(s->cylinders[i], d->cylinders[i]);
|
||||||
|
|
||||||
|
for (size_t i = d->cylinders.size(); i < s->cylinders.size(); i++)
|
||||||
|
d->cylinders.push_back(s->cylinders[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
cylinder_t *add_empty_cylinder(struct cylinder_table *t)
|
||||||
|
{
|
||||||
|
t->emplace_back();
|
||||||
|
return &t->back();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* access to cylinders is controlled by two functions:
|
||||||
|
* - get_cylinder() returns the cylinder of a dive and supposes that
|
||||||
|
* the cylinder with the given index exists. If it doesn't, an error
|
||||||
|
* message is printed and the "surface air" cylinder returned.
|
||||||
|
* (NOTE: this MUST not be written into!).
|
||||||
|
* - get_or_create_cylinder() creates an empty cylinder if it doesn't exist.
|
||||||
|
* Multiple cylinders might be created if the index is bigger than the
|
||||||
|
* number of existing cylinders
|
||||||
|
*/
|
||||||
|
cylinder_t *get_cylinder(struct dive *d, int idx)
|
||||||
|
{
|
||||||
|
return &d->cylinders[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
const cylinder_t *get_cylinder(const struct dive *d, int idx)
|
||||||
|
{
|
||||||
|
return &d->cylinders[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
cylinder_t *get_or_create_cylinder(struct dive *d, int idx)
|
||||||
|
{
|
||||||
|
if (idx < 0) {
|
||||||
|
report_info("Warning: accessing invalid cylinder %d", idx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
while (static_cast<size_t>(idx) >= d->cylinders.size())
|
||||||
|
add_empty_cylinder(&d->cylinders);
|
||||||
|
return &d->cylinders[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if a default cylinder is set, use that */
|
||||||
|
void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl)
|
||||||
|
{
|
||||||
|
const char *cyl_name = prefs.default_cylinder;
|
||||||
|
pressure_t pO2 = {.mbar = static_cast<int>(lrint(prefs.modpO2 * 1000.0))};
|
||||||
|
|
||||||
|
if (!cyl_name)
|
||||||
|
return;
|
||||||
|
for (auto &ti: tank_info_table) {
|
||||||
|
if (ti.name == cyl_name) {
|
||||||
|
cyl->type.description = ti.name;
|
||||||
|
if (ti.ml) {
|
||||||
|
cyl->type.size.mliter = ti.ml;
|
||||||
|
cyl->type.workingpressure.mbar = ti.bar * 1000;
|
||||||
|
} else {
|
||||||
|
cyl->type.workingpressure.mbar = psi_to_mbar(ti.psi);
|
||||||
|
if (ti.psi)
|
||||||
|
cyl->type.size.mliter = lrint(cuft_to_l(ti.cuft) * 1000 / bar_to_atm(psi_to_bar(ti.psi)));
|
||||||
|
}
|
||||||
|
// MOD of air
|
||||||
|
cyl->depth = gas_mod(cyl->gasmix, pO2, dive, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cylinder_t default_cylinder(const struct dive *d)
|
||||||
|
{
|
||||||
|
cylinder_t res;
|
||||||
|
fill_default_cylinder(d, &res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
cylinder_t create_new_cylinder(const struct dive *d)
|
||||||
|
{
|
||||||
|
cylinder_t cyl = default_cylinder(d);
|
||||||
|
cyl.start = cyl.type.workingpressure;
|
||||||
|
cyl.cylinder_use = OC_GAS;
|
||||||
|
return cyl;
|
||||||
|
}
|
||||||
|
|
||||||
|
cylinder_t create_new_manual_cylinder(const struct dive *d)
|
||||||
|
{
|
||||||
|
cylinder_t cyl = create_new_cylinder(d);
|
||||||
|
cyl.manually_added = true;
|
||||||
|
return cyl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_default_cylinder(struct dive *d)
|
||||||
|
{
|
||||||
|
// Only add if there are no cylinders yet
|
||||||
|
if (!d->cylinders.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
cylinder_t cyl;
|
||||||
|
if (!empty_string(prefs.default_cylinder)) {
|
||||||
|
cyl = create_new_cylinder(d);
|
||||||
|
} else {
|
||||||
|
// roughly an AL80
|
||||||
|
cyl.type.description = translate("gettextFromC", "unknown");
|
||||||
|
cyl.type.size.mliter = 11100;
|
||||||
|
cyl.type.workingpressure.mbar = 207000;
|
||||||
|
}
|
||||||
|
add_cylinder(&d->cylinders, 0, cyl);
|
||||||
|
reset_cylinders(d, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool show_cylinder(const struct dive *d, int i)
|
||||||
|
{
|
||||||
|
if (is_cylinder_used(d, i))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const cylinder_t &cyl = d->cylinders[i];
|
||||||
|
if (cyl.start.mbar || cyl.sample_start.mbar ||
|
||||||
|
cyl.end.mbar || cyl.sample_end.mbar)
|
||||||
|
return true;
|
||||||
|
if (cyl.manually_added)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The cylinder has some data, but none of it is very interesting,
|
||||||
|
* it has no pressures and no gas switches. Do we want to show it?
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The unused cylinders at the end of the cylinder list are hidden. */
|
||||||
|
int first_hidden_cylinder(const struct dive *d)
|
||||||
|
{
|
||||||
|
size_t res = d->cylinders.size();
|
||||||
|
while (res > 0 && !show_cylinder(d, res - 1))
|
||||||
|
--res;
|
||||||
|
return static_cast<int>(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_CYL
|
||||||
|
void dump_cylinders(struct dive *dive, bool verbose)
|
||||||
|
{
|
||||||
|
printf("Cylinder list:\n");
|
||||||
|
for (int i = 0; i < dive->cylinders; i++) {
|
||||||
|
cylinder_t *cyl = get_cylinder(dive, i);
|
||||||
|
|
||||||
|
printf("%02d: Type %s, %3.1fl, %3.0fbar\n", i, cyl->type.description.c_str(), cyl->type.size.mliter / 1000.0, cyl->type.workingpressure.mbar / 1000.0);
|
||||||
|
printf(" Gasmix O2 %2.0f%% He %2.0f%%\n", cyl->gasmix.o2.permille / 10.0, cyl->gasmix.he.permille / 10.0);
|
||||||
|
printf(" Pressure Start %3.0fbar End %3.0fbar Sample start %3.0fbar Sample end %3.0fbar\n", cyl->start.mbar / 1000.0, cyl->end.mbar / 1000.0, cyl->sample_start.mbar / 1000.0, cyl->sample_end.mbar / 1000.0);
|
||||||
|
if (verbose) {
|
||||||
|
printf(" Depth %3.0fm\n", cyl->depth.mm / 1000.0);
|
||||||
|
printf(" Added %s\n", (cyl->manually_added ? "manually" : ""));
|
||||||
|
printf(" Gas used Bottom %5.0fl Deco %5.0fl\n", cyl->gas_used.mliter / 1000.0, cyl->deco_gas_used.mliter / 1000.0);
|
||||||
|
printf(" Use %d\n", cyl->cylinder_use);
|
||||||
|
printf(" Bestmix %s %s\n", (cyl->bestmix_o2 ? "O2" : " "), (cyl->bestmix_he ? "He" : " "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
144
core/equipment.h
144
core/equipment.h
@ -4,95 +4,85 @@
|
|||||||
|
|
||||||
#include "gas.h"
|
#include "gas.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#include <memory>
|
||||||
extern "C" {
|
#include <string>
|
||||||
#endif
|
#include <vector>
|
||||||
|
|
||||||
struct dive;
|
struct dive;
|
||||||
|
|
||||||
enum cylinderuse {OC_GAS, DILUENT, OXYGEN, NOT_USED, NUM_GAS_USE}; // The different uses for cylinders
|
enum cylinderuse {OC_GAS, DILUENT, OXYGEN, NOT_USED, NUM_GAS_USE}; // The different uses for cylinders
|
||||||
extern const char *cylinderuse_text[NUM_GAS_USE];
|
extern const char *cylinderuse_text[NUM_GAS_USE];
|
||||||
|
|
||||||
typedef struct
|
struct cylinder_type_t
|
||||||
{
|
{
|
||||||
volume_t size;
|
volume_t size;
|
||||||
pressure_t workingpressure;
|
pressure_t workingpressure;
|
||||||
const char *description; /* "LP85", "AL72", "AL80", "HP100+" or whatever */
|
std::string description; /* "LP85", "AL72", "AL80", "HP100+" or whatever */
|
||||||
} cylinder_type_t;
|
};
|
||||||
|
|
||||||
typedef struct
|
struct cylinder_t
|
||||||
{
|
{
|
||||||
cylinder_type_t type;
|
cylinder_type_t type;
|
||||||
struct gasmix gasmix;
|
struct gasmix gasmix = gasmix_air;
|
||||||
pressure_t start, end, sample_start, sample_end;
|
pressure_t start, end, sample_start, sample_end;
|
||||||
depth_t depth;
|
depth_t depth;
|
||||||
bool manually_added;
|
bool manually_added = false;
|
||||||
volume_t gas_used;
|
volume_t gas_used;
|
||||||
volume_t deco_gas_used;
|
volume_t deco_gas_used;
|
||||||
enum cylinderuse cylinder_use;
|
enum cylinderuse cylinder_use = OC_GAS;
|
||||||
bool bestmix_o2;
|
bool bestmix_o2 = false;
|
||||||
bool bestmix_he;
|
bool bestmix_he = false;
|
||||||
} cylinder_t;
|
|
||||||
|
|
||||||
static const cylinder_t empty_cylinder = { { { 0 }, { 0 }, (const char *)0}, { { 0 }, { 0 } } , { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, false, { 0 }, { 0 }, OC_GAS, false, false };
|
cylinder_t();
|
||||||
|
~cylinder_t();
|
||||||
/* Table of cylinders. Attention: this stores cylinders,
|
|
||||||
* *not* pointers to cylinders. This has two crucial consequences:
|
|
||||||
* 1) Pointers to cylinders are not stable. They may be
|
|
||||||
* invalidated if the table is reallocated.
|
|
||||||
* 2) add_cylinder(), etc. take ownership of the
|
|
||||||
* cylinder. Notably of the description string. */
|
|
||||||
struct cylinder_table {
|
|
||||||
int nr, allocated;
|
|
||||||
cylinder_t *cylinders;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct
|
/* Table of cylinders.
|
||||||
|
* This is a crazy class: it is basically a std::vector<>, but overrides
|
||||||
|
* the [] accessor functions and allows out-of-bound accesses.
|
||||||
|
* This is used in the planner, which uses "max_index + 1" for the
|
||||||
|
* surface air cylinder.
|
||||||
|
* Note: an out-of-bound access returns a reference to an object with
|
||||||
|
* static linkage that MUST NOT be written into.
|
||||||
|
* Yes, this is all very mad, but it grew historically.
|
||||||
|
*/
|
||||||
|
struct cylinder_table : public std::vector<cylinder_t> {
|
||||||
|
cylinder_t &operator[](size_t i);
|
||||||
|
const cylinder_t &operator[](size_t i) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct weightsystem_t
|
||||||
{
|
{
|
||||||
weight_t weight;
|
weight_t weight;
|
||||||
const char *description; /* "integrated", "belt", "ankle" */
|
std::string description; /* "integrated", "belt", "ankle" */
|
||||||
bool auto_filled; /* weight was automatically derived from the type */
|
bool auto_filled = false; /* weight was automatically derived from the type */
|
||||||
} weightsystem_t;
|
|
||||||
|
|
||||||
static const weightsystem_t empty_weightsystem = { { 0 }, 0, false };
|
weightsystem_t();
|
||||||
|
weightsystem_t(weight_t w, std::string desc, bool auto_filled);
|
||||||
/* Table of weightsystems. Attention: this stores weightsystems,
|
~weightsystem_t();
|
||||||
* *not* pointers * to weightsystems. This has two crucial
|
|
||||||
* consequences:
|
|
||||||
* 1) Pointers to weightsystems are not stable. They may be
|
|
||||||
* invalidated if the table is reallocated.
|
|
||||||
* 2) add_to_weightsystem_table(), etc. takes ownership of the
|
|
||||||
* weightsystem. Notably of the description string */
|
|
||||||
struct weightsystem_table {
|
|
||||||
int nr, allocated;
|
|
||||||
weightsystem_t *weightsystems;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_WS_INFO (100)
|
/* Table of weightsystems. Attention: this stores weightsystems,
|
||||||
|
* *not* pointers * to weightsystems. Therefore pointers to
|
||||||
|
* weightsystems are *not* stable.
|
||||||
|
*/
|
||||||
|
using weightsystem_table = std::vector<weightsystem_t>;
|
||||||
|
|
||||||
extern enum cylinderuse cylinderuse_from_text(const char *text);
|
extern enum cylinderuse cylinderuse_from_text(const char *text);
|
||||||
extern void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d);
|
|
||||||
extern void copy_cylinders(const struct cylinder_table *s, struct cylinder_table *d);
|
|
||||||
extern weightsystem_t clone_weightsystem(weightsystem_t ws);
|
|
||||||
extern void free_weightsystem(weightsystem_t ws);
|
|
||||||
extern void copy_cylinder_types(const struct dive *s, struct dive *d);
|
extern void copy_cylinder_types(const struct dive *s, struct dive *d);
|
||||||
extern void add_cloned_weightsystem(struct weightsystem_table *t, weightsystem_t ws);
|
|
||||||
extern cylinder_t clone_cylinder(cylinder_t cyl);
|
|
||||||
extern void free_cylinder(cylinder_t cyl);
|
|
||||||
extern cylinder_t *add_empty_cylinder(struct cylinder_table *t);
|
extern cylinder_t *add_empty_cylinder(struct cylinder_table *t);
|
||||||
extern void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl);
|
extern cylinder_t *get_cylinder(struct dive *d, int idx);
|
||||||
extern cylinder_t *get_cylinder(const struct dive *d, int idx);
|
extern const cylinder_t *get_cylinder(const struct dive *d, int idx);
|
||||||
extern cylinder_t *get_or_create_cylinder(struct dive *d, int idx);
|
extern cylinder_t *get_or_create_cylinder(struct dive *d, int idx);
|
||||||
extern void add_cylinder_description(const cylinder_type_t *);
|
|
||||||
extern void add_weightsystem_description(const weightsystem_t *);
|
|
||||||
extern bool same_weightsystem(weightsystem_t w1, weightsystem_t w2);
|
extern bool same_weightsystem(weightsystem_t w1, weightsystem_t w2);
|
||||||
extern void remove_cylinder(struct dive *dive, int idx);
|
extern void remove_cylinder(struct dive *dive, int idx);
|
||||||
extern void remove_weightsystem(struct dive *dive, int idx);
|
extern void remove_weightsystem(struct dive *dive, int idx);
|
||||||
extern void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws);
|
extern void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws);
|
||||||
extern void reset_cylinders(struct dive *dive, bool track_gas);
|
extern void reset_cylinders(struct dive *dive, bool track_gas);
|
||||||
extern int gas_volume(const cylinder_t *cyl, pressure_t p); /* Volume in mliter of a cylinder at pressure 'p' */
|
extern int gas_volume(const cylinder_t *cyl, pressure_t p); /* Volume in mliter of a cylinder at pressure 'p' */
|
||||||
extern int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table *cylinders);
|
extern int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders);
|
||||||
extern void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl); /* dive is needed to fill out MOD, which depends on salinity. */
|
extern void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl); /* dive is needed to fill out MOD, which depends on salinity. */
|
||||||
|
extern cylinder_t default_cylinder(const struct dive *d);
|
||||||
extern cylinder_t create_new_manual_cylinder(const struct dive *dive); /* dive is needed to fill out MOD, which depends on salinity. */
|
extern cylinder_t create_new_manual_cylinder(const struct dive *dive); /* dive is needed to fill out MOD, which depends on salinity. */
|
||||||
extern void add_default_cylinder(struct dive *dive);
|
extern void add_default_cylinder(struct dive *dive);
|
||||||
extern int first_hidden_cylinder(const struct dive *d);
|
extern int first_hidden_cylinder(const struct dive *d);
|
||||||
@ -101,44 +91,34 @@ extern void dump_cylinders(struct dive *dive, bool verbose);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Weightsystem table functions */
|
/* Weightsystem table functions */
|
||||||
extern void clear_weightsystem_table(struct weightsystem_table *);
|
extern void add_to_weightsystem_table(weightsystem_table *, int idx, weightsystem_t ws);
|
||||||
extern void add_to_weightsystem_table(struct weightsystem_table *, int idx, weightsystem_t ws);
|
|
||||||
|
|
||||||
/* Cylinder table functions */
|
/* Cylinder table functions */
|
||||||
extern void clear_cylinder_table(struct cylinder_table *);
|
|
||||||
extern void add_cylinder(struct cylinder_table *, int idx, cylinder_t cyl);
|
extern void add_cylinder(struct cylinder_table *, int idx, cylinder_t cyl);
|
||||||
|
extern void append_cylinder(struct cylinder_table *, cylinder_t cyl);
|
||||||
|
|
||||||
void get_gas_string(struct gasmix gasmix, char *text, int len);
|
void get_gas_string(struct gasmix gasmix, char *text, int len);
|
||||||
const char *gasname(struct gasmix gasmix);
|
const char *gasname(struct gasmix gasmix);
|
||||||
|
|
||||||
typedef struct tank_info {
|
struct ws_info {
|
||||||
const char *name;
|
std::string name;
|
||||||
|
weight_t weight;
|
||||||
|
};
|
||||||
|
extern std::vector<ws_info> ws_info_table;
|
||||||
|
extern weight_t get_weightsystem_weight(const std::string &name); // returns 0 if not found
|
||||||
|
extern void add_weightsystem_description(const weightsystem_t &);
|
||||||
|
|
||||||
|
struct tank_info {
|
||||||
|
std::string name;
|
||||||
int cuft, ml, psi, bar;
|
int cuft, ml, psi, bar;
|
||||||
} tank_info_t;
|
|
||||||
|
|
||||||
struct tank_info_table {
|
|
||||||
int nr, allocated;
|
|
||||||
struct tank_info *infos;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct tank_info_table tank_info_table;
|
extern std::vector<tank_info> tank_info_table;
|
||||||
extern void reset_tank_info_table(struct tank_info_table *table);
|
extern struct tank_info *get_tank_info(std::vector<tank_info> &table, const std::string &name);
|
||||||
extern void clear_tank_info_table(struct tank_info_table *table);
|
extern void set_tank_info_data(std::vector<tank_info> &table, const std::string &name, volume_t size, pressure_t working_pressure);
|
||||||
extern void add_tank_info_metric(struct tank_info_table *table, const char *name, int ml, int bar);
|
extern std::pair<volume_t, pressure_t> extract_tank_info(const struct tank_info &info);
|
||||||
extern void add_tank_info_imperial(struct tank_info_table *table, const char *name, int cuft, int psi);
|
extern std::pair<volume_t, pressure_t> get_tank_info_data(const std::vector<tank_info> &table, const std::string &name);
|
||||||
extern void extract_tank_info(const struct tank_info *info, volume_t *size, pressure_t *working_pressure);
|
extern void add_cylinder_description(const cylinder_type_t &);
|
||||||
extern bool get_tank_info_data(struct tank_info_table *table, const char *name, volume_t *size, pressure_t *pressure);
|
extern void reset_tank_info_table(std::vector<tank_info> &table);
|
||||||
extern void set_tank_info_data(struct tank_info_table *table, const char *name, volume_t size, pressure_t working_pressure);
|
|
||||||
|
|
||||||
struct ws_info_t {
|
|
||||||
const char *name;
|
|
||||||
int grams;
|
|
||||||
};
|
|
||||||
extern struct ws_info_t ws_info[MAX_WS_INFO];
|
|
||||||
extern struct ws_info_t *get_weightsystem_description(const char *name);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // EQUIPMENT_H
|
#endif // EQUIPMENT_H
|
||||||
|
|||||||
@ -1,50 +1,44 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#ifdef __clang__
|
|
||||||
// Clang has a bug on zero-initialization of C structs.
|
|
||||||
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
|
||||||
#endif
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include "errorhelper.h"
|
#include "errorhelper.h"
|
||||||
#include "membuffer.h"
|
#include "format.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
#if !defined(Q_OS_ANDROID) && !defined(__ANDROID__)
|
#if !defined(Q_OS_ANDROID) && !defined(__ANDROID__)
|
||||||
#define LOG_MSG(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
|
#define LOG_MSG(fmt, s) fprintf(stderr, fmt, s)
|
||||||
#else
|
#else
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
#define LOG_MSG(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "Subsurface", fmt, ##__VA_ARGS__);
|
#define LOG_MSG(fmt, s) __android_log_print(ANDROID_LOG_INFO, "Subsurface", fmt, s);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define VA_BUF(b, fmt) do { va_list args; va_start(args, fmt); put_vformat(b, fmt, args); va_end(args); } while (0)
|
|
||||||
|
|
||||||
int verbose;
|
int verbose;
|
||||||
|
|
||||||
void report_info(const char *fmt, ...)
|
void report_info(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
struct membufferpp buf;
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
VA_BUF(&buf, fmt);
|
std::string s = vformat_string_std(fmt, args);
|
||||||
strip_mb(&buf);
|
va_end(args);
|
||||||
LOG_MSG("INFO: %s\n", mb_cstring(&buf));
|
LOG_MSG("INFO: %s\n", s.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void (*error_cb)(char *) = NULL;
|
static void (*error_cb)(std::string) = NULL;
|
||||||
|
|
||||||
int report_error(const char *fmt, ...)
|
int report_error(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
struct membufferpp buf;
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
VA_BUF(&buf, fmt);
|
std::string s = vformat_string_std(fmt, args);
|
||||||
strip_mb(&buf);
|
va_end(args);
|
||||||
LOG_MSG("ERROR: %s\n", mb_cstring(&buf));
|
LOG_MSG("ERROR: %s\n", s.c_str());
|
||||||
|
|
||||||
/* if there is no error callback registered, don't produce errors */
|
/* if there is no error callback registered, don't produce errors */
|
||||||
if (!error_cb)
|
if (error_cb)
|
||||||
return -1;
|
error_cb(std::move(s));
|
||||||
error_cb(detach_cstring(&buf));
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_error_cb(void(*cb)(char *))
|
void set_error_cb(void(*cb)(std::string))
|
||||||
{
|
{
|
||||||
error_cb = cb;
|
error_cb = cb;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,9 +4,7 @@
|
|||||||
|
|
||||||
// error reporting functions
|
// error reporting functions
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#include <string>
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#define __printf(x, y) __attribute__((__format__(__printf__, x, y)))
|
#define __printf(x, y) __attribute__((__format__(__printf__, x, y)))
|
||||||
@ -17,11 +15,6 @@ extern "C" {
|
|||||||
extern int verbose;
|
extern int verbose;
|
||||||
extern int __printf(1, 2) report_error(const char *fmt, ...);
|
extern int __printf(1, 2) report_error(const char *fmt, ...);
|
||||||
extern void __printf(1, 2) report_info(const char *fmt, ...);
|
extern void __printf(1, 2) report_info(const char *fmt, ...);
|
||||||
extern void set_error_cb(void(*cb)(char *)); // Callback takes ownership of passed string
|
extern void set_error_cb(void(*cb)(std::string s)); // Callback takes ownership of passed string
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
118
core/event.c
118
core/event.c
@ -1,118 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
#include "event.h"
|
|
||||||
#include "eventtype.h"
|
|
||||||
#include "subsurface-string.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
int event_is_gaschange(const struct event *ev)
|
|
||||||
{
|
|
||||||
return ev->type == SAMPLE_EVENT_GASCHANGE ||
|
|
||||||
ev->type == SAMPLE_EVENT_GASCHANGE2;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool event_is_divemodechange(const struct event *ev)
|
|
||||||
{
|
|
||||||
return same_string(ev->name, "modechange");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct event *clone_event(const struct event *src_ev)
|
|
||||||
{
|
|
||||||
struct event *ev;
|
|
||||||
if (!src_ev)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
size_t size = sizeof(*src_ev) + strlen(src_ev->name) + 1;
|
|
||||||
ev = (struct event*) malloc(size);
|
|
||||||
if (!ev)
|
|
||||||
exit(1);
|
|
||||||
memcpy(ev, src_ev, size);
|
|
||||||
ev->next = NULL;
|
|
||||||
|
|
||||||
return ev;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_events(struct event *ev)
|
|
||||||
{
|
|
||||||
while (ev) {
|
|
||||||
struct event *next = ev->next;
|
|
||||||
free(ev);
|
|
||||||
ev = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct event *create_event(unsigned int time, int type, int flags, int value, const char *name)
|
|
||||||
{
|
|
||||||
int gas_index = -1;
|
|
||||||
struct event *ev;
|
|
||||||
unsigned int size, len = strlen(name);
|
|
||||||
|
|
||||||
size = sizeof(*ev) + len + 1;
|
|
||||||
ev = malloc(size);
|
|
||||||
if (!ev)
|
|
||||||
return NULL;
|
|
||||||
memset(ev, 0, size);
|
|
||||||
memcpy(ev->name, name, len);
|
|
||||||
ev->time.seconds = time;
|
|
||||||
ev->type = type;
|
|
||||||
ev->flags = flags;
|
|
||||||
ev->value = value;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Expand the events into a sane format. Currently
|
|
||||||
* just gas switches
|
|
||||||
*/
|
|
||||||
switch (type) {
|
|
||||||
case SAMPLE_EVENT_GASCHANGE2:
|
|
||||||
/* High 16 bits are He percentage */
|
|
||||||
ev->gas.mix.he.permille = (value >> 16) * 10;
|
|
||||||
|
|
||||||
/* Extension to the GASCHANGE2 format: cylinder index in 'flags' */
|
|
||||||
/* TODO: verify that gas_index < num_cylinders. */
|
|
||||||
if (flags > 0)
|
|
||||||
gas_index = flags-1;
|
|
||||||
/* Fallthrough */
|
|
||||||
case SAMPLE_EVENT_GASCHANGE:
|
|
||||||
/* Low 16 bits are O2 percentage */
|
|
||||||
ev->gas.mix.o2.permille = (value & 0xffff) * 10;
|
|
||||||
ev->gas.index = gas_index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
remember_event_type(ev);
|
|
||||||
|
|
||||||
return ev;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct event *clone_event_rename(const struct event *ev, const char *name)
|
|
||||||
{
|
|
||||||
return create_event(ev->time.seconds, ev->type, ev->flags, ev->value, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool same_event(const struct event *a, const struct event *b)
|
|
||||||
{
|
|
||||||
if (a->time.seconds != b->time.seconds)
|
|
||||||
return 0;
|
|
||||||
if (a->type != b->type)
|
|
||||||
return 0;
|
|
||||||
if (a->flags != b->flags)
|
|
||||||
return 0;
|
|
||||||
if (a->value != b->value)
|
|
||||||
return 0;
|
|
||||||
return !strcmp(a->name, b->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern enum event_severity get_event_severity(const struct event *ev)
|
|
||||||
{
|
|
||||||
switch (ev->flags & SAMPLE_FLAGS_SEVERITY_MASK) {
|
|
||||||
case SAMPLE_FLAGS_SEVERITY_INFO:
|
|
||||||
return EVENT_SEVERITY_INFO;
|
|
||||||
case SAMPLE_FLAGS_SEVERITY_WARN:
|
|
||||||
return EVENT_SEVERITY_WARN;
|
|
||||||
case SAMPLE_FLAGS_SEVERITY_ALARM:
|
|
||||||
return EVENT_SEVERITY_ALARM;
|
|
||||||
default:
|
|
||||||
return EVENT_SEVERITY_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
112
core/event.cpp
Normal file
112
core/event.cpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#include "event.h"
|
||||||
|
#include "divecomputer.h"
|
||||||
|
#include "eventtype.h"
|
||||||
|
#include "subsurface-string.h"
|
||||||
|
|
||||||
|
event::event() : type(SAMPLE_EVENT_NONE), flags(0), value(0),
|
||||||
|
divemode(OC), hidden(false)
|
||||||
|
{
|
||||||
|
/* That overwrites divemode. Is this a smart thing to do? */
|
||||||
|
gas.index = -1;
|
||||||
|
gas.mix = gasmix_air;
|
||||||
|
}
|
||||||
|
|
||||||
|
event::event(unsigned int time, int type, int flags, int value, const std::string &name) :
|
||||||
|
type(type), flags(flags), value(value), divemode(OC),
|
||||||
|
hidden(false), name(name)
|
||||||
|
{
|
||||||
|
int gas_index = -1;
|
||||||
|
fraction_t he;
|
||||||
|
this->time.seconds = time;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Expand the events into a sane format. Currently
|
||||||
|
* just gas switches
|
||||||
|
*/
|
||||||
|
switch (type) {
|
||||||
|
case SAMPLE_EVENT_GASCHANGE2:
|
||||||
|
/* High 16 bits are He percentage */
|
||||||
|
he.permille = (value >> 16) * 10;
|
||||||
|
|
||||||
|
/* Extension to the GASCHANGE2 format: cylinder index in 'flags' */
|
||||||
|
/* TODO: verify that gas_index < num_cylinders. */
|
||||||
|
if (flags > 0)
|
||||||
|
gas_index = flags-1;
|
||||||
|
/* Fallthrough */
|
||||||
|
case SAMPLE_EVENT_GASCHANGE:
|
||||||
|
/* Low 16 bits are O2 percentage */
|
||||||
|
gas.mix.he = he;
|
||||||
|
gas.mix.o2.permille = (value & 0xffff) * 10;
|
||||||
|
gas.index = gas_index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
remember_event_type(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
event::~event()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool event::is_gaschange() const
|
||||||
|
{
|
||||||
|
return type == SAMPLE_EVENT_GASCHANGE || type == SAMPLE_EVENT_GASCHANGE2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool event::is_divemodechange() const
|
||||||
|
{
|
||||||
|
return name == "modechange";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool event::operator==(const event &b) const
|
||||||
|
{
|
||||||
|
return std::tie(time.seconds, type, flags, value, name) ==
|
||||||
|
std::tie(b.time.seconds, b.type, b.flags, b.value, b.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum event_severity event::get_severity() const
|
||||||
|
{
|
||||||
|
switch (flags & SAMPLE_FLAGS_SEVERITY_MASK) {
|
||||||
|
case SAMPLE_FLAGS_SEVERITY_INFO:
|
||||||
|
return EVENT_SEVERITY_INFO;
|
||||||
|
case SAMPLE_FLAGS_SEVERITY_WARN:
|
||||||
|
return EVENT_SEVERITY_WARN;
|
||||||
|
case SAMPLE_FLAGS_SEVERITY_ALARM:
|
||||||
|
return EVENT_SEVERITY_ALARM;
|
||||||
|
default:
|
||||||
|
return EVENT_SEVERITY_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event_loop::event_loop(const char *name) : name(name), idx(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
struct event *event_loop::next(struct divecomputer &dc)
|
||||||
|
{
|
||||||
|
if (name.empty())
|
||||||
|
return nullptr;
|
||||||
|
while (idx < dc.events.size()) {
|
||||||
|
struct event &ev = dc.events[idx++];
|
||||||
|
if (ev.name == name)
|
||||||
|
return &ev;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct event *event_loop::next(const struct divecomputer &dc)
|
||||||
|
{
|
||||||
|
return next(const_cast<divecomputer &>(dc));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct event *get_first_event(struct divecomputer &dc, const std::string &name)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(dc.events.begin(), dc.events.end(), [name](auto &ev) { return ev.name == name; });
|
||||||
|
return it != dc.events.end() ? &*it : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct event *get_first_event(const struct divecomputer &dc, const std::string &name)
|
||||||
|
{
|
||||||
|
return get_first_event(const_cast<divecomputer &>(dc), name);
|
||||||
|
}
|
||||||
66
core/event.h
66
core/event.h
@ -6,11 +6,10 @@
|
|||||||
#include "gas.h"
|
#include "gas.h"
|
||||||
#include "units.h"
|
#include "units.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <libdivecomputer/parser.h>
|
#include <libdivecomputer/parser.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
struct divecomputer;
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum event_severity {
|
enum event_severity {
|
||||||
EVENT_SEVERITY_NONE = 0,
|
EVENT_SEVERITY_NONE = 0,
|
||||||
@ -23,8 +22,8 @@ enum event_severity {
|
|||||||
* Events are currently based straight on what libdivecomputer gives us.
|
* Events are currently based straight on what libdivecomputer gives us.
|
||||||
* We need to wrap these into our own events at some point to remove some of the limitations.
|
* We need to wrap these into our own events at some point to remove some of the limitations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct event {
|
struct event {
|
||||||
struct event *next;
|
|
||||||
duration_t time;
|
duration_t time;
|
||||||
int type;
|
int type;
|
||||||
/* This is the annoying libdivecomputer format. */
|
/* This is the annoying libdivecomputer format. */
|
||||||
@ -42,26 +41,53 @@ struct event {
|
|||||||
struct gasmix mix;
|
struct gasmix mix;
|
||||||
} gas;
|
} gas;
|
||||||
};
|
};
|
||||||
bool deleted; // used internally in the parser and in fixup_dive().
|
|
||||||
bool hidden;
|
bool hidden;
|
||||||
char name[];
|
std::string name;
|
||||||
|
event();
|
||||||
|
event(unsigned int time, int type, int flags, int value, const std::string &name);
|
||||||
|
~event();
|
||||||
|
|
||||||
|
bool operator==(const event &b2) const;
|
||||||
|
|
||||||
|
bool is_gaschange() const;
|
||||||
|
bool is_divemodechange() const;
|
||||||
|
event_severity get_severity() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int event_is_gaschange(const struct event *ev);
|
class event_loop
|
||||||
extern bool event_is_divemodechange(const struct event *ev);
|
{
|
||||||
extern struct event *clone_event(const struct event *src_ev);
|
std::string name;
|
||||||
extern void free_events(struct event *ev);
|
size_t idx;
|
||||||
extern struct event *create_event(unsigned int time, int type, int flags, int value, const char *name);
|
public:
|
||||||
extern struct event *clone_event_rename(const struct event *ev, const char *name);
|
event_loop(const char *name);
|
||||||
extern bool same_event(const struct event *a, const struct event *b);
|
struct event *next(struct divecomputer &dc); // nullptr -> end
|
||||||
extern enum event_severity get_event_severity(const struct event *ev);
|
const struct event *next(const struct divecomputer &dc); // nullptr -> end
|
||||||
|
};
|
||||||
|
|
||||||
/* Since C doesn't have parameter-based overloading, two versions of get_next_event. */
|
/* Get gasmixes at increasing timestamps. */
|
||||||
extern const struct event *get_next_event(const struct event *event, const char *name);
|
class gasmix_loop {
|
||||||
extern struct event *get_next_event_mutable(struct event *event, const char *name);
|
const struct dive &dive;
|
||||||
|
const struct divecomputer &dc;
|
||||||
|
struct gasmix last;
|
||||||
|
event_loop loop;
|
||||||
|
const struct event *ev;
|
||||||
|
public:
|
||||||
|
gasmix_loop(const struct dive &dive, const struct divecomputer &dc);
|
||||||
|
gasmix next(int time);
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
/* Get divemodes at increasing timestamps. */
|
||||||
}
|
class divemode_loop {
|
||||||
#endif
|
const struct divecomputer &dc;
|
||||||
|
divemode_t last;
|
||||||
|
event_loop loop;
|
||||||
|
const struct event *ev;
|
||||||
|
public:
|
||||||
|
divemode_loop(const struct divecomputer &dc);
|
||||||
|
divemode_t next(int time);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const struct event *get_first_event(const struct divecomputer &dc, const std::string &name);
|
||||||
|
extern struct event *get_first_event(struct divecomputer &dc, const std::string &name);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -14,7 +14,7 @@ struct event_type {
|
|||||||
bool plot;
|
bool plot;
|
||||||
event_type(const struct event *ev) :
|
event_type(const struct event *ev) :
|
||||||
name(ev->name),
|
name(ev->name),
|
||||||
severity(get_event_severity(ev)),
|
severity(ev->get_severity()),
|
||||||
plot(true)
|
plot(true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -27,14 +27,14 @@ static bool operator==(const event_type &en1, const event_type &en2)
|
|||||||
return en1.name == en2.name && en1.severity == en2.severity;
|
return en1.name == en2.name && en1.severity == en2.severity;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void clear_event_types()
|
void clear_event_types()
|
||||||
{
|
{
|
||||||
event_types.clear();
|
event_types.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void remember_event_type(const struct event *ev)
|
void remember_event_type(const struct event *ev)
|
||||||
{
|
{
|
||||||
if (empty_string(ev->name))
|
if (ev->name.empty())
|
||||||
return;
|
return;
|
||||||
event_type type(ev);
|
event_type type(ev);
|
||||||
if (std::find(event_types.begin(), event_types.end(), type) != event_types.end())
|
if (std::find(event_types.begin(), event_types.end(), type) != event_types.end())
|
||||||
@ -42,33 +42,33 @@ extern "C" void remember_event_type(const struct event *ev)
|
|||||||
event_types.push_back(std::move(type));
|
event_types.push_back(std::move(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" bool is_event_type_hidden(const struct event *ev)
|
bool is_event_type_hidden(const struct event *ev)
|
||||||
{
|
{
|
||||||
auto it = std::find(event_types.begin(), event_types.end(), ev);
|
auto it = std::find(event_types.begin(), event_types.end(), ev);
|
||||||
return it != event_types.end() && !it->plot;
|
return it != event_types.end() && !it->plot;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void hide_event_type(const struct event *ev)
|
void hide_event_type(const struct event *ev)
|
||||||
{
|
{
|
||||||
auto it = std::find(event_types.begin(), event_types.end(), ev);
|
auto it = std::find(event_types.begin(), event_types.end(), ev);
|
||||||
if (it != event_types.end())
|
if (it != event_types.end())
|
||||||
it->plot = false;
|
it->plot = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void show_all_event_types()
|
void show_all_event_types()
|
||||||
{
|
{
|
||||||
for (event_type &e: event_types)
|
for (event_type &e: event_types)
|
||||||
e.plot = true;
|
e.plot = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void show_event_type(int idx)
|
void show_event_type(int idx)
|
||||||
{
|
{
|
||||||
if (idx < 0 || idx >= (int)event_types.size())
|
if (idx < 0 || idx >= (int)event_types.size())
|
||||||
return;
|
return;
|
||||||
event_types[idx].plot = true;
|
event_types[idx].plot = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" bool any_event_types_hidden()
|
bool any_event_types_hidden()
|
||||||
{
|
{
|
||||||
return std::any_of(event_types.begin(), event_types.end(),
|
return std::any_of(event_types.begin(), event_types.end(),
|
||||||
[] (const event_type &e) { return !e.plot; });
|
[] (const event_type &e) { return !e.plot; });
|
||||||
@ -102,13 +102,13 @@ static QString event_type_name(QString name, event_severity severity)
|
|||||||
return QStringLiteral("%1 (%2)").arg(name, severity_name);
|
return QStringLiteral("%1 (%2)").arg(name, severity_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString event_type_name(const event *ev)
|
QString event_type_name(const event &ev)
|
||||||
{
|
{
|
||||||
if (!ev || empty_string(ev->name))
|
if (ev.name.empty())
|
||||||
return QString();
|
return QString();
|
||||||
|
|
||||||
QString name = QString::fromUtf8(ev->name);
|
QString name = QString::fromStdString(ev.name);
|
||||||
return event_type_name(std::move(name), get_event_severity(ev));
|
return event_type_name(std::move(name), ev.get_severity());
|
||||||
}
|
}
|
||||||
|
|
||||||
QString event_type_name(int idx)
|
QString event_type_name(int idx)
|
||||||
|
|||||||
@ -3,29 +3,18 @@
|
|||||||
#ifndef EVENTNAME_H
|
#ifndef EVENTNAME_H
|
||||||
#define EVENTNAME_H
|
#define EVENTNAME_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#include <vector>
|
||||||
extern "C" {
|
#include <QString>
|
||||||
#endif
|
|
||||||
|
|
||||||
extern void clear_event_types(void);
|
extern void clear_event_types();
|
||||||
extern void remember_event_type(const struct event *ev);
|
extern void remember_event_type(const struct event *ev);
|
||||||
extern bool is_event_type_hidden(const struct event *ev);
|
extern bool is_event_type_hidden(const struct event *ev);
|
||||||
extern void hide_event_type(const struct event *ev);
|
extern void hide_event_type(const struct event *ev);
|
||||||
extern void show_all_event_types();
|
extern void show_all_event_types();
|
||||||
extern void show_event_type(int idx);
|
extern void show_event_type(int idx);
|
||||||
extern bool any_event_types_hidden();
|
extern bool any_event_types_hidden();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
|
|
||||||
// C++-only functions
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <QString>
|
|
||||||
extern std::vector<int> hidden_event_types();
|
extern std::vector<int> hidden_event_types();
|
||||||
QString event_type_name(const event *ev);
|
extern QString event_type_name(const event &ev);
|
||||||
QString event_type_name(int idx);
|
extern QString event_type_name(int idx);
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -2,10 +2,11 @@
|
|||||||
#ifndef EXTRADATA_H
|
#ifndef EXTRADATA_H
|
||||||
#define EXTRADATA_H
|
#define EXTRADATA_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
struct extra_data {
|
struct extra_data {
|
||||||
const char *key;
|
std::string key;
|
||||||
const char *value;
|
std::string value;
|
||||||
struct extra_data *next;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include "ssrf.h"
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -78,7 +77,7 @@ static void zip_read(struct zip_file *file, const char *filename, struct divelog
|
|||||||
(void) parse_xml_buffer(filename, mem.data(), read, log, NULL);
|
(void) parse_xml_buffer(filename, mem.data(), read, log, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int try_to_open_zip(const char *filename, struct divelog *log)
|
int try_to_open_zip(const char *filename, struct divelog *log)
|
||||||
{
|
{
|
||||||
int success = 0;
|
int success = 0;
|
||||||
/* Grr. libzip needs to re-open the file, it can't take a buffer */
|
/* Grr. libzip needs to re-open the file, it can't take a buffer */
|
||||||
@ -268,7 +267,7 @@ bool remote_repo_uptodate(const char *filename, struct git_info *info)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int parse_file(const char *filename, struct divelog *log)
|
int parse_file(const char *filename, struct divelog *log)
|
||||||
{
|
{
|
||||||
struct git_info info;
|
struct git_info info;
|
||||||
const char *fmt;
|
const char *fmt;
|
||||||
|
|||||||
20
core/file.h
20
core/file.h
@ -6,13 +6,12 @@
|
|||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
struct divelog;
|
struct divelog;
|
||||||
struct zip;
|
struct zip;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
extern void ostctools_import(const char *file, struct divelog *log);
|
extern void ostctools_import(const char *file, struct divelog *log);
|
||||||
|
|
||||||
extern int parse_file(const char *filename, struct divelog *log);
|
extern int parse_file(const char *filename, struct divelog *log);
|
||||||
@ -28,22 +27,9 @@ extern int subsurface_access(const char *path, int mode);
|
|||||||
extern int subsurface_stat(const char *path, struct stat *buf);
|
extern int subsurface_stat(const char *path, struct stat *buf);
|
||||||
extern struct zip *subsurface_zip_open_readonly(const char *path, int flags, int *errorp);
|
extern struct zip *subsurface_zip_open_readonly(const char *path, int flags, int *errorp);
|
||||||
extern int subsurface_zip_close(struct zip *zip);
|
extern int subsurface_zip_close(struct zip *zip);
|
||||||
|
extern std::pair<std::string, int> readfile(const char *filename); // return data, errorcode pair.
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// C++ only functions
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
// return data, errorcode pair.
|
|
||||||
extern std::pair<std::string, int> readfile(const char *filename);
|
|
||||||
extern int try_to_open_cochran(const char *filename, std::string &mem, struct divelog *log);
|
extern int try_to_open_cochran(const char *filename, std::string &mem, struct divelog *log);
|
||||||
extern int try_to_open_liquivision(const char *filename, std::string &mem, struct divelog *log);
|
extern int try_to_open_liquivision(const char *filename, std::string &mem, struct divelog *log);
|
||||||
extern int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log);
|
extern int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log);
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FILE_H
|
#endif // FILE_H
|
||||||
|
|||||||
@ -134,87 +134,87 @@ static const range_mode_description *get_range_mode_description(enum filter_cons
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum filter_constraint_type filter_constraint_type_from_string(const char *s)
|
static enum filter_constraint_type filter_constraint_type_from_string(const std::string &s)
|
||||||
{
|
{
|
||||||
for (const auto &desc: type_descriptions) {
|
for (const auto &desc: type_descriptions) {
|
||||||
if (same_string(desc.token, s))
|
if (desc.token == s)
|
||||||
return desc.type;
|
return desc.type;
|
||||||
}
|
}
|
||||||
report_error("unknown filter constraint type: %s", s);
|
report_error("unknown filter constraint type: %s", s.c_str());
|
||||||
return FILTER_CONSTRAINT_DATE;
|
return FILTER_CONSTRAINT_DATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum filter_constraint_string_mode filter_constraint_string_mode_from_string(const char *s)
|
static enum filter_constraint_string_mode filter_constraint_string_mode_from_string(const std::string &s)
|
||||||
{
|
{
|
||||||
for (const auto &desc: string_mode_descriptions) {
|
for (const auto &desc: string_mode_descriptions) {
|
||||||
if (same_string(desc.token, s))
|
if (desc.token == s)
|
||||||
return desc.mode;
|
return desc.mode;
|
||||||
}
|
}
|
||||||
report_error("unknown filter constraint string mode: %s", s);
|
report_error("unknown filter constraint string mode: %s", s.c_str());
|
||||||
return FILTER_CONSTRAINT_EXACT;
|
return FILTER_CONSTRAINT_EXACT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum filter_constraint_range_mode filter_constraint_range_mode_from_string(const char *s)
|
static enum filter_constraint_range_mode filter_constraint_range_mode_from_string(const std::string &s)
|
||||||
{
|
{
|
||||||
for (const auto &desc: range_mode_descriptions) {
|
for (const auto &desc: range_mode_descriptions) {
|
||||||
if (same_string(desc.token, s))
|
if (desc.token == s)
|
||||||
return desc.mode;
|
return desc.mode;
|
||||||
}
|
}
|
||||||
report_error("unknown filter constraint range mode: %s", s);
|
report_error("unknown filter constraint range mode: %s", s.c_str());
|
||||||
return FILTER_CONSTRAINT_EQUAL;
|
return FILTER_CONSTRAINT_EQUAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" const char *filter_constraint_type_to_string(enum filter_constraint_type type)
|
const char *filter_constraint_type_to_string(enum filter_constraint_type type)
|
||||||
{
|
{
|
||||||
const type_description *desc = get_type_description(type);
|
const type_description *desc = get_type_description(type);
|
||||||
return desc ? desc->token : "unknown";
|
return desc ? desc->token : "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" const char *filter_constraint_string_mode_to_string(enum filter_constraint_string_mode mode)
|
const char *filter_constraint_string_mode_to_string(enum filter_constraint_string_mode mode)
|
||||||
{
|
{
|
||||||
const string_mode_description *desc = get_string_mode_description(mode);
|
const string_mode_description *desc = get_string_mode_description(mode);
|
||||||
return desc ? desc->token : "unknown";
|
return desc ? desc->token : "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" const char *filter_constraint_range_mode_to_string(enum filter_constraint_range_mode mode)
|
const char *filter_constraint_range_mode_to_string(enum filter_constraint_range_mode mode)
|
||||||
{
|
{
|
||||||
const range_mode_description *desc = get_range_mode_description(mode);
|
const range_mode_description *desc = get_range_mode_description(mode);
|
||||||
return desc ? desc->token : "unknown";
|
return desc ? desc->token : "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int filter_constraint_type_to_index(enum filter_constraint_type type)
|
int filter_constraint_type_to_index(enum filter_constraint_type type)
|
||||||
{
|
{
|
||||||
const type_description *desc = get_type_description(type);
|
const type_description *desc = get_type_description(type);
|
||||||
return desc ? desc - type_descriptions : -1;
|
return desc ? desc - type_descriptions : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int filter_constraint_string_mode_to_index(enum filter_constraint_string_mode mode)
|
int filter_constraint_string_mode_to_index(enum filter_constraint_string_mode mode)
|
||||||
{
|
{
|
||||||
const string_mode_description *desc = get_string_mode_description(mode);
|
const string_mode_description *desc = get_string_mode_description(mode);
|
||||||
return desc ? desc - string_mode_descriptions : -1;
|
return desc ? desc - string_mode_descriptions : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int filter_constraint_range_mode_to_index(enum filter_constraint_range_mode mode)
|
int filter_constraint_range_mode_to_index(enum filter_constraint_range_mode mode)
|
||||||
{
|
{
|
||||||
const range_mode_description *desc = get_range_mode_description(mode);
|
const range_mode_description *desc = get_range_mode_description(mode);
|
||||||
return desc ? desc - range_mode_descriptions : -1;
|
return desc ? desc - range_mode_descriptions : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" enum filter_constraint_type filter_constraint_type_from_index(int index)
|
enum filter_constraint_type filter_constraint_type_from_index(int index)
|
||||||
{
|
{
|
||||||
if (index >= 0 && index < (int)std::size(type_descriptions))
|
if (index >= 0 && index < (int)std::size(type_descriptions))
|
||||||
return type_descriptions[index].type;
|
return type_descriptions[index].type;
|
||||||
return (enum filter_constraint_type)-1;
|
return (enum filter_constraint_type)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" enum filter_constraint_string_mode filter_constraint_string_mode_from_index(int index)
|
enum filter_constraint_string_mode filter_constraint_string_mode_from_index(int index)
|
||||||
{
|
{
|
||||||
if (index >= 0 && index < (int)std::size(string_mode_descriptions))
|
if (index >= 0 && index < (int)std::size(string_mode_descriptions))
|
||||||
return string_mode_descriptions[index].mode;
|
return string_mode_descriptions[index].mode;
|
||||||
return (enum filter_constraint_string_mode)-1;
|
return (enum filter_constraint_string_mode)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" enum filter_constraint_range_mode filter_constraint_range_mode_from_index(int index)
|
enum filter_constraint_range_mode filter_constraint_range_mode_from_index(int index)
|
||||||
{
|
{
|
||||||
if (index >= 0 && index < (int)std::size(range_mode_descriptions))
|
if (index >= 0 && index < (int)std::size(range_mode_descriptions))
|
||||||
return range_mode_descriptions[index].mode;
|
return range_mode_descriptions[index].mode;
|
||||||
@ -390,7 +390,7 @@ QStringList filter_contraint_multiple_choice_translated(enum filter_constraint_t
|
|||||||
return QStringList();
|
return QStringList();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" bool filter_constraint_is_string(filter_constraint_type type)
|
bool filter_constraint_is_string(filter_constraint_type type)
|
||||||
{
|
{
|
||||||
// Currently a constraint is filter based if and only if it has a string
|
// Currently a constraint is filter based if and only if it has a string
|
||||||
// mode (i.e. starts with, substring, exact). In the future we might also
|
// mode (i.e. starts with, substring, exact). In the future we might also
|
||||||
@ -398,7 +398,7 @@ extern "C" bool filter_constraint_is_string(filter_constraint_type type)
|
|||||||
return filter_constraint_has_string_mode(type);
|
return filter_constraint_has_string_mode(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" bool filter_constraint_is_timestamp(filter_constraint_type type)
|
bool filter_constraint_is_timestamp(filter_constraint_type type)
|
||||||
{
|
{
|
||||||
return type == FILTER_CONSTRAINT_DATE || type == FILTER_CONSTRAINT_DATE_TIME;
|
return type == FILTER_CONSTRAINT_DATE || type == FILTER_CONSTRAINT_DATE_TIME;
|
||||||
}
|
}
|
||||||
@ -408,37 +408,37 @@ static bool is_numerical_constraint(filter_constraint_type type)
|
|||||||
return !filter_constraint_is_string(type) && !filter_constraint_is_timestamp(type);
|
return !filter_constraint_is_string(type) && !filter_constraint_is_timestamp(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" bool filter_constraint_has_string_mode(enum filter_constraint_type type)
|
bool filter_constraint_has_string_mode(enum filter_constraint_type type)
|
||||||
{
|
{
|
||||||
const type_description *desc = get_type_description(type);
|
const type_description *desc = get_type_description(type);
|
||||||
return desc && desc->has_string_mode;
|
return desc && desc->has_string_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" bool filter_constraint_has_range_mode(enum filter_constraint_type type)
|
bool filter_constraint_has_range_mode(enum filter_constraint_type type)
|
||||||
{
|
{
|
||||||
const type_description *desc = get_type_description(type);
|
const type_description *desc = get_type_description(type);
|
||||||
return desc && desc->has_range_mode;
|
return desc && desc->has_range_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" bool filter_constraint_is_star(filter_constraint_type type)
|
bool filter_constraint_is_star(filter_constraint_type type)
|
||||||
{
|
{
|
||||||
const type_description *desc = get_type_description(type);
|
const type_description *desc = get_type_description(type);
|
||||||
return desc && desc->is_star_widget;
|
return desc && desc->is_star_widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" bool filter_constraint_has_date_widget(filter_constraint_type type)
|
bool filter_constraint_has_date_widget(filter_constraint_type type)
|
||||||
{
|
{
|
||||||
const type_description *desc = get_type_description(type);
|
const type_description *desc = get_type_description(type);
|
||||||
return desc && desc->has_date;
|
return desc && desc->has_date;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" bool filter_constraint_has_time_widget(filter_constraint_type type)
|
bool filter_constraint_has_time_widget(filter_constraint_type type)
|
||||||
{
|
{
|
||||||
const type_description *desc = get_type_description(type);
|
const type_description *desc = get_type_description(type);
|
||||||
return desc && desc->has_time;
|
return desc && desc->has_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int filter_constraint_num_decimals(enum filter_constraint_type type)
|
int filter_constraint_num_decimals(enum filter_constraint_type type)
|
||||||
{
|
{
|
||||||
const type_description *desc = get_type_description(type);
|
const type_description *desc = get_type_description(type);
|
||||||
return desc ? desc->decimal_places : 1;
|
return desc ? desc->decimal_places : 1;
|
||||||
@ -446,7 +446,7 @@ extern "C" int filter_constraint_num_decimals(enum filter_constraint_type type)
|
|||||||
|
|
||||||
// String constraints are valid if there is at least one term.
|
// String constraints are valid if there is at least one term.
|
||||||
// Other constraints are always valid.
|
// Other constraints are always valid.
|
||||||
extern "C" bool filter_constraint_is_valid(const struct filter_constraint *constraint)
|
bool filter_constraint_is_valid(const struct filter_constraint *constraint)
|
||||||
{
|
{
|
||||||
if (!filter_constraint_is_string(constraint->type))
|
if (!filter_constraint_is_string(constraint->type))
|
||||||
return true;
|
return true;
|
||||||
@ -551,14 +551,14 @@ filter_constraint::filter_constraint(const filter_constraint &c) :
|
|||||||
data.numerical_range = c.data.numerical_range;
|
data.numerical_range = c.data.numerical_range;
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_constraint::filter_constraint(const char *type_in, const char *string_mode_in,
|
filter_constraint::filter_constraint(const std::string &type_in, const std::string &string_mode_in,
|
||||||
const char *range_mode_in, bool negate_in, const char *s_in) :
|
const std::string &range_mode_in, bool negate_in, const std::string &s_in) :
|
||||||
type(filter_constraint_type_from_string(type_in)),
|
type(filter_constraint_type_from_string(type_in)),
|
||||||
string_mode(FILTER_CONSTRAINT_STARTS_WITH),
|
string_mode(FILTER_CONSTRAINT_STARTS_WITH),
|
||||||
range_mode(FILTER_CONSTRAINT_GREATER),
|
range_mode(FILTER_CONSTRAINT_GREATER),
|
||||||
negate(negate_in)
|
negate(negate_in)
|
||||||
{
|
{
|
||||||
QString s(s_in);
|
QString s = QString::fromStdString(s_in);
|
||||||
if (filter_constraint_has_string_mode(type))
|
if (filter_constraint_has_string_mode(type))
|
||||||
string_mode = filter_constraint_string_mode_from_string(string_mode_in);
|
string_mode = filter_constraint_string_mode_from_string(string_mode_in);
|
||||||
if (filter_constraint_has_range_mode(type))
|
if (filter_constraint_has_range_mode(type))
|
||||||
@ -622,22 +622,22 @@ filter_constraint::~filter_constraint()
|
|||||||
delete data.string_list;
|
delete data.string_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string filter_constraint_data_to_string(const filter_constraint *c)
|
std::string filter_constraint_data_to_string(const filter_constraint &c)
|
||||||
{
|
{
|
||||||
if (filter_constraint_is_timestamp(c->type)) {
|
if (filter_constraint_is_timestamp(c.type)) {
|
||||||
std::string from_s = format_datetime(c->data.timestamp_range.from);
|
std::string from_s = format_datetime(c.data.timestamp_range.from);
|
||||||
std::string to_s = format_datetime(c->data.timestamp_range.to);
|
std::string to_s = format_datetime(c.data.timestamp_range.to);
|
||||||
return from_s + ',' + to_s;
|
return from_s + ',' + to_s;
|
||||||
} else if (filter_constraint_is_string(c->type)) {
|
} else if (filter_constraint_is_string(c.type)) {
|
||||||
// TODO: this obviously breaks if the strings contain ",".
|
// TODO: this obviously breaks if the strings contain ",".
|
||||||
// That is currently not supported by the UI, but one day we might
|
// That is currently not supported by the UI, but one day we might
|
||||||
// have to escape the strings.
|
// have to escape the strings.
|
||||||
return c->data.string_list->join(",").toStdString();
|
return c.data.string_list->join(",").toStdString();
|
||||||
} else if (filter_constraint_is_multiple_choice(c->type)) {
|
} else if (filter_constraint_is_multiple_choice(c.type)) {
|
||||||
return std::to_string(c->data.multiple_choice);
|
return std::to_string(c.data.multiple_choice);
|
||||||
} else {
|
} else {
|
||||||
return std::to_string(c->data.numerical_range.from) + ',' +
|
return std::to_string(c.data.numerical_range.from) + ',' +
|
||||||
std::to_string(c->data.numerical_range.to);
|
std::to_string(c.data.numerical_range.to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -818,18 +818,18 @@ static bool check(const filter_constraint &c, const QStringList &list)
|
|||||||
static bool has_tags(const filter_constraint &c, const struct dive *d)
|
static bool has_tags(const filter_constraint &c, const struct dive *d)
|
||||||
{
|
{
|
||||||
QStringList dive_tags;
|
QStringList dive_tags;
|
||||||
for (const tag_entry *tag = d->tag_list; tag; tag = tag->next)
|
for (const divetag *tag: d->tags)
|
||||||
dive_tags.push_back(QString::fromStdString(tag->tag->name).trimmed());
|
dive_tags.push_back(QString::fromStdString(tag->name).trimmed());
|
||||||
dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dc.divemode]).trimmed());
|
dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dcs[0].divemode]).trimmed());
|
||||||
return check(c, dive_tags);
|
return check(c, dive_tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool has_people(const filter_constraint &c, const struct dive *d)
|
static bool has_people(const filter_constraint &c, const struct dive *d)
|
||||||
{
|
{
|
||||||
QStringList dive_people;
|
QStringList dive_people;
|
||||||
for (const QString &s: QString(d->buddy).split(",", SKIP_EMPTY))
|
for (const QString &s: QString::fromStdString(d->buddy).split(",", SKIP_EMPTY))
|
||||||
dive_people.push_back(s.trimmed());
|
dive_people.push_back(s.trimmed());
|
||||||
for (const QString &s: QString(d->diveguide).split(",", SKIP_EMPTY))
|
for (const QString &s: QString::fromStdString(d->diveguide).split(",", SKIP_EMPTY))
|
||||||
dive_people.push_back(s.trimmed());
|
dive_people.push_back(s.trimmed());
|
||||||
return check(c, dive_people);
|
return check(c, dive_people);
|
||||||
}
|
}
|
||||||
@ -838,10 +838,10 @@ static bool has_locations(const filter_constraint &c, const struct dive *d)
|
|||||||
{
|
{
|
||||||
QStringList diveLocations;
|
QStringList diveLocations;
|
||||||
if (d->divetrip)
|
if (d->divetrip)
|
||||||
diveLocations.push_back(QString(d->divetrip->location).trimmed());
|
diveLocations.push_back(QString::fromStdString(d->divetrip->location).trimmed());
|
||||||
|
|
||||||
if (d->dive_site)
|
if (d->dive_site)
|
||||||
diveLocations.push_back(QString(d->dive_site->name).trimmed());
|
diveLocations.push_back(QString::fromStdString(d->dive_site->name).trimmed());
|
||||||
|
|
||||||
return check(c, diveLocations);
|
return check(c, diveLocations);
|
||||||
}
|
}
|
||||||
@ -849,8 +849,8 @@ static bool has_locations(const filter_constraint &c, const struct dive *d)
|
|||||||
static bool has_weight_type(const filter_constraint &c, const struct dive *d)
|
static bool has_weight_type(const filter_constraint &c, const struct dive *d)
|
||||||
{
|
{
|
||||||
QStringList weightsystemTypes;
|
QStringList weightsystemTypes;
|
||||||
for (int i = 0; i < d->weightsystems.nr; ++i)
|
for (auto &ws: d->weightsystems)
|
||||||
weightsystemTypes.push_back(d->weightsystems.weightsystems[i].description);
|
weightsystemTypes.push_back(QString::fromStdString(ws.description));
|
||||||
|
|
||||||
return check(c, weightsystemTypes);
|
return check(c, weightsystemTypes);
|
||||||
}
|
}
|
||||||
@ -858,8 +858,8 @@ static bool has_weight_type(const filter_constraint &c, const struct dive *d)
|
|||||||
static bool has_cylinder_type(const filter_constraint &c, const struct dive *d)
|
static bool has_cylinder_type(const filter_constraint &c, const struct dive *d)
|
||||||
{
|
{
|
||||||
QStringList cylinderTypes;
|
QStringList cylinderTypes;
|
||||||
for (int i = 0; i < d->cylinders.nr; ++i)
|
for (const cylinder_t &cyl: d->cylinders)
|
||||||
cylinderTypes.push_back(d->cylinders.cylinders[i].type.description);
|
cylinderTypes.push_back(QString::fromStdString(cyl.type.description));
|
||||||
|
|
||||||
return check(c, cylinderTypes);
|
return check(c, cylinderTypes);
|
||||||
}
|
}
|
||||||
@ -867,16 +867,16 @@ static bool has_cylinder_type(const filter_constraint &c, const struct dive *d)
|
|||||||
static bool has_suits(const filter_constraint &c, const struct dive *d)
|
static bool has_suits(const filter_constraint &c, const struct dive *d)
|
||||||
{
|
{
|
||||||
QStringList diveSuits;
|
QStringList diveSuits;
|
||||||
if (d->suit)
|
if (!d->suit.empty())
|
||||||
diveSuits.push_back(QString(d->suit));
|
diveSuits.push_back(QString::fromStdString(d->suit));
|
||||||
return check(c, diveSuits);
|
return check(c, diveSuits);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool has_notes(const filter_constraint &c, const struct dive *d)
|
static bool has_notes(const filter_constraint &c, const struct dive *d)
|
||||||
{
|
{
|
||||||
QStringList diveNotes;
|
QStringList diveNotes;
|
||||||
if (d->notes)
|
if (!d->notes.empty())
|
||||||
diveNotes.push_back(QString(d->notes));
|
diveNotes.push_back(QString::fromStdString(d->notes));
|
||||||
return check(c, diveNotes);
|
return check(c, diveNotes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -905,22 +905,15 @@ static bool check_numerical_range_non_zero(const filter_constraint &c, int v)
|
|||||||
|
|
||||||
static bool check_cylinder_size(const filter_constraint &c, const struct dive *d)
|
static bool check_cylinder_size(const filter_constraint &c, const struct dive *d)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < d->cylinders.nr; ++i) {
|
return std::any_of(d->cylinders.begin(), d->cylinders.end(), [&c](auto &cyl)
|
||||||
const cylinder_t &cyl = d->cylinders.cylinders[i];
|
{ return cyl.type.size.mliter &&
|
||||||
if (cyl.type.size.mliter && check_numerical_range(c, cyl.type.size.mliter))
|
check_numerical_range(c, cyl.type.size.mliter); });
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_gas_range(const filter_constraint &c, const struct dive *d, gas_component component)
|
static bool check_gas_range(const filter_constraint &c, const struct dive *d, gas_component component)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < d->cylinders.nr; ++i) {
|
return std::any_of(d->cylinders.begin(), d->cylinders.end(), [&c, &component](auto &cyl)
|
||||||
const cylinder_t &cyl = d->cylinders.cylinders[i];
|
{ return check_numerical_range(c, get_gas_component_fraction(cyl.gasmix, component).permille); });
|
||||||
if (check_numerical_range(c, get_gas_component_fraction(cyl.gasmix, component).permille))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static long days_since_epoch(timestamp_t timestamp)
|
static long days_since_epoch(timestamp_t timestamp)
|
||||||
@ -959,12 +952,12 @@ static bool check_datetime_range(const filter_constraint &c, const struct dive *
|
|||||||
// where the given timestamp is during that dive.
|
// where the given timestamp is during that dive.
|
||||||
return time_during_dive_with_offset(d, c.data.timestamp_range.from, 0) != c.negate;
|
return time_during_dive_with_offset(d, c.data.timestamp_range.from, 0) != c.negate;
|
||||||
case FILTER_CONSTRAINT_LESS:
|
case FILTER_CONSTRAINT_LESS:
|
||||||
return (dive_endtime(d) <= c.data.timestamp_range.to) != c.negate;
|
return (d->endtime() <= c.data.timestamp_range.to) != c.negate;
|
||||||
case FILTER_CONSTRAINT_GREATER:
|
case FILTER_CONSTRAINT_GREATER:
|
||||||
return (d->when >= c.data.timestamp_range.from) != c.negate;
|
return (d->when >= c.data.timestamp_range.from) != c.negate;
|
||||||
case FILTER_CONSTRAINT_RANGE:
|
case FILTER_CONSTRAINT_RANGE:
|
||||||
return (d->when >= c.data.timestamp_range.from &&
|
return (d->when >= c.data.timestamp_range.from &&
|
||||||
dive_endtime(d) <= c.data.timestamp_range.to) != c.negate;
|
d->endtime() <= c.data.timestamp_range.to) != c.negate;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -979,14 +972,14 @@ static bool check_time_of_day_internal(const dive *d, enum filter_constraint_ran
|
|||||||
// where the given timestamp is during that dive. Note: this will fail for dives
|
// where the given timestamp is during that dive. Note: this will fail for dives
|
||||||
// that run past midnight. We might want to special case that.
|
// that run past midnight. We might want to special case that.
|
||||||
return (seconds_since_midnight(d->when) <= from &&
|
return (seconds_since_midnight(d->when) <= from &&
|
||||||
seconds_since_midnight(dive_endtime(d)) >= from) != negate;
|
seconds_since_midnight(d->endtime()) >= from) != negate;
|
||||||
case FILTER_CONSTRAINT_LESS:
|
case FILTER_CONSTRAINT_LESS:
|
||||||
return (seconds_since_midnight(dive_endtime(d)) <= to) != negate;
|
return (seconds_since_midnight(d->endtime()) <= to) != negate;
|
||||||
case FILTER_CONSTRAINT_GREATER:
|
case FILTER_CONSTRAINT_GREATER:
|
||||||
return (seconds_since_midnight(d->when) >= from) != negate;
|
return (seconds_since_midnight(d->when) >= from) != negate;
|
||||||
case FILTER_CONSTRAINT_RANGE:
|
case FILTER_CONSTRAINT_RANGE:
|
||||||
return (seconds_since_midnight(d->when) >= from &&
|
return (seconds_since_midnight(d->when) >= from &&
|
||||||
seconds_since_midnight(dive_endtime(d)) <= to) != negate;
|
seconds_since_midnight(d->endtime()) <= to) != negate;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1074,11 +1067,11 @@ bool filter_constraint_match_dive(const filter_constraint &c, const struct dive
|
|||||||
case FILTER_CONSTRAINT_SAC:
|
case FILTER_CONSTRAINT_SAC:
|
||||||
return check_numerical_range_non_zero(c, d->sac);
|
return check_numerical_range_non_zero(c, d->sac);
|
||||||
case FILTER_CONSTRAINT_LOGGED:
|
case FILTER_CONSTRAINT_LOGGED:
|
||||||
return is_logged(d) != c.negate;
|
return d->is_logged() != c.negate;
|
||||||
case FILTER_CONSTRAINT_PLANNED:
|
case FILTER_CONSTRAINT_PLANNED:
|
||||||
return is_planned(d) != c.negate;
|
return d->is_planned() != c.negate;
|
||||||
case FILTER_CONSTRAINT_DIVE_MODE:
|
case FILTER_CONSTRAINT_DIVE_MODE:
|
||||||
return check_multiple_choice(c, (int)d->dc.divemode); // should we be smarter and check all DCs?
|
return check_multiple_choice(c, (int)d->dcs[0].divemode); // should we be smarter and check all DCs?
|
||||||
case FILTER_CONSTRAINT_TAGS:
|
case FILTER_CONSTRAINT_TAGS:
|
||||||
return has_tags(c, d);
|
return has_tags(c, d);
|
||||||
case FILTER_CONSTRAINT_PEOPLE:
|
case FILTER_CONSTRAINT_PEOPLE:
|
||||||
|
|||||||
@ -5,16 +5,10 @@
|
|||||||
#define FILTER_CONSTRAINT_H
|
#define FILTER_CONSTRAINT_H
|
||||||
|
|
||||||
#include "units.h"
|
#include "units.h"
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
struct dive;
|
struct dive;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#include <QStringList>
|
|
||||||
extern "C" {
|
|
||||||
#else
|
|
||||||
typedef void QStringList;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum filter_constraint_type {
|
enum filter_constraint_type {
|
||||||
FILTER_CONSTRAINT_DATE,
|
FILTER_CONSTRAINT_DATE,
|
||||||
FILTER_CONSTRAINT_DATE_TIME,
|
FILTER_CONSTRAINT_DATE_TIME,
|
||||||
@ -82,16 +76,14 @@ struct filter_constraint {
|
|||||||
QStringList *string_list;
|
QStringList *string_list;
|
||||||
uint64_t multiple_choice; // bit-field for multiple choice lists. currently, we support 64 items, extend if needed.
|
uint64_t multiple_choice; // bit-field for multiple choice lists. currently, we support 64 items, extend if needed.
|
||||||
} data;
|
} data;
|
||||||
#ifdef __cplusplus
|
|
||||||
// For C++, define constructors, assignment operators and destructor to make our lives easier.
|
// For C++, define constructors, assignment operators and destructor to make our lives easier.
|
||||||
filter_constraint(filter_constraint_type type);
|
filter_constraint(filter_constraint_type type);
|
||||||
filter_constraint(const char *type, const char *string_mode,
|
filter_constraint(const std::string &type, const std::string &string_mode,
|
||||||
const char *range_mode, bool negate, const char *data); // from parser data
|
const std::string &range_mode, bool negate, const std::string &data); // from parser data
|
||||||
filter_constraint(const filter_constraint &);
|
filter_constraint(const filter_constraint &);
|
||||||
filter_constraint &operator=(const filter_constraint &);
|
filter_constraint &operator=(const filter_constraint &);
|
||||||
~filter_constraint();
|
~filter_constraint();
|
||||||
bool operator==(const filter_constraint &f2) const;
|
bool operator==(const filter_constraint &f2) const;
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const char *filter_constraint_type_to_string(enum filter_constraint_type);
|
extern const char *filter_constraint_type_to_string(enum filter_constraint_type);
|
||||||
@ -117,12 +109,6 @@ extern bool filter_constraint_has_time_widget(enum filter_constraint_type);
|
|||||||
extern int filter_constraint_num_decimals(enum filter_constraint_type);
|
extern int filter_constraint_num_decimals(enum filter_constraint_type);
|
||||||
extern bool filter_constraint_is_valid(const struct filter_constraint *constraint);
|
extern bool filter_constraint_is_valid(const struct filter_constraint *constraint);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// C++ only functions
|
|
||||||
#ifdef __cplusplus
|
|
||||||
QString filter_constraint_type_to_string_translated(enum filter_constraint_type);
|
QString filter_constraint_type_to_string_translated(enum filter_constraint_type);
|
||||||
QString filter_constraint_negate_to_string_translated(bool negate);
|
QString filter_constraint_negate_to_string_translated(bool negate);
|
||||||
QString filter_constraint_string_mode_to_string_translated(enum filter_constraint_string_mode);
|
QString filter_constraint_string_mode_to_string_translated(enum filter_constraint_string_mode);
|
||||||
@ -151,8 +137,6 @@ void filter_constraint_set_timestamp_from(filter_constraint &c, timestamp_t from
|
|||||||
void filter_constraint_set_timestamp_to(filter_constraint &c, timestamp_t to); // convert according to current units (metric or imperial)
|
void filter_constraint_set_timestamp_to(filter_constraint &c, timestamp_t to); // convert according to current units (metric or imperial)
|
||||||
void filter_constraint_set_multiple_choice(filter_constraint &c, uint64_t);
|
void filter_constraint_set_multiple_choice(filter_constraint &c, uint64_t);
|
||||||
bool filter_constraint_match_dive(const filter_constraint &c, const struct dive *d);
|
bool filter_constraint_match_dive(const filter_constraint &c, const struct dive *d);
|
||||||
std::string filter_constraint_data_to_string(const struct filter_constraint *constraint); // caller takes ownership of returned string
|
std::string filter_constraint_data_to_string(const struct filter_constraint &constraint); // caller takes ownership of returned string
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -4,24 +4,14 @@
|
|||||||
#include "qthelper.h"
|
#include "qthelper.h"
|
||||||
#include "subsurface-string.h"
|
#include "subsurface-string.h"
|
||||||
|
|
||||||
static filter_preset_table &global_table()
|
std::string filter_preset::fulltext_query() const
|
||||||
{
|
{
|
||||||
return *divelog.filter_presets;
|
return data.fullText.originalQuery.toStdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int filter_presets_count(void)
|
const char *filter_preset::fulltext_mode() const
|
||||||
{
|
{
|
||||||
return (int)global_table().size();
|
switch (data.fulltextStringMode) {
|
||||||
}
|
|
||||||
|
|
||||||
extern std::string filter_preset_fulltext_query(int preset)
|
|
||||||
{
|
|
||||||
return global_table()[preset].data.fullText.originalQuery.toStdString();
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" const char *filter_preset_fulltext_mode(int preset)
|
|
||||||
{
|
|
||||||
switch (global_table()[preset].data.fulltextStringMode) {
|
|
||||||
default:
|
default:
|
||||||
case StringFilterMode::SUBSTRING:
|
case StringFilterMode::SUBSTRING:
|
||||||
return "substring";
|
return "substring";
|
||||||
@ -32,98 +22,19 @@ extern "C" const char *filter_preset_fulltext_mode(int preset)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void filter_preset_set_fulltext(struct filter_preset *preset, const char *fulltext, const char *fulltext_string_mode)
|
void filter_preset::set_fulltext(const std::string fulltext, const std::string &fulltext_string_mode)
|
||||||
{
|
{
|
||||||
if (same_string(fulltext_string_mode, "substring"))
|
if (fulltext_string_mode == "substring")
|
||||||
preset->data.fulltextStringMode = StringFilterMode::SUBSTRING;
|
data.fulltextStringMode = StringFilterMode::SUBSTRING;
|
||||||
else if (same_string(fulltext_string_mode, "startswith"))
|
else if (fulltext_string_mode == "startswith")
|
||||||
preset->data.fulltextStringMode = StringFilterMode::STARTSWITH;
|
data.fulltextStringMode = StringFilterMode::STARTSWITH;
|
||||||
else // if (same_string(fulltext_string_mode, "exact"))
|
else // if (fulltext_string_mode == "exact"))
|
||||||
preset->data.fulltextStringMode = StringFilterMode::EXACT;
|
data.fulltextStringMode = StringFilterMode::EXACT;
|
||||||
preset->data.fullText = fulltext;
|
data.fullText = QString::fromStdString(std::move(fulltext));
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int filter_preset_constraint_count(int preset)
|
void filter_preset::add_constraint(const std::string &type, const std::string &string_mode,
|
||||||
|
const std::string &range_mode, bool negate, const std::string &constraint_data)
|
||||||
{
|
{
|
||||||
return (int)global_table()[preset].data.constraints.size();
|
data.constraints.emplace_back(type, string_mode, range_mode, negate, constraint_data);
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" const filter_constraint *filter_preset_constraint(int preset, int constraint)
|
|
||||||
{
|
|
||||||
return &global_table()[preset].data.constraints[constraint];
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void filter_preset_set_name(struct filter_preset *preset, const char *name)
|
|
||||||
{
|
|
||||||
preset->name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int filter_preset_add_to_table(const std::string name, const FilterData &d, struct filter_preset_table &table)
|
|
||||||
{
|
|
||||||
// std::lower_bound does a binary search - the vector must be sorted.
|
|
||||||
filter_preset newEntry { name, d };
|
|
||||||
auto it = std::lower_bound(table.begin(), table.end(), newEntry,
|
|
||||||
[](const filter_preset &p1, const filter_preset &p2)
|
|
||||||
{ return p1.name < p2.name; });
|
|
||||||
it = table.insert(it, newEntry);
|
|
||||||
return it - table.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take care that the name doesn't already exist by adding numbers
|
|
||||||
static std::string get_unique_preset_name(const std::string &orig, const struct filter_preset_table &table)
|
|
||||||
{
|
|
||||||
std::string res = orig;
|
|
||||||
int count = 2;
|
|
||||||
while (std::find_if(table.begin(), table.end(),
|
|
||||||
[&res](const filter_preset &preset)
|
|
||||||
{ return preset.name == res; }) != table.end()) {
|
|
||||||
res = orig + "#" + std::to_string(count);
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void add_filter_preset_to_table(const struct filter_preset *preset, struct filter_preset_table *table)
|
|
||||||
{
|
|
||||||
std::string name = get_unique_preset_name(preset->name, *table);
|
|
||||||
filter_preset_add_to_table(name, preset->data, *table);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void filter_preset_add_constraint(struct filter_preset *preset, const char *type, const char *string_mode,
|
|
||||||
const char *range_mode, bool negate, const char *data)
|
|
||||||
{
|
|
||||||
preset->data.constraints.emplace_back(type, string_mode, range_mode, negate, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
int filter_preset_id(const std::string &name)
|
|
||||||
{
|
|
||||||
auto it = std::find_if(global_table().begin(), global_table().end(),
|
|
||||||
[&name] (filter_preset &p) { return p.name == name; });
|
|
||||||
return it != global_table().end() ? it - global_table().begin() : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string filter_preset_name(int preset)
|
|
||||||
{
|
|
||||||
return global_table()[preset].name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void filter_preset_set(int preset, const FilterData &data)
|
|
||||||
{
|
|
||||||
global_table()[preset].data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
FilterData filter_preset_get(int preset)
|
|
||||||
{
|
|
||||||
return global_table()[preset].data;
|
|
||||||
}
|
|
||||||
|
|
||||||
int filter_preset_add(const std::string &nameIn, const FilterData &d)
|
|
||||||
{
|
|
||||||
std::string name = get_unique_preset_name(nameIn, global_table());
|
|
||||||
return filter_preset_add_to_table(name, d, global_table());
|
|
||||||
}
|
|
||||||
|
|
||||||
void filter_preset_delete(int preset)
|
|
||||||
{
|
|
||||||
global_table().erase(global_table().begin() + preset);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,75 +1,23 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
// A list of filter settings with names. Every name is unique, which
|
|
||||||
// means that saving to an old name will overwrite the old preset.
|
|
||||||
//
|
|
||||||
// Even though the filter data itself is a C++/Qt class to simplify
|
|
||||||
// string manipulation and memory management, the data is accessible
|
|
||||||
// via pure C functions so that it can be written to the log (git or xml).
|
|
||||||
#ifndef FILTER_PRESETS_H
|
#ifndef FILTER_PRESETS_H
|
||||||
#define FILTER_PRESETS_H
|
#define FILTER_PRESETS_H
|
||||||
|
|
||||||
|
#include "divefilter.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
struct dive;
|
struct dive;
|
||||||
struct filter_constraint;
|
struct filter_constraint;
|
||||||
|
struct FilterData;
|
||||||
|
|
||||||
// So that we can pass filter preset table between C and C++ we define
|
|
||||||
// it as an opaque type in C. Thus we can easily create the table in C++
|
|
||||||
// without having to do our own memory management and pass pointers to
|
|
||||||
// void through C.
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#include "divefilter.h"
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
struct filter_preset {
|
struct filter_preset {
|
||||||
std::string name;
|
std::string name;
|
||||||
FilterData data;
|
FilterData data;
|
||||||
|
|
||||||
|
std::string fulltext_query() const; // Fulltext query of filter preset.
|
||||||
|
const char *fulltext_mode() const; // String mode of fulltext query. Ownership is *not* passed to caller.
|
||||||
|
void set_fulltext(const std::string fulltext, const std::string &fulltext_string_mode); // First argument is consumed.
|
||||||
|
void add_constraint(const std::string &type, const std::string &string_mode,
|
||||||
|
const std::string &range_mode, bool negate, const std::string &data); // called by the parser, therefore data passed as strings.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Subclassing standard library containers is generally
|
|
||||||
// not recommended. However, this makes interaction with
|
|
||||||
// C-code easier and since we don't add any member functions,
|
|
||||||
// this is not a problem.
|
|
||||||
struct filter_preset_table : public std::vector<filter_preset>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
#else
|
|
||||||
struct filter_preset;
|
|
||||||
struct filter_preset_table;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The C IO code accesses the filter presets via integer indices.
|
|
||||||
extern int filter_presets_count(void);
|
|
||||||
extern const char *filter_preset_fulltext_mode(int preset); // string mode of fulltext query. ownership is *not* passed to caller.
|
|
||||||
extern int filter_preset_constraint_count(int preset); // number of constraints in the filter preset.
|
|
||||||
extern const struct filter_constraint *filter_preset_constraint(int preset, int constraint); // get constraint. ownership is *not* passed to caller.
|
|
||||||
extern void filter_preset_set_name(struct filter_preset *preset, const char *name);
|
|
||||||
extern void filter_preset_set_fulltext(struct filter_preset *preset, const char *fulltext, const char *fulltext_string_mode);
|
|
||||||
extern void add_filter_preset_to_table(const struct filter_preset *preset, struct filter_preset_table *table);
|
|
||||||
extern void filter_preset_add_constraint(struct filter_preset *preset, const char *type, const char *string_mode,
|
|
||||||
const char *range_mode, bool negate, const char *data); // called by the parser, therefore data passed as strings.
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// C++ only functions
|
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
struct FilterData;
|
|
||||||
|
|
||||||
int filter_preset_id(const std::string &s); // for now, we assume that names are unique. returns -1 if no preset with that name.
|
|
||||||
void filter_preset_set(int preset, const FilterData &d); // this will override a preset if the name already exists.
|
|
||||||
FilterData filter_preset_get(int preset);
|
|
||||||
int filter_preset_add(const std::string &name, const FilterData &d); // returns point of insertion
|
|
||||||
void filter_preset_delete(int preset);
|
|
||||||
std::string filter_preset_name(int preset); // name of filter preset - caller must free the result.
|
|
||||||
std::string filter_preset_fulltext_query(int preset); // fulltext query of filter preset - caller must free the result.
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
53
core/filterpresettable.cpp
Normal file
53
core/filterpresettable.cpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#include "filterpresettable.h"
|
||||||
|
#include "filterpreset.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
int filter_preset_table::preset_id(const std::string &name) const
|
||||||
|
{
|
||||||
|
auto it = std::find_if(begin(), end(), [&name] (const filter_preset &p) { return p.name == name; });
|
||||||
|
return it != end() ? it - begin() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take care that the name doesn't already exist by adding numbers
|
||||||
|
static std::string get_unique_preset_name(const std::string &orig, const struct filter_preset_table &table)
|
||||||
|
{
|
||||||
|
std::string res = orig;
|
||||||
|
int count = 2;
|
||||||
|
while (std::find_if(table.begin(), table.end(),
|
||||||
|
[&res](const filter_preset &preset)
|
||||||
|
{ return preset.name == res; }) != table.end()) {
|
||||||
|
res = orig + "#" + std::to_string(count);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int filter_preset_add_to_table(const std::string name, const FilterData &d, struct filter_preset_table &table)
|
||||||
|
{
|
||||||
|
// std::lower_bound does a binary search - the vector must be sorted.
|
||||||
|
filter_preset newEntry { name, d };
|
||||||
|
auto it = std::lower_bound(table.begin(), table.end(), newEntry,
|
||||||
|
[](const filter_preset &p1, const filter_preset &p2)
|
||||||
|
{ return p1.name < p2.name; });
|
||||||
|
it = table.insert(it, newEntry);
|
||||||
|
return it - table.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void filter_preset_table::add(const filter_preset &preset)
|
||||||
|
{
|
||||||
|
std::string name = get_unique_preset_name(preset.name, *this);
|
||||||
|
filter_preset_add_to_table(name, preset.data, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
int filter_preset_table::add(const std::string &nameIn, const FilterData &d)
|
||||||
|
{
|
||||||
|
std::string name = get_unique_preset_name(nameIn, *this);
|
||||||
|
return filter_preset_add_to_table(name, d, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void filter_preset_table::remove(int preset)
|
||||||
|
{
|
||||||
|
erase(begin() + preset);
|
||||||
|
}
|
||||||
25
core/filterpresettable.h
Normal file
25
core/filterpresettable.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
// A list of filter settings with names. Every name is unique, which
|
||||||
|
// means that saving to an old name will overwrite the old preset.
|
||||||
|
#ifndef FILTER_PRESETSTABLE_H
|
||||||
|
#define FILTER_PRESETSTABLE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct filter_preset;
|
||||||
|
struct FilterData;
|
||||||
|
|
||||||
|
// Subclassing standard library containers is generally
|
||||||
|
// not recommended. However, this makes interaction with
|
||||||
|
// C-code easier and since we don't add any member functions,
|
||||||
|
// this is not a problem.
|
||||||
|
struct filter_preset_table : public std::vector<filter_preset>
|
||||||
|
{
|
||||||
|
void add(const filter_preset &preset);
|
||||||
|
int add(const std::string &name, const FilterData &d); // returns point of insertion
|
||||||
|
void remove(int iox);
|
||||||
|
int preset_id(const std::string &s) const; // for now, we assume that names are unique. returns -1 if no preset with that name
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -353,20 +353,27 @@ std::string casprintf_loc(const char *cformat, ...)
|
|||||||
return std::string(utf8.constData(), utf8.size());
|
return std::string(utf8.constData(), utf8.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string __printf(1, 2) format_string_std(const char *fmt, ...)
|
std::string format_string_std(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
size_t stringsize = vsnprintf(NULL, 0, fmt, ap);
|
std::string res = vformat_string_std(fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string vformat_string_std(const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
va_list ap2;
|
||||||
|
va_copy(ap2, ap);
|
||||||
|
size_t stringsize = vsnprintf(NULL, 0, fmt, ap2);
|
||||||
|
va_end(ap2);
|
||||||
if (stringsize == 0)
|
if (stringsize == 0)
|
||||||
return std::string();
|
return std::string();
|
||||||
std::string res;
|
std::string res;
|
||||||
res.resize(stringsize); // Pointless clearing, oh my.
|
res.resize(stringsize); // Pointless clearing, oh my.
|
||||||
// This overwrites the terminal null-byte of std::string.
|
// This overwrites the terminal null-byte of std::string.
|
||||||
// That's probably "undefined behavior". Oh my.
|
// That's probably "undefined behavior". Oh my.
|
||||||
va_start(ap, fmt);
|
|
||||||
vsnprintf(res.data(), stringsize + 1, fmt, ap);
|
vsnprintf(res.data(), stringsize + 1, fmt, ap);
|
||||||
va_end(ap);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,12 +7,11 @@
|
|||||||
#define __printf(x, y)
|
#define __printf(x, y)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
__printf(1, 2) QString qasprintf_loc(const char *cformat, ...);
|
__printf(1, 2) QString qasprintf_loc(const char *cformat, ...);
|
||||||
__printf(1, 0) QString vqasprintf_loc(const char *cformat, va_list ap);
|
__printf(1, 0) QString vqasprintf_loc(const char *cformat, va_list ap);
|
||||||
__printf(1, 2) std::string casprintf_loc(const char *cformat, ...);
|
__printf(1, 2) std::string casprintf_loc(const char *cformat, ...);
|
||||||
|
__printf(1, 0) std::string vformat_string_std(const char *fmt, va_list ap);
|
||||||
__printf(1, 2) std::string format_string_std(const char *fmt, ...);
|
__printf(1, 2) std::string format_string_std(const char *fmt, ...);
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -10,11 +10,6 @@
|
|||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
// This class caches each dives words, so that we can unregister a dive from the full text search
|
|
||||||
struct full_text_cache {
|
|
||||||
std::vector<QString> words;
|
|
||||||
};
|
|
||||||
|
|
||||||
// The FullText-search class
|
// The FullText-search class
|
||||||
class FullText {
|
class FullText {
|
||||||
std::map<QString, std::vector<dive *>> words; // Dives that belong to each word
|
std::map<QString, std::vector<dive *>> words; // Dives that belong to each word
|
||||||
@ -35,8 +30,6 @@ static FullText self;
|
|||||||
|
|
||||||
// C-interface functions
|
// C-interface functions
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
void fulltext_register(struct dive *d)
|
void fulltext_register(struct dive *d)
|
||||||
{
|
{
|
||||||
self.registerDive(d);
|
self.registerDive(d);
|
||||||
@ -57,8 +50,6 @@ void fulltext_populate()
|
|||||||
self.populate();
|
self.populate();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // extern "C"
|
|
||||||
|
|
||||||
// C++-only interface functions
|
// C++-only interface functions
|
||||||
FullTextResult fulltext_find_dives(const FullTextQuery &q, StringFilterMode mode)
|
FullTextResult fulltext_find_dives(const FullTextQuery &q, StringFilterMode mode)
|
||||||
{
|
{
|
||||||
@ -123,31 +114,27 @@ static void tokenize(QString s, std::vector<QString> &res)
|
|||||||
static std::vector<QString> getWords(const dive *d)
|
static std::vector<QString> getWords(const dive *d)
|
||||||
{
|
{
|
||||||
std::vector<QString> res;
|
std::vector<QString> res;
|
||||||
tokenize(QString(d->notes), res);
|
tokenize(QString::fromStdString(d->notes), res);
|
||||||
tokenize(QString(d->diveguide), res);
|
tokenize(QString::fromStdString(d->diveguide), res);
|
||||||
tokenize(QString(d->buddy), res);
|
tokenize(QString::fromStdString(d->buddy), res);
|
||||||
tokenize(QString(d->suit), res);
|
tokenize(QString::fromStdString(d->suit), res);
|
||||||
for (const tag_entry *tag = d->tag_list; tag; tag = tag->next)
|
for (const divetag *tag: d->tags)
|
||||||
tokenize(QString::fromStdString(tag->tag->name), res);
|
tokenize(QString::fromStdString(tag->name), res);
|
||||||
for (int i = 0; i < d->cylinders.nr; ++i) {
|
for (auto &cyl: d->cylinders)
|
||||||
const cylinder_t &cyl = *get_cylinder(d, i);
|
tokenize(QString::fromStdString(cyl.type.description), res);
|
||||||
tokenize(QString(cyl.type.description), res);
|
for (auto &ws: d->weightsystems)
|
||||||
}
|
tokenize(QString::fromStdString(ws.description), res);
|
||||||
for (int i = 0; i < d->weightsystems.nr; ++i) {
|
|
||||||
const weightsystem_t &ws = d->weightsystems.weightsystems[i];
|
|
||||||
tokenize(QString(ws.description), res);
|
|
||||||
}
|
|
||||||
// TODO: We should tokenize all dive-sites and trips first and then
|
// TODO: We should tokenize all dive-sites and trips first and then
|
||||||
// take the tokens from a cache.
|
// take the tokens from a cache.
|
||||||
if (d->dive_site) {
|
if (d->dive_site) {
|
||||||
tokenize(d->dive_site->name, res);
|
tokenize(QString::fromStdString(d->dive_site->name), res);
|
||||||
const char *country = taxonomy_get_country(&d->dive_site->taxonomy);
|
std::string country = taxonomy_get_country(d->dive_site->taxonomy);
|
||||||
if (country)
|
if (!country.empty())
|
||||||
tokenize(country, res);
|
tokenize(country.c_str(), res);
|
||||||
}
|
}
|
||||||
// TODO: We should index trips separately!
|
// TODO: We should index trips separately!
|
||||||
if (d->divetrip)
|
if (d->divetrip)
|
||||||
tokenize(d->divetrip->location, res);
|
tokenize(QString::fromStdString(d->divetrip->location), res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,11 +143,9 @@ void FullText::populate()
|
|||||||
// we want this to be two calls as the second text is overwritten below by the lines starting with "\r"
|
// we want this to be two calls as the second text is overwritten below by the lines starting with "\r"
|
||||||
uiNotification(QObject::tr("Create full text index"));
|
uiNotification(QObject::tr("Create full text index"));
|
||||||
uiNotification(QObject::tr("start processing"));
|
uiNotification(QObject::tr("start processing"));
|
||||||
int i;
|
for (auto &d: divelog.dives)
|
||||||
dive *d;
|
registerDive(d.get());
|
||||||
for_each_dive(i, d)
|
uiNotification(QObject::tr("%1 dives processed").arg(divelog.dives.size()));
|
||||||
registerDive(d);
|
|
||||||
uiNotification(QObject::tr("%1 dives processed").arg(divelog.dives->nr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FullText::registerDive(struct dive *d)
|
void FullText::registerDive(struct dive *d)
|
||||||
@ -168,7 +153,7 @@ void FullText::registerDive(struct dive *d)
|
|||||||
if (d->full_text)
|
if (d->full_text)
|
||||||
unregisterWords(d, d->full_text->words);
|
unregisterWords(d, d->full_text->words);
|
||||||
else
|
else
|
||||||
d->full_text = new full_text_cache;
|
d->full_text = std::make_unique<full_text_cache>();
|
||||||
d->full_text->words = getWords(d);
|
d->full_text->words = getWords(d);
|
||||||
registerWords(d, d->full_text->words);
|
registerWords(d, d->full_text->words);
|
||||||
}
|
}
|
||||||
@ -178,18 +163,13 @@ void FullText::unregisterDive(struct dive *d)
|
|||||||
if (!d->full_text)
|
if (!d->full_text)
|
||||||
return;
|
return;
|
||||||
unregisterWords(d, d->full_text->words);
|
unregisterWords(d, d->full_text->words);
|
||||||
delete d->full_text;
|
d->full_text.reset();
|
||||||
d->full_text = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FullText::unregisterAll()
|
void FullText::unregisterAll()
|
||||||
{
|
{
|
||||||
int i;
|
for (auto &d: divelog.dives)
|
||||||
dive *d;
|
d->full_text.reset();
|
||||||
for_each_dive(i, d) {
|
|
||||||
delete d->full_text;
|
|
||||||
d->full_text = nullptr;
|
|
||||||
}
|
|
||||||
words.clear();
|
words.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,43 +5,30 @@
|
|||||||
// issues such as COW semantics and UTF-16 encoding, it provides
|
// issues such as COW semantics and UTF-16 encoding, it provides
|
||||||
// platform independence and reasonable performance. Therefore,
|
// platform independence and reasonable performance. Therefore,
|
||||||
// this is based in QString instead of std::string.
|
// this is based in QString instead of std::string.
|
||||||
//
|
|
||||||
// To make this accessible from C, this does manual memory management:
|
|
||||||
// Every dive is associated with a cache of words. Thus, when deleting
|
|
||||||
// a dive, a function freeing that data has to be called.
|
|
||||||
|
|
||||||
#ifndef FULLTEXT_H
|
#ifndef FULLTEXT_H
|
||||||
#define FULLTEXT_H
|
#define FULLTEXT_H
|
||||||
|
|
||||||
// 1) The C-accessible interface
|
#include <QString>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct full_text_cache;
|
|
||||||
struct dive;
|
struct dive;
|
||||||
void fulltext_register(struct dive *d); // Note: can be called repeatedly
|
void fulltext_register(struct dive *d); // Note: can be called repeatedly
|
||||||
void fulltext_unregister(struct dive *d); // Note: can be called repeatedly
|
void fulltext_unregister(struct dive *d); // Note: can be called repeatedly
|
||||||
void fulltext_unregister_all(); // Unregisters all dives in the dive table
|
void fulltext_unregister_all(); // Unregisters all dives in the dive table
|
||||||
void fulltext_populate(); // Registers all dives in the dive table
|
void fulltext_populate(); // Registers all dives in the dive table
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 2) The C++-only interface
|
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
enum class StringFilterMode {
|
enum class StringFilterMode {
|
||||||
SUBSTRING = 0,
|
SUBSTRING = 0,
|
||||||
STARTSWITH = 1,
|
STARTSWITH = 1,
|
||||||
EXACT = 2
|
EXACT = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This class caches each dives words, so that we can unregister a dive from the full text search
|
||||||
|
struct full_text_cache {
|
||||||
|
std::vector<QString> words;
|
||||||
|
};
|
||||||
|
|
||||||
// A fulltext query. Basically a list of normalized words we search for
|
// A fulltext query. Basically a list of normalized words we search for
|
||||||
struct FullTextQuery {
|
struct FullTextQuery {
|
||||||
std::vector<QString> words;
|
std::vector<QString> words;
|
||||||
@ -63,4 +50,3 @@ FullTextResult fulltext_find_dives(const FullTextQuery &q, StringFilterMode);
|
|||||||
bool fulltext_dive_matches(const struct dive *d, const FullTextQuery &q, StringFilterMode);
|
bool fulltext_dive_matches(const struct dive *d, const FullTextQuery &q, StringFilterMode);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/* gas-model.c */
|
/* gas-model.cpp */
|
||||||
/* gas compressibility model */
|
/* gas compressibility model */
|
||||||
|
#include <algorithm> // std::clamp
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
@ -46,23 +47,20 @@ double gas_compressibility_factor(struct gasmix gas, double bar)
|
|||||||
-8.83632921053e-08,
|
-8.83632921053e-08,
|
||||||
+5.33304543646e-11
|
+5.33304543646e-11
|
||||||
};
|
};
|
||||||
int o2, he;
|
|
||||||
double Z;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The curve fitting range is only [0,500] bar.
|
* The curve fitting range is only [0,500] bar.
|
||||||
* Anything else is way out of range for cylinder
|
* Anything else is way out of range for cylinder
|
||||||
* pressures.
|
* pressures.
|
||||||
*/
|
*/
|
||||||
if (bar < 0) bar = 0;
|
bar = std::clamp(bar, 0.0, 500.0);
|
||||||
if (bar > 500) bar = 500;
|
|
||||||
|
|
||||||
o2 = get_o2(gas);
|
int o2 = get_o2(gas);
|
||||||
he = get_he(gas);
|
int he = get_he(gas);
|
||||||
|
|
||||||
Z = virial_m1(o2_coefficients, bar) * o2 +
|
double Z = virial_m1(o2_coefficients, bar) * o2 +
|
||||||
virial_m1(he_coefficients, bar) * he +
|
virial_m1(he_coefficients, bar) * he +
|
||||||
virial_m1(n2_coefficients, bar) * (1000 - o2 - he);
|
virial_m1(n2_coefficients, bar) * (1000 - o2 - he);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We add the 1.0 at the very end - the linear mixing of the
|
* We add the 1.0 at the very end - the linear mixing of the
|
||||||
@ -5,6 +5,7 @@
|
|||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <QtGlobal> // for QT_TRANSLATE_NOOP
|
||||||
|
|
||||||
/* Perform isobaric counterdiffusion calculations for gas changes in trimix dives.
|
/* Perform isobaric counterdiffusion calculations for gas changes in trimix dives.
|
||||||
* Here we use the rule-of-fifths where, during a change involving trimix gas, the increase in nitrogen
|
* Here we use the rule-of-fifths where, during a change involving trimix gas, the increase in nitrogen
|
||||||
@ -39,20 +40,20 @@ int same_gasmix(struct gasmix a, struct gasmix b)
|
|||||||
return get_o2(a) == get_o2(b) && get_he(a) == get_he(b);
|
return get_o2(a) == get_o2(b) && get_he(a) == get_he(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sanitize_gasmix(struct gasmix *mix)
|
void sanitize_gasmix(struct gasmix &mix)
|
||||||
{
|
{
|
||||||
unsigned int o2, he;
|
unsigned int o2, he;
|
||||||
|
|
||||||
o2 = get_o2(*mix);
|
o2 = get_o2(mix);
|
||||||
he = get_he(*mix);
|
he = get_he(mix);
|
||||||
|
|
||||||
/* Regular air: leave empty */
|
/* Regular air: leave empty */
|
||||||
if (!he) {
|
if (!he) {
|
||||||
if (!o2)
|
if (!o2)
|
||||||
return;
|
return;
|
||||||
/* 20.8% to 21% O2 is just air */
|
/* 20.8% to 21% O2 is just air */
|
||||||
if (gasmix_is_air(*mix)) {
|
if (gasmix_is_air(mix)) {
|
||||||
mix->o2.permille = 0;
|
mix.o2.permille = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,7 +62,7 @@ void sanitize_gasmix(struct gasmix *mix)
|
|||||||
if (o2 <= 1000 && he <= 1000 && o2 + he <= 1000)
|
if (o2 <= 1000 && he <= 1000 && o2 + he <= 1000)
|
||||||
return;
|
return;
|
||||||
report_info("Odd gasmix: %u O2 %u He", o2, he);
|
report_info("Odd gasmix: %u O2 %u He", o2, he);
|
||||||
memset(mix, 0, sizeof(*mix));
|
mix = gasmix_air;
|
||||||
}
|
}
|
||||||
|
|
||||||
int gasmix_distance(struct gasmix a, struct gasmix b)
|
int gasmix_distance(struct gasmix a, struct gasmix b)
|
||||||
@ -115,42 +116,43 @@ int pscr_o2(const double amb_pressure, struct gasmix mix)
|
|||||||
* The structure "pressures" is used to return calculated gas pressures to the calling software.
|
* The structure "pressures" is used to return calculated gas pressures to the calling software.
|
||||||
* Call parameters: po2 = po2 value applicable to the record in calling function
|
* Call parameters: po2 = po2 value applicable to the record in calling function
|
||||||
* amb_pressure = ambient pressure applicable to the record in calling function
|
* amb_pressure = ambient pressure applicable to the record in calling function
|
||||||
* *pressures = structure for communicating o2 sensor values from and gas pressures to the calling function.
|
|
||||||
* *mix = structure containing cylinder gas mixture information.
|
* *mix = structure containing cylinder gas mixture information.
|
||||||
* divemode = the dive mode pertaining to this point in the dive profile.
|
* divemode = the dive mode pertaining to this point in the dive profile.
|
||||||
* This function called by: calculate_gas_information_new() in profile.cpp; add_segment() in deco.cpp.
|
* This function called by: calculate_gas_information_new() in profile.cpp; add_segment() in deco.cpp.
|
||||||
*/
|
*/
|
||||||
void fill_pressures(struct gas_pressures *pressures, const double amb_pressure, struct gasmix mix, double po2, enum divemode_t divemode)
|
gas_pressures fill_pressures(const double amb_pressure, struct gasmix mix, double po2, enum divemode_t divemode)
|
||||||
{
|
{
|
||||||
if ((divemode != OC) && po2) { // This is a rebreather dive where pressures->o2 is defined
|
struct gas_pressures pressures;
|
||||||
|
if ((divemode != OC) && po2) { // This is a rebreather dive where pressures.o2 is defined
|
||||||
if (po2 >= amb_pressure) {
|
if (po2 >= amb_pressure) {
|
||||||
pressures->o2 = amb_pressure;
|
pressures.o2 = amb_pressure;
|
||||||
pressures->n2 = pressures->he = 0.0;
|
pressures.n2 = pressures.he = 0.0;
|
||||||
} else {
|
} else {
|
||||||
pressures->o2 = po2;
|
pressures.o2 = po2;
|
||||||
if (get_o2(mix) == 1000) {
|
if (get_o2(mix) == 1000) {
|
||||||
pressures->he = pressures->n2 = 0;
|
pressures.he = pressures.n2 = 0;
|
||||||
} else {
|
} else {
|
||||||
pressures->he = (amb_pressure - pressures->o2) * (double)get_he(mix) / (1000 - get_o2(mix));
|
pressures.he = (amb_pressure - pressures.o2) * (double)get_he(mix) / (1000 - get_o2(mix));
|
||||||
pressures->n2 = amb_pressure - pressures->o2 - pressures->he;
|
pressures.n2 = amb_pressure - pressures.o2 - pressures.he;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (divemode == PSCR) { /* The steady state approximation should be good enough */
|
if (divemode == PSCR) { /* The steady state approximation should be good enough */
|
||||||
pressures->o2 = pscr_o2(amb_pressure, mix) / 1000.0;
|
pressures.o2 = pscr_o2(amb_pressure, mix) / 1000.0;
|
||||||
if (get_o2(mix) != 1000) {
|
if (get_o2(mix) != 1000) {
|
||||||
pressures->he = (amb_pressure - pressures->o2) * get_he(mix) / (1000.0 - get_o2(mix));
|
pressures.he = (amb_pressure - pressures.o2) * get_he(mix) / (1000.0 - get_o2(mix));
|
||||||
pressures->n2 = (amb_pressure - pressures->o2) * get_n2(mix) / (1000.0 - get_o2(mix));
|
pressures.n2 = (amb_pressure - pressures.o2) * get_n2(mix) / (1000.0 - get_o2(mix));
|
||||||
} else {
|
} else {
|
||||||
pressures->he = pressures->n2 = 0;
|
pressures.he = pressures.n2 = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Open circuit dives: no gas pressure values available, they need to be calculated
|
// Open circuit dives: no gas pressure values available, they need to be calculated
|
||||||
pressures->o2 = get_o2(mix) / 1000.0 * amb_pressure; // These calculations are also used if the CCR calculation above..
|
pressures.o2 = get_o2(mix) / 1000.0 * amb_pressure; // These calculations are also used if the CCR calculation above..
|
||||||
pressures->he = get_he(mix) / 1000.0 * amb_pressure; // ..returned a po2 of zero (i.e. o2 sensor data not resolvable)
|
pressures.he = get_he(mix) / 1000.0 * amb_pressure; // ..returned a po2 of zero (i.e. o2 sensor data not resolvable)
|
||||||
pressures->n2 = get_n2(mix) / 1000.0 * amb_pressure;
|
pressures.n2 = get_n2(mix) / 1000.0 * amb_pressure;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return pressures;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum gastype gasmix_to_type(struct gasmix mix)
|
enum gastype gasmix_to_type(struct gasmix mix)
|
||||||
16
core/gas.h
16
core/gas.h
@ -5,12 +5,6 @@
|
|||||||
#include "divemode.h"
|
#include "divemode.h"
|
||||||
#include "units.h"
|
#include "units.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#else
|
|
||||||
#include <stdbool.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum gas_component { N2, HE, O2 };
|
enum gas_component { N2, HE, O2 };
|
||||||
|
|
||||||
// o2 == 0 && he == 0 -> air
|
// o2 == 0 && he == 0 -> air
|
||||||
@ -61,13 +55,13 @@ static inline int get_n2(struct gasmix mix)
|
|||||||
int pscr_o2(const double amb_pressure, struct gasmix mix);
|
int pscr_o2(const double amb_pressure, struct gasmix mix);
|
||||||
|
|
||||||
struct gas_pressures {
|
struct gas_pressures {
|
||||||
double o2, n2, he;
|
double o2 = 0.0, n2 = 0.0, he = 0.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void sanitize_gasmix(struct gasmix *mix);
|
extern void sanitize_gasmix(struct gasmix &mix);
|
||||||
extern int gasmix_distance(struct gasmix a, struct gasmix b);
|
extern int gasmix_distance(struct gasmix a, struct gasmix b);
|
||||||
extern fraction_t get_gas_component_fraction(struct gasmix mix, enum gas_component component);
|
extern fraction_t get_gas_component_fraction(struct gasmix mix, enum gas_component component);
|
||||||
extern void fill_pressures(struct gas_pressures *pressures, double amb_pressure, struct gasmix mix, double po2, enum divemode_t dctype);
|
extern gas_pressures fill_pressures(double amb_pressure, struct gasmix mix, double po2, enum divemode_t dctype);
|
||||||
|
|
||||||
extern bool gasmix_is_air(struct gasmix gasmix);
|
extern bool gasmix_is_air(struct gasmix gasmix);
|
||||||
extern bool gasmix_is_invalid(struct gasmix mix);
|
extern bool gasmix_is_invalid(struct gasmix mix);
|
||||||
@ -75,8 +69,4 @@ extern enum gastype gasmix_to_type(struct gasmix mix);
|
|||||||
extern const char *gastype_name(enum gastype type);
|
extern const char *gastype_name(enum gastype type);
|
||||||
extern fraction_t make_fraction(int f);
|
extern fraction_t make_fraction(int f);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/* gaspressures.c
|
/* gaspressures.cpp
|
||||||
* ---------------
|
* ----------------
|
||||||
* This file contains the routines to calculate the gas pressures in the cylinders.
|
* This file contains the routines to calculate the gas pressures in the cylinders.
|
||||||
* The functions below support the code in profile.cpp.
|
* The functions below support the code in profile.cpp.
|
||||||
* The high-level function is populate_pressure_information(), called by function
|
* The high-level function is populate_pressure_information(), called by function
|
||||||
@ -10,15 +10,8 @@
|
|||||||
* populate_pressure_information() -> calc_pressure_time()
|
* populate_pressure_information() -> calc_pressure_time()
|
||||||
* -> fill_missing_tank_pressures() -> fill_missing_segment_pressures()
|
* -> fill_missing_tank_pressures() -> fill_missing_segment_pressures()
|
||||||
* -> get_pr_interpolate_data()
|
* -> get_pr_interpolate_data()
|
||||||
*
|
|
||||||
* The pr_track_t related functions below implement a linked list that is used by
|
|
||||||
* the majority of the functions below. The linked list covers a part of the dive profile
|
|
||||||
* for which there are no cylinder pressure data. Each element in the linked list
|
|
||||||
* represents a segment between two consecutive points on the dive profile.
|
|
||||||
* pr_track_t is defined in gaspressures.h
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ssrf.h"
|
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
#include "profile.h"
|
#include "profile.h"
|
||||||
@ -26,23 +19,29 @@
|
|||||||
#include "pref.h"
|
#include "pref.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* simple structure to track the beginning and end tank pressure as
|
* simple structure to track the beginning and end tank pressure as
|
||||||
* well as the integral of depth over time spent while we have no
|
* well as the integral of depth over time spent while we have no
|
||||||
* pressure reading from the tank */
|
* pressure reading from the tank */
|
||||||
typedef struct pr_track_struct pr_track_t;
|
struct pr_track_t {
|
||||||
struct pr_track_struct {
|
|
||||||
int start;
|
int start;
|
||||||
int end;
|
int end;
|
||||||
int t_start;
|
int t_start;
|
||||||
int t_end;
|
int t_end;
|
||||||
int pressure_time;
|
int pressure_time;
|
||||||
pr_track_t *next;
|
pr_track_t(int start, int t_start) :
|
||||||
|
start(start),
|
||||||
|
end(0),
|
||||||
|
t_start(t_start),
|
||||||
|
t_end(t_start),
|
||||||
|
pressure_time(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct pr_interpolate_struct pr_interpolate_t;
|
struct pr_interpolate_t {
|
||||||
struct pr_interpolate_struct {
|
|
||||||
int start;
|
int start;
|
||||||
int end;
|
int end;
|
||||||
int pressure_time;
|
int pressure_time;
|
||||||
@ -51,61 +50,17 @@ struct pr_interpolate_struct {
|
|||||||
|
|
||||||
enum interpolation_strategy {SAC, TIME, CONSTANT};
|
enum interpolation_strategy {SAC, TIME, CONSTANT};
|
||||||
|
|
||||||
static pr_track_t *pr_track_alloc(int start, int t_start)
|
|
||||||
{
|
|
||||||
pr_track_t *pt = malloc(sizeof(pr_track_t));
|
|
||||||
pt->start = start;
|
|
||||||
pt->end = 0;
|
|
||||||
pt->t_start = pt->t_end = t_start;
|
|
||||||
pt->pressure_time = 0;
|
|
||||||
pt->next = NULL;
|
|
||||||
return pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* poor man's linked list */
|
|
||||||
static pr_track_t *list_last(pr_track_t *list)
|
|
||||||
{
|
|
||||||
pr_track_t *tail = list;
|
|
||||||
if (!tail)
|
|
||||||
return NULL;
|
|
||||||
while (tail->next) {
|
|
||||||
tail = tail->next;
|
|
||||||
}
|
|
||||||
return tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
static pr_track_t *list_add(pr_track_t *list, pr_track_t *element)
|
|
||||||
{
|
|
||||||
pr_track_t *tail = list_last(list);
|
|
||||||
if (!tail)
|
|
||||||
return element;
|
|
||||||
tail->next = element;
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void list_free(pr_track_t *list)
|
|
||||||
{
|
|
||||||
if (!list)
|
|
||||||
return;
|
|
||||||
list_free(list->next);
|
|
||||||
free(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_PR_TRACK
|
#ifdef DEBUG_PR_TRACK
|
||||||
static void dump_pr_track(int cyl, pr_track_t *track_pr)
|
static void dump_pr_track(int cyl, std::vector<pr_track_t> &track_pr)
|
||||||
{
|
{
|
||||||
pr_track_t *list;
|
|
||||||
|
|
||||||
printf("cyl%d:\n", cyl);
|
printf("cyl%d:\n", cyl);
|
||||||
list = track_pr;
|
for (const auto &item: track_pr) {
|
||||||
while (list) {
|
|
||||||
printf(" start %f end %f t_start %d:%02d t_end %d:%02d pt %d\n",
|
printf(" start %f end %f t_start %d:%02d t_end %d:%02d pt %d\n",
|
||||||
mbar_to_PSI(list->start),
|
mbar_to_PSI(item.start),
|
||||||
mbar_to_PSI(list->end),
|
mbar_to_PSI(item.end),
|
||||||
FRACTION_TUPLE(list->t_start, 60),
|
FRACTION_TUPLE(item.t_start, 60),
|
||||||
FRACTION_TUPLE(list->t_end, 60),
|
FRACTION_TUPLE(item.t_end, 60),
|
||||||
list->pressure_time);
|
item.pressure_time);
|
||||||
list = list->next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -128,24 +83,22 @@ static void dump_pr_track(int cyl, pr_track_t *track_pr)
|
|||||||
* segments according to how big of a time_pressure area
|
* segments according to how big of a time_pressure area
|
||||||
* they have.
|
* they have.
|
||||||
*/
|
*/
|
||||||
static void fill_missing_segment_pressures(pr_track_t *list, enum interpolation_strategy strategy)
|
static void fill_missing_segment_pressures(std::vector<pr_track_t> &list, enum interpolation_strategy strategy)
|
||||||
{
|
{
|
||||||
double magic;
|
for (auto it = list.begin(); it != list.end(); ++it) {
|
||||||
|
int start = it->start, end;
|
||||||
while (list) {
|
|
||||||
int start = list->start, end;
|
|
||||||
pr_track_t *tmp = list;
|
|
||||||
int pt_sum = 0, pt = 0;
|
int pt_sum = 0, pt = 0;
|
||||||
|
|
||||||
|
auto tmp = it;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
pt_sum += tmp->pressure_time;
|
pt_sum += tmp->pressure_time;
|
||||||
end = tmp->end;
|
end = tmp->end;
|
||||||
if (end)
|
if (end)
|
||||||
break;
|
break;
|
||||||
end = start;
|
end = start;
|
||||||
if (!tmp->next)
|
if (std::next(tmp) == list.end())
|
||||||
break;
|
break;
|
||||||
tmp = tmp->next;
|
++tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!start)
|
if (!start)
|
||||||
@ -159,37 +112,34 @@ static void fill_missing_segment_pressures(pr_track_t *list, enum interpolation_
|
|||||||
*
|
*
|
||||||
* Now dole out the pressures relative to pressure-time.
|
* Now dole out the pressures relative to pressure-time.
|
||||||
*/
|
*/
|
||||||
list->start = start;
|
it->start = start;
|
||||||
tmp->end = end;
|
tmp->end = end;
|
||||||
switch (strategy) {
|
switch (strategy) {
|
||||||
case SAC:
|
case SAC:
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int pressure;
|
int pressure;
|
||||||
pt += list->pressure_time;
|
pt += it->pressure_time;
|
||||||
pressure = start;
|
pressure = start;
|
||||||
if (pt_sum)
|
if (pt_sum)
|
||||||
pressure -= lrint((start - end) * (double)pt / pt_sum);
|
pressure -= lrint((start - end) * (double)pt / pt_sum);
|
||||||
list->end = pressure;
|
it->end = pressure;
|
||||||
if (list == tmp)
|
if (it == tmp)
|
||||||
break;
|
break;
|
||||||
list = list->next;
|
++it;
|
||||||
list->start = pressure;
|
it->start = pressure;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TIME:
|
case TIME:
|
||||||
if (list->t_end && (tmp->t_start - tmp->t_end)) {
|
if (it->t_end && (tmp->t_start - tmp->t_end)) {
|
||||||
magic = (list->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end);
|
double magic = (it->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end);
|
||||||
list->end = lrint(start - (start - end) * magic);
|
it->end = lrint(start - (start - end) * magic);
|
||||||
} else {
|
} else {
|
||||||
list->end = start;
|
it->end = start;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CONSTANT:
|
case CONSTANT:
|
||||||
list->end = start;
|
it->end = start;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ok, we've done that set of segments */
|
|
||||||
list = list->next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,42 +152,39 @@ void dump_pr_interpolate(int i, pr_interpolate_t interpolate_pr)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static struct pr_interpolate_struct get_pr_interpolate_data(pr_track_t *segment, struct plot_info *pi, int cur)
|
static pr_interpolate_t get_pr_interpolate_data(const pr_track_t &segment, struct plot_info &pi, int cur)
|
||||||
{ // cur = index to pi->entry corresponding to t_end of segment;
|
{ // cur = index to pi.entry corresponding to t_end of segment;
|
||||||
struct pr_interpolate_struct interpolate;
|
pr_interpolate_t interpolate;
|
||||||
int i;
|
int i;
|
||||||
struct plot_data *entry;
|
|
||||||
|
|
||||||
interpolate.start = segment->start;
|
interpolate.start = segment.start;
|
||||||
interpolate.end = segment->end;
|
interpolate.end = segment.end;
|
||||||
interpolate.acc_pressure_time = 0;
|
interpolate.acc_pressure_time = 0;
|
||||||
interpolate.pressure_time = 0;
|
interpolate.pressure_time = 0;
|
||||||
|
|
||||||
for (i = 0; i < pi->nr; i++) {
|
for (i = 0; i < pi.nr; i++) {
|
||||||
entry = pi->entry + i;
|
const plot_data &entry = pi.entry[i];
|
||||||
|
|
||||||
if (entry->sec < segment->t_start)
|
if (entry.sec < segment.t_start)
|
||||||
continue;
|
continue;
|
||||||
interpolate.pressure_time += entry->pressure_time;
|
interpolate.pressure_time += entry.pressure_time;
|
||||||
if (entry->sec >= segment->t_end)
|
if (entry.sec >= segment.t_end)
|
||||||
break;
|
break;
|
||||||
if (i <= cur)
|
if (i <= cur)
|
||||||
interpolate.acc_pressure_time += entry->pressure_time;
|
interpolate.acc_pressure_time += entry.pressure_time;
|
||||||
}
|
}
|
||||||
return interpolate;
|
return interpolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fill_missing_tank_pressures(const struct dive *dive, struct plot_info *pi, pr_track_t *track_pr, int cyl)
|
static void fill_missing_tank_pressures(const struct dive *dive, struct plot_info &pi, std::vector<pr_track_t> &track_pr, int cyl)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct plot_data *entry;
|
|
||||||
pr_interpolate_t interpolate = { 0, 0, 0, 0 };
|
pr_interpolate_t interpolate = { 0, 0, 0, 0 };
|
||||||
pr_track_t *last_segment = NULL;
|
|
||||||
int cur_pr;
|
int cur_pr;
|
||||||
enum interpolation_strategy strategy;
|
enum interpolation_strategy strategy;
|
||||||
|
|
||||||
/* no segment where this cylinder is used */
|
/* no segment where this cylinder is used */
|
||||||
if (!track_pr)
|
if (track_pr.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (get_cylinder(dive, cyl)->cylinder_use == OC_GAS)
|
if (get_cylinder(dive, cyl)->cylinder_use == OC_GAS)
|
||||||
@ -245,7 +192,7 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf
|
|||||||
else
|
else
|
||||||
strategy = TIME;
|
strategy = TIME;
|
||||||
fill_missing_segment_pressures(track_pr, strategy); // Interpolate the missing tank pressure values ..
|
fill_missing_segment_pressures(track_pr, strategy); // Interpolate the missing tank pressure values ..
|
||||||
cur_pr = track_pr->start; // in the pr_track_t lists of structures
|
cur_pr = track_pr[0].start; // in the pr_track_t lists of structures
|
||||||
// and keep the starting pressure for each cylinder.
|
// and keep the starting pressure for each cylinder.
|
||||||
#ifdef DEBUG_PR_TRACK
|
#ifdef DEBUG_PR_TRACK
|
||||||
dump_pr_track(cyl, track_pr);
|
dump_pr_track(cyl, track_pr);
|
||||||
@ -261,47 +208,44 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf
|
|||||||
*
|
*
|
||||||
* The first two pi structures are "fillers", but in case we don't have a sample
|
* The first two pi structures are "fillers", but in case we don't have a sample
|
||||||
* at time 0 we need to process the second of them here, therefore i=1 */
|
* at time 0 we need to process the second of them here, therefore i=1 */
|
||||||
for (i = 1; i < pi->nr; i++) { // For each point on the profile:
|
auto last_segment = track_pr.end();
|
||||||
double magic;
|
for (i = 1; i < pi.nr; i++) { // For each point on the profile:
|
||||||
pr_track_t *segment;
|
const struct plot_data &entry = pi.entry[i];
|
||||||
int pressure;
|
|
||||||
|
|
||||||
entry = pi->entry + i;
|
int pressure = get_plot_pressure(pi, i, cyl);
|
||||||
|
|
||||||
pressure = get_plot_pressure(pi, i, cyl);
|
if (pressure) { // If there is a valid pressure value,
|
||||||
|
last_segment = track_pr.end(); // get rid of interpolation data,
|
||||||
if (pressure) { // If there is a valid pressure value,
|
cur_pr = pressure; // set current pressure
|
||||||
last_segment = NULL; // get rid of interpolation data,
|
continue; // and skip to next point.
|
||||||
cur_pr = pressure; // set current pressure
|
|
||||||
continue; // and skip to next point.
|
|
||||||
}
|
}
|
||||||
// If there is NO valid pressure value..
|
// If there is NO valid pressure value..
|
||||||
// Find the pressure segment corresponding to this entry..
|
// Find the pressure segment corresponding to this entry..
|
||||||
segment = track_pr;
|
auto it = track_pr.begin();
|
||||||
while (segment && segment->t_end < entry->sec) // Find the track_pr with end time..
|
while (it != track_pr.end() && it->t_end < entry.sec) // Find the track_pr with end time..
|
||||||
segment = segment->next; // ..that matches the plot_info time (entry->sec)
|
++it; // ..that matches the plot_info time (entry.sec)
|
||||||
|
|
||||||
// After last segment? All done.
|
// After last segment? All done.
|
||||||
if (!segment)
|
if (it == track_pr.end())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Before first segment, or between segments.. Go on, no interpolation.
|
// Before first segment, or between segments.. Go on, no interpolation.
|
||||||
if (segment->t_start > entry->sec)
|
if (it->t_start > entry.sec)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!segment->pressure_time) { // Empty segment?
|
if (!it->pressure_time) { // Empty segment?
|
||||||
set_plot_pressure_data(pi, i, SENSOR_PR, cyl, cur_pr);
|
set_plot_pressure_data(pi, i, SENSOR_PR, cyl, cur_pr);
|
||||||
// Just use our current pressure
|
// Just use our current pressure
|
||||||
continue; // and skip to next point.
|
continue; // and skip to next point.
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a valid segment but no tank pressure ..
|
// If there is a valid segment but no tank pressure ..
|
||||||
if (segment == last_segment) {
|
if (it == last_segment) {
|
||||||
interpolate.acc_pressure_time += entry->pressure_time;
|
interpolate.acc_pressure_time += entry.pressure_time;
|
||||||
} else {
|
} else {
|
||||||
// Set up an interpolation structure
|
// Set up an interpolation structure
|
||||||
interpolate = get_pr_interpolate_data(segment, pi, i);
|
interpolate = get_pr_interpolate_data(*it, pi, i);
|
||||||
last_segment = segment;
|
last_segment = it;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(get_cylinder(dive, cyl)->cylinder_use == OC_GAS) {
|
if(get_cylinder(dive, cyl)->cylinder_use == OC_GAS) {
|
||||||
@ -309,20 +253,19 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf
|
|||||||
/* if this segment has pressure_time, then calculate a new interpolated pressure */
|
/* if this segment has pressure_time, then calculate a new interpolated pressure */
|
||||||
if (interpolate.pressure_time) {
|
if (interpolate.pressure_time) {
|
||||||
/* Overall pressure change over total pressure-time for this segment*/
|
/* Overall pressure change over total pressure-time for this segment*/
|
||||||
magic = (interpolate.end - interpolate.start) / (double)interpolate.pressure_time;
|
double magic = (interpolate.end - interpolate.start) / (double)interpolate.pressure_time;
|
||||||
|
|
||||||
/* Use that overall pressure change to update the current pressure */
|
/* Use that overall pressure change to update the current pressure */
|
||||||
cur_pr = lrint(interpolate.start + magic * interpolate.acc_pressure_time);
|
cur_pr = lrint(interpolate.start + magic * interpolate.acc_pressure_time);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
magic = (interpolate.end - interpolate.start) / (segment->t_end - segment->t_start);
|
double magic = (interpolate.end - interpolate.start) / (it->t_end - it->t_start);
|
||||||
cur_pr = lrint(segment->start + magic * (entry->sec - segment->t_start));
|
cur_pr = lrint(it->start + magic * (entry.sec - it->t_start));
|
||||||
}
|
}
|
||||||
set_plot_pressure_data(pi, i, INTERPOLATED_PR, cyl, cur_pr); // and store the interpolated data in plot_info
|
set_plot_pressure_data(pi, i, INTERPOLATED_PR, cyl, cur_pr); // and store the interpolated data in plot_info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* What's the pressure-time between two plot data entries?
|
* What's the pressure-time between two plot data entries?
|
||||||
* We're calculating the integral of pressure over time by
|
* We're calculating the integral of pressure over time by
|
||||||
@ -334,10 +277,10 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf
|
|||||||
* scale pressures, so it ends up being a unitless scaling
|
* scale pressures, so it ends up being a unitless scaling
|
||||||
* factor.
|
* factor.
|
||||||
*/
|
*/
|
||||||
static inline int calc_pressure_time(const struct dive *dive, struct plot_data *a, struct plot_data *b)
|
static inline int calc_pressure_time(const struct dive *dive, const struct plot_data &a, const struct plot_data &b)
|
||||||
{
|
{
|
||||||
int time = b->sec - a->sec;
|
int time = b.sec - a.sec;
|
||||||
int depth = (a->depth + b->depth) / 2;
|
int depth = (a.depth + b.depth) / 2;
|
||||||
|
|
||||||
if (depth <= SURFACE_THRESHOLD)
|
if (depth <= SURFACE_THRESHOLD)
|
||||||
return 0;
|
return 0;
|
||||||
@ -347,36 +290,33 @@ static inline int calc_pressure_time(const struct dive *dive, struct plot_data *
|
|||||||
|
|
||||||
#ifdef PRINT_PRESSURES_DEBUG
|
#ifdef PRINT_PRESSURES_DEBUG
|
||||||
// A CCR debugging tool that prints the gas pressures in cylinder 0 and in the diluent cylinder, used in populate_pressure_information():
|
// A CCR debugging tool that prints the gas pressures in cylinder 0 and in the diluent cylinder, used in populate_pressure_information():
|
||||||
static void debug_print_pressures(struct plot_info *pi)
|
static void debug_print_pressures(struct plot_info &pi)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < pi->nr; i++)
|
for (i = 0; i < pi.nr; i++)
|
||||||
printf("%5d |%9d | %9d |\n", i, get_plot_sensor_pressure(pi, i), get_plot_interpolated_pressure(pi, i));
|
printf("%5d |%9d | %9d |\n", i, get_plot_sensor_pressure(pi, i), get_plot_interpolated_pressure(pi, i));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* This function goes through the list of tank pressures, of structure plot_info for the dive profile where each
|
/* This function goes through the list of tank pressures, of structure plot_info for the dive profile where each
|
||||||
* item in the list corresponds to one point (node) of the profile. It finds values for which there are no tank
|
* item in the list corresponds to one point (node) of the profile. It finds values for which there are no tank
|
||||||
* pressures (pressure==0). For each missing item (node) of tank pressure it creates a pr_track_alloc structure
|
* pressures (pressure==0). For each missing item (node) of tank pressure it creates a pr_track_t structure
|
||||||
* that represents a segment on the dive profile and that contains tank pressures. There is a linked list of
|
* that represents a segment on the dive profile and that contains tank pressures. There is a linked list of
|
||||||
* pr_track_alloc structures for each cylinder. These pr_track_alloc structures ultimately allow for filling
|
* pr_track_t structures for each cylinder. These pr_track_t structures ultimately allow for filling
|
||||||
* the missing tank pressure values on the dive profile using the depth_pressure of the dive. To do this, it
|
* the missing tank pressure values on the dive profile using the depth_pressure of the dive. To do this, it
|
||||||
* calculates the summed pressure-time value for the duration of the dive and stores these * in the pr_track_alloc
|
* calculates the summed pressure-time value for the duration of the dive and stores these in the pr_track_t
|
||||||
* structures. This function is called by create_plot_info_new() in profile.cpp
|
* structures. This function is called by create_plot_info_new() in profile.cpp
|
||||||
*/
|
*/
|
||||||
void populate_pressure_information(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi, int sensor)
|
void populate_pressure_information(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi, int sensor)
|
||||||
{
|
{
|
||||||
UNUSED(dc);
|
|
||||||
int first, last, cyl;
|
int first, last, cyl;
|
||||||
cylinder_t *cylinder = get_cylinder(dive, sensor);
|
const cylinder_t *cylinder = get_cylinder(dive, sensor);
|
||||||
pr_track_t *track = NULL;
|
std::vector<pr_track_t> track;
|
||||||
pr_track_t *current = NULL;
|
size_t current = std::string::npos;
|
||||||
const struct event *ev, *b_ev;
|
|
||||||
int missing_pr = 0, dense = 1;
|
int missing_pr = 0, dense = 1;
|
||||||
enum divemode_t dmode = dc->divemode;
|
|
||||||
const double gasfactor[5] = {1.0, 0.0, prefs.pscr_ratio/1000.0, 1.0, 1.0 };
|
const double gasfactor[5] = {1.0, 0.0, prefs.pscr_ratio/1000.0, 1.0, 1.0 };
|
||||||
|
|
||||||
if (sensor < 0 || sensor >= dive->cylinders.nr)
|
if (sensor < 0 || static_cast<size_t>(sensor) >= dive->cylinders.size())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* if we have no pressure data whatsoever, this is pointless, so let's just return */
|
/* if we have no pressure data whatsoever, this is pointless, so let's just return */
|
||||||
@ -386,7 +326,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
|
|||||||
|
|
||||||
/* Get a rough range of where we have any pressures at all */
|
/* Get a rough range of where we have any pressures at all */
|
||||||
first = last = -1;
|
first = last = -1;
|
||||||
for (int i = 0; i < pi->nr; i++) {
|
for (int i = 0; i < pi.nr; i++) {
|
||||||
int pressure = get_plot_sensor_pressure(pi, i, sensor);
|
int pressure = get_plot_sensor_pressure(pi, i, sensor);
|
||||||
|
|
||||||
if (!pressure)
|
if (!pressure)
|
||||||
@ -409,34 +349,31 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
|
|||||||
* itself has a gas change event.
|
* itself has a gas change event.
|
||||||
*/
|
*/
|
||||||
cyl = sensor;
|
cyl = sensor;
|
||||||
ev = NULL;
|
event_loop loop_gas("gaschange");
|
||||||
if (has_gaschange_event(dive, dc, sensor))
|
const struct event *ev = has_gaschange_event(dive, dc, sensor) ?
|
||||||
ev = get_next_event(dc->events, "gaschange");
|
loop_gas.next(*dc) : nullptr;
|
||||||
b_ev = get_next_event(dc->events, "modechange");
|
divemode_loop loop_mode(*dc);
|
||||||
|
|
||||||
for (int i = first; i <= last; i++) {
|
for (int i = first; i <= last; i++) {
|
||||||
struct plot_data *entry = pi->entry + i;
|
struct plot_data &entry = pi.entry[i];
|
||||||
int pressure = get_plot_sensor_pressure(pi, i, sensor);
|
int pressure = get_plot_sensor_pressure(pi, i, sensor);
|
||||||
int time = entry->sec;
|
int time = entry.sec;
|
||||||
|
|
||||||
while (ev && ev->time.seconds <= time) { // Find 1st gaschange event after
|
while (ev && ev->time.seconds <= time) { // Find 1st gaschange event after
|
||||||
cyl = get_cylinder_index(dive, ev); // the current gas change.
|
cyl = get_cylinder_index(dive, *ev); // the current gas change.
|
||||||
if (cyl < 0)
|
if (cyl < 0)
|
||||||
cyl = sensor;
|
cyl = sensor;
|
||||||
ev = get_next_event(ev->next, "gaschange");
|
ev = loop_gas.next(*dc);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (b_ev && b_ev->time.seconds <= time) { // Keep existing divemode, then
|
divemode_t dmode = loop_mode.next(time);
|
||||||
dmode = b_ev->value; // find 1st divemode change event after the current
|
|
||||||
b_ev = get_next_event(b_ev->next, "modechange"); // divemode change.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current) { // calculate pressure-time, taking into account the dive mode for this specific segment.
|
if (current != std::string::npos) { // calculate pressure-time, taking into account the dive mode for this specific segment.
|
||||||
entry->pressure_time = (int)(calc_pressure_time(dive, entry - 1, entry) * gasfactor[dmode] + 0.5);
|
entry.pressure_time = (int)(calc_pressure_time(dive, pi.entry[i - 1], entry) * gasfactor[dmode] + 0.5);
|
||||||
current->pressure_time += entry->pressure_time;
|
track[current].pressure_time += entry.pressure_time;
|
||||||
current->t_end = entry->sec;
|
track[current].t_end = entry.sec;
|
||||||
if (pressure)
|
if (pressure)
|
||||||
current->end = pressure;
|
track[current].end = pressure;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have a final pressure for 'current'
|
// We have a final pressure for 'current'
|
||||||
@ -444,7 +381,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
|
|||||||
// current pressure track entry and continue
|
// current pressure track entry and continue
|
||||||
// until we get back to this cylinder.
|
// until we get back to this cylinder.
|
||||||
if (cyl != sensor) {
|
if (cyl != sensor) {
|
||||||
current = NULL;
|
current = std::string::npos;
|
||||||
set_plot_pressure_data(pi, i, SENSOR_PR, sensor, 0);
|
set_plot_pressure_data(pi, i, SENSOR_PR, sensor, 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -453,7 +390,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
|
|||||||
// continue with or without a tracking entry. Mark any
|
// continue with or without a tracking entry. Mark any
|
||||||
// existing tracking entry as non-dense, and remember
|
// existing tracking entry as non-dense, and remember
|
||||||
// to fill in interpolated data.
|
// to fill in interpolated data.
|
||||||
if (current && !pressure) {
|
if (current != std::string::npos && !pressure) {
|
||||||
missing_pr = 1;
|
missing_pr = 1;
|
||||||
dense = 0;
|
dense = 0;
|
||||||
continue;
|
continue;
|
||||||
@ -462,7 +399,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
|
|||||||
// If we already have a pressure tracking entry, and
|
// If we already have a pressure tracking entry, and
|
||||||
// it has not had any missing samples, just continue
|
// it has not had any missing samples, just continue
|
||||||
// using it - there's nothing to interpolate yet.
|
// using it - there's nothing to interpolate yet.
|
||||||
if (current && dense)
|
if (current != std::string::npos && dense)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// We need to start a new tracking entry, either
|
// We need to start a new tracking entry, either
|
||||||
@ -471,18 +408,15 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
|
|||||||
// missing entries that need to be interpolated.
|
// missing entries that need to be interpolated.
|
||||||
// Or maybe we didn't have a previous one at all,
|
// Or maybe we didn't have a previous one at all,
|
||||||
// and this is the first pressure entry.
|
// and this is the first pressure entry.
|
||||||
current = pr_track_alloc(pressure, entry->sec);
|
track.emplace_back(pressure, entry.sec);
|
||||||
track = list_add(track, current);
|
current = track.size() - 1;
|
||||||
dense = 1;
|
dense = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (missing_pr) {
|
if (missing_pr)
|
||||||
fill_missing_tank_pressures(dive, pi, track, sensor);
|
fill_missing_tank_pressures(dive, pi, track, sensor);
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef PRINT_PRESSURES_DEBUG
|
#ifdef PRINT_PRESSURES_DEBUG
|
||||||
debug_print_pressures(pi);
|
debug_print_pressures(pi);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
list_free(track);
|
|
||||||
}
|
}
|
||||||
@ -2,13 +2,6 @@
|
|||||||
#ifndef GASPRESSURES_H
|
#ifndef GASPRESSURES_H
|
||||||
#define GASPRESSURES_H
|
#define GASPRESSURES_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
void populate_pressure_information(const struct dive *, const struct divecomputer *, struct plot_info &, int);
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void populate_pressure_information(const struct dive *, const struct divecomputer *, struct plot_info *, int);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif // GASPRESSURES_H
|
#endif // GASPRESSURES_H
|
||||||
|
|||||||
@ -2,22 +2,10 @@
|
|||||||
#ifndef MYGETTEXT_H
|
#ifndef MYGETTEXT_H
|
||||||
#define MYGETTEXT_H
|
#define MYGETTEXT_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
const char *trGettext(const char *);
|
||||||
|
|
||||||
extern "C" const char *trGettext(const char *);
|
|
||||||
static inline const char *translate(const char *, const char *arg)
|
static inline const char *translate(const char *, const char *arg)
|
||||||
{
|
{
|
||||||
return trGettext(arg);
|
return trGettext(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
/* this is for the Qt based translations */
|
|
||||||
extern const char *trGettext(const char *);
|
|
||||||
#define translate(_context, arg) trGettext(arg)
|
|
||||||
#define QT_TRANSLATE_NOOP(_context, arg) arg
|
|
||||||
#define QT_TRANSLATE_NOOP3(_context, arg, _comment) arg
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // MYGETTEXT_H
|
#endif // MYGETTEXT_H
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
static QHash<QByteArray, QByteArray> translationCache;
|
static QHash<QByteArray, QByteArray> translationCache;
|
||||||
static QMutex lock;
|
static QMutex lock;
|
||||||
|
|
||||||
extern "C" const char *trGettext(const char *text)
|
const char *trGettext(const char *text)
|
||||||
{
|
{
|
||||||
QByteArray key(text);
|
QByteArray key(text);
|
||||||
QMutexLocker l(&lock);
|
QMutexLocker l(&lock);
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
extern "C" const char *trGettext(const char *text);
|
const char *trGettext(const char *text);
|
||||||
|
|
||||||
class gettextFromC {
|
class gettextFromC {
|
||||||
Q_DECLARE_TR_FUNCTIONS(gettextFromC)
|
Q_DECLARE_TR_FUNCTIONS(gettextFromC)
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "ssrf.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -58,7 +57,7 @@ static bool includes_string_caseinsensitive(const char *haystack, const char *ne
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void set_git_update_cb(int(*cb)(const char *))
|
void set_git_update_cb(int(*cb)(const char *))
|
||||||
{
|
{
|
||||||
update_progress_cb = cb;
|
update_progress_cb = cb;
|
||||||
}
|
}
|
||||||
@ -69,7 +68,7 @@ extern "C" void set_git_update_cb(int(*cb)(const char *))
|
|||||||
// proportional - some parts are based on compute performance, some on network speed)
|
// proportional - some parts are based on compute performance, some on network speed)
|
||||||
// they also provide information where in the process we are so we can analyze the log
|
// they also provide information where in the process we are so we can analyze the log
|
||||||
// to understand which parts of the process take how much time.
|
// to understand which parts of the process take how much time.
|
||||||
extern "C" int git_storage_update_progress(const char *text)
|
int git_storage_update_progress(const char *text)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
if (update_progress_cb)
|
if (update_progress_cb)
|
||||||
@ -135,9 +134,6 @@ std::string normalize_cloud_name(const std::string &remote_in)
|
|||||||
|
|
||||||
std::string get_local_dir(const std::string &url, const std::string &branch)
|
std::string get_local_dir(const std::string &url, const std::string &branch)
|
||||||
{
|
{
|
||||||
SHA_CTX ctx;
|
|
||||||
unsigned char hash[20];
|
|
||||||
|
|
||||||
// this optimization could in theory lead to odd things happening if the
|
// this optimization could in theory lead to odd things happening if the
|
||||||
// cloud backend servers ever get out of sync - but when a user switches
|
// cloud backend servers ever get out of sync - but when a user switches
|
||||||
// between those servers (either because one is down, or because the algorithm
|
// between those servers (either because one is down, or because the algorithm
|
||||||
@ -148,11 +144,11 @@ std::string get_local_dir(const std::string &url, const std::string &branch)
|
|||||||
|
|
||||||
// That zero-byte update is so that we don't get hash
|
// That zero-byte update is so that we don't get hash
|
||||||
// collisions for "repo1 branch" vs "repo 1branch".
|
// collisions for "repo1 branch" vs "repo 1branch".
|
||||||
SHA1_Init(&ctx);
|
SHA1 sha;
|
||||||
SHA1_Update(&ctx, remote.c_str(), remote.size());
|
sha.update(remote);
|
||||||
SHA1_Update(&ctx, "", 1);
|
sha.update("", 1);
|
||||||
SHA1_Update(&ctx, branch.c_str(), branch.size());
|
sha.update(branch);
|
||||||
SHA1_Final(hash, &ctx);
|
auto hash = sha.hash();
|
||||||
return format_string_std("%s/cloudstorage/%02x%02x%02x%02x%02x%02x%02x%02x",
|
return format_string_std("%s/cloudstorage/%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||||
system_default_directory(),
|
system_default_directory(),
|
||||||
hash[0], hash[1], hash[2], hash[3],
|
hash[0], hash[1], hash[2], hash[3],
|
||||||
@ -244,7 +240,7 @@ static bool exceeded_auth_attempts()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int credential_ssh_cb(git_cred **out,
|
int credential_ssh_cb(git_cred **out,
|
||||||
const char *,
|
const char *,
|
||||||
const char *,
|
const char *,
|
||||||
unsigned int allowed_types,
|
unsigned int allowed_types,
|
||||||
@ -276,7 +272,7 @@ extern "C" int credential_ssh_cb(git_cred **out,
|
|||||||
return GIT_EUSER;
|
return GIT_EUSER;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int credential_https_cb(git_cred **out,
|
int credential_https_cb(git_cred **out,
|
||||||
const char *,
|
const char *,
|
||||||
const char *,
|
const char *,
|
||||||
unsigned int,
|
unsigned int,
|
||||||
@ -291,7 +287,7 @@ extern "C" int credential_https_cb(git_cred **out,
|
|||||||
return git_cred_userpass_plaintext_new(out, username, password);
|
return git_cred_userpass_plaintext_new(out, username, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int certificate_check_cb(git_cert *cert, int valid, const char *host, void *)
|
int certificate_check_cb(git_cert *cert, int valid, const char *host, void *)
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
report_info("git storage: certificate callback for host %s with validity %d\n", host, valid);
|
report_info("git storage: certificate callback for host %s with validity %d\n", host, valid);
|
||||||
@ -342,7 +338,7 @@ static int update_remote(struct git_info *info, git_remote *origin, git_referenc
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree);
|
int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree);
|
||||||
|
|
||||||
static int try_to_git_merge(struct git_info *info, git_reference **local_p, git_reference *, git_oid *base, const git_oid *local_id, const git_oid *remote_id)
|
static int try_to_git_merge(struct git_info *info, git_reference **local_p, git_reference *, git_oid *base, const git_oid *local_id, const git_oid *remote_id)
|
||||||
{
|
{
|
||||||
@ -350,7 +346,7 @@ static int try_to_git_merge(struct git_info *info, git_reference **local_p, git_
|
|||||||
git_commit *local_commit, *remote_commit, *base_commit;
|
git_commit *local_commit, *remote_commit, *base_commit;
|
||||||
git_index *merged_index;
|
git_index *merged_index;
|
||||||
git_merge_options merge_options;
|
git_merge_options merge_options;
|
||||||
struct membufferpp msg;
|
membuffer msg;
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
char outlocal[41], outremote[41];
|
char outlocal[41], outremote[41];
|
||||||
|
|||||||
@ -4,14 +4,12 @@
|
|||||||
|
|
||||||
#include "git2.h"
|
#include "git2.h"
|
||||||
#include "filterpreset.h"
|
#include "filterpreset.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
struct dive_log;
|
struct dive_log;
|
||||||
|
struct git_oid;
|
||||||
#ifdef __cplusplus
|
struct git_repository;
|
||||||
extern "C" {
|
struct divelog;
|
||||||
#else
|
|
||||||
#include <stdbool.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CLOUD_HOST_US "ssrf-cloud-us.subsurface-divelog.org" // preferred (faster/bigger) server in the US
|
#define CLOUD_HOST_US "ssrf-cloud-us.subsurface-divelog.org" // preferred (faster/bigger) server in the US
|
||||||
#define CLOUD_HOST_U2 "ssrf-cloud-u2.subsurface-divelog.org" // secondary (older) server in the US
|
#define CLOUD_HOST_U2 "ssrf-cloud-u2.subsurface-divelog.org" // secondary (older) server in the US
|
||||||
@ -24,21 +22,12 @@ enum remote_transport { RT_LOCAL, RT_HTTPS, RT_SSH, RT_OTHER };
|
|||||||
|
|
||||||
extern bool git_local_only;
|
extern bool git_local_only;
|
||||||
extern bool git_remote_sync_successful;
|
extern bool git_remote_sync_successful;
|
||||||
extern void clear_git_id(void);
|
extern void clear_git_id();
|
||||||
extern void set_git_id(const struct git_oid *);
|
extern void set_git_id(const struct git_oid *);
|
||||||
void set_git_update_cb(int(*)(const char *));
|
void set_git_update_cb(int(*)(const char *));
|
||||||
int git_storage_update_progress(const char *text);
|
int git_storage_update_progress(const char *text);
|
||||||
int get_authorship(git_repository *repo, git_signature **authorp);
|
int get_authorship(git_repository *repo, git_signature **authorp);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
struct git_oid;
|
|
||||||
struct git_repository;
|
|
||||||
struct divelog;
|
|
||||||
|
|
||||||
struct git_info {
|
struct git_info {
|
||||||
std::string url;
|
std::string url;
|
||||||
std::string branch;
|
std::string branch;
|
||||||
@ -63,6 +52,4 @@ extern int git_load_dives(struct git_info *, struct divelog *log);
|
|||||||
extern int do_git_save(struct git_info *, bool select_only, bool create_empty);
|
extern int do_git_save(struct git_info *, bool select_only, bool create_empty);
|
||||||
extern int git_create_local_repo(const std::string &filename);
|
extern int git_create_local_repo(const std::string &filename);
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif // GITACCESS_H
|
#endif // GITACCESS_H
|
||||||
|
|
||||||
|
|||||||
@ -96,7 +96,7 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const
|
|||||||
|
|
||||||
// For io error or video, return early with the appropriate dummy-icon.
|
// For io error or video, return early with the appropriate dummy-icon.
|
||||||
if (type == MEDIATYPE_IO_ERROR)
|
if (type == MEDIATYPE_IO_ERROR)
|
||||||
return { failImage, MEDIATYPE_IO_ERROR, zero_duration };
|
return { failImage, MEDIATYPE_IO_ERROR, duration_t() };
|
||||||
else if (type == MEDIATYPE_VIDEO)
|
else if (type == MEDIATYPE_VIDEO)
|
||||||
return fetchVideoThumbnail(filename, originalFilename, md.duration);
|
return fetchVideoThumbnail(filename, originalFilename, md.duration);
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const
|
|||||||
// Try to check for a video-file extension. Since we couldn't parse the video file,
|
// Try to check for a video-file extension. Since we couldn't parse the video file,
|
||||||
// we pass 0 as the duration.
|
// we pass 0 as the duration.
|
||||||
if (hasVideoFileExtension(filename))
|
if (hasVideoFileExtension(filename))
|
||||||
return fetchVideoThumbnail(filename, originalFilename, zero_duration);
|
return fetchVideoThumbnail(filename, originalFilename, duration_t());
|
||||||
|
|
||||||
// Give up: we simply couldn't determine what this thing is.
|
// Give up: we simply couldn't determine what this thing is.
|
||||||
// But since we managed to read this file, mark this file in the cache as unknown.
|
// But since we managed to read this file, mark this file in the cache as unknown.
|
||||||
@ -122,9 +122,9 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const
|
|||||||
// to treat requests from other threads. invokeMethod() is Qt's way of calling a
|
// to treat requests from other threads. invokeMethod() is Qt's way of calling a
|
||||||
// function in a different thread, namely the thread the called object is associated to.
|
// function in a different thread, namely the thread the called object is associated to.
|
||||||
QMetaObject::invokeMethod(ImageDownloader::instance(), "load", Qt::AutoConnection, Q_ARG(QUrl, url), Q_ARG(QString, originalFilename));
|
QMetaObject::invokeMethod(ImageDownloader::instance(), "load", Qt::AutoConnection, Q_ARG(QUrl, url), Q_ARG(QString, originalFilename));
|
||||||
return { QImage(), MEDIATYPE_STILL_LOADING, zero_duration };
|
return { QImage(), MEDIATYPE_STILL_LOADING, duration_t() };
|
||||||
}
|
}
|
||||||
return { QImage(), MEDIATYPE_IO_ERROR, zero_duration };
|
return { QImage(), MEDIATYPE_IO_ERROR, duration_t() };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch a picture based on its original filename. If there is a translated filename (obtained either
|
// Fetch a picture based on its original filename. If there is a translated filename (obtained either
|
||||||
@ -140,7 +140,7 @@ Thumbnailer::Thumbnail Thumbnailer::getHashedImage(const QString &filename, bool
|
|||||||
// If there is a translated filename, try that first.
|
// If there is a translated filename, try that first.
|
||||||
// Note that we set the default type to io-error, so that if we didn't try
|
// Note that we set the default type to io-error, so that if we didn't try
|
||||||
// the local filename first, we will load the file from the canonical filename.
|
// the local filename first, we will load the file from the canonical filename.
|
||||||
Thumbnail thumbnail { QImage(), MEDIATYPE_IO_ERROR, zero_duration };
|
Thumbnail thumbnail { QImage(), MEDIATYPE_IO_ERROR, duration_t() };
|
||||||
if (localFilename != filename)
|
if (localFilename != filename)
|
||||||
thumbnail = fetchImage(localFilename, filename, tryDownload);
|
thumbnail = fetchImage(localFilename, filename, tryDownload);
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ Thumbnailer::Thumbnail Thumbnailer::getPictureThumbnailFromStream(QDataStream &s
|
|||||||
{
|
{
|
||||||
QImage res;
|
QImage res;
|
||||||
stream >> res;
|
stream >> res;
|
||||||
return { std::move(res), MEDIATYPE_PICTURE, zero_duration };
|
return { std::move(res), MEDIATYPE_PICTURE, duration_t() };
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thumbnailer::markVideoThumbnail(QImage &img)
|
void Thumbnailer::markVideoThumbnail(QImage &img)
|
||||||
@ -210,7 +210,7 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str
|
|||||||
// Likewise test the duration and number of pictures for sanity (no videos longer than 10 h,
|
// Likewise test the duration and number of pictures for sanity (no videos longer than 10 h,
|
||||||
// no more than 10000 pictures).
|
// no more than 10000 pictures).
|
||||||
if (stream.status() != QDataStream::Ok || duration > 36000 || numPics > 10000)
|
if (stream.status() != QDataStream::Ok || duration > 36000 || numPics > 10000)
|
||||||
return { QImage(), MEDIATYPE_VIDEO, zero_duration };
|
return { QImage(), MEDIATYPE_VIDEO, duration_t() };
|
||||||
|
|
||||||
// If the file didn't contain an image, but user turned on thumbnail extraction, schedule thumbnail
|
// If the file didn't contain an image, but user turned on thumbnail extraction, schedule thumbnail
|
||||||
// for extraction. TODO: save failure to extract thumbnails to disk so that thumbnailing
|
// for extraction. TODO: save failure to extract thumbnails to disk so that thumbnailing
|
||||||
@ -240,7 +240,7 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture
|
|||||||
{
|
{
|
||||||
QString filename = thumbnailFileName(picture_filename);
|
QString filename = thumbnailFileName(picture_filename);
|
||||||
if (filename.isEmpty())
|
if (filename.isEmpty())
|
||||||
return { QImage(), MEDIATYPE_UNKNOWN, zero_duration };
|
return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
|
||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
|
|
||||||
if (prefs.auto_recalculate_thumbnails) {
|
if (prefs.auto_recalculate_thumbnails) {
|
||||||
@ -254,13 +254,13 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture
|
|||||||
if (pictureTime.isValid() && thumbnailTime.isValid() && thumbnailTime < pictureTime) {
|
if (pictureTime.isValid() && thumbnailTime.isValid() && thumbnailTime < pictureTime) {
|
||||||
// Both files exist, have valid timestamps and thumbnail was calculated before picture.
|
// Both files exist, have valid timestamps and thumbnail was calculated before picture.
|
||||||
// Return an empty thumbnail to signal recalculation of the thumbnail
|
// Return an empty thumbnail to signal recalculation of the thumbnail
|
||||||
return { QImage(), MEDIATYPE_UNKNOWN, zero_duration };
|
return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file.open(QIODevice::ReadOnly))
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
return { QImage(), MEDIATYPE_UNKNOWN, zero_duration };
|
return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
|
||||||
QDataStream stream(&file);
|
QDataStream stream(&file);
|
||||||
|
|
||||||
// Each thumbnail file is composed of a media-type and an image file.
|
// Each thumbnail file is composed of a media-type and an image file.
|
||||||
@ -271,8 +271,8 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case MEDIATYPE_PICTURE: return getPictureThumbnailFromStream(stream);
|
case MEDIATYPE_PICTURE: return getPictureThumbnailFromStream(stream);
|
||||||
case MEDIATYPE_VIDEO: return getVideoThumbnailFromStream(stream, picture_filename);
|
case MEDIATYPE_VIDEO: return getVideoThumbnailFromStream(stream, picture_filename);
|
||||||
case MEDIATYPE_UNKNOWN: return { unknownImage, MEDIATYPE_UNKNOWN, zero_duration };
|
case MEDIATYPE_UNKNOWN: return { unknownImage, MEDIATYPE_UNKNOWN, duration_t() };
|
||||||
default: return { QImage(), MEDIATYPE_UNKNOWN, zero_duration };
|
default: return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,7 +319,7 @@ Thumbnailer::Thumbnail Thumbnailer::fetchVideoThumbnail(const QString &filename,
|
|||||||
return { videoImage, MEDIATYPE_VIDEO, duration };
|
return { videoImage, MEDIATYPE_VIDEO, duration };
|
||||||
} else {
|
} else {
|
||||||
// Video-thumbnailing is disabled. Write a thumbnail without picture.
|
// Video-thumbnailing is disabled. Write a thumbnail without picture.
|
||||||
return addVideoThumbnailToCache(originalFilename, duration, QImage(), zero_duration);
|
return addVideoThumbnailToCache(originalFilename, duration, QImage(), duration_t());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,7 +337,7 @@ Thumbnailer::Thumbnail Thumbnailer::addPictureThumbnailToCache(const QString &pi
|
|||||||
stream << thumbnail;
|
stream << thumbnail;
|
||||||
file.commit();
|
file.commit();
|
||||||
}
|
}
|
||||||
return { thumbnail, MEDIATYPE_PICTURE, zero_duration };
|
return { thumbnail, MEDIATYPE_PICTURE, duration_t() };
|
||||||
}
|
}
|
||||||
|
|
||||||
Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &picture_filename)
|
Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &picture_filename)
|
||||||
@ -348,7 +348,7 @@ Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &pi
|
|||||||
QDataStream stream(&file);
|
QDataStream stream(&file);
|
||||||
stream << (quint32)MEDIATYPE_UNKNOWN;
|
stream << (quint32)MEDIATYPE_UNKNOWN;
|
||||||
}
|
}
|
||||||
return { unknownImage, MEDIATYPE_UNKNOWN, zero_duration };
|
return { unknownImage, MEDIATYPE_UNKNOWN, duration_t() };
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thumbnailer::frameExtracted(QString filename, QImage thumbnail, duration_t duration, duration_t offset)
|
void Thumbnailer::frameExtracted(QString filename, QImage thumbnail, duration_t duration, duration_t offset)
|
||||||
@ -374,7 +374,7 @@ void Thumbnailer::frameExtractionFailed(QString filename, duration_t duration)
|
|||||||
{
|
{
|
||||||
// Frame extraction failed, but this was due to ffmpeg not starting
|
// Frame extraction failed, but this was due to ffmpeg not starting
|
||||||
// add to the thumbnail cache as a video image with unknown thumbnail.
|
// add to the thumbnail cache as a video image with unknown thumbnail.
|
||||||
addVideoThumbnailToCache(filename, duration, QImage(), zero_duration);
|
addVideoThumbnailToCache(filename, duration, QImage(), duration_t());
|
||||||
QMutexLocker l(&lock);
|
QMutexLocker l(&lock);
|
||||||
workingOn.remove(filename);
|
workingOn.remove(filename);
|
||||||
}
|
}
|
||||||
@ -435,7 +435,7 @@ void Thumbnailer::imageDownloaded(QString filename)
|
|||||||
|
|
||||||
void Thumbnailer::imageDownloadFailed(QString filename)
|
void Thumbnailer::imageDownloadFailed(QString filename)
|
||||||
{
|
{
|
||||||
emit thumbnailChanged(filename, failImage, zero_duration);
|
emit thumbnailChanged(filename, failImage, duration_t());
|
||||||
QMutexLocker l(&lock);
|
QMutexLocker l(&lock);
|
||||||
workingOn.remove(filename);
|
workingOn.remove(filename);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "ssrf.h"
|
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
#include "divesite.h"
|
#include "divesite.h"
|
||||||
#include "errorhelper.h"
|
#include "errorhelper.h"
|
||||||
@ -29,7 +28,7 @@ static int cobalt_profile_sample(void *param, int, char **data, char **)
|
|||||||
if (data[1])
|
if (data[1])
|
||||||
state->cur_sample->depth.mm = atoi(data[1]);
|
state->cur_sample->depth.mm = atoi(data[1]);
|
||||||
if (data[2])
|
if (data[2])
|
||||||
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(strtod_flags(data[2], NULL, 0)) : F_to_mkelvin(strtod_flags(data[2], NULL, 0));
|
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL));
|
||||||
sample_end(state);
|
sample_end(state);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -64,7 +63,7 @@ static int cobalt_buddies(void *param, int, char **data, char **)
|
|||||||
struct parser_state *state = (struct parser_state *)param;
|
struct parser_state *state = (struct parser_state *)param;
|
||||||
|
|
||||||
if (data[0])
|
if (data[0])
|
||||||
utf8_string(data[0], &state->cur_dive->buddy);
|
utf8_string_std(data[0], &state->cur_dive->buddy);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -81,8 +80,8 @@ static int cobalt_visibility(void *, int, char **, char **)
|
|||||||
|
|
||||||
static int cobalt_location(void *param, int, char **data, char **)
|
static int cobalt_location(void *param, int, char **data, char **)
|
||||||
{
|
{
|
||||||
char **location = (char **)param;
|
std::string *location = (std::string *)param;
|
||||||
*location = data[0] ? strdup(data[0]) : NULL;
|
*location = data[0] ? data[0] : NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +91,7 @@ static int cobalt_dive(void *param, int, char **data, char **)
|
|||||||
int retval = 0;
|
int retval = 0;
|
||||||
struct parser_state *state = (struct parser_state *)param;
|
struct parser_state *state = (struct parser_state *)param;
|
||||||
sqlite3 *handle = state->sql_handle;
|
sqlite3 *handle = state->sql_handle;
|
||||||
char *location, *location_site;
|
std::string location, location_site;
|
||||||
char get_profile_template[] = "select runtime*60,(DepthPressure*10000/SurfacePressure)-10000,p.Temperature from Dive AS d JOIN TrackPoints AS p ON d.Id=p.DiveId where d.Id=%d";
|
char get_profile_template[] = "select runtime*60,(DepthPressure*10000/SurfacePressure)-10000,p.Temperature from Dive AS d JOIN TrackPoints AS p ON d.Id=p.DiveId where d.Id=%d";
|
||||||
char get_cylinder_template[] = "select FO2,FHe,StartingPressure,EndingPressure,TankSize,TankPressure,TotalConsumption from GasMixes where DiveID=%d and StartingPressure>0 and EndingPressure > 0 group by FO2,FHe";
|
char get_cylinder_template[] = "select FO2,FHe,StartingPressure,EndingPressure,TankSize,TankPressure,TotalConsumption from GasMixes where DiveID=%d and StartingPressure>0 and EndingPressure > 0 group by FO2,FHe";
|
||||||
char get_buddy_template[] = "select l.Data from Items AS i, List AS l ON i.Value1=l.Id where i.DiveId=%d and l.Type=4";
|
char get_buddy_template[] = "select l.Data from Items AS i, List AS l ON i.Value1=l.Id where i.DiveId=%d and l.Type=4";
|
||||||
@ -107,7 +106,7 @@ static int cobalt_dive(void *param, int, char **data, char **)
|
|||||||
state->cur_dive->when = (time_t)(atol(data[1]));
|
state->cur_dive->when = (time_t)(atol(data[1]));
|
||||||
|
|
||||||
if (data[4])
|
if (data[4])
|
||||||
utf8_string(data[4], &state->cur_dive->notes);
|
utf8_string_std(data[4], &state->cur_dive->notes);
|
||||||
|
|
||||||
/* data[5] should have information on Units used, but I cannot
|
/* data[5] should have information on Units used, but I cannot
|
||||||
* parse it at all based on the sample log I have received. The
|
* parse it at all based on the sample log I have received. The
|
||||||
@ -119,13 +118,13 @@ static int cobalt_dive(void *param, int, char **data, char **)
|
|||||||
|
|
||||||
/* Cobalt stores the pressures, not the depth */
|
/* Cobalt stores the pressures, not the depth */
|
||||||
if (data[6])
|
if (data[6])
|
||||||
state->cur_dive->dc.maxdepth.mm = atoi(data[6]);
|
state->cur_dive->dcs[0].maxdepth.mm = atoi(data[6]);
|
||||||
|
|
||||||
if (data[7])
|
if (data[7])
|
||||||
state->cur_dive->dc.duration.seconds = atoi(data[7]);
|
state->cur_dive->dcs[0].duration.seconds = atoi(data[7]);
|
||||||
|
|
||||||
if (data[8])
|
if (data[8])
|
||||||
state->cur_dive->dc.surface_pressure.mbar = atoi(data[8]);
|
state->cur_dive->dcs[0].surface_pressure.mbar = atoi(data[8]);
|
||||||
/*
|
/*
|
||||||
* TODO: the deviceid hash should be calculated here.
|
* TODO: the deviceid hash should be calculated here.
|
||||||
*/
|
*/
|
||||||
@ -134,15 +133,15 @@ static int cobalt_dive(void *param, int, char **data, char **)
|
|||||||
if (data[9]) {
|
if (data[9]) {
|
||||||
utf8_string_std(data[9], &state->cur_settings.dc.serial_nr);
|
utf8_string_std(data[9], &state->cur_settings.dc.serial_nr);
|
||||||
state->cur_settings.dc.deviceid = atoi(data[9]);
|
state->cur_settings.dc.deviceid = atoi(data[9]);
|
||||||
state->cur_settings.dc.model = strdup("Cobalt import");
|
state->cur_settings.dc.model = "Cobalt import";
|
||||||
}
|
}
|
||||||
|
|
||||||
dc_settings_end(state);
|
dc_settings_end(state);
|
||||||
settings_end(state);
|
settings_end(state);
|
||||||
|
|
||||||
if (data[9]) {
|
if (data[9]) {
|
||||||
state->cur_dive->dc.deviceid = atoi(data[9]);
|
state->cur_dive->dcs[0].deviceid = atoi(data[9]);
|
||||||
state->cur_dive->dc.model = strdup("Cobalt import");
|
state->cur_dive->dcs[0].model = "Cobalt import";
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder_template, state->cur_dive->number);
|
snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder_template, state->cur_dive->number);
|
||||||
@ -180,19 +179,10 @@ static int cobalt_dive(void *param, int, char **data, char **)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (location && location_site) {
|
if (!location.empty() && !location_site.empty()) {
|
||||||
char *tmp = (char *)malloc(strlen(location) + strlen(location_site) + 4);
|
std::string tmp = location + " / " + location_site;
|
||||||
if (!tmp) {
|
state->log->sites.find_or_create(tmp)->add_dive(state->cur_dive.get());
|
||||||
free(location);
|
|
||||||
free(location_site);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
sprintf(tmp, "%s / %s", location, location_site);
|
|
||||||
add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(tmp, state->log->sites));
|
|
||||||
free(tmp);
|
|
||||||
}
|
}
|
||||||
free(location);
|
|
||||||
free(location_site);
|
|
||||||
|
|
||||||
snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, state->cur_dive->number);
|
snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, state->cur_dive->number);
|
||||||
retval = sqlite3_exec(handle, get_buffer, &cobalt_profile_sample, state, NULL);
|
retval = sqlite3_exec(handle, get_buffer, &cobalt_profile_sample, state, NULL);
|
||||||
@ -206,8 +196,7 @@ static int cobalt_dive(void *param, int, char **data, char **)
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
||||||
extern "C" int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
struct parser_state state;
|
struct parser_state state;
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
#include "errorhelper.h"
|
#include "errorhelper.h"
|
||||||
#include "ssrf.h"
|
|
||||||
#include "subsurface-string.h"
|
#include "subsurface-string.h"
|
||||||
#include "divelist.h"
|
#include "divelist.h"
|
||||||
#include "divelog.h"
|
#include "divelog.h"
|
||||||
@ -270,7 +269,7 @@ static int parse_dan_format(const char *filename, struct xml_params *params, str
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log)
|
int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
std::string mem;
|
std::string mem;
|
||||||
@ -402,7 +401,6 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log)
|
|||||||
char *header[8];
|
char *header[8];
|
||||||
int i, time;
|
int i, time;
|
||||||
timestamp_t date;
|
timestamp_t date;
|
||||||
struct dive *dive;
|
|
||||||
struct divecomputer *dc;
|
struct divecomputer *dc;
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) {
|
for (i = 0; i < 8; i++) {
|
||||||
@ -417,10 +415,10 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log)
|
|||||||
if (!date)
|
if (!date)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
dive = alloc_dive();
|
auto dive = std::make_unique<struct dive>();
|
||||||
dive->when = date;
|
dive->when = date;
|
||||||
dive->number = atoi(header[1]);
|
dive->number = atoi(header[1]);
|
||||||
dc = &dive->dc;
|
dc = &dive->dcs[0];
|
||||||
|
|
||||||
time = 0;
|
time = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@ -438,7 +436,6 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log)
|
|||||||
sample = prepare_sample(dc);
|
sample = prepare_sample(dc);
|
||||||
sample->time.seconds = time;
|
sample->time.seconds = time;
|
||||||
add_sample_data(sample, type, val);
|
add_sample_data(sample, type, val);
|
||||||
finish_sample(dc);
|
|
||||||
|
|
||||||
time++;
|
time++;
|
||||||
dc->duration.seconds = time;
|
dc->duration.seconds = time;
|
||||||
@ -446,7 +443,7 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log)
|
|||||||
break;
|
break;
|
||||||
p = end + 1;
|
p = end + 1;
|
||||||
}
|
}
|
||||||
record_dive_to_table(dive, log->dives);
|
log->dives.record_dive(std::move(dive));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,9 +494,7 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
|
|||||||
bool has_depth = false, has_setpoint = false, has_ndl = false;
|
bool has_depth = false, has_setpoint = false, has_ndl = false;
|
||||||
char *lineptr;
|
char *lineptr;
|
||||||
int prev_time = 0;
|
int prev_time = 0;
|
||||||
cylinder_t cyl = empty_cylinder;
|
|
||||||
|
|
||||||
struct dive *dive;
|
|
||||||
struct divecomputer *dc;
|
struct divecomputer *dc;
|
||||||
struct tm cur_tm;
|
struct tm cur_tm;
|
||||||
|
|
||||||
@ -513,34 +508,40 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
|
|||||||
cur_tm.tm_min = mm;
|
cur_tm.tm_min = mm;
|
||||||
cur_tm.tm_sec = ss;
|
cur_tm.tm_sec = ss;
|
||||||
|
|
||||||
dive = alloc_dive();
|
auto dive = std::make_unique<struct dive>();
|
||||||
dive->when = utc_mktime(&cur_tm);;
|
dive->when = utc_mktime(&cur_tm);;
|
||||||
dive->dc.model = strdup("Poseidon MkVI Discovery");
|
dive->dcs[0].model = "Poseidon MkVI Discovery";
|
||||||
value = parse_mkvi_value(memtxt.data(), "Rig Serial number");
|
value = parse_mkvi_value(memtxt.data(), "Rig Serial number");
|
||||||
dive->dc.deviceid = atoi(value.c_str());
|
dive->dcs[0].deviceid = atoi(value.c_str());
|
||||||
dive->dc.divemode = CCR;
|
dive->dcs[0].divemode = CCR;
|
||||||
dive->dc.no_o2sensors = 2;
|
dive->dcs[0].no_o2sensors = 2;
|
||||||
|
|
||||||
cyl.cylinder_use = OXYGEN;
|
{
|
||||||
cyl.type.size.mliter = 3000;
|
cylinder_t cyl;
|
||||||
cyl.type.workingpressure.mbar = 200000;
|
cyl.cylinder_use = OXYGEN;
|
||||||
cyl.type.description = "3l Mk6";
|
cyl.type.size.mliter = 3000;
|
||||||
cyl.gasmix.o2.permille = 1000;
|
cyl.type.workingpressure.mbar = 200000;
|
||||||
cyl.manually_added = true;
|
cyl.type.description = "3l Mk6";
|
||||||
cyl.bestmix_o2 = 0;
|
cyl.gasmix.o2.permille = 1000;
|
||||||
cyl.bestmix_he = 0;
|
cyl.manually_added = true;
|
||||||
add_cloned_cylinder(&dive->cylinders, cyl);
|
cyl.bestmix_o2 = 0;
|
||||||
|
cyl.bestmix_he = 0;
|
||||||
|
dive->cylinders.push_back(std::move(cyl));
|
||||||
|
}
|
||||||
|
|
||||||
cyl.cylinder_use = DILUENT;
|
{
|
||||||
cyl.type.size.mliter = 3000;
|
cylinder_t cyl;
|
||||||
cyl.type.workingpressure.mbar = 200000;
|
cyl.cylinder_use = DILUENT;
|
||||||
cyl.type.description = "3l Mk6";
|
cyl.type.size.mliter = 3000;
|
||||||
value = parse_mkvi_value(memtxt.data(), "Helium percentage");
|
cyl.type.workingpressure.mbar = 200000;
|
||||||
he = atoi(value.c_str());
|
cyl.type.description = "3l Mk6";
|
||||||
value = parse_mkvi_value(memtxt.data(), "Nitrogen percentage");
|
value = parse_mkvi_value(memtxt.data(), "Helium percentage");
|
||||||
cyl.gasmix.o2.permille = (100 - atoi(value.c_str()) - he) * 10;
|
he = atoi(value.c_str());
|
||||||
cyl.gasmix.he.permille = he * 10;
|
value = parse_mkvi_value(memtxt.data(), "Nitrogen percentage");
|
||||||
add_cloned_cylinder(&dive->cylinders, cyl);
|
cyl.gasmix.o2.permille = (100 - atoi(value.c_str()) - he) * 10;
|
||||||
|
cyl.gasmix.he.permille = he * 10;
|
||||||
|
dive->cylinders.push_back(std::move(cyl));
|
||||||
|
}
|
||||||
|
|
||||||
lineptr = strstr(memtxt.data(), "Dive started at");
|
lineptr = strstr(memtxt.data(), "Dive started at");
|
||||||
while (!empty_string(lineptr) && (lineptr = strchr(lineptr, '\n'))) {
|
while (!empty_string(lineptr) && (lineptr = strchr(lineptr, '\n'))) {
|
||||||
@ -551,9 +552,9 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
|
|||||||
std::string value = parse_mkvi_value(lineptr, key.c_str());
|
std::string value = parse_mkvi_value(lineptr, key.c_str());
|
||||||
if (value.empty())
|
if (value.empty())
|
||||||
break;
|
break;
|
||||||
add_extra_data(&dive->dc, key.c_str(), value.c_str());
|
add_extra_data(&dive->dcs[0], key, value);
|
||||||
}
|
}
|
||||||
dc = &dive->dc;
|
dc = &dive->dcs[0];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read samples from the CSV file. A sample contains all the lines with same timestamp. The CSV file has
|
* Read samples from the CSV file. A sample contains all the lines with same timestamp. The CSV file has
|
||||||
@ -573,10 +574,8 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
auto [memcsv, err] = readfile(csv);
|
auto [memcsv, err] = readfile(csv);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
free_dive(dive);
|
|
||||||
return report_error(translate("gettextFromC", "Poseidon import failed: unable to read '%s'"), csv);
|
return report_error(translate("gettextFromC", "Poseidon import failed: unable to read '%s'"), csv);
|
||||||
}
|
|
||||||
lineptr = memcsv.data();
|
lineptr = memcsv.data();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
struct sample *sample;
|
struct sample *sample;
|
||||||
@ -746,12 +745,11 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
|
|||||||
add_sample_data(sample, POSEIDON_SETPOINT, prev_setpoint);
|
add_sample_data(sample, POSEIDON_SETPOINT, prev_setpoint);
|
||||||
if (!has_ndl && prev_ndl >= 0)
|
if (!has_ndl && prev_ndl >= 0)
|
||||||
add_sample_data(sample, POSEIDON_NDL, prev_ndl);
|
add_sample_data(sample, POSEIDON_NDL, prev_ndl);
|
||||||
finish_sample(dc);
|
|
||||||
|
|
||||||
if (!lineptr || !*lineptr)
|
if (!lineptr || !*lineptr)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
record_dive_to_table(dive, log->dives);
|
log->dives.record_dive(std::move(dive));
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -21,10 +21,6 @@ enum csv_format {
|
|||||||
|
|
||||||
#define MAXCOLDIGITS 10
|
#define MAXCOLDIGITS 10
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log);
|
int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log);
|
||||||
int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log);
|
int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log);
|
||||||
int parse_txt_file(const char *filename, const char *csv, struct divelog *log);
|
int parse_txt_file(const char *filename, const char *csv, struct divelog *log);
|
||||||
@ -32,8 +28,4 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log);
|
|||||||
int parse_seabear_log(const char *filename, struct divelog *log);
|
int parse_seabear_log(const char *filename, struct divelog *log);
|
||||||
int parse_manual_file(const char *filename, struct xml_params *params, struct divelog *log);
|
int parse_manual_file(const char *filename, struct xml_params *params, struct divelog *log);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // IMPORTCSV_H
|
#endif // IMPORTCSV_H
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "ssrf.h"
|
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
#include "divesite.h"
|
#include "divesite.h"
|
||||||
#include "sample.h"
|
#include "sample.h"
|
||||||
@ -56,6 +55,8 @@ static int divinglog_cylinder(void *param, int, char **data, char **)
|
|||||||
|
|
||||||
static int divinglog_profile(void *param, int, char **data, char **)
|
static int divinglog_profile(void *param, int, char **data, char **)
|
||||||
{
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
struct parser_state *state = (struct parser_state *)param;
|
struct parser_state *state = (struct parser_state *)param;
|
||||||
|
|
||||||
int sinterval = 0;
|
int sinterval = 0;
|
||||||
@ -129,14 +130,14 @@ static int divinglog_profile(void *param, int, char **data, char **)
|
|||||||
state->cur_sample->temperature.mkelvin = C_to_mkelvin(temp / 10.0f);
|
state->cur_sample->temperature.mkelvin = C_to_mkelvin(temp / 10.0f);
|
||||||
state->cur_sample->pressure[0].mbar = pressure * 100;
|
state->cur_sample->pressure[0].mbar = pressure * 100;
|
||||||
state->cur_sample->rbt.seconds = rbt;
|
state->cur_sample->rbt.seconds = rbt;
|
||||||
if (oldcyl != tank && tank >= 0 && tank < state->cur_dive->cylinders.nr) {
|
if (oldcyl != tank && tank >= 0 && static_cast<size_t>(tank) < state->cur_dive->cylinders.size()) {
|
||||||
struct gasmix mix = get_cylinder(state->cur_dive, tank)->gasmix;
|
struct gasmix mix = get_cylinder(state->cur_dive.get(), tank)->gasmix;
|
||||||
int o2 = get_o2(mix);
|
int o2 = get_o2(mix);
|
||||||
int he = get_he(mix);
|
int he = get_he(mix);
|
||||||
|
|
||||||
event_start(state);
|
event_start(state);
|
||||||
state->cur_event.time.seconds = time;
|
state->cur_event.time.seconds = time;
|
||||||
strcpy(state->cur_event.name, "gaschange");
|
state->cur_event.name = "gaschange"s;
|
||||||
|
|
||||||
o2 = (o2 + 5) / 10;
|
o2 = (o2 + 5) / 10;
|
||||||
he = (he + 5) / 10;
|
he = (he + 5) / 10;
|
||||||
@ -211,8 +212,8 @@ static int divinglog_profile(void *param, int, char **data, char **)
|
|||||||
* Count the number of o2 sensors
|
* Count the number of o2 sensors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!state->cur_dive->dc.no_o2sensors && (state->cur_sample->o2sensor[0].mbar || state->cur_sample->o2sensor[1].mbar || state->cur_sample->o2sensor[2].mbar)) {
|
if (!state->cur_dive->dcs[0].no_o2sensors && (state->cur_sample->o2sensor[0].mbar || state->cur_sample->o2sensor[1].mbar || state->cur_sample->o2sensor[2].mbar)) {
|
||||||
state->cur_dive->dc.no_o2sensors = state->cur_sample->o2sensor[0].mbar ? 1 : 0 +
|
state->cur_dive->dcs[0].no_o2sensors = state->cur_sample->o2sensor[0].mbar ? 1 : 0 +
|
||||||
state->cur_sample->o2sensor[1].mbar ? 1 : 0 +
|
state->cur_sample->o2sensor[1].mbar ? 1 : 0 +
|
||||||
state->cur_sample->o2sensor[2].mbar ? 1 : 0;
|
state->cur_sample->o2sensor[2].mbar ? 1 : 0;
|
||||||
}
|
}
|
||||||
@ -223,7 +224,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
|
|||||||
if (ptr1[6] - '0') {
|
if (ptr1[6] - '0') {
|
||||||
event_start(state);
|
event_start(state);
|
||||||
state->cur_event.time.seconds = time;
|
state->cur_event.time.seconds = time;
|
||||||
strcpy(state->cur_event.name, "rbt");
|
state->cur_event.name = "rbt"s;
|
||||||
event_end(state);
|
event_end(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +232,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
|
|||||||
if (ptr1[7] - '0') {
|
if (ptr1[7] - '0') {
|
||||||
event_start(state);
|
event_start(state);
|
||||||
state->cur_event.time.seconds = time;
|
state->cur_event.time.seconds = time;
|
||||||
strcpy(state->cur_event.name, "ascent");
|
state->cur_event.name = "ascent"s;
|
||||||
event_end(state);
|
event_end(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +240,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
|
|||||||
if (ptr1[8] - '0') {
|
if (ptr1[8] - '0') {
|
||||||
event_start(state);
|
event_start(state);
|
||||||
state->cur_event.time.seconds = time;
|
state->cur_event.time.seconds = time;
|
||||||
strcpy(state->cur_event.name, "violation");
|
state->cur_event.name = "violation"s;
|
||||||
event_end(state);
|
event_end(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +248,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
|
|||||||
if (ptr1[9] - '0') {
|
if (ptr1[9] - '0') {
|
||||||
event_start(state);
|
event_start(state);
|
||||||
state->cur_event.time.seconds = time;
|
state->cur_event.time.seconds = time;
|
||||||
strcpy(state->cur_event.name, "workload");
|
state->cur_event.name = "workload"s;
|
||||||
event_end(state);
|
event_end(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,22 +276,22 @@ static int divinglog_dive(void *param, int, char **data, char **)
|
|||||||
state->cur_dive->when = (time_t)(atol(data[1]));
|
state->cur_dive->when = (time_t)(atol(data[1]));
|
||||||
|
|
||||||
if (data[2])
|
if (data[2])
|
||||||
add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(data[2], state->log->sites));
|
state->log->sites.find_or_create(std::string(data[2]))->add_dive(state->cur_dive.get());
|
||||||
|
|
||||||
if (data[3])
|
if (data[3])
|
||||||
utf8_string(data[3], &state->cur_dive->buddy);
|
utf8_string_std(data[3], &state->cur_dive->buddy);
|
||||||
|
|
||||||
if (data[4])
|
if (data[4])
|
||||||
utf8_string(data[4], &state->cur_dive->notes);
|
utf8_string_std(data[4], &state->cur_dive->notes);
|
||||||
|
|
||||||
if (data[5])
|
if (data[5])
|
||||||
state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[5], NULL, 0) * 1000);
|
state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[5], NULL) * 1000);
|
||||||
|
|
||||||
if (data[6])
|
if (data[6])
|
||||||
state->cur_dive->dc.duration.seconds = atoi(data[6]) * 60;
|
state->cur_dive->dcs[0].duration.seconds = atoi(data[6]) * 60;
|
||||||
|
|
||||||
if (data[7])
|
if (data[7])
|
||||||
utf8_string(data[7], &state->cur_dive->diveguide);
|
utf8_string_std(data[7], &state->cur_dive->diveguide);
|
||||||
|
|
||||||
if (data[8])
|
if (data[8])
|
||||||
state->cur_dive->airtemp.mkelvin = C_to_mkelvin(atol(data[8]));
|
state->cur_dive->airtemp.mkelvin = C_to_mkelvin(atol(data[8]));
|
||||||
@ -300,11 +301,11 @@ static int divinglog_dive(void *param, int, char **data, char **)
|
|||||||
|
|
||||||
if (data[10]) {
|
if (data[10]) {
|
||||||
weightsystem_t ws = { { atoi(data[10]) * 1000 }, translate("gettextFromC", "unknown"), false };
|
weightsystem_t ws = { { atoi(data[10]) * 1000 }, translate("gettextFromC", "unknown"), false };
|
||||||
add_cloned_weightsystem(&state->cur_dive->weightsystems, ws);
|
state->cur_dive->weightsystems.push_back(std::move(ws));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[11])
|
if (data[11])
|
||||||
state->cur_dive->suit = strdup(data[11]);
|
state->cur_dive->suit = data[11];
|
||||||
|
|
||||||
/* Divinglog has following visibility options: good, medium, bad */
|
/* Divinglog has following visibility options: good, medium, bad */
|
||||||
if (data[14]) {
|
if (data[14]) {
|
||||||
@ -329,9 +330,9 @@ static int divinglog_dive(void *param, int, char **data, char **)
|
|||||||
dc_settings_start(state);
|
dc_settings_start(state);
|
||||||
|
|
||||||
if (data[12]) {
|
if (data[12]) {
|
||||||
state->cur_dive->dc.model = strdup(data[12]);
|
state->cur_dive->dcs[0].model = data[12];
|
||||||
} else {
|
} else {
|
||||||
state->cur_settings.dc.model = strdup("Divinglog import");
|
state->cur_settings.dc.model = "Divinglog import";
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder0_template, diveid);
|
snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder0_template, diveid);
|
||||||
@ -354,10 +355,10 @@ static int divinglog_dive(void *param, int, char **data, char **)
|
|||||||
case '0':
|
case '0':
|
||||||
break;
|
break;
|
||||||
case '1':
|
case '1':
|
||||||
state->cur_dive->dc.divemode = PSCR;
|
state->cur_dive->dcs[0].divemode = PSCR;
|
||||||
break;
|
break;
|
||||||
case '2':
|
case '2':
|
||||||
state->cur_dive->dc.divemode = CCR;
|
state->cur_dive->dcs[0].divemode = CCR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -366,9 +367,9 @@ static int divinglog_dive(void *param, int, char **data, char **)
|
|||||||
settings_end(state);
|
settings_end(state);
|
||||||
|
|
||||||
if (data[12]) {
|
if (data[12]) {
|
||||||
state->cur_dive->dc.model = strdup(data[12]);
|
state->cur_dive->dcs[0].model = data[12];
|
||||||
} else {
|
} else {
|
||||||
state->cur_dive->dc.model = strdup("Divinglog import");
|
state->cur_dive->dcs[0].model = "Divinglog import";
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, diveid);
|
snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, diveid);
|
||||||
@ -384,7 +385,7 @@ static int divinglog_dive(void *param, int, char **data, char **)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
struct parser_state state;
|
struct parser_state state;
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "qthelper.h"
|
#include "qthelper.h"
|
||||||
#include "ssrf.h"
|
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
#include "sample.h"
|
#include "sample.h"
|
||||||
#include "subsurface-string.h"
|
#include "subsurface-string.h"
|
||||||
@ -27,11 +26,12 @@
|
|||||||
*/
|
*/
|
||||||
static int seac_gaschange(void *param, sqlite3_stmt *sqlstmt)
|
static int seac_gaschange(void *param, sqlite3_stmt *sqlstmt)
|
||||||
{
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
struct parser_state *state = (struct parser_state *)param;
|
struct parser_state *state = (struct parser_state *)param;
|
||||||
|
|
||||||
event_start(state);
|
event_start(state);
|
||||||
state->cur_event.time.seconds = sqlite3_column_int(sqlstmt, 1);
|
state->cur_event.time.seconds = sqlite3_column_int(sqlstmt, 1);
|
||||||
strcpy(state->cur_event.name, "gaschange");
|
state->cur_event.name = "gaschange"s;
|
||||||
state->cur_event.gas.mix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4);
|
state->cur_event.gas.mix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4);
|
||||||
event_end(state);
|
event_end(state);
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ static int seac_dive(void *param, int, char **data, char **)
|
|||||||
state->cur_dive->number = atoi(data[0]);
|
state->cur_dive->number = atoi(data[0]);
|
||||||
|
|
||||||
// Create first cylinder
|
// Create first cylinder
|
||||||
cylinder_t *curcyl = get_or_create_cylinder(state->cur_dive, 0);
|
cylinder_t *curcyl = get_or_create_cylinder(state->cur_dive.get(), 0);
|
||||||
|
|
||||||
// Get time and date
|
// Get time and date
|
||||||
sscanf(data[2], "%d/%d/%2d", &day, &month, &year);
|
sscanf(data[2], "%d/%d/%2d", &day, &month, &year);
|
||||||
@ -132,14 +132,14 @@ static int seac_dive(void *param, int, char **data, char **)
|
|||||||
if (data[6]) {
|
if (data[6]) {
|
||||||
switch (atoi(data[6])) {
|
switch (atoi(data[6])) {
|
||||||
case 1:
|
case 1:
|
||||||
state->cur_dive->dc.divemode = OC;
|
state->cur_dive->dcs[0].divemode = OC;
|
||||||
break;
|
break;
|
||||||
// Gauge Mode
|
// Gauge Mode
|
||||||
case 2:
|
case 2:
|
||||||
state->cur_dive->dc.divemode = UNDEF_COMP_TYPE;
|
state->cur_dive->dcs[0].divemode = UNDEF_COMP_TYPE;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
state->cur_dive->dc.divemode = FREEDIVE;
|
state->cur_dive->dcs[0].divemode = FREEDIVE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
@ -150,12 +150,12 @@ static int seac_dive(void *param, int, char **data, char **)
|
|||||||
|
|
||||||
// 9 = comments from seac app
|
// 9 = comments from seac app
|
||||||
if (data[9]) {
|
if (data[9]) {
|
||||||
utf8_string(data[9], &state->cur_dive->notes);
|
utf8_string_std(data[9], &state->cur_dive->notes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10 = dive duration
|
// 10 = dive duration
|
||||||
if (data[10]) {
|
if (data[10]) {
|
||||||
state->cur_dive->dc.duration.seconds = atoi(data[10]);
|
state->cur_dive->dcs[0].duration.seconds = atoi(data[10]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8 = water_type
|
// 8 = water_type
|
||||||
@ -181,7 +181,7 @@ static int seac_dive(void *param, int, char **data, char **)
|
|||||||
|
|
||||||
|
|
||||||
if (data[11]) {
|
if (data[11]) {
|
||||||
state->cur_dive->dc.maxdepth.mm = 10 * atoi(data[11]);
|
state->cur_dive->dcs[0].maxdepth.mm = 10 * atoi(data[11]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create sql_stmt type to query DB
|
// Create sql_stmt type to query DB
|
||||||
@ -205,27 +205,24 @@ static int seac_dive(void *param, int, char **data, char **)
|
|||||||
settings_start(state);
|
settings_start(state);
|
||||||
dc_settings_start(state);
|
dc_settings_start(state);
|
||||||
|
|
||||||
// These dc values are const char *, therefore we have to cast.
|
utf8_string_std(data[1], &state->cur_dive->dcs[0].serial);
|
||||||
// Will be fixed by converting to std::string
|
utf8_string_std(data[12],&state->cur_dive->dcs[0].fw_version);
|
||||||
utf8_string(data[1], (char **)&state->cur_dive->dc.serial);
|
state->cur_dive->dcs[0].model = "Seac Action";
|
||||||
utf8_string(data[12], (char **)&state->cur_dive->dc.fw_version);
|
|
||||||
state->cur_dive->dc.model = strdup("Seac Action");
|
|
||||||
|
|
||||||
state->cur_dive->dc.deviceid = calculate_string_hash(data[1]);
|
state->cur_dive->dcs[0].deviceid = calculate_string_hash(data[1]);
|
||||||
|
|
||||||
add_extra_data(&state->cur_dive->dc, "GF-Lo", (const char*)sqlite3_column_text(sqlstmt, 9));
|
add_extra_data(&state->cur_dive->dcs[0], "GF-Lo", (const char*)sqlite3_column_text(sqlstmt, 9));
|
||||||
add_extra_data(&state->cur_dive->dc, "GF-Hi", (const char*)sqlite3_column_text(sqlstmt, 10));
|
add_extra_data(&state->cur_dive->dcs[0], "GF-Hi", (const char*)sqlite3_column_text(sqlstmt, 10));
|
||||||
|
|
||||||
dc_settings_end(state);
|
dc_settings_end(state);
|
||||||
settings_end(state);
|
settings_end(state);
|
||||||
|
|
||||||
if (data[11]) {
|
if (data[11]) {
|
||||||
state->cur_dive->dc.maxdepth.mm = 10 * atoi(data[11]);
|
state->cur_dive->dcs[0].maxdepth.mm = 10 * atoi(data[11]);
|
||||||
}
|
}
|
||||||
|
|
||||||
curcyl->gasmix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4);
|
curcyl->gasmix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4);
|
||||||
|
|
||||||
|
|
||||||
// Track gasses to tell when switch occurs
|
// Track gasses to tell when switch occurs
|
||||||
lastgas = curcyl->gasmix;
|
lastgas = curcyl->gasmix;
|
||||||
curgas = curcyl->gasmix;
|
curgas = curcyl->gasmix;
|
||||||
@ -241,7 +238,7 @@ static int seac_dive(void *param, int, char **data, char **)
|
|||||||
seac_gaschange(state, sqlstmt);
|
seac_gaschange(state, sqlstmt);
|
||||||
lastgas = curgas;
|
lastgas = curgas;
|
||||||
cylnum ^= 1; // Only need to toggle between two cylinders
|
cylnum ^= 1; // Only need to toggle between two cylinders
|
||||||
curcyl = get_or_create_cylinder(state->cur_dive, cylnum);
|
curcyl = get_or_create_cylinder(state->cur_dive.get(), cylnum);
|
||||||
curcyl->gasmix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4);
|
curcyl->gasmix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4);
|
||||||
}
|
}
|
||||||
state->cur_sample->stopdepth.mm = 10 * sqlite3_column_int(sqlstmt, 5);
|
state->cur_sample->stopdepth.mm = 10 * sqlite3_column_int(sqlstmt, 5);
|
||||||
@ -264,7 +261,7 @@ static int seac_dive(void *param, int, char **data, char **)
|
|||||||
* The callback function performs another SQL query on the other
|
* The callback function performs another SQL query on the other
|
||||||
* table, to read in the sample values.
|
* table, to read in the sample values.
|
||||||
*/
|
*/
|
||||||
extern "C" int parse_seac_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
int parse_seac_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
char *err = NULL;
|
char *err = NULL;
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "ssrf.h"
|
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
#include "sample.h"
|
#include "sample.h"
|
||||||
#include "subsurface-string.h"
|
#include "subsurface-string.h"
|
||||||
@ -23,8 +22,8 @@ static int shearwater_cylinders(void *param, int, char **data, char **)
|
|||||||
struct parser_state *state = (struct parser_state *)param;
|
struct parser_state *state = (struct parser_state *)param;
|
||||||
cylinder_t *cyl;
|
cylinder_t *cyl;
|
||||||
|
|
||||||
int o2 = lrint(strtod_flags(data[0], NULL, 0) * 1000);
|
int o2 = lrint(permissive_strtod(data[0], NULL) * 1000);
|
||||||
int he = lrint(strtod_flags(data[1], NULL, 0) * 1000);
|
int he = lrint(permissive_strtod(data[1], NULL) * 1000);
|
||||||
|
|
||||||
/* Shearwater allows entering only 99%, not 100%
|
/* Shearwater allows entering only 99%, not 100%
|
||||||
* so assume 99% to be pure oxygen */
|
* so assume 99% to be pure oxygen */
|
||||||
@ -50,8 +49,8 @@ static int shearwater_changes(void *param, int columns, char **data, char **)
|
|||||||
if (!data[0] || !data[1] || !data[2]) {
|
if (!data[0] || !data[1] || !data[2]) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
int o2 = lrint(strtod_flags(data[1], NULL, 0) * 1000);
|
int o2 = lrint(permissive_strtod(data[1], NULL) * 1000);
|
||||||
int he = lrint(strtod_flags(data[2], NULL, 0) * 1000);
|
int he = lrint(permissive_strtod(data[2], NULL) * 1000);
|
||||||
|
|
||||||
/* Shearwater allows entering only 99%, not 100%
|
/* Shearwater allows entering only 99%, not 100%
|
||||||
* so assume 99% to be pure oxygen */
|
* so assume 99% to be pure oxygen */
|
||||||
@ -59,24 +58,20 @@ static int shearwater_changes(void *param, int columns, char **data, char **)
|
|||||||
o2 = 1000;
|
o2 = 1000;
|
||||||
|
|
||||||
// Find the cylinder index
|
// Find the cylinder index
|
||||||
int index;
|
auto it = std::find_if(state->cur_dive->cylinders.begin(), state->cur_dive->cylinders.end(),
|
||||||
bool found = false;
|
[o2, he](auto &cyl)
|
||||||
for (index = 0; index < state->cur_dive->cylinders.nr; ++index) {
|
{ return cyl.gasmix.o2.permille == o2 && cyl.gasmix.he.permille == he; });
|
||||||
const cylinder_t *cyl = get_cylinder(state->cur_dive, index);
|
if (it == state->cur_dive->cylinders.end()) {
|
||||||
if (cyl->gasmix.o2.permille == o2 && cyl->gasmix.he.permille == he) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
// Cylinder not found, creating a new one
|
// Cylinder not found, creating a new one
|
||||||
cyl = cylinder_start(state);
|
cyl = cylinder_start(state);
|
||||||
cyl->gasmix.o2.permille = o2;
|
cyl->gasmix.o2.permille = o2;
|
||||||
cyl->gasmix.he.permille = he;
|
cyl->gasmix.he.permille = he;
|
||||||
cylinder_end(state);
|
cylinder_end(state);
|
||||||
|
it = std::prev(state->cur_dive->cylinders.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
add_gas_switch_event(state->cur_dive, get_dc(state), state->sample_rate ? atoi(data[0]) / state->sample_rate * 10 : atoi(data[0]), index);
|
add_gas_switch_event(state->cur_dive.get(), get_dc(state), state->sample_rate ? atoi(data[0]) / state->sample_rate * 10 : atoi(data[0]),
|
||||||
|
it - state->cur_dive->cylinders.begin());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,12 +96,11 @@ static int shearwater_profile_sample(void *param, int, char **data, char **)
|
|||||||
|
|
||||||
|
|
||||||
if (data[1])
|
if (data[1])
|
||||||
state->cur_sample->depth.mm = state->metric ? lrint(strtod_flags(data[1], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[1], NULL, 0));
|
state->cur_sample->depth.mm = state->metric ? lrint(permissive_strtod(data[1], NULL) * 1000) : feet_to_mm(permissive_strtod(data[1], NULL));
|
||||||
if (data[2])
|
if (data[2])
|
||||||
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(strtod_flags(data[2], NULL, 0)) : F_to_mkelvin(strtod_flags(data[2], NULL, 0));
|
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL));
|
||||||
if (data[3]) {
|
if (data[3])
|
||||||
state->cur_sample->setpoint.mbar = lrint(strtod_flags(data[3], NULL, 0) * 1000);
|
state->cur_sample->setpoint.mbar = lrint(permissive_strtod(data[3], NULL) * 1000);
|
||||||
}
|
|
||||||
if (data[4])
|
if (data[4])
|
||||||
state->cur_sample->ndl.seconds = atoi(data[4]) * 60;
|
state->cur_sample->ndl.seconds = atoi(data[4]) * 60;
|
||||||
if (data[5])
|
if (data[5])
|
||||||
@ -161,11 +155,11 @@ static int shearwater_ai_profile_sample(void *param, int, char **data, char **)
|
|||||||
state->cur_sample->time.seconds = atoi(data[0]);
|
state->cur_sample->time.seconds = atoi(data[0]);
|
||||||
|
|
||||||
if (data[1])
|
if (data[1])
|
||||||
state->cur_sample->depth.mm = state->metric ? lrint(strtod_flags(data[1], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[1], NULL, 0));
|
state->cur_sample->depth.mm = state->metric ? lrint(permissive_strtod(data[1], NULL) * 1000) : feet_to_mm(permissive_strtod(data[1], NULL));
|
||||||
if (data[2])
|
if (data[2])
|
||||||
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(strtod_flags(data[2], NULL, 0)) : F_to_mkelvin(strtod_flags(data[2], NULL, 0));
|
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL));
|
||||||
if (data[3]) {
|
if (data[3]) {
|
||||||
state->cur_sample->setpoint.mbar = lrint(strtod_flags(data[3], NULL, 0) * 1000);
|
state->cur_sample->setpoint.mbar = lrint(permissive_strtod(data[3], NULL) * 1000);
|
||||||
}
|
}
|
||||||
if (data[4])
|
if (data[4])
|
||||||
state->cur_sample->ndl.seconds = atoi(data[4]) * 60;
|
state->cur_sample->ndl.seconds = atoi(data[4]) * 60;
|
||||||
@ -215,7 +209,7 @@ static int shearwater_mode(void *param, int, char **data, char **)
|
|||||||
struct parser_state *state = (struct parser_state *)param;
|
struct parser_state *state = (struct parser_state *)param;
|
||||||
|
|
||||||
if (data[0])
|
if (data[0])
|
||||||
state->cur_dive->dc.divemode = atoi(data[0]) == 0 ? CCR : OC;
|
state->cur_dive->dcs[0].divemode = atoi(data[0]) == 0 ? CCR : OC;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -240,23 +234,23 @@ static int shearwater_dive(void *param, int, char **data, char **)
|
|||||||
long int dive_id = atol(data[11]);
|
long int dive_id = atol(data[11]);
|
||||||
|
|
||||||
if (data[2])
|
if (data[2])
|
||||||
add_dive_site(data[2], state->cur_dive, state);
|
add_dive_site(data[2], state->cur_dive.get(), state);
|
||||||
if (data[3])
|
if (data[3])
|
||||||
utf8_string(data[3], &state->cur_dive->buddy);
|
utf8_string_std(data[3], &state->cur_dive->buddy);
|
||||||
if (data[4])
|
if (data[4])
|
||||||
utf8_string(data[4], &state->cur_dive->notes);
|
utf8_string_std(data[4], &state->cur_dive->notes);
|
||||||
|
|
||||||
state->metric = atoi(data[5]) == 1 ? 0 : 1;
|
state->metric = atoi(data[5]) == 1 ? 0 : 1;
|
||||||
|
|
||||||
/* TODO: verify that metric calculation is correct */
|
/* TODO: verify that metric calculation is correct */
|
||||||
if (data[6])
|
if (data[6])
|
||||||
state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(strtod_flags(data[6], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[6], NULL, 0));
|
state->cur_dive->dcs[0].maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL));
|
||||||
|
|
||||||
if (data[7])
|
if (data[7])
|
||||||
state->cur_dive->dc.duration.seconds = atoi(data[7]) * 60;
|
state->cur_dive->dcs[0].duration.seconds = atoi(data[7]) * 60;
|
||||||
|
|
||||||
if (data[8])
|
if (data[8])
|
||||||
state->cur_dive->dc.surface_pressure.mbar = atoi(data[8]);
|
state->cur_dive->dcs[0].surface_pressure.mbar = atoi(data[8]);
|
||||||
/*
|
/*
|
||||||
* TODO: the deviceid hash should be calculated here.
|
* TODO: the deviceid hash should be calculated here.
|
||||||
*/
|
*/
|
||||||
@ -267,13 +261,13 @@ static int shearwater_dive(void *param, int, char **data, char **)
|
|||||||
if (data[10]) {
|
if (data[10]) {
|
||||||
switch (atoi(data[10])) {
|
switch (atoi(data[10])) {
|
||||||
case 2:
|
case 2:
|
||||||
state->cur_settings.dc.model = strdup("Shearwater Petrel/Perdix");
|
state->cur_settings.dc.model = "Shearwater Petrel/Perdix";
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
state->cur_settings.dc.model = strdup("Shearwater Predator");
|
state->cur_settings.dc.model = "Shearwater Predator";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
state->cur_settings.dc.model = strdup("Shearwater import");
|
state->cur_settings.dc.model = "Shearwater import";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,13 +280,13 @@ static int shearwater_dive(void *param, int, char **data, char **)
|
|||||||
if (data[10]) {
|
if (data[10]) {
|
||||||
switch (atoi(data[10])) {
|
switch (atoi(data[10])) {
|
||||||
case 2:
|
case 2:
|
||||||
state->cur_dive->dc.model = strdup("Shearwater Petrel/Perdix");
|
state->cur_dive->dcs[0].model = "Shearwater Petrel/Perdix";
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
state->cur_dive->dc.model = strdup("Shearwater Predator");
|
state->cur_dive->dcs[0].model = "Shearwater Predator";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
state->cur_dive->dc.model = strdup("Shearwater import");
|
state->cur_dive->dcs[0].model = "Shearwater import";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -370,23 +364,23 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
|
|||||||
state->sample_rate = 0;
|
state->sample_rate = 0;
|
||||||
|
|
||||||
if (data[2])
|
if (data[2])
|
||||||
add_dive_site(data[2], state->cur_dive, state);
|
add_dive_site(data[2], state->cur_dive.get(), state);
|
||||||
if (data[3])
|
if (data[3])
|
||||||
utf8_string(data[3], &state->cur_dive->buddy);
|
utf8_string_std(data[3], &state->cur_dive->buddy);
|
||||||
if (data[4])
|
if (data[4])
|
||||||
utf8_string(data[4], &state->cur_dive->notes);
|
utf8_string_std(data[4], &state->cur_dive->notes);
|
||||||
|
|
||||||
state->metric = atoi(data[5]) == 1 ? 0 : 1;
|
state->metric = atoi(data[5]) == 1 ? 0 : 1;
|
||||||
|
|
||||||
/* TODO: verify that metric calculation is correct */
|
/* TODO: verify that metric calculation is correct */
|
||||||
if (data[6])
|
if (data[6])
|
||||||
state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(strtod_flags(data[6], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[6], NULL, 0));
|
state->cur_dive->dcs[0].maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL));
|
||||||
|
|
||||||
if (data[7])
|
if (data[7])
|
||||||
state->cur_dive->dc.duration.seconds = atoi(data[7]);
|
state->cur_dive->dcs[0].duration.seconds = atoi(data[7]);
|
||||||
|
|
||||||
if (data[8])
|
if (data[8])
|
||||||
state->cur_dive->dc.surface_pressure.mbar = atoi(data[8]);
|
state->cur_dive->dcs[0].surface_pressure.mbar = atoi(data[8]);
|
||||||
/*
|
/*
|
||||||
* TODO: the deviceid hash should be calculated here.
|
* TODO: the deviceid hash should be calculated here.
|
||||||
*/
|
*/
|
||||||
@ -397,13 +391,13 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
|
|||||||
if (data[10]) {
|
if (data[10]) {
|
||||||
switch (atoi(data[10])) {
|
switch (atoi(data[10])) {
|
||||||
case 2:
|
case 2:
|
||||||
state->cur_settings.dc.model = strdup("Shearwater Petrel/Perdix");
|
state->cur_settings.dc.model = "Shearwater Petrel/Perdix";
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
state->cur_settings.dc.model = strdup("Shearwater Predator");
|
state->cur_settings.dc.model = "Shearwater Predator";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
state->cur_settings.dc.model = strdup("Shearwater import");
|
state->cur_settings.dc.model = "Shearwater import";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -416,13 +410,13 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
|
|||||||
if (data[10]) {
|
if (data[10]) {
|
||||||
switch (atoi(data[10])) {
|
switch (atoi(data[10])) {
|
||||||
case 2:
|
case 2:
|
||||||
state->cur_dive->dc.model = strdup("Shearwater Petrel/Perdix");
|
state->cur_dive->dcs[0].model = "Shearwater Petrel/Perdix";
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
state->cur_dive->dc.model = strdup("Shearwater Predator");
|
state->cur_dive->dcs[0].model = "Shearwater Predator";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
state->cur_dive->dc.model = strdup("Shearwater import");
|
state->cur_dive->dcs[0].model = "Shearwater import";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -473,7 +467,7 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
struct parser_state state;
|
struct parser_state state;
|
||||||
@ -496,7 +490,7 @@ extern "C" int parse_shearwater_buffer(sqlite3 *handle, const char *url, const c
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int parse_shearwater_cloud_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
int parse_shearwater_cloud_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
struct parser_state state;
|
struct parser_state state;
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "ssrf.h"
|
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
#include "parse.h"
|
#include "parse.h"
|
||||||
#include "sample.h"
|
#include "sample.h"
|
||||||
@ -21,6 +20,7 @@
|
|||||||
|
|
||||||
static int dm4_events(void *param, int, char **data, char **)
|
static int dm4_events(void *param, int, char **data, char **)
|
||||||
{
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
struct parser_state *state = (struct parser_state *)param;
|
struct parser_state *state = (struct parser_state *)param;
|
||||||
|
|
||||||
event_start(state);
|
event_start(state);
|
||||||
@ -31,108 +31,108 @@ static int dm4_events(void *param, int, char **data, char **)
|
|||||||
switch (atoi(data[2])) {
|
switch (atoi(data[2])) {
|
||||||
case 1:
|
case 1:
|
||||||
/* 1 Mandatory Safety Stop */
|
/* 1 Mandatory Safety Stop */
|
||||||
strcpy(state->cur_event.name, "safety stop (mandatory)");
|
state->cur_event.name = "safety stop (mandatory)"s;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
/* 3 Deco */
|
/* 3 Deco */
|
||||||
/* What is Subsurface's term for going to
|
/* What is Subsurface's term for going to
|
||||||
* deco? */
|
* deco? */
|
||||||
strcpy(state->cur_event.name, "deco");
|
state->cur_event.name = "deco"s;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
/* 4 Ascent warning */
|
/* 4 Ascent warning */
|
||||||
strcpy(state->cur_event.name, "ascent");
|
state->cur_event.name = "ascent"s;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
/* 5 Ceiling broken */
|
/* 5 Ceiling broken */
|
||||||
strcpy(state->cur_event.name, "violation");
|
state->cur_event.name = "violation"s;
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
/* 6 Mandatory safety stop ceiling error */
|
/* 6 Mandatory safety stop ceiling error */
|
||||||
strcpy(state->cur_event.name, "violation");
|
state->cur_event.name = "violation"s;
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
/* 7 Below deco floor */
|
/* 7 Below deco floor */
|
||||||
strcpy(state->cur_event.name, "below floor");
|
state->cur_event.name = "below floor"s;
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
/* 8 Dive time alarm */
|
/* 8 Dive time alarm */
|
||||||
strcpy(state->cur_event.name, "divetime");
|
state->cur_event.name = "divetime"s;
|
||||||
break;
|
break;
|
||||||
case 9:
|
case 9:
|
||||||
/* 9 Depth alarm */
|
/* 9 Depth alarm */
|
||||||
strcpy(state->cur_event.name, "maxdepth");
|
state->cur_event.name = "maxdepth"s;
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
/* 10 OLF 80% */
|
/* 10 OLF 80% */
|
||||||
case 11:
|
case 11:
|
||||||
/* 11 OLF 100% */
|
/* 11 OLF 100% */
|
||||||
strcpy(state->cur_event.name, "OLF");
|
state->cur_event.name = "OLF"s;
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
/* 12 High pO₂ */
|
/* 12 High pO₂ */
|
||||||
strcpy(state->cur_event.name, "PO2");
|
state->cur_event.name = "PO2"s;
|
||||||
break;
|
break;
|
||||||
case 13:
|
case 13:
|
||||||
/* 13 Air time */
|
/* 13 Air time */
|
||||||
strcpy(state->cur_event.name, "airtime");
|
state->cur_event.name = "airtime"s;
|
||||||
break;
|
break;
|
||||||
case 17:
|
case 17:
|
||||||
/* 17 Ascent warning */
|
/* 17 Ascent warning */
|
||||||
strcpy(state->cur_event.name, "ascent");
|
state->cur_event.name = "ascent"s;
|
||||||
break;
|
break;
|
||||||
case 18:
|
case 18:
|
||||||
/* 18 Ceiling error */
|
/* 18 Ceiling error */
|
||||||
strcpy(state->cur_event.name, "ceiling");
|
state->cur_event.name = "ceiling"s;
|
||||||
break;
|
break;
|
||||||
case 19:
|
case 19:
|
||||||
/* 19 Surfaced */
|
/* 19 Surfaced */
|
||||||
strcpy(state->cur_event.name, "surface");
|
state->cur_event.name = "surface"s;
|
||||||
break;
|
break;
|
||||||
case 20:
|
case 20:
|
||||||
/* 20 Deco */
|
/* 20 Deco */
|
||||||
strcpy(state->cur_event.name, "deco");
|
state->cur_event.name = "deco"s;
|
||||||
break;
|
break;
|
||||||
case 22:
|
case 22:
|
||||||
case 32:
|
case 32:
|
||||||
/* 22 Mandatory safety stop violation */
|
/* 22 Mandatory safety stop violation */
|
||||||
/* 32 Deep stop violation */
|
/* 32 Deep stop violation */
|
||||||
strcpy(state->cur_event.name, "violation");
|
state->cur_event.name = "violation"s;
|
||||||
break;
|
break;
|
||||||
case 30:
|
case 30:
|
||||||
/* Tissue level warning */
|
/* Tissue level warning */
|
||||||
strcpy(state->cur_event.name, "tissue warning");
|
state->cur_event.name = "tissue warning"s;
|
||||||
break;
|
break;
|
||||||
case 37:
|
case 37:
|
||||||
/* Tank pressure alarm */
|
/* Tank pressure alarm */
|
||||||
strcpy(state->cur_event.name, "tank pressure");
|
state->cur_event.name = "tank pressure"s;
|
||||||
break;
|
break;
|
||||||
case 257:
|
case 257:
|
||||||
/* 257 Dive active */
|
/* 257 Dive active */
|
||||||
/* This seems to be given after surface when
|
/* This seems to be given after surface when
|
||||||
* descending again. */
|
* descending again. */
|
||||||
strcpy(state->cur_event.name, "surface");
|
state->cur_event.name = "surface"s;
|
||||||
break;
|
break;
|
||||||
case 258:
|
case 258:
|
||||||
/* 258 Bookmark */
|
/* 258 Bookmark */
|
||||||
if (data[3]) {
|
if (data[3]) {
|
||||||
strcpy(state->cur_event.name, "heading");
|
state->cur_event.name = "heading"s;
|
||||||
state->cur_event.value = atoi(data[3]);
|
state->cur_event.value = atoi(data[3]);
|
||||||
} else {
|
} else {
|
||||||
strcpy(state->cur_event.name, "bookmark");
|
state->cur_event.name = "bookmark"s;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 259:
|
case 259:
|
||||||
/* Deep stop */
|
/* Deep stop */
|
||||||
strcpy(state->cur_event.name, "Deep stop");
|
state->cur_event.name = "Deep stop"s;
|
||||||
break;
|
break;
|
||||||
case 260:
|
case 260:
|
||||||
/* Deep stop */
|
/* Deep stop */
|
||||||
strcpy(state->cur_event.name, "Deep stop cleared");
|
state->cur_event.name = "Deep stop cleared"s;
|
||||||
break;
|
break;
|
||||||
case 266:
|
case 266:
|
||||||
/* Mandatory safety stop activated */
|
/* Mandatory safety stop activated */
|
||||||
strcpy(state->cur_event.name, "safety stop (mandatory)");
|
state->cur_event.name = "safety stop (mandatory)"s;
|
||||||
break;
|
break;
|
||||||
case 267:
|
case 267:
|
||||||
/* Mandatory safety stop deactivated */
|
/* Mandatory safety stop deactivated */
|
||||||
@ -140,7 +140,7 @@ static int dm4_events(void *param, int, char **data, char **)
|
|||||||
* profile so skipping as well for now */
|
* profile so skipping as well for now */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
strcpy(state->cur_event.name, "unknown");
|
state->cur_event.name = "unknown"s;
|
||||||
state->cur_event.value = atoi(data[2]);
|
state->cur_event.value = atoi(data[2]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -155,7 +155,7 @@ static int dm4_tags(void *param, int, char **data, char **)
|
|||||||
struct parser_state *state = (struct parser_state *)param;
|
struct parser_state *state = (struct parser_state *)param;
|
||||||
|
|
||||||
if (data[0])
|
if (data[0])
|
||||||
taglist_add_tag(&state->cur_dive->tag_list, data[0]);
|
taglist_add_tag(state->cur_dive->tags, data[0]);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -179,7 +179,7 @@ static int dm4_dive(void *param, int, char **data, char **)
|
|||||||
|
|
||||||
state->cur_dive->when = (time_t)(atol(data[1]));
|
state->cur_dive->when = (time_t)(atol(data[1]));
|
||||||
if (data[2])
|
if (data[2])
|
||||||
utf8_string(data[2], &state->cur_dive->notes);
|
utf8_string_std(data[2], &state->cur_dive->notes);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DM4 stores Duration and DiveTime. It looks like DiveTime is
|
* DM4 stores Duration and DiveTime. It looks like DiveTime is
|
||||||
@ -191,7 +191,7 @@ static int dm4_dive(void *param, int, char **data, char **)
|
|||||||
if (data[3])
|
if (data[3])
|
||||||
state->cur_dive->duration.seconds = atoi(data[3]);
|
state->cur_dive->duration.seconds = atoi(data[3]);
|
||||||
if (data[15])
|
if (data[15])
|
||||||
state->cur_dive->dc.duration.seconds = atoi(data[15]);
|
state->cur_dive->dcs[0].duration.seconds = atoi(data[15]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: the deviceid hash should be calculated here.
|
* TODO: the deviceid hash should be calculated here.
|
||||||
@ -208,11 +208,11 @@ static int dm4_dive(void *param, int, char **data, char **)
|
|||||||
settings_end(state);
|
settings_end(state);
|
||||||
|
|
||||||
if (data[6])
|
if (data[6])
|
||||||
state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[6], NULL, 0) * 1000);
|
state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000);
|
||||||
if (data[8])
|
if (data[8])
|
||||||
state->cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
|
state->cur_dive->dcs[0].airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
|
||||||
if (data[9])
|
if (data[9])
|
||||||
state->cur_dive->dc.watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
|
state->cur_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: handle multiple cylinders
|
* TODO: handle multiple cylinders
|
||||||
@ -227,7 +227,7 @@ static int dm4_dive(void *param, int, char **data, char **)
|
|||||||
if (data[11] && atoi(data[11]) > 0)
|
if (data[11] && atoi(data[11]) > 0)
|
||||||
cyl->end.mbar = (atoi(data[11]));
|
cyl->end.mbar = (atoi(data[11]));
|
||||||
if (data[12])
|
if (data[12])
|
||||||
cyl->type.size.mliter = lrint((strtod_flags(data[12], NULL, 0)) * 1000);
|
cyl->type.size.mliter = lrint((permissive_strtod(data[12], NULL)) * 1000);
|
||||||
if (data[13])
|
if (data[13])
|
||||||
cyl->type.workingpressure.mbar = (atoi(data[13]));
|
cyl->type.workingpressure.mbar = (atoi(data[13]));
|
||||||
if (data[20])
|
if (data[20])
|
||||||
@ -237,7 +237,7 @@ static int dm4_dive(void *param, int, char **data, char **)
|
|||||||
cylinder_end(state);
|
cylinder_end(state);
|
||||||
|
|
||||||
if (data[14])
|
if (data[14])
|
||||||
state->cur_dive->dc.surface_pressure.mbar = (atoi(data[14]) * 1000);
|
state->cur_dive->dcs[0].surface_pressure.mbar = (atoi(data[14]) * 1000);
|
||||||
|
|
||||||
interval = data[16] ? atoi(data[16]) : 0;
|
interval = data[16] ? atoi(data[16]) : 0;
|
||||||
profileBlob = (float *)data[17];
|
profileBlob = (float *)data[17];
|
||||||
@ -249,7 +249,7 @@ static int dm4_dive(void *param, int, char **data, char **)
|
|||||||
if (profileBlob)
|
if (profileBlob)
|
||||||
state->cur_sample->depth.mm = lrintf(profileBlob[i] * 1000.0f);
|
state->cur_sample->depth.mm = lrintf(profileBlob[i] * 1000.0f);
|
||||||
else
|
else
|
||||||
state->cur_sample->depth.mm = state->cur_dive->dc.maxdepth.mm;
|
state->cur_sample->depth.mm = state->cur_dive->dcs[0].maxdepth.mm;
|
||||||
|
|
||||||
if (data[18] && data[18][0])
|
if (data[18] && data[18][0])
|
||||||
state->cur_sample->temperature.mkelvin = C_to_mkelvin(tempBlob[i]);
|
state->cur_sample->temperature.mkelvin = C_to_mkelvin(tempBlob[i]);
|
||||||
@ -277,7 +277,7 @@ static int dm4_dive(void *param, int, char **data, char **)
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
char *err = NULL;
|
char *err = NULL;
|
||||||
@ -314,10 +314,10 @@ static int dm5_cylinders(void *param, int, char **data, char **)
|
|||||||
/* DM5 shows tank size of 12 liters when the actual
|
/* DM5 shows tank size of 12 liters when the actual
|
||||||
* value is 0 (and using metric units). So we just use
|
* value is 0 (and using metric units). So we just use
|
||||||
* the same 12 liters when size is not available */
|
* the same 12 liters when size is not available */
|
||||||
if (strtod_flags(data[6], NULL, 0) == 0.0 && cyl->start.mbar)
|
if (permissive_strtod(data[6], NULL) == 0.0 && cyl->start.mbar)
|
||||||
cyl->type.size.mliter = 12000;
|
cyl->type.size.mliter = 12000;
|
||||||
else
|
else
|
||||||
cyl->type.size.mliter = lrint((strtod_flags(data[6], NULL, 0)) * 1000);
|
cyl->type.size.mliter = lrint((permissive_strtod(data[6], NULL)) * 1000);
|
||||||
}
|
}
|
||||||
if (data[2])
|
if (data[2])
|
||||||
cyl->gasmix.o2.permille = atoi(data[2]) * 10;
|
cyl->gasmix.o2.permille = atoi(data[2]) * 10;
|
||||||
@ -329,19 +329,20 @@ static int dm5_cylinders(void *param, int, char **data, char **)
|
|||||||
|
|
||||||
static int dm5_gaschange(void *param, int, char **data, char **)
|
static int dm5_gaschange(void *param, int, char **data, char **)
|
||||||
{
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
struct parser_state *state = (struct parser_state *)param;
|
struct parser_state *state = (struct parser_state *)param;
|
||||||
|
|
||||||
event_start(state);
|
event_start(state);
|
||||||
if (data[0])
|
if (data[0])
|
||||||
state->cur_event.time.seconds = atoi(data[0]);
|
state->cur_event.time.seconds = atoi(data[0]);
|
||||||
if (data[1]) {
|
if (data[1]) {
|
||||||
strcpy(state->cur_event.name, "gaschange");
|
state->cur_event.name = "gaschange"s;
|
||||||
state->cur_event.value = lrint(strtod_flags(data[1], NULL, 0));
|
state->cur_event.value = lrint(permissive_strtod(data[1], NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* He part of the mix */
|
/* He part of the mix */
|
||||||
if (data[2])
|
if (data[2])
|
||||||
state->cur_event.value += lrint(strtod_flags(data[2], NULL, 0)) << 16;
|
state->cur_event.value += lrint(permissive_strtod(data[2], NULL)) << 16;
|
||||||
event_end(state);
|
event_end(state);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -366,12 +367,12 @@ static int dm5_dive(void *param, int, char **data, char **)
|
|||||||
|
|
||||||
state->cur_dive->when = (time_t)(atol(data[1]));
|
state->cur_dive->when = (time_t)(atol(data[1]));
|
||||||
if (data[2])
|
if (data[2])
|
||||||
utf8_string(data[2], &state->cur_dive->notes);
|
utf8_string_std(data[2], &state->cur_dive->notes);
|
||||||
|
|
||||||
if (data[3])
|
if (data[3])
|
||||||
state->cur_dive->duration.seconds = atoi(data[3]);
|
state->cur_dive->duration.seconds = atoi(data[3]);
|
||||||
if (data[15])
|
if (data[15])
|
||||||
state->cur_dive->dc.duration.seconds = atoi(data[15]);
|
state->cur_dive->dcs[0].duration.seconds = atoi(data[15]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: the deviceid hash should be calculated here.
|
* TODO: the deviceid hash should be calculated here.
|
||||||
@ -389,30 +390,28 @@ static int dm5_dive(void *param, int, char **data, char **)
|
|||||||
settings_end(state);
|
settings_end(state);
|
||||||
|
|
||||||
if (data[6])
|
if (data[6])
|
||||||
state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[6], NULL, 0) * 1000);
|
state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000);
|
||||||
if (data[8])
|
if (data[8])
|
||||||
state->cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
|
state->cur_dive->dcs[0].airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
|
||||||
if (data[9])
|
if (data[9])
|
||||||
state->cur_dive->dc.watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
|
state->cur_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
|
||||||
|
|
||||||
if (data[4]) {
|
if (data[4]) {
|
||||||
state->cur_dive->dc.deviceid = atoi(data[4]);
|
state->cur_dive->dcs[0].deviceid = atoi(data[4]);
|
||||||
}
|
}
|
||||||
// Ugh. dc.model is const char * -> we are not supposed to write into it. This will
|
|
||||||
// change when we convert to std::string.
|
|
||||||
if (data[5])
|
if (data[5])
|
||||||
utf8_string(data[5], (char **)&state->cur_dive->dc.model);
|
utf8_string_std(data[5], &state->cur_dive->dcs[0].model);
|
||||||
|
|
||||||
if (data[25]) {
|
if (data[25]) {
|
||||||
switch(atoi(data[25])) {
|
switch(atoi(data[25])) {
|
||||||
case 1:
|
case 1:
|
||||||
state->cur_dive->dc.divemode = OC;
|
state->cur_dive->dcs[0].divemode = OC;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
state->cur_dive->dc.divemode = CCR;
|
state->cur_dive->dcs[0].divemode = CCR;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
state->cur_dive->dc.divemode = OC;
|
state->cur_dive->dcs[0].divemode = OC;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -425,7 +424,7 @@ static int dm5_dive(void *param, int, char **data, char **)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data[14])
|
if (data[14])
|
||||||
state->cur_dive->dc.surface_pressure.mbar = (atoi(data[14]) / 100);
|
state->cur_dive->dcs[0].surface_pressure.mbar = (atoi(data[14]) / 100);
|
||||||
|
|
||||||
interval = data[16] ? atoi(data[16]) : 0;
|
interval = data[16] ? atoi(data[16]) : 0;
|
||||||
|
|
||||||
@ -513,7 +512,7 @@ static int dm5_dive(void *param, int, char **data, char **)
|
|||||||
if (profileBlob)
|
if (profileBlob)
|
||||||
state->cur_sample->depth.mm = lrintf(profileBlob[i] * 1000.0f);
|
state->cur_sample->depth.mm = lrintf(profileBlob[i] * 1000.0f);
|
||||||
else
|
else
|
||||||
state->cur_sample->depth.mm = state->cur_dive->dc.maxdepth.mm;
|
state->cur_sample->depth.mm = state->cur_dive->dcs[0].maxdepth.mm;
|
||||||
|
|
||||||
if (data[18] && data[18][0])
|
if (data[18] && data[18][0])
|
||||||
state->cur_sample->temperature.mkelvin = C_to_mkelvin(tempBlob[i]);
|
state->cur_sample->temperature.mkelvin = C_to_mkelvin(tempBlob[i]);
|
||||||
@ -549,7 +548,7 @@ static int dm5_dive(void *param, int, char **data, char **)
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int parse_dm5_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
int parse_dm5_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
char *err = NULL;
|
char *err = NULL;
|
||||||
|
|||||||
13
core/ios.cpp
13
core/ios.cpp
@ -32,13 +32,11 @@ static std::string make_default_filename()
|
|||||||
return system_default_path() + "/subsurface.xml";
|
return system_default_path() + "/subsurface.xml";
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
const char mac_system_divelist_default_font[] = "Arial";
|
const char mac_system_divelist_default_font[] = "Arial";
|
||||||
const char *system_divelist_default_font = mac_system_divelist_default_font;
|
const char *system_divelist_default_font = mac_system_divelist_default_font;
|
||||||
double system_divelist_default_font_size = -1.0;
|
double system_divelist_default_font_size = -1.0;
|
||||||
|
|
||||||
void subsurface_OS_pref_setup(void)
|
void subsurface_OS_pref_setup()
|
||||||
{
|
{
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
@ -49,13 +47,13 @@ bool subsurface_ignore_font(const char*)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *system_default_directory(void)
|
const char *system_default_directory()
|
||||||
{
|
{
|
||||||
static const std::string path = system_default_path();
|
static const std::string path = system_default_path();
|
||||||
return path.c_str();
|
return path.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *system_default_filename(void)
|
const char *system_default_filename()
|
||||||
{
|
{
|
||||||
static const std::string fn = make_default_filename();
|
static const std::string fn = make_default_filename();
|
||||||
return fn.c_str();
|
return fn.c_str();
|
||||||
@ -109,12 +107,12 @@ int subsurface_zip_close(struct zip *zip)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* win32 console */
|
/* win32 console */
|
||||||
void subsurface_console_init(void)
|
void subsurface_console_init()
|
||||||
{
|
{
|
||||||
/* NOP */
|
/* NOP */
|
||||||
}
|
}
|
||||||
|
|
||||||
void subsurface_console_exit(void)
|
void subsurface_console_exit()
|
||||||
{
|
{
|
||||||
/* NOP */
|
/* NOP */
|
||||||
}
|
}
|
||||||
@ -123,4 +121,3 @@ bool subsurface_user_is_root()
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
/* libdivecomputer */
|
/* libdivecomputer */
|
||||||
|
|
||||||
@ -20,47 +21,42 @@
|
|||||||
#define dc_usb_storage_open(stream, context, devname) (DC_STATUS_UNSUPPORTED)
|
#define dc_usb_storage_open(stream, context, devname) (DC_STATUS_UNSUPPORTED)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#else
|
|
||||||
#include <stdbool.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct dive;
|
struct dive;
|
||||||
struct divelog;
|
struct divelog;
|
||||||
struct devices;
|
struct devices;
|
||||||
|
|
||||||
typedef struct {
|
struct device_data_t {
|
||||||
dc_descriptor_t *descriptor;
|
dc_descriptor_t *descriptor = nullptr;
|
||||||
const char *vendor, *product, *devname;
|
std::string vendor, product, devname;
|
||||||
const char *model, *btname;
|
std::string model, btname;
|
||||||
unsigned char *fingerprint;
|
unsigned char *fingerprint = nullptr;
|
||||||
unsigned int fsize, fdeviceid, fdiveid;
|
unsigned int fsize = 0, fdeviceid = 0, fdiveid = 0;
|
||||||
struct dc_event_devinfo_t devinfo;
|
struct dc_event_devinfo_t devinfo = { };
|
||||||
uint32_t diveid;
|
uint32_t diveid = 0;
|
||||||
dc_device_t *device;
|
dc_device_t *device = nullptr;
|
||||||
dc_context_t *context;
|
dc_context_t *context = nullptr;
|
||||||
dc_iostream_t *iostream;
|
dc_iostream_t *iostream = nullptr;
|
||||||
bool force_download;
|
bool force_download = false;
|
||||||
bool libdc_log;
|
bool libdc_log = false;
|
||||||
bool libdc_dump;
|
bool libdc_dump = false;
|
||||||
bool bluetooth_mode;
|
bool bluetooth_mode = false;
|
||||||
bool sync_time;
|
bool sync_time = false;
|
||||||
FILE *libdc_logfile;
|
FILE *libdc_logfile = nullptr;
|
||||||
struct divelog *log;
|
struct divelog *log = nullptr;
|
||||||
void *androidUsbDeviceDescriptor;
|
void *androidUsbDeviceDescriptor = nullptr;
|
||||||
} device_data_t;
|
device_data_t();
|
||||||
|
~device_data_t();
|
||||||
|
};
|
||||||
|
|
||||||
const char *errmsg (dc_status_t rc);
|
const char *errmsg (dc_status_t rc);
|
||||||
const char *do_libdivecomputer_import(device_data_t *data);
|
std::string do_libdivecomputer_import(device_data_t *data);
|
||||||
const char *do_uemis_import(device_data_t *data);
|
|
||||||
dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, unsigned char *buffer, int size);
|
dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, unsigned char *buffer, int size);
|
||||||
void logfunc(dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata);
|
void logfunc(dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata);
|
||||||
dc_descriptor_t *get_descriptor(dc_family_t type, unsigned int model);
|
dc_descriptor_t *get_descriptor(dc_family_t type, unsigned int model);
|
||||||
|
|
||||||
extern int import_thread_cancelled;
|
extern int import_thread_cancelled;
|
||||||
extern const char *progress_bar_text;
|
extern std::string progress_bar_text;
|
||||||
extern void (*progress_callback)(const char *text);
|
extern void (*progress_callback)(const std::string &text);
|
||||||
extern double progress_bar_fraction;
|
extern double progress_bar_fraction;
|
||||||
|
|
||||||
dc_status_t ble_packet_open(dc_iostream_t **iostream, dc_context_t *context, const char* devaddr, void *userdata);
|
dc_status_t ble_packet_open(dc_iostream_t **iostream, dc_context_t *context, const char* devaddr, void *userdata);
|
||||||
@ -72,13 +68,7 @@ dc_status_t divecomputer_device_open(device_data_t *data);
|
|||||||
|
|
||||||
unsigned int get_supported_transports(device_data_t *data);
|
unsigned int get_supported_transports(device_data_t *data);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
extern std::string logfile_name;
|
extern std::string logfile_name;
|
||||||
extern std::string dumpfile_name;
|
extern std::string dumpfile_name;
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // LIBDIVECOMPUTER_H
|
#endif // LIBDIVECOMPUTER_H
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "ssrf.h"
|
|
||||||
#include "divesite.h"
|
#include "divesite.h"
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
#include "divelog.h"
|
#include "divelog.h"
|
||||||
@ -132,25 +131,23 @@ static int handle_event_ver3(int code, const unsigned char *ps, unsigned int ps_
|
|||||||
return skip;
|
return skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_dives(int log_version, const unsigned char *buf, unsigned int buf_size, struct dive_table *table, struct dive_site_table *sites)
|
static void parse_dives(int log_version, const unsigned char *buf, unsigned int buf_size, struct dive_table &table, dive_site_table &sites)
|
||||||
{
|
{
|
||||||
unsigned int ptr = 0;
|
unsigned int ptr = 0;
|
||||||
unsigned char model;
|
unsigned char model;
|
||||||
|
|
||||||
struct dive *dive;
|
|
||||||
struct divecomputer *dc;
|
struct divecomputer *dc;
|
||||||
struct sample *sample;
|
struct sample *sample;
|
||||||
|
|
||||||
while (ptr < buf_size) {
|
while (ptr < buf_size) {
|
||||||
int i;
|
int i;
|
||||||
dive = alloc_dive();
|
auto dive = std::make_unique<struct dive>();
|
||||||
memset(&sensor_ids, 0, sizeof(sensor_ids));
|
memset(&sensor_ids, 0, sizeof(sensor_ids));
|
||||||
dc = &dive->dc;
|
dc = &dive->dcs[0];
|
||||||
|
|
||||||
/* Just the main cylinder until we can handle the buddy cylinder porperly */
|
/* Just the main cylinder until we can handle the buddy cylinder porperly */
|
||||||
for (i = 0; i < 1; i++) {
|
for (i = 0; i < 1; i++) {
|
||||||
cylinder_t cyl = empty_cylinder;
|
cylinder_t cyl = default_cylinder(dive.get());
|
||||||
fill_default_cylinder(dive, &cyl);
|
|
||||||
add_cylinder(&dive->cylinders, i, cyl);
|
add_cylinder(&dive->cylinders, i, cyl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,17 +155,17 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
|
|||||||
model = *(buf + ptr);
|
model = *(buf + ptr);
|
||||||
switch (model) {
|
switch (model) {
|
||||||
case 0:
|
case 0:
|
||||||
dc->model = strdup("Xen");
|
dc->model = "Xen";
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
dc->model = strdup("Xeo");
|
dc->model = "Xeo";
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
dc->model = strdup("Lynx");
|
dc->model = "Lynx";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dc->model = strdup("Liquivision");
|
dc->model = "Liquivision";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ptr++;
|
ptr++;
|
||||||
@ -191,7 +188,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
|
|||||||
|
|
||||||
/* Store the location only if we have one */
|
/* Store the location only if we have one */
|
||||||
if (!location.empty())
|
if (!location.empty())
|
||||||
add_dive_to_dive_site(dive, find_or_create_dive_site_with_name(location.c_str(), sites));
|
sites.find_or_create(location)->add_dive(dive.get());
|
||||||
|
|
||||||
ptr += len + 4 + place_len;
|
ptr += len + 4 + place_len;
|
||||||
|
|
||||||
@ -202,7 +199,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
|
|||||||
// Blank notes are better than the default text
|
// Blank notes are better than the default text
|
||||||
std::string notes((char *)buf + ptr, len);
|
std::string notes((char *)buf + ptr, len);
|
||||||
if (!starts_with(notes, "Comment ..."))
|
if (!starts_with(notes, "Comment ..."))
|
||||||
dive->notes = strdup(notes.c_str());
|
dive->notes = notes;
|
||||||
ptr += len;
|
ptr += len;
|
||||||
|
|
||||||
dive->id = array_uint32_le(buf + ptr);
|
dive->id = array_uint32_le(buf + ptr);
|
||||||
@ -336,7 +333,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
|
|||||||
sample->depth.mm = array_uint16_le(ds + (d - 1) * 2) * 10; // cm->mm
|
sample->depth.mm = array_uint16_le(ds + (d - 1) * 2) * 10; // cm->mm
|
||||||
sample->temperature.mkelvin = C_to_mkelvin((float) array_uint16_le(ts + (d - 1) * 2) / 10); // dC->mK
|
sample->temperature.mkelvin = C_to_mkelvin((float) array_uint16_le(ts + (d - 1) * 2) / 10); // dC->mK
|
||||||
add_sample_pressure(sample, event.pressure.sensor, event.pressure.mbar);
|
add_sample_pressure(sample, event.pressure.sensor, event.pressure.mbar);
|
||||||
finish_sample(dc);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
} else if (event.time > sample_time) {
|
} else if (event.time > sample_time) {
|
||||||
@ -344,7 +340,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
|
|||||||
sample->time.seconds = sample_time;
|
sample->time.seconds = sample_time;
|
||||||
sample->depth.mm = depth_mm;
|
sample->depth.mm = depth_mm;
|
||||||
sample->temperature.mkelvin = temp_mk;
|
sample->temperature.mkelvin = temp_mk;
|
||||||
finish_sample(dc);
|
|
||||||
d++;
|
d++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@ -353,7 +348,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
|
|||||||
sample->depth.mm = depth_mm;
|
sample->depth.mm = depth_mm;
|
||||||
sample->temperature.mkelvin = temp_mk;
|
sample->temperature.mkelvin = temp_mk;
|
||||||
add_sample_pressure(sample, event.pressure.sensor, event.pressure.mbar);
|
add_sample_pressure(sample, event.pressure.sensor, event.pressure.mbar);
|
||||||
finish_sample(dc);
|
|
||||||
d++;
|
d++;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -372,7 +366,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
|
|||||||
sample->temperature.mkelvin = last_temp + (temp_mk - last_temp)
|
sample->temperature.mkelvin = last_temp + (temp_mk - last_temp)
|
||||||
* ((int)event.time - (int)last_time) / sample_interval;
|
* ((int)event.time - (int)last_time) / sample_interval;
|
||||||
}
|
}
|
||||||
finish_sample(dc);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -387,7 +380,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
|
|||||||
sample->depth.mm = array_uint16_le(ds + d * 2) * 10; // cm->mm
|
sample->depth.mm = array_uint16_le(ds + d * 2) * 10; // cm->mm
|
||||||
sample->temperature.mkelvin =
|
sample->temperature.mkelvin =
|
||||||
C_to_mkelvin((float)array_uint16_le(ts + d * 2) / 10);
|
C_to_mkelvin((float)array_uint16_le(ts + d * 2) / 10);
|
||||||
finish_sample(dc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log_version == 3 && model == 4) {
|
if (log_version == 3 && model == 4) {
|
||||||
@ -415,17 +407,11 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
|
|||||||
}
|
}
|
||||||
|
|
||||||
// End dive
|
// End dive
|
||||||
record_dive_to_table(dive, table);
|
table.record_dive(std::move(dive));
|
||||||
dive = NULL;
|
|
||||||
|
|
||||||
// Advance ptr for next dive
|
// Advance ptr for next dive
|
||||||
ptr += ps_ptr + 4;
|
ptr += ps_ptr + 4;
|
||||||
} // while
|
} // while
|
||||||
|
|
||||||
//DEBUG save_dives("/tmp/test.xml");
|
|
||||||
|
|
||||||
// if we bailed out of the loop, the dive hasn't been recorded and dive hasn't been set to NULL
|
|
||||||
free_dive(dive);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int try_to_open_liquivision(const char *, std::string &mem, struct divelog *log)
|
int try_to_open_liquivision(const char *, std::string &mem, struct divelog *log)
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include "ssrf.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -38,8 +37,8 @@ std::string saved_git_id;
|
|||||||
struct git_parser_state {
|
struct git_parser_state {
|
||||||
git_repository *repo = nullptr;
|
git_repository *repo = nullptr;
|
||||||
struct divecomputer *active_dc = nullptr;
|
struct divecomputer *active_dc = nullptr;
|
||||||
struct dive *active_dive = nullptr;
|
std::unique_ptr<dive> active_dive;
|
||||||
dive_trip_t *active_trip = nullptr;
|
std::unique_ptr<dive_trip> active_trip;
|
||||||
std::string fulltext_mode;
|
std::string fulltext_mode;
|
||||||
std::string fulltext_query;
|
std::string fulltext_query;
|
||||||
std::string filter_constraint_type;
|
std::string filter_constraint_type;
|
||||||
@ -47,7 +46,7 @@ struct git_parser_state {
|
|||||||
std::string filter_constraint_range_mode;
|
std::string filter_constraint_range_mode;
|
||||||
bool filter_constraint_negate = false;
|
bool filter_constraint_negate = false;
|
||||||
std::string filter_constraint_data;
|
std::string filter_constraint_data;
|
||||||
struct picture active_pic = { 0 };
|
struct picture active_pic;
|
||||||
struct dive_site *active_site = nullptr;
|
struct dive_site *active_site = nullptr;
|
||||||
std::unique_ptr<filter_preset> active_filter;
|
std::unique_ptr<filter_preset> active_filter;
|
||||||
struct divelog *log = nullptr;
|
struct divelog *log = nullptr;
|
||||||
@ -172,16 +171,16 @@ static int get_hex(const char *line)
|
|||||||
static void parse_dive_gps(char *line, struct git_parser_state *state)
|
static void parse_dive_gps(char *line, struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
location_t location;
|
location_t location;
|
||||||
struct dive_site *ds = get_dive_site_for_dive(state->active_dive);
|
struct dive_site *ds = get_dive_site_for_dive(state->active_dive.get());
|
||||||
|
|
||||||
parse_location(line, &location);
|
parse_location(line, &location);
|
||||||
if (!ds) {
|
if (!ds) {
|
||||||
ds = get_dive_site_by_gps(&location, state->log->sites);
|
ds = state->log->sites.get_by_gps(&location);
|
||||||
if (!ds)
|
if (!ds)
|
||||||
ds = create_dive_site_with_gps("", &location, state->log->sites);
|
ds = state->log->sites.create(std::string(), location);
|
||||||
add_dive_to_dive_site(state->active_dive, ds);
|
ds->add_dive(state->active_dive.get());
|
||||||
} else {
|
} else {
|
||||||
if (dive_site_has_gps_location(ds) && !same_location(&ds->location, &location)) {
|
if (dive_site_has_gps_location(ds) && ds->location != location) {
|
||||||
std::string coords = printGPSCoordsC(&location);
|
std::string coords = printGPSCoordsC(&location);
|
||||||
// we have a dive site that already has GPS coordinates
|
// we have a dive site that already has GPS coordinates
|
||||||
// note 1: there will be much less copying once the core
|
// note 1: there will be much less copying once the core
|
||||||
@ -189,10 +188,8 @@ static void parse_dive_gps(char *line, struct git_parser_state *state)
|
|||||||
// note 2: we could include the first newline in the
|
// note 2: we could include the first newline in the
|
||||||
// translation string, but that would be weird and cause
|
// translation string, but that would be weird and cause
|
||||||
// a new string.
|
// a new string.
|
||||||
std::string new_text = std::string(ds->notes) + '\n' +
|
ds->notes += '\n';
|
||||||
format_string_std(translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords.c_str());
|
ds->notes += format_string_std(translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords.c_str());
|
||||||
free(ds->notes);
|
|
||||||
ds->notes = strdup(new_text.c_str());
|
|
||||||
}
|
}
|
||||||
ds->location = location;
|
ds->location = location;
|
||||||
}
|
}
|
||||||
@ -210,54 +207,43 @@ static std::string get_first_converted_string(struct git_parser_state *state)
|
|||||||
return std::move(state->converted_strings.front());
|
return std::move(state->converted_strings.front());
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a dummy function that converts the first
|
|
||||||
// converted string to a newly allocated C-string.
|
|
||||||
// Will be removed when the core data structures are
|
|
||||||
// converted to std::string.
|
|
||||||
static char *get_first_converted_string_c(struct git_parser_state *state)
|
|
||||||
{
|
|
||||||
return strdup(get_first_converted_string(state).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void parse_dive_location(char *, struct git_parser_state *state)
|
static void parse_dive_location(char *, struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
std::string name = get_first_converted_string(state);
|
std::string name = get_first_converted_string(state);
|
||||||
struct dive_site *ds = get_dive_site_for_dive(state->active_dive);
|
struct dive_site *ds = get_dive_site_for_dive(state->active_dive.get());
|
||||||
if (!ds) {
|
if (!ds) {
|
||||||
ds = get_dive_site_by_name(name.c_str(), state->log->sites);
|
ds = state->log->sites.get_by_name(name);
|
||||||
if (!ds)
|
if (!ds)
|
||||||
ds = create_dive_site(name.c_str(), state->log->sites);
|
ds = state->log->sites.create(name);
|
||||||
add_dive_to_dive_site(state->active_dive, ds);
|
ds->add_dive(state->active_dive.get());
|
||||||
} else {
|
} else {
|
||||||
// we already had a dive site linked to the dive
|
// we already had a dive site linked to the dive
|
||||||
if (empty_string(ds->name)) {
|
if (ds->name.empty()) {
|
||||||
free(ds->name); // empty_string could mean pointer to a 0-byte!
|
ds->name = name.c_str();
|
||||||
ds->name = strdup(name.c_str());
|
|
||||||
} else {
|
} else {
|
||||||
// and that dive site had a name. that's weird - if our name is different, add it to the notes
|
// and that dive site had a name. that's weird - if our name is different, add it to the notes
|
||||||
if (!same_string(ds->name, name.c_str())) {
|
if (ds->name == name) {
|
||||||
std::string new_string = std::string(ds->notes) + '\n' +
|
ds->notes += '\n';
|
||||||
format_string_std(translate("gettextFromC", "additional name for site: %s\n"), name.c_str());
|
ds->notes += format_string_std(translate("gettextFromC", "additional name for site: %s\n"), name.c_str());
|
||||||
ds->notes = strdup(new_string.c_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_dive_diveguide(char *, struct git_parser_state *state)
|
static void parse_dive_diveguide(char *, struct git_parser_state *state)
|
||||||
{ state->active_dive->diveguide = get_first_converted_string_c(state); }
|
{ state->active_dive->diveguide = get_first_converted_string(state); }
|
||||||
|
|
||||||
static void parse_dive_buddy(char *, struct git_parser_state *state)
|
static void parse_dive_buddy(char *, struct git_parser_state *state)
|
||||||
{ state->active_dive->buddy = get_first_converted_string_c(state); }
|
{ state->active_dive->buddy = get_first_converted_string(state); }
|
||||||
|
|
||||||
static void parse_dive_suit(char *, struct git_parser_state *state)
|
static void parse_dive_suit(char *, struct git_parser_state *state)
|
||||||
{ state->active_dive->suit = get_first_converted_string_c(state); }
|
{ state->active_dive->suit = get_first_converted_string(state); }
|
||||||
|
|
||||||
static void parse_dive_notes(char *, struct git_parser_state *state)
|
static void parse_dive_notes(char *, struct git_parser_state *state)
|
||||||
{ state->active_dive->notes = get_first_converted_string_c(state); }
|
{ state->active_dive->notes = get_first_converted_string(state); }
|
||||||
|
|
||||||
static void parse_dive_divesiteid(char *line, struct git_parser_state *state)
|
static void parse_dive_divesiteid(char *line, struct git_parser_state *state)
|
||||||
{ add_dive_to_dive_site(state->active_dive, get_dive_site_by_uuid(get_hex(line), state->log->sites)); }
|
{ state->log->sites.get_by_uuid(get_hex(line))->add_dive(state->active_dive.get()); }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can have multiple tags.
|
* We can have multiple tags.
|
||||||
@ -266,7 +252,7 @@ static void parse_dive_tags(char *, struct git_parser_state *state)
|
|||||||
{
|
{
|
||||||
for (const std::string &tag: state->converted_strings) {
|
for (const std::string &tag: state->converted_strings) {
|
||||||
if (!tag.empty())
|
if (!tag.empty())
|
||||||
taglist_add_tag(&state->active_dive->tag_list, tag.c_str());
|
taglist_add_tag(state->active_dive->tags, tag.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,13 +300,13 @@ static void parse_dive_invalid(char *, struct git_parser_state *state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void parse_site_description(char *, struct git_parser_state *state)
|
static void parse_site_description(char *, struct git_parser_state *state)
|
||||||
{ state->active_site->description = get_first_converted_string_c(state); }
|
{ state->active_site->description = get_first_converted_string(state); }
|
||||||
|
|
||||||
static void parse_site_name(char *, struct git_parser_state *state)
|
static void parse_site_name(char *, struct git_parser_state *state)
|
||||||
{ state->active_site->name = get_first_converted_string_c(state); }
|
{ state->active_site->name = get_first_converted_string(state); }
|
||||||
|
|
||||||
static void parse_site_notes(char *, struct git_parser_state *state)
|
static void parse_site_notes(char *, struct git_parser_state *state)
|
||||||
{ state->active_site->notes = get_first_converted_string_c(state); }
|
{ state->active_site->notes = get_first_converted_string(state); }
|
||||||
|
|
||||||
static void parse_site_gps(char *line, struct git_parser_state *state)
|
static void parse_site_gps(char *line, struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
@ -332,8 +318,8 @@ static void parse_site_geo(char *line, struct git_parser_state *state)
|
|||||||
int origin;
|
int origin;
|
||||||
int category;
|
int category;
|
||||||
sscanf(line, "cat %d origin %d \"", &category, &origin);
|
sscanf(line, "cat %d origin %d \"", &category, &origin);
|
||||||
taxonomy_set_category(&state->active_site->taxonomy, (taxonomy_category)category,
|
taxonomy_set_category(state->active_site->taxonomy, (taxonomy_category)category,
|
||||||
get_first_converted_string(state).c_str(), (taxonomy_origin)origin);
|
get_first_converted_string(state), (taxonomy_origin)origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string pop_cstring(struct git_parser_state *state, const char *err)
|
static std::string pop_cstring(struct git_parser_state *state, const char *err)
|
||||||
@ -390,7 +376,7 @@ static void parse_cylinder_keyvalue(void *_cylinder, const char *key, const std:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!strcmp(key, "description")) {
|
if (!strcmp(key, "description")) {
|
||||||
cylinder->type.description = strdup(value.c_str());
|
cylinder->type.description = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!strcmp(key, "o2")) {
|
if (!strcmp(key, "o2")) {
|
||||||
@ -434,7 +420,7 @@ static void parse_cylinder_keyvalue(void *_cylinder, const char *key, const std:
|
|||||||
|
|
||||||
static void parse_dive_cylinder(char *line, struct git_parser_state *state)
|
static void parse_dive_cylinder(char *line, struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
cylinder_t cylinder = empty_cylinder;
|
cylinder_t cylinder;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
char c;
|
char c;
|
||||||
@ -445,9 +431,9 @@ static void parse_dive_cylinder(char *line, struct git_parser_state *state)
|
|||||||
line = parse_keyvalue_entry(parse_cylinder_keyvalue, &cylinder, line, state);
|
line = parse_keyvalue_entry(parse_cylinder_keyvalue, &cylinder, line, state);
|
||||||
}
|
}
|
||||||
if (cylinder.cylinder_use == OXYGEN)
|
if (cylinder.cylinder_use == OXYGEN)
|
||||||
state->o2pressure_sensor = state->active_dive->cylinders.nr;
|
state->o2pressure_sensor = static_cast<int>(state->active_dive->cylinders.size());
|
||||||
|
|
||||||
add_cylinder(&state->active_dive->cylinders, state->active_dive->cylinders.nr, cylinder);
|
state->active_dive->cylinders.push_back(std::move(cylinder));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_weightsystem_keyvalue(void *_ws, const char *key, const std::string &value)
|
static void parse_weightsystem_keyvalue(void *_ws, const char *key, const std::string &value)
|
||||||
@ -458,7 +444,7 @@ static void parse_weightsystem_keyvalue(void *_ws, const char *key, const std::s
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!strcmp(key, "description")) {
|
if (!strcmp(key, "description")) {
|
||||||
ws->description = strdup(value.c_str());
|
ws->description = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
report_error("Unknown weightsystem key/value pair (%s/%s)", key, value.c_str());
|
report_error("Unknown weightsystem key/value pair (%s/%s)", key, value.c_str());
|
||||||
@ -466,7 +452,7 @@ static void parse_weightsystem_keyvalue(void *_ws, const char *key, const std::s
|
|||||||
|
|
||||||
static void parse_dive_weightsystem(char *line, struct git_parser_state *state)
|
static void parse_dive_weightsystem(char *line, struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
weightsystem_t ws = empty_weightsystem;
|
weightsystem_t ws;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
char c;
|
char c;
|
||||||
@ -477,7 +463,7 @@ static void parse_dive_weightsystem(char *line, struct git_parser_state *state)
|
|||||||
line = parse_keyvalue_entry(parse_weightsystem_keyvalue, &ws, line, state);
|
line = parse_keyvalue_entry(parse_weightsystem_keyvalue, &ws, line, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_to_weightsystem_table(&state->active_dive->weightsystems, state->active_dive->weightsystems.nr, ws);
|
state->active_dive->weightsystems.push_back(std::move(ws));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int match_action(char *line, void *data,
|
static int match_action(char *line, void *data,
|
||||||
@ -656,7 +642,7 @@ static char *parse_sample_unit(struct sample *sample, double val, char *unit)
|
|||||||
*/
|
*/
|
||||||
static int sanitize_sensor_id(const struct dive *d, int nr)
|
static int sanitize_sensor_id(const struct dive *d, int nr)
|
||||||
{
|
{
|
||||||
return d && nr >= 0 && nr < d->cylinders.nr ? nr : NO_SENSOR;
|
return d && nr >= 0 && static_cast<size_t>(nr) < d->cylinders.size() ? nr : NO_SENSOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -680,13 +666,14 @@ static int sanitize_sensor_id(const struct dive *d, int nr)
|
|||||||
static struct sample *new_sample(struct git_parser_state *state)
|
static struct sample *new_sample(struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
struct sample *sample = prepare_sample(state->active_dc);
|
struct sample *sample = prepare_sample(state->active_dc);
|
||||||
if (sample != state->active_dc->sample) {
|
size_t num_samples = state->active_dc->samples.size();
|
||||||
memcpy(sample, sample - 1, sizeof(struct sample));
|
if (num_samples >= 2) {
|
||||||
|
*sample = state->active_dc->samples[num_samples - 2];
|
||||||
sample->pressure[0].mbar = 0;
|
sample->pressure[0].mbar = 0;
|
||||||
sample->pressure[1].mbar = 0;
|
sample->pressure[1].mbar = 0;
|
||||||
} else {
|
} else {
|
||||||
sample->sensor[0] = sanitize_sensor_id(state->active_dive, !state->o2pressure_sensor);
|
sample->sensor[0] = sanitize_sensor_id(state->active_dive.get(), !state->o2pressure_sensor);
|
||||||
sample->sensor[1] = sanitize_sensor_id(state->active_dive, state->o2pressure_sensor);
|
sample->sensor[1] = sanitize_sensor_id(state->active_dive.get(), state->o2pressure_sensor);
|
||||||
}
|
}
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
@ -722,7 +709,6 @@ static void sample_parser(char *line, struct git_parser_state *state)
|
|||||||
line = parse_sample_unit(sample, val, line);
|
line = parse_sample_unit(sample, val, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finish_sample(state->active_dc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_dc_airtemp(char *line, struct git_parser_state *state)
|
static void parse_dc_airtemp(char *line, struct git_parser_state *state)
|
||||||
@ -755,7 +741,7 @@ static void parse_dc_meandepth(char *line, struct git_parser_state *state)
|
|||||||
{ state->active_dc->meandepth = get_depth(line); }
|
{ state->active_dc->meandepth = get_depth(line); }
|
||||||
|
|
||||||
static void parse_dc_model(char *, struct git_parser_state *state)
|
static void parse_dc_model(char *, struct git_parser_state *state)
|
||||||
{ state->active_dc->model = get_first_converted_string_c(state); }
|
{ state->active_dc->model = get_first_converted_string(state); }
|
||||||
|
|
||||||
static void parse_dc_numberofoxygensensors(char *line, struct git_parser_state *state)
|
static void parse_dc_numberofoxygensensors(char *line, struct git_parser_state *state)
|
||||||
{ state->active_dc->no_o2sensors = get_index(line); }
|
{ state->active_dc->no_o2sensors = get_index(line); }
|
||||||
@ -795,7 +781,7 @@ static int get_divemode(const char *divemodestring) {
|
|||||||
struct parse_event {
|
struct parse_event {
|
||||||
std::string name;
|
std::string name;
|
||||||
int has_divemode = false;
|
int has_divemode = false;
|
||||||
struct event ev = { 0 };
|
struct event ev;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void parse_event_keyvalue(void *_parse, const char *key, const std::string &value)
|
static void parse_event_keyvalue(void *_parse, const char *key, const std::string &value)
|
||||||
@ -833,14 +819,13 @@ static void parse_dc_keyvalue(char *line, struct git_parser_state *state)
|
|||||||
if (state->converted_strings.size() != 2)
|
if (state->converted_strings.size() != 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
add_extra_data(state->active_dc, state->converted_strings[0].c_str(), state->converted_strings[1].c_str());
|
add_extra_data(state->active_dc, state->converted_strings[0], state->converted_strings[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_dc_event(char *line, struct git_parser_state *state)
|
static void parse_dc_event(char *line, struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
int m, s = 0;
|
int m, s = 0;
|
||||||
struct parse_event p;
|
struct parse_event p;
|
||||||
struct event *ev;
|
|
||||||
|
|
||||||
m = strtol(line, &line, 10);
|
m = strtol(line, &line, 10);
|
||||||
if (*line == ':')
|
if (*line == ':')
|
||||||
@ -860,17 +845,17 @@ static void parse_dc_event(char *line, struct git_parser_state *state)
|
|||||||
if (p.has_divemode && p.name != "modechange")
|
if (p.has_divemode && p.name != "modechange")
|
||||||
p.name = "modechange";
|
p.name = "modechange";
|
||||||
|
|
||||||
ev = add_event(state->active_dc, p.ev.time.seconds, p.ev.type, p.ev.flags, p.ev.value, p.name.c_str());
|
struct event *ev = add_event(state->active_dc, p.ev.time.seconds, p.ev.type, p.ev.flags, p.ev.value, p.name.c_str());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Older logs might mark the dive to be CCR by having an "SP change" event at time 0:00.
|
* Older logs might mark the dive to be CCR by having an "SP change" event at time 0:00.
|
||||||
* Better to mark them being CCR on import so no need for special treatments elsewhere on
|
* Better to mark them being CCR on import so no need for special treatments elsewhere on
|
||||||
* the code.
|
* the code.
|
||||||
*/
|
*/
|
||||||
if (ev && p.ev.time.seconds == 0 && p.ev.type == SAMPLE_EVENT_PO2 && p.ev.value && state->active_dc->divemode==OC)
|
if (p.ev.time.seconds == 0 && p.ev.type == SAMPLE_EVENT_PO2 && p.ev.value && state->active_dc->divemode==OC)
|
||||||
state->active_dc->divemode = CCR;
|
state->active_dc->divemode = CCR;
|
||||||
|
|
||||||
if (ev && event_is_gaschange(ev)) {
|
if (ev->is_gaschange()) {
|
||||||
/*
|
/*
|
||||||
* We subtract one here because "0" is "no index",
|
* We subtract one here because "0" is "no index",
|
||||||
* and the parsing will add one for actual cylinder
|
* and the parsing will add one for actual cylinder
|
||||||
@ -891,10 +876,10 @@ static void parse_trip_time(char *, struct git_parser_state *)
|
|||||||
{ }
|
{ }
|
||||||
|
|
||||||
static void parse_trip_location(char *, struct git_parser_state *state)
|
static void parse_trip_location(char *, struct git_parser_state *state)
|
||||||
{ state->active_trip->location = get_first_converted_string_c(state); }
|
{ state->active_trip->location = get_first_converted_string(state); }
|
||||||
|
|
||||||
static void parse_trip_notes(char *, struct git_parser_state *state)
|
static void parse_trip_notes(char *, struct git_parser_state *state)
|
||||||
{ state->active_trip->notes = get_first_converted_string_c(state); }
|
{ state->active_trip->notes = get_first_converted_string(state); }
|
||||||
|
|
||||||
static void parse_settings_autogroup(char *, struct git_parser_state *state)
|
static void parse_settings_autogroup(char *, struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
@ -1044,13 +1029,13 @@ static void parse_settings_fingerprint(char *line, struct git_parser_state *stat
|
|||||||
}
|
}
|
||||||
if (verbose > 1)
|
if (verbose > 1)
|
||||||
report_info("fingerprint %08x %08x %08x %08x %s\n", fph.model, fph.serial, fph.fdeviceid, fph.fdiveid, fph.hex_data.c_str());
|
report_info("fingerprint %08x %08x %08x %08x %s\n", fph.model, fph.serial, fph.fdeviceid, fph.fdiveid, fph.hex_data.c_str());
|
||||||
create_fingerprint_node_from_hex(&fingerprint_table, fph.model, fph.serial,
|
create_fingerprint_node_from_hex(fingerprints, fph.model, fph.serial,
|
||||||
fph.hex_data.c_str(), fph.fdeviceid, fph.fdiveid);
|
fph.hex_data, fph.fdeviceid, fph.fdiveid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_picture_filename(char *, struct git_parser_state *state)
|
static void parse_picture_filename(char *, struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
state->active_pic.filename = get_first_converted_string_c(state);
|
state->active_pic.filename = get_first_converted_string(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_picture_gps(char *line, struct git_parser_state *state)
|
static void parse_picture_gps(char *line, struct git_parser_state *state)
|
||||||
@ -1185,10 +1170,10 @@ static void parse_filter_preset_constraint(char *line, struct git_parser_state *
|
|||||||
line = parse_keyvalue_entry(parse_filter_preset_constraint_keyvalue, state, line, state);
|
line = parse_keyvalue_entry(parse_filter_preset_constraint_keyvalue, state, line, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_preset_add_constraint(state->active_filter.get(), state->filter_constraint_type.c_str(),
|
state->active_filter->add_constraint(state->filter_constraint_type,
|
||||||
state->filter_constraint_string_mode.c_str(),
|
state->filter_constraint_string_mode,
|
||||||
state->filter_constraint_range_mode.c_str(),
|
state->filter_constraint_range_mode,
|
||||||
state->filter_constraint_negate, state->filter_constraint_data.c_str());
|
state->filter_constraint_negate, state->filter_constraint_data);
|
||||||
state->filter_constraint_type.clear();
|
state->filter_constraint_type.clear();
|
||||||
state->filter_constraint_string_mode.clear();
|
state->filter_constraint_string_mode.clear();
|
||||||
state->filter_constraint_range_mode.clear();
|
state->filter_constraint_range_mode.clear();
|
||||||
@ -1222,14 +1207,14 @@ static void parse_filter_preset_fulltext(char *line, struct git_parser_state *st
|
|||||||
line = parse_keyvalue_entry(parse_filter_preset_fulltext_keyvalue, state, line, state);
|
line = parse_keyvalue_entry(parse_filter_preset_fulltext_keyvalue, state, line, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_preset_set_fulltext(state->active_filter.get(), state->fulltext_query.c_str(), state->fulltext_mode.c_str());
|
state->active_filter.get()->set_fulltext(std::move(state->fulltext_query), state->fulltext_mode);
|
||||||
state->fulltext_mode.clear();
|
state->fulltext_mode.clear();
|
||||||
state->fulltext_query.clear();
|
state->fulltext_query.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_filter_preset_name(char *, struct git_parser_state *state)
|
static void parse_filter_preset_name(char *, struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
filter_preset_set_name(state->active_filter.get(), get_first_converted_string_c(state));
|
state->active_filter->name = get_first_converted_string(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* These need to be sorted! */
|
/* These need to be sorted! */
|
||||||
@ -1383,33 +1368,27 @@ static void for_each_line(git_blob *blob, line_fn_t *fn, struct git_parser_state
|
|||||||
|
|
||||||
static void finish_active_trip(struct git_parser_state *state)
|
static void finish_active_trip(struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
dive_trip_t *trip = state->active_trip;
|
auto &trip = state->active_trip;
|
||||||
|
|
||||||
if (trip) {
|
if (trip)
|
||||||
state->active_trip = NULL;
|
state->log->trips.put(std::move(trip));
|
||||||
insert_trip(trip, state->log->trips);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void finish_active_dive(struct git_parser_state *state)
|
static void finish_active_dive(struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
struct dive *dive = state->active_dive;
|
if (state->active_dive)
|
||||||
|
state->log->dives.record_dive(std::move(state->active_dive));
|
||||||
if (dive) {
|
|
||||||
state->active_dive = NULL;
|
|
||||||
record_dive_to_table(dive, state->log->dives);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_new_dive(timestamp_t when, struct git_parser_state *state)
|
static void create_new_dive(timestamp_t when, struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
state->active_dive = alloc_dive();
|
state->active_dive = std::make_unique<dive>();
|
||||||
|
|
||||||
/* We'll fill in more data from the dive file */
|
/* We'll fill in more data from the dive file */
|
||||||
state->active_dive->when = when;
|
state->active_dive->when = when;
|
||||||
|
|
||||||
if (state->active_trip)
|
if (state->active_trip)
|
||||||
add_dive_to_trip(state->active_dive, state->active_trip);
|
state->active_trip->add_dive(state->active_dive.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool validate_date(int yyyy, int mm, int dd)
|
static bool validate_date(int yyyy, int mm, int dd)
|
||||||
@ -1439,7 +1418,7 @@ static int dive_trip_directory(const char *root, const char *name, struct git_pa
|
|||||||
if (!validate_date(yyyy, mm, dd))
|
if (!validate_date(yyyy, mm, dd))
|
||||||
return GIT_WALK_SKIP;
|
return GIT_WALK_SKIP;
|
||||||
finish_active_trip(state);
|
finish_active_trip(state);
|
||||||
state->active_trip = alloc_trip();
|
state->active_trip = std::make_unique<dive_trip>();
|
||||||
return GIT_WALK_OK;
|
return GIT_WALK_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1649,17 +1628,12 @@ static git_blob *git_tree_entry_blob(git_repository *repo, const git_tree_entry
|
|||||||
|
|
||||||
static struct divecomputer *create_new_dc(struct dive *dive)
|
static struct divecomputer *create_new_dc(struct dive *dive)
|
||||||
{
|
{
|
||||||
struct divecomputer *dc = &dive->dc;
|
struct divecomputer *dc = &dive->dcs.back();
|
||||||
|
|
||||||
while (dc->next)
|
|
||||||
dc = dc->next;
|
|
||||||
/* Did we already fill that in? */
|
/* Did we already fill that in? */
|
||||||
if (dc->samples || dc->model || dc->when) {
|
if (!dc->samples.empty() || !dc->model.empty() || dc->when) {
|
||||||
struct divecomputer *newdc = (divecomputer *)calloc(1, sizeof(*newdc));
|
dive->dcs.emplace_back();
|
||||||
if (!newdc)
|
dc = &dive->dcs.back();
|
||||||
return NULL;
|
|
||||||
dc->next = newdc;
|
|
||||||
dc = newdc;
|
|
||||||
}
|
}
|
||||||
dc->when = dive->when;
|
dc->when = dive->when;
|
||||||
dc->duration = dive->duration;
|
dc->duration = dive->duration;
|
||||||
@ -1679,7 +1653,7 @@ static int parse_divecomputer_entry(struct git_parser_state *state, const git_tr
|
|||||||
if (!blob)
|
if (!blob)
|
||||||
return report_error("Unable to read divecomputer file");
|
return report_error("Unable to read divecomputer file");
|
||||||
|
|
||||||
state->active_dc = create_new_dc(state->active_dive);
|
state->active_dc = create_new_dc(state->active_dive.get());
|
||||||
for_each_line(blob, divecomputer_parser, state);
|
for_each_line(blob, divecomputer_parser, state);
|
||||||
git_blob_free(blob);
|
git_blob_free(blob);
|
||||||
state->active_dc = NULL;
|
state->active_dc = NULL;
|
||||||
@ -1694,13 +1668,12 @@ static int parse_divecomputer_entry(struct git_parser_state *state, const git_tr
|
|||||||
*/
|
*/
|
||||||
static int parse_dive_entry(struct git_parser_state *state, const git_tree_entry *entry, const char *suffix)
|
static int parse_dive_entry(struct git_parser_state *state, const git_tree_entry *entry, const char *suffix)
|
||||||
{
|
{
|
||||||
struct dive *dive = state->active_dive;
|
|
||||||
git_blob *blob = git_tree_entry_blob(state->repo, entry);
|
git_blob *blob = git_tree_entry_blob(state->repo, entry);
|
||||||
if (!blob)
|
if (!blob)
|
||||||
return report_error("Unable to read dive file");
|
return report_error("Unable to read dive file");
|
||||||
if (*suffix)
|
if (*suffix)
|
||||||
dive->number = atoi(suffix + 1);
|
state->active_dive->number = atoi(suffix + 1);
|
||||||
clear_weightsystem_table(&state->active_dive->weightsystems);
|
state->active_dive->weightsystems.clear();
|
||||||
state->o2pressure_sensor = 1;
|
state->o2pressure_sensor = 1;
|
||||||
for_each_line(blob, dive_parser, state);
|
for_each_line(blob, dive_parser, state);
|
||||||
git_blob_free(blob);
|
git_blob_free(blob);
|
||||||
@ -1712,7 +1685,7 @@ static int parse_site_entry(struct git_parser_state *state, const git_tree_entry
|
|||||||
if (*suffix == '\0')
|
if (*suffix == '\0')
|
||||||
return report_error("Dive site without uuid");
|
return report_error("Dive site without uuid");
|
||||||
uint32_t uuid = strtoul(suffix, NULL, 16);
|
uint32_t uuid = strtoul(suffix, NULL, 16);
|
||||||
state->active_site = alloc_or_get_dive_site(uuid, state->log->sites);
|
state->active_site = state->log->sites.alloc_or_get(uuid);
|
||||||
git_blob *blob = git_tree_entry_blob(state->repo, entry);
|
git_blob *blob = git_tree_entry_blob(state->repo, entry);
|
||||||
if (!blob)
|
if (!blob)
|
||||||
return report_error("Unable to read dive site file");
|
return report_error("Unable to read dive site file");
|
||||||
@ -1768,12 +1741,12 @@ static int parse_picture_entry(struct git_parser_state *state, const git_tree_en
|
|||||||
state->active_pic.offset.seconds = offset;
|
state->active_pic.offset.seconds = offset;
|
||||||
|
|
||||||
for_each_line(blob, picture_parser, state);
|
for_each_line(blob, picture_parser, state);
|
||||||
add_picture(&state->active_dive->pictures, state->active_pic);
|
add_picture(state->active_dive->pictures, std::move(state->active_pic));
|
||||||
git_blob_free(blob);
|
git_blob_free(blob);
|
||||||
|
|
||||||
/* add_picture took ownership of the data -
|
/* add_picture took ownership of the data -
|
||||||
* clear out our copy just to be sure. */
|
* clear out our copy just to be sure. */
|
||||||
state->active_pic = empty_picture;
|
state->active_pic = picture();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1788,7 +1761,7 @@ static int parse_filter_preset(struct git_parser_state *state, const git_tree_en
|
|||||||
|
|
||||||
git_blob_free(blob);
|
git_blob_free(blob);
|
||||||
|
|
||||||
add_filter_preset_to_table(state->active_filter.get(), state->log->filter_presets);
|
state->log->filter_presets.add(*state->active_filter);
|
||||||
state->active_filter.reset();
|
state->active_filter.reset();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -1796,8 +1769,8 @@ static int parse_filter_preset(struct git_parser_state *state, const git_tree_en
|
|||||||
|
|
||||||
static int walk_tree_file(const char *root, const git_tree_entry *entry, struct git_parser_state *state)
|
static int walk_tree_file(const char *root, const git_tree_entry *entry, struct git_parser_state *state)
|
||||||
{
|
{
|
||||||
struct dive *dive = state->active_dive;
|
auto &dive = state->active_dive;
|
||||||
dive_trip_t *trip = state->active_trip;
|
auto &trip = state->active_trip;
|
||||||
const char *name = git_tree_entry_name(entry);
|
const char *name = git_tree_entry_name(entry);
|
||||||
if (verbose > 1)
|
if (verbose > 1)
|
||||||
report_info("git load handling file %s\n", name);
|
report_info("git load handling file %s\n", name);
|
||||||
@ -1827,7 +1800,7 @@ static int walk_tree_file(const char *root, const git_tree_entry *entry, struct
|
|||||||
return parse_settings_entry(state, entry);
|
return parse_settings_entry(state, entry);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
report_error("Unknown file %s%s (%p %p)", root, name, dive, trip);
|
report_error("Unknown file %s%s (%p %p)", root, name, dive.get(), trip.get());
|
||||||
return GIT_WALK_SKIP;
|
return GIT_WALK_SKIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1850,12 +1823,12 @@ static int load_dives_from_tree(git_repository *repo, git_tree *tree, struct git
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void clear_git_id(void)
|
void clear_git_id()
|
||||||
{
|
{
|
||||||
saved_git_id.clear();
|
saved_git_id.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void set_git_id(const struct git_oid *id)
|
void set_git_id(const struct git_oid *id)
|
||||||
{
|
{
|
||||||
char git_id_buffer[GIT_OID_HEXSZ + 1];
|
char git_id_buffer[GIT_OID_HEXSZ + 1];
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/* macos.c */
|
/* macos.c */
|
||||||
/* implements Mac OS X specific functions */
|
/* implements Mac OS X specific functions */
|
||||||
#include "ssrf.h"
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fnmatch.h>
|
#include <fnmatch.h>
|
||||||
@ -48,13 +47,11 @@ static std::string make_default_filename()
|
|||||||
return system_default_path() + "/" + user + ".xml";
|
return system_default_path() + "/" + user + ".xml";
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
const char mac_system_divelist_default_font[] = "Arial";
|
const char mac_system_divelist_default_font[] = "Arial";
|
||||||
const char *system_divelist_default_font = mac_system_divelist_default_font;
|
const char *system_divelist_default_font = mac_system_divelist_default_font;
|
||||||
double system_divelist_default_font_size = -1.0;
|
double system_divelist_default_font_size = -1.0;
|
||||||
|
|
||||||
void subsurface_OS_pref_setup(void)
|
void subsurface_OS_pref_setup()
|
||||||
{
|
{
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
@ -65,13 +62,13 @@ bool subsurface_ignore_font(const char *)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *system_default_directory(void)
|
const char *system_default_directory()
|
||||||
{
|
{
|
||||||
static const std::string path = system_default_path();
|
static const std::string path = system_default_path();
|
||||||
return path.c_str();
|
return path.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *system_default_filename(void)
|
const char *system_default_filename()
|
||||||
{
|
{
|
||||||
static const std::string fn = make_default_filename();
|
static const std::string fn = make_default_filename();
|
||||||
return fn.c_str();
|
return fn.c_str();
|
||||||
@ -185,12 +182,12 @@ int subsurface_zip_close(struct zip *zip)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* win32 console */
|
/* win32 console */
|
||||||
void subsurface_console_init(void)
|
void subsurface_console_init()
|
||||||
{
|
{
|
||||||
/* NOP */
|
/* NOP */
|
||||||
}
|
}
|
||||||
|
|
||||||
void subsurface_console_exit(void)
|
void subsurface_console_exit()
|
||||||
{
|
{
|
||||||
/* NOP */
|
/* NOP */
|
||||||
}
|
}
|
||||||
@ -199,5 +196,3 @@ bool subsurface_user_is_root()
|
|||||||
{
|
{
|
||||||
return geteuid() == 0;
|
return geteuid() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user