Compare commits

..

1 Commits

Author SHA1 Message Date
Michael Keller
b126fccb1b Desktop: Fix Inconsistencies in Handling of Salinity.
- add correct setting of the water type drop down for the dive shown
  initially after program start;
- change salinity to have 3 decimals in planner, to make it consistency
  with the log.

Fixes #4240.

Reported-by: @ccsieh
Signed-off-by: Michael Keller <github@ike.ch>
2024-06-10 15:54:22 +12:00
278 changed files with 13771 additions and 10472 deletions

View File

@ -10,7 +10,7 @@ ColumnLimit: 0
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
ForEachMacros: [ 'for_each_line' ]
ForEachMacros: [ 'for_each_dc', 'for_each_relevant_dc', 'for_each_dive', 'for_each_line' ]
IndentFunctionDeclarationAfterType: false #personal taste, good for long methods
IndentWidth: 8
MaxEmptyLinesToKeep: 2

View File

@ -401,6 +401,7 @@ close to our coding standards.
filetype plugin indent on
filetype detect
set cindent tabstop=8 shiftwidth=8 cinoptions=l1,:0,(0,g0
" TODO: extern "C" gets indented
" And some sane defaults, optional, but quite nice
set nocompatible

View File

@ -45,28 +45,28 @@ SOURCES += subsurface-mobile-main.cpp \
core/fulltext.cpp \
core/subsurfacestartup.cpp \
core/subsurface-string.cpp \
core/pref.cpp \
core/pref.c \
core/profile.cpp \
core/device.cpp \
core/dive.cpp \
core/divecomputer.cpp \
core/divecomputer.c \
core/divefilter.cpp \
core/event.cpp \
core/event.c \
core/eventtype.cpp \
core/filterconstraint.cpp \
core/filterpreset.cpp \
core/filterpresettable.cpp \
core/divelist.cpp \
core/divelist.c \
core/divelog.cpp \
core/gas-model.cpp \
core/gaspressures.cpp \
core/gas-model.c \
core/gaspressures.c \
core/git-access.cpp \
core/globals.cpp \
core/liquivision.cpp \
core/load-git.cpp \
core/parse-xml.cpp \
core/parse.cpp \
core/picture.cpp \
core/picture.c \
core/pictureobj.cpp \
core/sample.cpp \
core/import-suunto.cpp \
core/import-shearwater.cpp \
@ -75,38 +75,37 @@ SOURCES += subsurface-mobile-main.cpp \
core/import-divinglog.cpp \
core/import-csv.cpp \
core/save-html.cpp \
core/statistics.cpp \
core/statistics.c \
core/worldmap-save.cpp \
core/libdivecomputer.cpp \
core/version.cpp \
core/version.c \
core/save-git.cpp \
core/datatrak.cpp \
core/ostctools.cpp \
core/ostctools.c \
core/planner.cpp \
core/save-xml.cpp \
core/cochran.cpp \
core/deco.cpp \
core/divesite.cpp \
core/equipment.cpp \
core/gas.cpp \
core/divesite.c \
core/equipment.c \
core/gas.c \
core/membuffer.cpp \
core/selection.cpp \
core/sha1.cpp \
core/sha1.c \
core/string-format.cpp \
core/strtod.cpp \
core/strtod.c \
core/tag.cpp \
core/taxonomy.cpp \
core/taxonomy.c \
core/time.cpp \
core/trip.cpp \
core/triptable.cpp \
core/units.cpp \
core/uemis.cpp \
core/trip.c \
core/units.c \
core/uemis.c \
core/btdiscovery.cpp \
core/connectionlistmodel.cpp \
core/qt-ble.cpp \
core/uploadDiveShare.cpp \
core/uploadDiveLogsDE.cpp \
core/save-profiledata.cpp \
core/save-profiledata.c \
core/xmlparams.cpp \
core/settings/qPref.cpp \
core/settings/qPrefCloudStorage.cpp \
@ -208,6 +207,7 @@ HEADERS += \
core/extradata.h \
core/git-access.h \
core/globals.h \
core/owning_ptrs.h \
core/pref.h \
core/profile.h \
core/qthelper.h \
@ -217,9 +217,9 @@ HEADERS += \
core/units.h \
core/version.h \
core/picture.h \
core/pictureobj.h \
core/planner.h \
core/divesite.h \
core/divesitetable.h \
core/checkcloudconnection.h \
core/cochran.h \
core/color.h \
@ -229,7 +229,6 @@ HEADERS += \
core/divefilter.h \
core/filterconstraint.h \
core/filterpreset.h \
core/filterpresettable.h \
core/divelist.h \
core/divelog.h \
core/divelogexportlogic.h \
@ -250,8 +249,6 @@ HEADERS += \
core/subsurfacestartup.h \
core/subsurfacesysinfo.h \
core/taxonomy.h \
core/trip.h \
core/triptable.h \
core/uemis.h \
core/webservice.h \
core/windowtitleupdate.h \

View File

@ -13,7 +13,6 @@
#include "core/divesite.h"
#include "core/picture.h"
#include "core/pref.h"
#include "core/range.h"
#include "core/sample.h"
#include "core/selection.h"
#include "core/taxonomy.h"
@ -41,12 +40,12 @@ static constexpr int profileScale = 4;
static constexpr int profileWidth = 800 * 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);
QPainter paint;
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);
}
@ -57,15 +56,17 @@ static std::unique_ptr<ProfileScene> getPrintProfile()
void exportProfile(QString filename, bool selected_only, ExportCallback &cb)
{
struct dive *dive;
int i;
int count = 0;
if (!filename.endsWith(".png", Qt::CaseInsensitive))
filename = filename.append(".png");
QFileInfo fi(filename);
int todo = selected_only ? amount_selected : static_cast<int>(divelog.dives.size());
int todo = selected_only ? amount_selected : divelog.dives->nr;
int done = 0;
auto profile = getPrintProfile();
for (auto &dive: divelog.dives) {
for_each_dive (i, dive) {
if (cb.canceled())
return;
if (selected_only && !dive->selected)
@ -73,7 +74,7 @@ void exportProfile(QString filename, bool selected_only, ExportCallback &cb)
cb.setProgress(done++ * 1000 / todo);
QString fn = count ? fi.path() + QDir::separator() + fi.completeBaseName().append(QString("-%1.").arg(count)) + fi.suffix()
: filename;
exportProfile(*profile, *dive, fn);
exportProfile(profile.get(), dive, fn);
++count;
}
}
@ -82,12 +83,14 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
{
FILE *f;
QDir texdir = QFileInfo(filename).dir();
struct dive *dive;
const struct units *units = get_units();
const char *unit;
const char *ssrf;
int i;
bool need_pagebreak = false;
membuffer buf;
struct membufferpp buf;
if (plain) {
ssrf = "";
@ -129,29 +132,34 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
put_format(&buf, "\n%%%%%%%%%% Begin Dive Data: %%%%%%%%%%\n");
int todo = selected_only ? amount_selected : static_cast<int>(divelog.dives.size());
int todo = selected_only ? amount_selected : divelog.dives->nr;
int done = 0;
auto profile = getPrintProfile();
for (auto &dive: divelog.dives) {
for_each_dive (i, dive) {
if (cb.canceled())
return;
if (selected_only && !dive->selected)
continue;
cb.setProgress(done++ * 1000 / todo);
exportProfile(*profile, *dive, texdir.filePath(QString("profile%1.png").arg(dive->number)));
exportProfile(profile.get(), dive, texdir.filePath(QString("profile%1.png").arg(dive->number)));
struct tm tm;
utc_mkdate(dive->when, &tm);
std::string country;
const char *country = NULL;
dive_site *site = dive->dive_site;
if (site)
country = taxonomy_get_country(site->taxonomy);
pressure_t delta_p;
country = taxonomy_get_country(&site->taxonomy);
pressure_t delta_p = {.mbar = 0};
QString star = "*";
QString viz = star.repeated(dive->visibility);
QString rating = star.repeated(dive->rating);
int i;
int qty_cyl;
int qty_weight;
double total_weight;
if (need_pagebreak) {
if (plain)
put_format(&buf, "\\vfill\\eject\n");
@ -166,13 +174,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\\%sminute{%02u}\n", ssrf, tm.tm_min);
put_format(&buf, "\\def\\%snumber{%d}\n", ssrf, dive->number);
put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name.c_str() : "");
put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name : "");
put_format(&buf, "\\def\\%sspot{}\n", ssrf);
put_format(&buf, "\\def\\%ssitename{%s}\n", ssrf, site ? site->name.c_str() : "");
put_format(&buf, "\\def\\%ssitename{%s}\n", ssrf, site ? site->name : "");
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");
put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dcs[0].model.c_str());
put_format(&buf, "\\def\\%scountry{%s}\n", ssrf, country.c_str());
put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dc.model);
put_format(&buf, "\\def\\%scountry{%s}\n", ssrf, country ?: "");
put_format(&buf, "\\def\\%stime{%u:%02u}\n", ssrf, FRACTION_TUPLE(dive->duration.seconds, 60));
put_format(&buf, "\n%% Dive Profile Details:\n");
@ -183,23 +191,24 @@ 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->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->tags);
std::string tags = taglist_get_tagstring(dive->tag_list);
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\\%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\\%sprofilename{profile%d}\n", ssrf, dive->number);
put_format(&buf, "\\def\\%scomment{%s}\n", ssrf, dive->notes.c_str());
put_format(&buf, "\\def\\%sbuddy{%s}\n", ssrf, dive->buddy.c_str());
put_format(&buf, "\\def\\%sdivemaster{%s}\n", ssrf, dive->diveguide.c_str());
put_format(&buf, "\\def\\%ssuit{%s}\n", ssrf, dive->suit.c_str());
put_format(&buf, "\\def\\%scomment{%s}\n", ssrf, dive->notes ? dive->notes : "");
put_format(&buf, "\\def\\%sbuddy{%s}\n", ssrf, dive->buddy ? dive->buddy : "");
put_format(&buf, "\\def\\%sdivemaster{%s}\n", ssrf, dive->diveguide ? dive->diveguide : "");
put_format(&buf, "\\def\\%ssuit{%s}\n", ssrf, dive->suit ? dive->suit : "");
// Print cylinder data
put_format(&buf, "\n%% Gas use information:\n");
int qty_cyl = 0;
for (auto [i, cyl]: enumerated_range(dive->cylinders)) {
if (is_cylinder_used(dive.get(), i) || (prefs.include_unused_tanks && !cyl.type.description.empty())){
put_format(&buf, "\\def\\%scyl%cdescription{%s}\n", ssrf, 'a' + i, cyl.type.description.c_str());
qty_cyl = 0;
for (i = 0; i < dive->cylinders.nr; i++){
const cylinder_t &cyl = *get_cylinder(dive, i);
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);
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%cmixHe{%.1f\\%%}\n", ssrf, 'a' + i, get_he(cyl.gasmix)/10.0);
@ -226,10 +235,11 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
//Code block prints all weights listed in dive.
put_format(&buf, "\n%% Weighting information:\n");
int qty_weight = 0;
double total_weight = 0;
for (auto [i, w]: enumerated_range(dive->weightsystems)) {
put_format(&buf, "\\def\\%sweight%ctype{%s}\n", ssrf, 'a' + i, w.description.c_str());
qty_weight = 0;
total_weight = 0;
for (i = 0; i < dive->weightsystems.nr; i++) {
weightsystem_t w = dive->weightsystems.weightsystems[i];
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);
qty_weight += 1;
total_weight += get_weight_units(w.weight.grams, NULL, &unit);
@ -241,7 +251,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
// Legacy fields
put_format(&buf, "\\def\\%sspot{}\n", ssrf);
put_format(&buf, "\\def\\%sentrance{}\n", ssrf);
put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name.c_str() : "");
put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name : "");
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);
@ -265,22 +275,26 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall
void export_depths(const char *filename, bool selected_only)
{
FILE *f;
struct dive *dive;
depth_t depth;
int i;
const char *unit = NULL;
membuffer buf;
struct membufferpp buf;
for (auto &dive: divelog.dives) {
for_each_dive (i, dive) {
if (selected_only && !dive->selected)
continue;
for (auto &picture: dive->pictures) {
depth_t depth;
for (auto &s: dive->dcs[0].samples) {
if ((int32_t)s.time.seconds > picture.offset.seconds)
break;
depth = s.depth;
FOR_EACH_PICTURE (dive) {
int n = dive->dc.samples;
struct sample *s = dive->dc.sample;
depth.mm = 0;
while (--n >= 0 && (int32_t)s->time.seconds <= picture->offset.seconds) {
depth.mm = s->depth.mm;
s++;
}
put_format(&buf, "%s\t%.1f", picture.filename.c_str(), get_depth_units(depth.mm, NULL, &unit));
put_format(&buf, "%s\t%.1f", picture->filename, get_depth_units(depth.mm, NULL, &unit));
put_format(&buf, "%s\n", unit);
}
}
@ -304,23 +318,28 @@ std::vector<const dive_site *> getDiveSitesToExport(bool selectedOnly)
if (selectedOnly && DiveFilter::instance()->diveSiteMode()) {
// Special case in dive site mode: export all selected dive sites,
// not the dive sites of selected dives.
for (auto ds: DiveFilter::instance()->filteredDiveSites())
QVector<dive_site *> sites = DiveFilter::instance()->filteredDiveSites();
res.reserve(sites.size());
for (const dive_site *ds: sites)
res.push_back(ds);
return res;
}
res.reserve(divelog.sites.size());
for (const auto &ds: divelog.sites) {
if (ds->is_empty())
res.reserve(divelog.sites->nr);
for (int i = 0; i < divelog.sites->nr; i++) {
struct dive_site *ds = get_dive_site(i, divelog.sites);
if (dive_site_is_empty(ds))
continue;
if (selectedOnly && !ds->is_selected())
if (selectedOnly && !is_dive_site_selected(ds))
continue;
res.push_back(ds.get());
res.push_back(ds);
}
#else
/* walk the dive site list */
for (const auto &ds: divelog.sites)
res.push_back(ds.get());
int i;
const struct dive_site *ds;
for_each_dive_site (i, ds, divelog.sites)
res.push_back(get_dive_site(i, divelog.sites));
#endif
return res;
}

View File

@ -134,9 +134,9 @@ void addDiveSite(const QString &name)
execute(new AddDiveSite(name));
}
void importDiveSites(dive_site_table sites, const QString &source)
void importDiveSites(struct dive_site_table *sites, const QString &source)
{
execute(new ImportDiveSites(std::move(sites), source));
execute(new ImportDiveSites(sites, source));
}
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));
}
void renameEvent(struct dive *d, int dcNr, int idx, const std::string name)
void renameEvent(struct dive *d, int dcNr, struct event *ev, const char *name)
{
execute(new RenameEvent(d, dcNr, idx, std::move(name)));
execute(new RenameEvent(d, dcNr, ev, name));
}
void removeEvent(struct dive *d, int dcNr, int idx)
void removeEvent(struct dive *d, int dcNr, struct event *ev)
{
execute(new RemoveEvent(d, dcNr, idx));
execute(new RemoveEvent(d, dcNr, ev));
}
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank)

View File

@ -4,7 +4,7 @@
#include "core/divelog.h"
#include "core/equipment.h"
#include "core/picture.h"
#include "core/pictureobj.h"
#include "core/taxonomy.h"
#include <QVector>
#include <QAction>
@ -68,7 +68,7 @@ void editDiveSiteCountry(dive_site *ds, const QString &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 addDiveSite(const QString &name);
void importDiveSites(dive_site_table sites, const QString &source); // takes ownership of dive site table
void importDiveSites(struct dive_site_table *sites, const QString &source);
void mergeDiveSites(dive_site *ds, const QVector<dive_site *> &sites);
void purgeUnusedDiveSites();
@ -132,8 +132,8 @@ void editTripNotes(dive_trip *trip, const QString &s);
void addEventBookmark(struct dive *d, int dcNr, int seconds);
void addEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode);
void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2);
void renameEvent(struct dive *d, int dcNr, int idx, std::string name);
void removeEvent(struct dive *d, int dcNr, int idx);
void renameEvent(struct dive *d, int dcNr, struct event *ev, const char *name);
void removeEvent(struct dive *d, int dcNr, struct event *ev);
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank);
// 7) Picture (media) commands
@ -144,7 +144,7 @@ struct PictureListForDeletion {
};
struct PictureListForAddition {
dive *d;
std::vector<picture> pics;
std::vector<PictureObj> pics;
};
void setPictureOffset(dive *d, const QString &filename, offset_t offset);
void removePictures(const std::vector<PictureListForDeletion> &pictures);

View File

@ -66,7 +66,7 @@ QString diveNumberOrDate(struct dive *d)
QString getListOfDives(const std::vector<struct dive*> &dives)
{
QString listOfDives;
if (dives.size() == divelog.dives.size())
if ((int)dives.size() == divelog.dives->nr)
return Base::tr("all dives");
int i = 0;
for (dive *d: dives) {

View File

@ -7,6 +7,7 @@
#include "core/divesite.h"
#include "core/trip.h"
#include "core/dive.h"
#include "core/owning_ptrs.h"
#include <QUndoCommand>
#include <QCoreApplication> // For Q_DECLARE_TR_FUNCTIONS
@ -105,8 +106,9 @@
// 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.
//
// To take ownership of dives/trips, std::unique_ptr<>s are used.
// Expressed in C-terms: std::unique_ptr<T> is the same as T* with the following
// To take ownership of dives/trips, the OnwingDivePtr and OwningTripPtr types are used. These
// are simply derived from std::unique_ptr and therefore use well-established semantics.
// Expressed in C-terms: std::unique_ptr<T> is exactly the same as T* with the following
// twists:
// 1) default-initialized to NULL.
// 2) if it goes out of scope (local scope or containing object destroyed), it does:
@ -120,8 +122,8 @@
// move-semantics and Qt's containers are incompatible, owing to COW semantics.
//
// Usage:
// std::unique_ptr<dive> dPtr; // Initialize to null-state: not owning any dive.
// std::unique_ptr<dive> dPtr(dive); // Take ownership of dive (which is of type struct dive *).
// OwningDivePtr dPtr; // Initialize to null-state: not owning any dive.
// OwningDivePtr 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().
// 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.
@ -129,10 +131,10 @@
// dPtr.reset(); // Delete currently owned dive and reset to null.
// dPtr2 = dPtr1; // Fails to compile.
// dPtr2 = std::move(dPtr1); // dPtr2 takes ownership, dPtr1 is reset to null.
// std::unique_ptr<dive> fun();
// OwningDivePtr fun();
// dPtr1 = fun(); // Compiles. Simply put: the compiler knows that the result of fun() will
// // be trashed and therefore can be moved-from.
// std::vector<std::unique_ptr<dive>> v: // Define an empty vector of owning pointers.
// std::vector<OwningDivePtr> v: // Define an empty vector of owning pointers.
// 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().
// v.clear(v); // Reset the vector to zero length. If the elements weren't release()d,

View File

@ -13,19 +13,20 @@ EditDeviceNickname::EditDeviceNickname(const struct divecomputer *dc, const QStr
if (index == -1)
return;
setText(Command::Base::tr("Set nickname of device %1 (serial %2) to %3").arg(dc->model.c_str(), dc->serial.c_str(), nicknameIn));
setText(Command::Base::tr("Set nickname of device %1 (serial %2) to %3").arg(dc->model, dc->serial, nicknameIn));
}
bool EditDeviceNickname::workToBeDone()
{
return index >= 0;
return get_device(divelog.devices, index) != nullptr;
}
void EditDeviceNickname::redo()
{
if (index < 0 || static_cast<size_t>(index) >= divelog.devices.size())
device *dev = get_device_mutable(divelog.devices, index);
if (!dev)
return;
std::swap(divelog.devices[index].nickName, nickname);
std::swap(dev->nickName, nickname);
emit diveListNotifier.deviceEdited();
}

View File

@ -9,22 +9,23 @@
#include "qt-models/filtermodels.h"
#include "core/divefilter.h"
#include <array>
namespace Command {
// Helper function that takes care to unselect trips that are removed from the backend
static std::unique_ptr<dive_trip> remove_trip_from_backend(dive_trip *trip)
static void remove_trip_from_backend(dive_trip *trip)
{
if (trip->selected)
deselect_trip(trip);
auto [t, idx] = divelog.trips.pull(trip);
return std::move(t);
remove_trip(trip, divelog.trips); // Remove trip from backend
}
// 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.
// 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!
DiveToAdd DiveListBase::removeDive(struct dive *d, std::vector<std::unique_ptr<dive_trip>> &tripsToAdd)
DiveToAdd DiveListBase::removeDive(struct dive *d, std::vector<OwningTripPtr> &tripsToAdd)
{
// If the dive was the current dive, reset the current dive. The calling
// command is responsible of finding a new dive.
@ -38,19 +39,18 @@ DiveToAdd DiveListBase::removeDive(struct dive *d, std::vector<std::unique_ptr<d
if (d->dive_site)
diveSiteCountChanged(d->dive_site);
res.site = unregister_dive_from_dive_site(d);
if (res.trip && res.trip->dives.empty()) {
divelog.trips.sort(); // Removal of dives has changed order of trips! (TODO: remove this)
auto trip = remove_trip_from_backend(res.trip); // Remove trip from backend
tripsToAdd.push_back(std::move(trip)); // Take ownership of trip
if (res.trip && res.trip->dives.nr == 0) {
remove_trip_from_backend(res.trip); // Remove trip from backend
tripsToAdd.emplace_back(res.trip); // Take ownership of trip
}
size_t idx = divelog.dives.get_idx(d);
if (idx == std::string::npos)
int idx = get_divenr(d);
if (idx < 0)
qWarning("Deletion of unknown dive!");
DiveFilter::instance()->diveRemoved(d);
res.dive = std::move(divelog.dives.unregister_dive(idx)); // Remove dive from backend
res.dive.reset(unregister_dive(idx)); // Remove dive from backend
return res;
}
@ -67,12 +67,23 @@ void DiveListBase::diveSiteCountChanged(struct dive_site *ds)
dive *DiveListBase::addDive(DiveToAdd &d)
{
if (d.trip)
d.trip->add_dive(d.dive.get());
add_dive_to_trip(d.dive.get(), d.trip);
if (d.site) {
d.site->add_dive(d.dive.get());
add_dive_to_dive_site(d.dive.get(), d.site);
diveSiteCountChanged(d.site);
}
return register_dive(std::move(d.dive)); // Transfer ownership to core and update fulltext index
dive *res = d.dive.release(); // Give up ownership of dive
// 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
@ -88,7 +99,7 @@ void processByTrip(std::vector<std::pair<dive_trip *, dive *>> &dives, Function
// Sort lexicographically by trip then according to the dive_less_than() function.
std::sort(dives.begin(), dives.end(),
[](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
size_t i, j; // Begin and end of batch
@ -113,8 +124,8 @@ void processByTrip(std::vector<std::pair<dive_trip *, dive *>> &dives, Function
DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSitesToDelete)
{
std::vector<DiveToAdd> divesToAdd;
std::vector<std::unique_ptr<dive_trip>> tripsToAdd;
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
std::vector<OwningTripPtr> tripsToAdd;
std::vector<OwningDiveSitePtr> sitesToAdd;
divesToAdd.reserve(divesAndSitesToDelete.dives.size());
sitesToAdd.reserve(divesAndSitesToDelete.sites.size());
@ -124,17 +135,16 @@ DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSite
// 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
// in the core list.
std::sort(divesAndSitesToDelete.dives.begin(), divesAndSitesToDelete.dives.end(),
[](const dive *d1, const dive *d2) { return dive_less_than(*d1, *d2); });
std::sort(divesAndSitesToDelete.dives.begin(), divesAndSitesToDelete.dives.end(), dive_less_than);
for (dive *d: divesAndSitesToDelete.dives)
divesToAdd.push_back(removeDive(d, tripsToAdd));
divesAndSitesToDelete.dives.clear();
for (dive_site *ds: divesAndSitesToDelete.sites) {
auto res = divelog.sites.pull(ds);
sitesToAdd.push_back(std::move(res.ptr));
emit diveListNotifier.diveSiteDeleted(ds, res.idx);
int idx = unregister_dive_site(ds);
sitesToAdd.emplace_back(ds);
emit diveListNotifier.diveSiteDeleted(ds, idx);
}
divesAndSitesToDelete.sites.clear();
@ -149,7 +159,7 @@ DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSite
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".
bool deleteTrip = trip &&
std::find_if(tripsToAdd.begin(), tripsToAdd.end(), [trip](const std::unique_ptr<dive_trip> &ptr)
std::find_if(tripsToAdd.begin(), tripsToAdd.end(), [trip](const OwningTripPtr &ptr)
{ return ptr.get() == trip; }) != tripsToAdd.end();
emit diveListNotifier.divesDeleted(trip, deleteTrip, divesInTrip);
});
@ -178,7 +188,7 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd)
// in the core list.
std::sort(toAdd.dives.begin(), toAdd.dives.end(),
[](const DiveToAdd &d, const DiveToAdd &d2)
{ return dive_less_than(*d.dive, *d2.dive); });
{ return dive_less_than(d.dive.get(), d2.dive.get()); });
// Now, add the dives
// Note: the idiomatic STL-way would be std::transform, but let's use a loop since
@ -199,17 +209,17 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd)
// Remember the pointers so that we can later check if a trip was newly added
std::vector<dive_trip *> addedTrips;
addedTrips.reserve(toAdd.trips.size());
for (std::unique_ptr<dive_trip> &trip: toAdd.trips) {
auto [t, idx] = divelog.trips.put(std::move(trip)); // Return ownership to backend
addedTrips.push_back(t);
for (OwningTripPtr &trip: toAdd.trips) {
addedTrips.push_back(trip.get());
insert_trip(trip.release(), divelog.trips); // Return ownership to backend
}
toAdd.trips.clear();
// Finally, add any necessary dive sites
for (std::unique_ptr<dive_site> &ds: toAdd.sites) {
auto res = divelog.sites.register_site(std::move(ds));
sites.push_back(res.ptr);
emit diveListNotifier.diveSiteAdded(sites.back(), res.idx);
for (OwningDiveSitePtr &ds: toAdd.sites) {
sites.push_back(ds.get());
int idx = register_dive_site(ds.release()); // Return ownership to backend
emit diveListNotifier.diveSiteAdded(sites.back(), idx);
}
toAdd.sites.clear();
@ -250,24 +260,26 @@ static void renumberDives(QVector<QPair<dive *, int>> &divesToRenumber)
// 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
// core, an owning pointer to the removed trip is returned, otherwise a null pointer.
static std::unique_ptr<dive_trip> moveDiveToTrip(DiveToTrip &diveToTrip)
static OwningTripPtr moveDiveToTrip(DiveToTrip &diveToTrip)
{
// Firstly, check if we move to the same trip and bail if this is a no-op.
if (diveToTrip.trip == diveToTrip.dive->divetrip)
return {};
// Remove from old trip
std::unique_ptr<dive_trip> res;
OwningTripPtr res;
// 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);
if (trip && trip->dives.empty())
res = remove_trip_from_backend(trip); // Remove trip from backend
if (trip && trip->dives.nr == 0) {
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
std::swap(trip, diveToTrip.trip);
if (trip)
trip->add_dive(diveToTrip.dive);
add_dive_to_trip(diveToTrip.dive, trip);
invalidate_dive_cache(diveToTrip.dive); // Ensure that dive is written in git_save()
return res;
}
@ -286,14 +298,15 @@ static void moveDivesBetweenTrips(DivesToTrip &dives)
createdTrips.reserve(dives.tripsToAdd.size());
// First, bring back the trip(s)
for (std::unique_ptr<dive_trip> &trip: dives.tripsToAdd) {
auto [t, idx] = divelog.trips.put(std::move(trip)); // Return ownership to backend
for (OwningTripPtr &trip: dives.tripsToAdd) {
dive_trip *t = trip.release(); // Give up ownership
createdTrips.push_back(t);
insert_trip(t, divelog.trips); // Return ownership to backend
}
dives.tripsToAdd.clear();
for (DiveToTrip &dive: dives.divesToMove) {
std::unique_ptr<dive_trip> tripToAdd = moveDiveToTrip(dive);
OwningTripPtr tripToAdd = moveDiveToTrip(dive);
// register trips that we'll have to readd
if (tripToAdd)
dives.tripsToAdd.push_back(std::move(tripToAdd));
@ -337,7 +350,7 @@ static void moveDivesBetweenTrips(DivesToTrip &dives)
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() &&
std::find_if(dives.tripsToAdd.begin(), dives.tripsToAdd.end(), // Is "from" in tripsToAdd?
[from](const std::unique_ptr<dive_trip> &trip) { return trip.get() == from; }) != dives.tripsToAdd.end();
[from](const OwningTripPtr &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.
bool createTo = false;
if (to) {
@ -391,20 +404,20 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber)
setText(Command::Base::tr("add dive"));
// By convention, d is a pointer to "displayed dive" or a temporary variable and can be overwritten.
d->maxdepth.mm = 0;
d->dcs[0].maxdepth.mm = 0;
d->dc.maxdepth.mm = 0;
fixup_dive(d);
// this only matters if undoit were called before redoit
currentDive = nullptr;
// Get an owning pointer to a moved dive.
std::unique_ptr<dive> divePtr = move_dive(d);
OwningDivePtr divePtr(move_dive(d));
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
// on dive-addition.
// If we alloc a new-trip for autogrouping, get an owning pointer to it.
std::unique_ptr<dive_trip> allocTrip;
OwningTripPtr allocTrip;
dive_trip *trip = divePtr->divetrip;
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
@ -412,12 +425,13 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber)
divePtr->divetrip = nullptr;
divePtr->dive_site = nullptr;
if (!trip && autogroup) {
auto [t, allocated] = get_trip_for_new_dive(divelog, divePtr.get());
trip = t;
allocTrip = std::move(allocated);
bool alloc;
trip = get_trip_for_new_dive(divePtr.get(), &alloc);
if (alloc)
allocTrip.reset(trip);
}
int idx = divelog.dives.get_insertion_index(divePtr.get());
int idx = dive_table_get_insertion_index(divelog.dives, divePtr.get());
if (newNumber)
divePtr->number = get_dive_nr_at_idx(idx);
@ -438,7 +452,7 @@ void AddDive::redoit()
currentDive = current_dive;
divesAndSitesToRemove = addDives(divesToAdd);
divelog.trips.sort(); // Though unlikely, adding a dive may reorder trips
sort_trip_table(divelog.trips); // Though unlikely, adding a dive may reorder trips
// Select the newly added dive
setSelection(divesAndSitesToRemove.dives, divesAndSitesToRemove.dives[0], -1);
@ -448,7 +462,7 @@ void AddDive::undoit()
{
// Simply remove the dive that was previously added...
divesToAdd = removeDives(divesAndSitesToRemove);
divelog.trips.sort(); // Though unlikely, removing a dive may reorder trips
sort_trip_table(divelog.trips); // Though unlikely, removing a dive may reorder trips
// ...and restore the selection
setSelection(selection, currentDive, -1);
@ -456,28 +470,33 @@ void AddDive::undoit()
ImportDives::ImportDives(struct divelog *log, int flags, const QString &source)
{
setText(Command::Base::tr("import %n dive(s) from %1", "", log->dives.size()).arg(source));
setText(Command::Base::tr("import %n dive(s) from %1", "", log->dives->nr).arg(source));
// this only matters if undoit were called before redoit
currentDive = nullptr;
auto [dives_to_add, dives_to_remove, trips_to_add, sites_to_add, devices_to_add] =
process_imported_dives(*log, flags);
// Add devices to devicesToAddAndRemove structure
devicesToAddAndRemove = std::move(devices_to_add);
struct dive_table dives_to_add = empty_dive_table;
struct dive_table dives_to_remove = empty_dive_table;
struct trip_table trips_to_add = empty_trip_table;
struct dive_site_table sites_to_add = empty_dive_site_table;
process_imported_dives(log, flags,
&dives_to_add, &dives_to_remove, &trips_to_add,
&sites_to_add, &devicesToAddAndRemove);
// Add trips to the divesToAdd.trips structure
divesToAdd.trips.reserve(trips_to_add.size());
for (auto &trip: trips_to_add)
divesToAdd.trips.push_back(std::move(trip));
divesToAdd.trips.reserve(trips_to_add.nr);
for (int i = 0; i < trips_to_add.nr; ++i)
divesToAdd.trips.emplace_back(trips_to_add.trips[i]);
// Add sites to the divesToAdd.sites structure
divesToAdd.sites = std::move(sites_to_add);
divesToAdd.sites.reserve(sites_to_add.nr);
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
divesToAdd.dives.reserve(dives_to_add.size());
for (auto &divePtr: dives_to_add) {
divesToAdd.dives.reserve(dives_to_add.nr);
for (int i = 0; i < dives_to_add.nr; ++i) {
OwningDivePtr divePtr(dives_to_add.dives[i]);
divePtr->selected = false; // See above in AddDive::AddDive()
dive_trip *trip = divePtr->divetrip;
divePtr->divetrip = nullptr; // See above in AddDive::AddDive()
@ -488,18 +507,25 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source)
}
// Add dive to be deleted to the divesToRemove structure
divesAndSitesToRemove.dives = std::move(dives_to_remove);
divesAndSitesToRemove.dives.reserve(dives_to_remove.nr);
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
// 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;
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; });
if (it != divelog.filter_presets.end() && it->data == preset.data)
if (it != divelog.filter_presets->end() && it->data == preset.data)
continue;
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()
@ -525,12 +551,12 @@ void ImportDives::redoit()
divesAndSitesToRemove = std::move(divesAndSitesToRemoveNew);
// Add devices
for (const device &dev: devicesToAddAndRemove)
add_to_device_table(divelog.devices, dev);
for (const device &dev: devicesToAddAndRemove.devices)
add_to_device_table(divelog.devices, &dev);
// Add new filter presets
for (auto &it: filterPresetsToAdd) {
filterPresetsToRemove.push_back(divelog.filter_presets.add(it.first, it.second));
filterPresetsToRemove.push_back(filter_preset_add(it.first, it.second));
emit diveListNotifier.filterPresetAdded(filterPresetsToRemove.back());
}
filterPresetsToAdd.clear();
@ -553,16 +579,15 @@ void ImportDives::undoit()
setSelection(selection, currentDive, -1);
// Remove devices
for (const device &dev: devicesToAddAndRemove)
remove_device(divelog.devices, dev);
for (const device &dev: devicesToAddAndRemove.devices)
remove_device(divelog.devices, &dev);
// Remove filter presets. Do this in reverse order.
for (auto it = filterPresetsToRemove.rbegin(); it != filterPresetsToRemove.rend(); ++it) {
int index = *it;
const filter_preset &preset = divelog.filter_presets[index];
std::string oldName = preset.name;
FilterData oldData = preset.data;
divelog.filter_presets.remove(index);
std::string oldName = filter_preset_name(index);
FilterData oldData = filter_preset_get(index);
filter_preset_delete(index);
emit diveListNotifier.filterPresetRemoved(index);
filterPresetsToAdd.emplace_back(oldName, oldData);
}
@ -584,7 +609,7 @@ bool DeleteDive::workToBeDone()
void DeleteDive::undoit()
{
divesToDelete = addDives(divesToAdd);
divelog.trips.sort(); // Though unlikely, removing a dive may reorder trips
sort_trip_table(divelog.trips); // Though unlikely, removing a dive may reorder trips
// Select all re-added dives and make the first one current
dive *currentDive = !divesToDelete.dives.empty() ? divesToDelete.dives[0] : nullptr;
@ -594,7 +619,7 @@ void DeleteDive::undoit()
void DeleteDive::redoit()
{
divesToAdd = removeDives(divesToDelete);
divelog.trips.sort(); // Though unlikely, adding a dive may reorder trips
sort_trip_table(divelog.trips); // Though unlikely, adding a dive may reorder trips
// Deselect all dives and select dive that was close to the first deleted dive
dive *newCurrent = nullptr;
@ -622,10 +647,10 @@ void ShiftTime::redoit()
}
// Changing times may have unsorted the dive and trip tables
divelog.dives.sort();
divelog.trips.sort();
sort_dive_table(divelog.dives);
sort_trip_table(divelog.trips);
for (dive_trip *trip: trips)
trip->sort_dives();
sort_dive_table(&trip->dives); // Keep the trip-table in order
// Send signals
QVector<dive *> dives = stdToQt<dive *>(diveList);
@ -691,7 +716,7 @@ bool TripBase::workToBeDone()
void TripBase::redoit()
{
moveDivesBetweenTrips(divesToMove);
divelog.trips.sort(); // Though unlikely, moving dives may reorder trips
sort_trip_table(divelog.trips); // Though unlikely, moving dives may reorder trips
// Select the moved dives
std::vector<dive *> dives;
@ -729,9 +754,11 @@ RemoveAutogenTrips::RemoveAutogenTrips()
{
setText(Command::Base::tr("remove autogenerated trips"));
// TODO: don't touch core-innards directly
for (auto &d: divelog.dives) {
if (d->divetrip && d->divetrip->autogen)
divesToMove.divesToMove.push_back( {d.get(), nullptr} );
int i;
struct dive *dive;
for_each_dive(i, dive) {
if (dive->divetrip && dive->divetrip->autogen)
divesToMove.divesToMove.push_back( {dive, nullptr} );
}
}
@ -749,22 +776,25 @@ CreateTrip::CreateTrip(const QVector<dive *> &divesToAddIn)
if (divesToAddIn.isEmpty())
return;
auto trip = create_trip_from_dive(divesToAddIn[0]);
dive_trip *trip = create_trip_from_dive(divesToAddIn[0]);
divesToMove.tripsToAdd.emplace_back(trip);
for (dive *d: divesToAddIn)
divesToMove.divesToMove.push_back( { d, trip.get() });
divesToMove.tripsToAdd.push_back(std::move(trip));
divesToMove.divesToMove.push_back( {d, trip} );
}
AutogroupDives::AutogroupDives()
{
setText(Command::Base::tr("autogroup dives"));
for (auto &entry: get_dives_to_autogroup(divelog.dives)) {
dive_trip *trip;
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 (entry.created_trip)
divesToMove.tripsToAdd.push_back(std::move(entry.created_trip));
for (auto it = divelog.dives.begin() + entry.from; it != divelog.dives.begin() + entry.to; ++it)
divesToMove.divesToMove.push_back( { it->get(), entry.trip } );
if (alloc)
divesToMove.tripsToAdd.emplace_back(trip);
for (int j = from; j < to; ++j)
divesToMove.divesToMove.push_back( { get_dive(j), trip } );
}
}
@ -772,15 +802,18 @@ MergeTrips::MergeTrips(dive_trip *trip1, dive_trip *trip2)
{
if (trip1 == trip2)
return;
std::unique_ptr<dive_trip> newTrip = combine_trips(trip1, trip2);
for (dive *d: trip1->dives)
divesToMove.divesToMove.push_back( { d, newTrip.get() } );
for (dive *d: trip2->dives)
divesToMove.divesToMove.push_back( { d, newTrip.get() } );
divesToMove.tripsToAdd.push_back(std::move(newTrip));
dive_trip *newTrip = combine_trips(trip1, trip2);
divesToMove.tripsToAdd.emplace_back(newTrip);
for (int i = 0; i < trip1->dives.nr; ++i)
divesToMove.divesToMove.push_back( { trip1->dives.dives[i], newTrip } );
for (int i = 0; i < trip2->dives.nr; ++i)
divesToMove.divesToMove.push_back( { trip2->dives.dives[i], newTrip } );
}
SplitDivesBase::SplitDivesBase(dive *d, std::array<std::unique_ptr<dive>, 2> newDives)
// std::array<dive *, 2> is the same as struct *dive[2], with the fundamental
// 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 (!newDives[0] || !newDives[1])
@ -800,10 +833,10 @@ SplitDivesBase::SplitDivesBase(dive *d, std::array<std::unique_ptr<dive>, 2> new
diveToSplit.dives.push_back(d);
splitDives.dives.resize(2);
splitDives.dives[0].dive = std::move(newDives[0]);
splitDives.dives[0].dive.reset(newDives[0]);
splitDives.dives[0].trip = d->divetrip;
splitDives.dives[0].site = d->dive_site;
splitDives.dives[1].dive = std::move(newDives[1]);
splitDives.dives[1].dive.reset(newDives[1]);
splitDives.dives[1].trip = d->divetrip;
splitDives.dives[1].site = d->dive_site;
}
@ -832,13 +865,16 @@ void SplitDivesBase::undoit()
setSelection(diveToSplit.dives, diveToSplit.dives[0], -1);
}
static std::array<std::unique_ptr<dive>, 2> doSplitDives(const dive *d, duration_t time)
static std::array<dive *, 2> doSplitDives(const dive *d, duration_t time)
{
// Split the dive
dive *new1, *new2;
if (time.seconds < 0)
return split_dive(*d);
split_dive(d, &new1, &new2);
else
return split_dive_at_time(*d, time);
split_dive_at_time(d, time, &new1, &new2);
return { new1, new2 };
}
SplitDives::SplitDives(dive *d, duration_t time) : SplitDivesBase(d, doSplitDives(d, time))
@ -846,13 +882,25 @@ SplitDives::SplitDives(dive *d, duration_t time) : SplitDivesBase(d, doSplitDive
setText(Command::Base::tr("split dive"));
}
SplitDiveComputer::SplitDiveComputer(dive *d, int dc_num) :
SplitDivesBase(d, split_divecomputer(*d, dc_num))
static std::array<dive *, 2> splitDiveComputer(const dive *d, int 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"));
}
DiveComputerBase::DiveComputerBase(dive *old_dive, std::unique_ptr<dive> new_dive, int dc_nr_before, int dc_nr_after) :
DiveComputerBase::DiveComputerBase(dive *old_dive, dive *new_dive, int dc_nr_before, int dc_nr_after) :
dc_nr_before(dc_nr_before),
dc_nr_after(dc_nr_after)
{
@ -872,7 +920,7 @@ DiveComputerBase::DiveComputerBase(dive *old_dive, std::unique_ptr<dive> new_div
new_dive->dive_site = nullptr;
diveToAdd.dives.resize(1);
diveToAdd.dives[0].dive = std::move(new_dive);
diveToAdd.dives[0].dive.reset(new_dive);
diveToAdd.dives[0].trip = old_dive->divetrip;
diveToAdd.dives[0].site = old_dive->dive_site;
}
@ -902,13 +950,13 @@ void DiveComputerBase::undoit()
}
MoveDiveComputerToFront::MoveDiveComputerToFront(dive *d, int dc_num)
: DiveComputerBase(d, clone_make_first_dc(*d, dc_num), dc_num, 0)
: DiveComputerBase(d, make_first_dc(d, dc_num), dc_num, 0)
{
setText(Command::Base::tr("move dive computer to front"));
}
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"));
}
@ -917,14 +965,16 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
{
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.
if (dives.count() < 2) {
qWarning("Merging less than two dives");
return;
}
auto [d, trip, site] = merge_dives(*dives[0], *dives[1], dives[1]->when - dives[0]->when, false);
dive_trip *preferred_trip;
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
// we manually manage the selection post-command.
@ -932,15 +982,18 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
d->selected = false;
// Set the preferred dive trip, so that for subsequent merges the better trip can be selected
d->divetrip = trip;
d->divetrip = preferred_trip;
for (int i = 2; i < dives.count(); ++i) {
auto [d2, trip, site] = merge_dives(*d, *dives[i], dives[i]->when - d->when, false);
d = std::move(d2);
d.reset(merge_dives(d.get(), dives[i], dives[i]->when - d->when, false, &preferred_trip, &preferred_site));
// Set the preferred dive trip and site, so that for subsequent merges the better trip and site can be selected
d->divetrip = trip;
d->dive_site = site;
d->divetrip = preferred_trip;
d->dive_site = preferred_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
for (const dive *dive: dives) {
if (dive->number) {
@ -952,9 +1005,9 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
// 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
// like the best option.
size_t idx = divelog.dives.get_idx(dives[0]);
size_t num = dives.count();
if (idx == std::string::npos) {
int idx = get_divenr(dives[0]);
int num = dives.count();
if (idx < 0 || idx + num > divelog.dives->nr) {
// It was the callers responsibility to pass only known dives.
// Something is seriously wrong - give up.
qWarning("Merging unknown dives");
@ -962,8 +1015,7 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
}
// 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.
if (std::equal(&dives[0], &dives[0] + num, divelog.dives.begin() + idx, [](dive *d1,
const std::unique_ptr<dive> &d2) { return d1 == d2.get(); }) &&
if (std::equal(&dives[0], &dives[0] + num, &divelog.dives->dives[idx]) &&
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
// number of erased dives. This considers that there might be missing numbers.
@ -979,28 +1031,24 @@ MergeDives::MergeDives(const QVector <dive *> &dives)
// consecutive, and the difference will be 1, so the
// above example is not supposed to be normal.
int diff = dives.last()->number - dives[0]->number;
divesToRenumber.reserve(divelog.dives->nr - idx - num);
int previousnr = dives[0]->number;
for (size_t i = idx + num; i < divelog.dives.size(); ++i) {
int newnr = divelog.dives[i]->number - diff;
for (int i = idx + num; i < divelog.dives->nr; ++i) {
int newnr = divelog.dives->dives[i]->number - diff;
// Stop renumbering if stuff isn't in order (see also core/divelist.c)
if (newnr <= previousnr)
break;
divesToRenumber.append(QPair<dive *,int>(divelog.dives[i].get(), newnr));
divesToRenumber.append(QPair<dive *,int>(divelog.dives->dives[i], newnr));
previousnr = newnr;
}
}
mergedDive.dives.resize(1);
mergedDive.dives[0].trip = d->divetrip;
mergedDive.dives[0].site = d->dive_site;
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);
mergedDive.dives[0].trip = preferred_trip;
mergedDive.dives[0].site = preferred_site;
divesToMerge.dives = std::vector<dive *>(dives.begin(), dives.end());
}
bool MergeDives::workToBeDone()

View File

@ -15,16 +15,16 @@ namespace Command {
// This helper structure describes a dive that we want to add.
struct DiveToAdd {
std::unique_ptr<struct dive> dive; // Dive to add
dive_trip *trip; // Trip the dive belongs to, may be null
dive_site *site; // Site the dive is associated with, may be null
OwningDivePtr dive; // Dive to add
dive_trip *trip; // Trip the dive belongs to, 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
struct DivesAndTripsToAdd {
std::vector<DiveToAdd> dives;
std::vector<std::unique_ptr<dive_trip>> trips;
std::vector<std::unique_ptr<dive_site>> sites;
std::vector<OwningTripPtr> trips;
std::vector<OwningDiveSitePtr> sites;
};
// Dives and sites that have to be removed for a command
@ -48,7 +48,7 @@ struct DiveToTrip
struct DivesToTrip
{
std::vector<DiveToTrip> divesToMove; // If dive_trip is null, remove from trip
std::vector<std::unique_ptr<dive_trip>> tripsToAdd;
std::vector<OwningTripPtr> tripsToAdd;
};
// All divelist commands derive from a common base class. It keeps track
@ -58,7 +58,7 @@ struct DivesToTrip
class DiveListBase : public Base {
protected:
// These are helper functions to add / remove dive from the C-core structures.
DiveToAdd removeDive(struct dive *d, std::vector<std::unique_ptr<dive_trip>> &tripsToAdd);
DiveToAdd removeDive(struct dive *d, std::vector<OwningTripPtr> &tripsToAdd);
dive *addDive(DiveToAdd &d);
DivesAndTripsToAdd removeDives(DivesAndSitesToRemove &divesAndSitesToDelete);
DivesAndSitesToRemove addDives(DivesAndTripsToAdd &toAdd);
@ -108,10 +108,10 @@ private:
// For redo and undo
DivesAndTripsToAdd divesToAdd;
DivesAndSitesToRemove divesAndSitesToRemove;
device_table devicesToAddAndRemove;
struct device_table devicesToAddAndRemove;
// For redo
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
std::vector<OwningDiveSitePtr> sitesToAdd;
std::vector<std::pair<std::string,FilterData>>
filterPresetsToAdd;
@ -133,7 +133,7 @@ private:
// For redo
DivesAndSitesToRemove divesToDelete;
std::vector<std::unique_ptr<dive_trip>> tripsToAdd;
std::vector<OwningTripPtr> tripsToAdd;
DivesAndTripsToAdd divesToAdd;
};
@ -196,7 +196,7 @@ struct MergeTrips : public TripBase {
class SplitDivesBase : public DiveListBase {
protected:
SplitDivesBase(dive *old, std::array<std::unique_ptr<dive>, 2> newDives);
SplitDivesBase(dive *old, std::array<dive *, 2> newDives);
private:
void undoit() override;
void redoit() override;
@ -237,7 +237,7 @@ class DiveComputerBase : public DiveListBase {
protected:
// old_dive must be a dive known to the core.
// new_dive must be new dive whose ownership is taken.
DiveComputerBase(dive *old_dive, std::unique_ptr<dive> new_dive, int dc_nr_before, int dc_nr_after);
DiveComputerBase(dive *old_dive, dive *new_dive, int dc_nr_before, int dc_nr_after);
private:
void undoit() override;
void redoit() override;

View File

@ -15,24 +15,25 @@ namespace Command {
// 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.
static std::vector<dive_site *> addDiveSites(std::vector<std::unique_ptr<dive_site>> &sites)
static std::vector<dive_site *> addDiveSites(std::vector<OwningDiveSitePtr> &sites)
{
std::vector<dive_site *> res;
QVector<dive *> changedDives;
res.reserve(sites.size());
for (std::unique_ptr<dive_site> &ds: sites) {
for (OwningDiveSitePtr &ds: sites) {
// Readd the dives that belonged to this site
for (dive *d: ds->dives) {
for (int i = 0; i < ds->dives.nr; ++i) {
// TODO: send dive site changed signal
struct dive *d = ds->dives.dives[i];
d->dive_site = ds.get();
changedDives.push_back(d);
}
// Add dive site to core, but remember a non-owning pointer first.
auto add_res = divelog.sites.put(std::move(ds)); // Return ownership to backend.
res.push_back(add_res.ptr);
emit diveListNotifier.diveSiteAdded(res.back(), add_res.idx); // Inform frontend of new dive site.
res.push_back(ds.get());
int idx = register_dive_site(ds.release()); // Return ownership to backend.
emit diveListNotifier.diveSiteAdded(res.back(), idx); // Inform frontend of new dive site.
}
emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE);
@ -46,23 +47,24 @@ static std::vector<dive_site *> addDiveSites(std::vector<std::unique_ptr<dive_si
// 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
// that the dives can be readded to the site on undo.
static std::vector<std::unique_ptr<dive_site>> removeDiveSites(std::vector<dive_site *> &sites)
static std::vector<OwningDiveSitePtr> removeDiveSites(std::vector<dive_site *> &sites)
{
std::vector<std::unique_ptr<dive_site>> res;
std::vector<OwningDiveSitePtr> res;
QVector<dive *> changedDives;
res.reserve(sites.size());
for (dive_site *ds: sites) {
// Reset the dive_site field of the affected dives
for (dive *d: ds->dives) {
for (int i = 0; i < ds->dives.nr; ++i) {
struct dive *d = ds->dives.dives[i];
d->dive_site = nullptr;
changedDives.push_back(d);
}
// Remove dive site from core and take ownership.
auto pull_res = divelog.sites.pull(ds);
res.push_back(std::move(pull_res.ptr));
emit diveListNotifier.diveSiteDeleted(ds, pull_res.idx); // Inform frontend of removed dive site.
int idx = unregister_dive_site(ds);
res.emplace_back(ds);
emit diveListNotifier.diveSiteDeleted(ds, idx); // Inform frontend of removed dive site.
}
emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE);
@ -75,8 +77,8 @@ static std::vector<std::unique_ptr<dive_site>> removeDiveSites(std::vector<dive_
AddDiveSite::AddDiveSite(const QString &name)
{
setText(Command::Base::tr("add dive site"));
sitesToAdd.push_back(std::make_unique<dive_site>());
sitesToAdd.back()->name = name.toStdString();
sitesToAdd.emplace_back(alloc_dive_site());
sitesToAdd.back()->name = copy_qstring(name);
}
bool AddDiveSite::workToBeDone()
@ -94,17 +96,25 @@ void AddDiveSite::undo()
sitesToAdd = removeDiveSites(sitesToRemove);
}
ImportDiveSites::ImportDiveSites(dive_site_table sites, const QString &source)
ImportDiveSites::ImportDiveSites(struct dive_site_table *sites, const QString &source)
{
setText(Command::Base::tr("import dive sites from %1").arg(source));
for (auto &new_ds: sites) {
// Don't import dive sites that already exist.
// We might want to be smarter here and merge dive site data, etc.
if (divelog.sites.get_same(*new_ds))
for (int i = 0; i < sites->nr; ++i) {
struct dive_site *new_ds = sites->dive_sites[i];
// Don't import dive sites that already exist. Currently we only check for
// 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;
sitesToAdd.push_back(std::move(new_ds));
}
sitesToAdd.emplace_back(new_ds);
}
// All site have been consumed
sites->nr = 0;
}
bool ImportDiveSites::workToBeDone()
@ -145,9 +155,10 @@ void DeleteDiveSites::undo()
PurgeUnusedDiveSites::PurgeUnusedDiveSites()
{
setText(Command::Base::tr("purge unused dive sites"));
for (const auto &ds: divelog.sites) {
if (ds->dives.empty())
sitesToRemove.push_back(ds.get());
for (int i = 0; i < divelog.sites->nr; ++i) {
dive_site *ds = divelog.sites->dive_sites[i];
if (ds->dives.nr == 0)
sitesToRemove.push_back(ds);
}
}
@ -166,15 +177,24 @@ void PurgeUnusedDiveSites::undo()
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),
value(name.toStdString())
value(name)
{
setText(Command::Base::tr("Edit dive site name"));
}
bool EditDiveSiteName::workToBeDone()
{
return value != ds->name;
return value != QString(ds->name);
}
void EditDiveSiteName::redo()
@ -190,14 +210,14 @@ void EditDiveSiteName::undo()
}
EditDiveSiteDescription::EditDiveSiteDescription(dive_site *dsIn, const QString &description) : ds(dsIn),
value(description.toStdString())
value(description)
{
setText(Command::Base::tr("Edit dive site description"));
}
bool EditDiveSiteDescription::workToBeDone()
{
return value != ds->description;
return value != QString(ds->description);
}
void EditDiveSiteDescription::redo()
@ -213,14 +233,14 @@ void EditDiveSiteDescription::undo()
}
EditDiveSiteNotes::EditDiveSiteNotes(dive_site *dsIn, const QString &notes) : ds(dsIn),
value(notes.toStdString())
value(notes)
{
setText(Command::Base::tr("Edit dive site notes"));
}
bool EditDiveSiteNotes::workToBeDone()
{
return value != ds->notes;
return value != QString(ds->notes);
}
void EditDiveSiteNotes::redo()
@ -236,20 +256,20 @@ void EditDiveSiteNotes::undo()
}
EditDiveSiteCountry::EditDiveSiteCountry(dive_site *dsIn, const QString &country) : ds(dsIn),
value(country.toStdString())
value(country)
{
setText(Command::Base::tr("Edit dive site country"));
}
bool EditDiveSiteCountry::workToBeDone()
{
return value == taxonomy_get_country(ds->taxonomy);
return !same_string(qPrintable(value), taxonomy_get_country(&ds->taxonomy));
}
void EditDiveSiteCountry::redo()
{
std::string old = taxonomy_get_country(ds->taxonomy);
taxonomy_set_country(ds->taxonomy, value, taxonomy_origin::GEOMANUAL);
QString old = taxonomy_get_country(&ds->taxonomy);
taxonomy_set_country(&ds->taxonomy, qPrintable(value), taxonomy_origin::GEOMANUAL);
value = old;
emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::TAXONOMY); // Inform frontend of changed dive site.
}
@ -272,7 +292,7 @@ bool EditDiveSiteLocation::workToBeDone()
bool old_ok = has_location(&ds->location);
if (ok != old_ok)
return true;
return ok && value != ds->location;
return ok && !same_location(&value, &ds->location);
}
void EditDiveSiteLocation::redo()
@ -290,11 +310,14 @@ void EditDiveSiteLocation::undo()
EditDiveSiteTaxonomy::EditDiveSiteTaxonomy(dive_site *dsIn, taxonomy_data &taxonomy) : ds(dsIn),
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"));
}
EditDiveSiteTaxonomy::~EditDiveSiteTaxonomy()
{
free_taxonomy(&value);
}
bool EditDiveSiteTaxonomy::workToBeDone()
@ -341,10 +364,10 @@ void MergeDiveSites::redo()
// The dives of the above dive sites were reset to no dive sites.
// Add them to the merged-into dive site. Thankfully, we remember
// the dives in the sitesToAdd vector.
for (const std::unique_ptr<dive_site> &site: sitesToAdd) {
for (dive *d: site->dives) {
ds->add_dive(d);
divesChanged.push_back(d);
for (const OwningDiveSitePtr &site: sitesToAdd) {
for (int i = 0; i < site->dives.nr; ++i) {
add_dive_to_dive_site(site->dives.dives[i], ds);
divesChanged.push_back(site->dives.dives[i]);
}
}
emit diveListNotifier.divesChanged(divesChanged, DiveField::DIVESITE);
@ -357,10 +380,10 @@ void MergeDiveSites::undo()
// Before readding the dive sites, unregister the corresponding dives so that they can be
// readded to their old dive sites.
for (const std::unique_ptr<dive_site> &site: sitesToAdd) {
for (dive *d: site->dives) {
unregister_dive_from_dive_site(d);
divesChanged.push_back(d);
for (const OwningDiveSitePtr &site: sitesToAdd) {
for (int i = 0; i < site->dives.nr; ++i) {
unregister_dive_from_dive_site(site->dives.dives[i]);
divesChanged.push_back(site->dives.dives[i]);
}
}
@ -382,9 +405,9 @@ ApplyGPSFixes::ApplyGPSFixes(const std::vector<DiveAndLocation> &fixes)
siteLocations.push_back({ ds, dl.location });
}
} else {
ds = divelog.sites.create(dl.name.toStdString());
ds = create_dive_site(qPrintable(dl.name), divelog.sites);
ds->location = dl.location;
ds->add_dive(dl.d);
add_dive_to_dive_site(dl.d, ds);
dl.d->dive_site = nullptr; // This will be set on redo()
sitesToAdd.emplace_back(ds);
}

View File

@ -31,13 +31,13 @@ private:
std::vector<dive_site *> sitesToRemove;
// For redo
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
std::vector<OwningDiveSitePtr> sitesToAdd;
};
class ImportDiveSites : public Base {
public:
// Note: Takes ownership of dive site table
ImportDiveSites(dive_site_table sites, const QString &source);
// Note: the dive site table is consumed after the call it will be empty.
ImportDiveSites(struct dive_site_table *sites, const QString &source);
private:
bool workToBeDone() override;
void undo() override;
@ -47,7 +47,7 @@ private:
std::vector<dive_site *> sitesToRemove;
// For redo
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
std::vector<OwningDiveSitePtr> sitesToAdd;
};
class DeleteDiveSites : public Base {
@ -62,7 +62,7 @@ private:
std::vector<dive_site *> sitesToRemove;
// For undo
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
std::vector<OwningDiveSitePtr> sitesToAdd;
};
class PurgeUnusedDiveSites : public Base {
@ -77,7 +77,7 @@ private:
std::vector<dive_site *> sitesToRemove;
// For undo
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
std::vector<OwningDiveSitePtr> sitesToAdd;
};
class EditDiveSiteName : public Base {
@ -89,7 +89,7 @@ private:
void redo() override;
dive_site *ds;
std::string value; // Value to be set
QString value; // Value to be set
};
class EditDiveSiteDescription : public Base {
@ -101,7 +101,7 @@ private:
void redo() override;
dive_site *ds;
std::string value; // Value to be set
QString value; // Value to be set
};
class EditDiveSiteNotes : public Base {
@ -113,7 +113,7 @@ private:
void redo() override;
dive_site *ds;
std::string value; // Value to be set
QString value; // Value to be set
};
class EditDiveSiteCountry : public Base {
@ -125,7 +125,7 @@ private:
void redo() override;
dive_site *ds;
std::string value; // Value to be set
QString value; // Value to be set
};
class EditDiveSiteLocation : public Base {
@ -167,7 +167,7 @@ private:
std::vector<dive_site *> sitesToRemove;
// For undo
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
std::vector<OwningDiveSitePtr> sitesToAdd;
};
class ApplyGPSFixes : public Base {
@ -183,7 +183,7 @@ private:
std::vector<dive_site *> sitesToRemove;
// For redo
std::vector<std::unique_ptr<dive_site>> sitesToAdd;
std::vector<OwningDiveSitePtr> sitesToAdd;
// For redo and undo
struct SiteAndLocation {

View File

@ -3,10 +3,8 @@
#include "command_edit.h"
#include "core/divelist.h"
#include "core/divelog.h"
#include "core/event.h"
#include "core/fulltext.h"
#include "core/qthelper.h" // for copy_qstring
#include "core/range.h"
#include "core/sample.h"
#include "core/selection.h"
#include "core/subsurface-string.h"
@ -43,16 +41,17 @@ T EditDefaultSetter<T, ID, PTR>::data(struct dive *d) const
return d->*PTR;
}
template <DiveField::Flags ID, std::string dive::*PTR>
template <DiveField::Flags ID, char *dive::*PTR>
void EditStringSetter<ID, PTR>::set(struct dive *d, QString v) const
{
d->*PTR = v.toStdString();
free(d->*PTR);
d->*PTR = copy_qstring(v);
}
template <DiveField::Flags ID, std::string dive::*PTR>
template <DiveField::Flags ID, char *dive::*PTR>
QString EditStringSetter<ID, PTR>::data(struct dive *d) const
{
return QString::fromStdString(d->*PTR);
return QString(d->*PTR);
}
static std::vector<dive *> getDives(bool currentDiveOnly)
@ -62,9 +61,11 @@ static std::vector<dive *> getDives(bool currentDiveOnly)
: std::vector<dive *> { };
std::vector<dive *> res;
for (auto &d: divelog.dives) {
struct dive *d;
int i;
for_each_dive (i, d) {
if (d->selected)
res.push_back(d.get());
res.push_back(d);
}
return res;
}
@ -304,11 +305,11 @@ QString EditAtmPress::fieldName() const
// ***** Duration *****
void EditDuration::set(struct dive *d, int value) const
{
d->dcs[0].duration.seconds = value;
d->duration = d->dcs[0].duration;
d->dcs[0].meandepth.mm = 0;
d->dcs[0].samples.clear();
fake_dc(&d->dcs[0]);
d->dc.duration.seconds = value;
d->duration = d->dc.duration;
d->dc.meandepth.mm = 0;
d->dc.samples = 0;
fake_dc(&d->dc);
}
int EditDuration::data(struct dive *d) const
@ -324,11 +325,11 @@ QString EditDuration::fieldName() const
// ***** Depth *****
void EditDepth::set(struct dive *d, int value) const
{
d->dcs[0].maxdepth.mm = value;
d->maxdepth = d->dcs[0].maxdepth;
d->dcs[0].meandepth.mm = 0;
d->dcs[0].samples.clear();
fake_dc(&d->dcs[0]);
d->dc.maxdepth.mm = value;
d->maxdepth = d->dc.maxdepth;
d->dc.meandepth.mm = 0;
d->dc.samples = 0;
fake_dc(&d->dc);
}
int EditDepth::data(struct dive *d) const
@ -345,7 +346,7 @@ QString EditDepth::fieldName() const
void EditDiveSite::set(struct dive *d, struct dive_site *dive_site) const
{
unregister_dive_from_dive_site(d);
dive_site->add_dive(d);
add_dive_to_dive_site(d, dive_site);
}
struct dive_site *EditDiveSite::data(struct dive *d) const
@ -373,11 +374,14 @@ void EditDiveSite::redo()
EditDiveSite::undo(); // Undo and redo do the same
}
static struct dive_site *createDiveSite(const std::string &name)
static struct dive_site *createDiveSite(const QString &name)
{
struct dive_site *ds = new dive_site;
if (current_dive && current_dive->dive_site)
*ds = *current_dive->dive_site;
struct dive_site *ds = alloc_dive_site();
struct dive_site *old = current_dive ? current_dive->dive_site : nullptr;
if (old) {
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 (current_dive) {
@ -386,12 +390,12 @@ static struct dive_site *createDiveSite(const std::string &name)
ds->location = loc;
}
ds->name = name;
ds->name = copy_qstring(name);
return ds;
}
EditDiveSiteNew::EditDiveSiteNew(const QString &newName, bool currentDiveOnly) :
EditDiveSite(createDiveSite(newName.toStdString()), currentDiveOnly),
EditDiveSite(createDiveSite(newName), currentDiveOnly),
diveSiteToAdd(value),
diveSiteToRemove(nullptr)
{
@ -400,17 +404,17 @@ EditDiveSiteNew::EditDiveSiteNew(const QString &newName, bool currentDiveOnly) :
void EditDiveSiteNew::undo()
{
EditDiveSite::undo();
auto res = divelog.sites.pull(diveSiteToRemove);
diveSiteToAdd = std::move(res.ptr);
emit diveListNotifier.diveSiteDeleted(diveSiteToRemove, res.idx); // Inform frontend of removed dive site.
int idx = unregister_dive_site(diveSiteToRemove);
diveSiteToAdd.reset(diveSiteToRemove);
emit diveListNotifier.diveSiteDeleted(diveSiteToRemove, idx); // Inform frontend of removed dive site.
diveSiteToRemove = nullptr;
}
void EditDiveSiteNew::redo()
{
auto res = divelog.sites.register_site(std::move(diveSiteToAdd)); // Return ownership to backend.
diveSiteToRemove = res.ptr;
emit diveListNotifier.diveSiteAdded(diveSiteToRemove, res.idx); // Inform frontend of new dive site.
diveSiteToRemove = diveSiteToAdd.get();
int idx = register_dive_site(diveSiteToAdd.release()); // Return ownership to backend.
emit diveListNotifier.diveSiteAdded(diveSiteToRemove, idx); // Inform frontend of new dive site.
EditDiveSite::redo();
}
@ -561,17 +565,18 @@ void EditTagsBase::redo()
QStringList EditTags::data(struct dive *d) const
{
QStringList res;
for (const divetag *tag: d->tags)
res.push_back(QString::fromStdString(tag->name));
for (const struct tag_entry *tag = d->tag_list; tag; tag = tag->next)
res.push_back(QString::fromStdString(tag->tag->name));
return res;
}
void EditTags::set(struct dive *d, const QStringList &v) const
{
d->tags.clear();
taglist_free(d->tag_list);
d->tag_list = NULL;
for (const QString &tag: v)
taglist_add_tag(d->tags, tag.toStdString());
taglist_cleanup(d->tags);
taglist_add_tag(&d->tag_list, qPrintable(tag));
taglist_cleanup(&d->tag_list);
}
QString EditTags::fieldName() const
@ -582,13 +587,14 @@ QString EditTags::fieldName() const
// ***** Buddies *****
QStringList EditBuddies::data(struct dive *d) const
{
return stringToList(QString::fromStdString(d->buddy));
return stringToList(d->buddy);
}
void EditBuddies::set(struct dive *d, const QStringList &v) const
{
QString text = v.join(", ");
d->buddy = text.toStdString();
free(d->buddy);
d->buddy = copy_qstring(text);
}
QString EditBuddies::fieldName() const
@ -599,13 +605,14 @@ QString EditBuddies::fieldName() const
// ***** DiveGuide *****
QStringList EditDiveGuide::data(struct dive *d) const
{
return stringToList(QString::fromStdString(d->diveguide));
return stringToList(d->diveguide);
}
void EditDiveGuide::set(struct dive *d, const QStringList &v) const
{
QString text = v.join(", ");
d->diveguide = text.toStdString();
free(d->diveguide);
d->diveguide = copy_qstring(text);
}
QString EditDiveGuide::fieldName() const
@ -613,8 +620,19 @@ QString EditDiveGuide::fieldName() const
return Command::Base::tr("dive guide");
}
PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn)
static void swapCandQString(QString &q, char *&c)
{
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)
notes = data->notes;
if (what.diveguide)
@ -638,16 +656,16 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
if (what.divesite)
divesite = data->dive_site;
if (what.tags)
tags = data->tags;
tags = taglist_copy(data->tag_list);
if (what.cylinders) {
cylinders = data->cylinders;
copy_cylinders(&data->cylinders, &cylinders);
// Paste cylinders is "special":
// 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.
// Moreover, for these we set the manually_added flag, because they weren't downloaded from a DC.
for (size_t i = 0; i < d->cylinders.size() && i < cylinders.size(); ++i) {
const cylinder_t &src = d->cylinders[i];
cylinder_t &dst = cylinders[i];
for (int i = 0; i < d->cylinders.nr && i < cylinders.nr; ++i) {
const cylinder_t &src = *get_cylinder(d, i);
cylinder_t &dst = cylinders.cylinders[i];
dst.gasmix = src.gasmix;
dst.start = src.start;
dst.end = src.end;
@ -661,8 +679,8 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
dst.bestmix_o2 = src.bestmix_o2;
dst.bestmix_he = src.bestmix_he;
}
for (size_t i = d->cylinders.size(); i < cylinders.size(); ++i) {
cylinder_t &cyl = cylinders[i];
for (int i = d->cylinders.nr; i < cylinders.nr; ++i) {
cylinder_t &cyl = cylinders.cylinders[i];
cyl.start.mbar = 0;
cyl.end.mbar = 0;
cyl.sample_start.mbar = 0;
@ -671,7 +689,7 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
}
}
if (what.weights)
weightsystems = data->weightsystems;
copy_weights(&data->weightsystems, &weightsystems);
if (what.number)
number = data->number;
if (what.when)
@ -680,18 +698,22 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
PasteState::~PasteState()
{
taglist_free(tags);
clear_cylinder_table(&cylinders);
clear_weightsystem_table(&weightsystems);
free(weightsystems.weightsystems);
}
void PasteState::swap(dive_components what)
{
if (what.notes)
std::swap(notes, d->notes);
swapCandQString(notes, d->notes);
if (what.diveguide)
std::swap(diveguide, d->diveguide);
swapCandQString(diveguide, d->diveguide);
if (what.buddy)
std::swap(buddy, d->buddy);
swapCandQString(buddy, d->buddy);
if (what.suit)
std::swap(suit, d->suit);
swapCandQString(suit, d->suit);
if (what.rating)
std::swap(rating, d->rating);
if (what.visibility)
@ -707,7 +729,7 @@ void PasteState::swap(dive_components what)
if (what.divesite)
std::swap(divesite, d->dive_site);
if (what.tags)
std::swap(tags, d->tags);
std::swap(tags, d->tag_list);
if (what.cylinders)
std::swap(cylinders, d->cylinders);
if (what.weights)
@ -778,34 +800,40 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive),
when(0),
maxdepth({0}),
meandepth({0}),
dc({ 0 }),
notes(nullptr),
surface_pressure({0}),
duration({0}),
salinity(0)
{
memset(&cylinders, 0, sizeof(cylinders));
if (!d)
return;
// Fix source. Things might be inconsistent after modifying the profile.
source->maxdepth.mm = source->dcs[0].maxdepth.mm = 0;
source->maxdepth.mm = source->dc.maxdepth.mm = 0;
fixup_dive(source);
when = source->when;
maxdepth = source->maxdepth;
meandepth = source->meandepth;
notes = source->notes;
notes = copy_string(source->notes);
duration = source->duration;
salinity = source->salinity;
surface_pressure = source->surface_pressure;
// This resets the dive computers and cylinders of the source dive, avoiding deep copies.
std::swap(source->cylinders, cylinders);
std::swap(source->dcs[0], dc);
std::swap(source->dc, dc);
setText(Command::Base::tr("Replan dive"));
}
ReplanDive::~ReplanDive()
{
clear_cylinder_table(&cylinders);
free_dive_dcs(&dc);
free(notes);
}
bool ReplanDive::workToBeDone()
@ -819,7 +847,7 @@ void ReplanDive::undo()
std::swap(d->maxdepth, maxdepth);
std::swap(d->meandepth, meandepth);
std::swap(d->cylinders, cylinders);
std::swap(d->dcs[0], dc);
std::swap(d->dc, dc);
std::swap(d->notes, notes);
std::swap(d->surface_pressure, surface_pressure);
std::swap(d->duration, duration);
@ -860,9 +888,10 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int
maxdepth({0}),
meandepth({0}),
dcmaxdepth({0}),
duration({0})
duration({0}),
dc({ 0 })
{
const struct divecomputer *sdc = get_dive_dc(source, dcNr);
const struct divecomputer *sdc = get_dive_dc_const(source, dcNr);
if (!sdc)
d = nullptr; // Signal that we refuse to do anything.
if (!d)
@ -873,14 +902,15 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int
meandepth = source->meandepth;
duration = source->duration;
dc.samples = sdc->samples;
dc.events = sdc->events;
copy_samples(sdc, &dc);
copy_events(sdc, &dc);
setText(editProfileTypeToString(type, count) + " " + diveNumberOrDate(d));
}
EditProfile::~EditProfile()
{
free_dive_dcs(&dc);
}
bool EditProfile::workToBeDone()
@ -894,6 +924,8 @@ void EditProfile::undo()
if (!sdc)
return;
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->maxdepth, dc.maxdepth);
std::swap(d->maxdepth, maxdepth);
@ -932,10 +964,10 @@ bool AddWeight::workToBeDone()
void AddWeight::undo()
{
for (dive *d: dives) {
if (d->weightsystems.empty())
if (d->weightsystems.nr <= 0)
continue;
d->weightsystems.pop_back();
emit diveListNotifier.weightRemoved(d, d->weightsystems.size());
remove_weightsystem(d, d->weightsystems.nr - 1);
emit diveListNotifier.weightRemoved(d, d->weightsystems.nr);
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
}
}
@ -943,26 +975,31 @@ void AddWeight::undo()
void AddWeight::redo()
{
for (dive *d: dives) {
d->weightsystems.emplace_back();
emit diveListNotifier.weightAdded(d, d->weightsystems.size() - 1);
add_cloned_weightsystem(&d->weightsystems, empty_weightsystem);
emit diveListNotifier.weightAdded(d, d->weightsystems.nr - 1);
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
}
}
static int find_weightsystem_index(const struct dive *d, const weightsystem_t &ws)
static int find_weightsystem_index(const struct dive *d, weightsystem_t ws)
{
return index_of_if(d->weightsystems, [&ws](auto &ws2) { return same_weightsystem(ws2, ws); });
for (int idx = 0; idx < d->weightsystems.nr; ++idx) {
if (same_weightsystem(d->weightsystems.weightsystems[idx], ws))
return idx;
}
return -1;
}
EditWeightBase::EditWeightBase(int index, bool currentDiveOnly) :
EditDivesBase(currentDiveOnly)
EditDivesBase(currentDiveOnly),
ws(empty_weightsystem)
{
// Get the old weightsystem, bail if index is invalid
if (!current || index < 0 || static_cast<size_t>(index) >= current->weightsystems.size()) {
if (!current || index < 0 || index >= current->weightsystems.nr) {
dives.clear();
return;
}
ws = current->weightsystems[index];
ws = clone_weightsystem(current->weightsystems.weightsystems[index]);
// Deleting a weightsystem from multiple dives is semantically ill-defined.
// What we will do is trying to delete the same weightsystem if it exists.
@ -988,6 +1025,7 @@ EditWeightBase::EditWeightBase(int index, bool currentDiveOnly) :
EditWeightBase::~EditWeightBase()
{
free_weightsystem(ws);
}
bool EditWeightBase::workToBeDone()
@ -1009,7 +1047,7 @@ RemoveWeight::RemoveWeight(int index, bool currentDiveOnly) :
void RemoveWeight::undo()
{
for (size_t i = 0; i < dives.size(); ++i) {
add_to_weightsystem_table(&dives[i]->weightsystems, indices[i], ws);
add_to_weightsystem_table(&dives[i]->weightsystems, indices[i], clone_weightsystem(ws));
emit diveListNotifier.weightAdded(dives[i], indices[i]);
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
}
@ -1026,7 +1064,8 @@ void RemoveWeight::redo()
// ***** Edit Weight *****
EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) :
EditWeightBase(index, currentDiveOnly)
EditWeightBase(index, currentDiveOnly),
new_ws(empty_weightsystem)
{
if (dives.empty())
return;
@ -1038,13 +1077,15 @@ 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)));
// Try to untranslate the weightsystem name
new_ws = std::move(wsIn);
QString vString = QString::fromStdString(new_ws.description);
auto it = std::find_if(ws_info_table.begin(), ws_info_table.end(),
[&vString](const ws_info &info)
{ return gettextFromC::tr(info.name.c_str()) == vString; });
if (it != ws_info_table.end())
new_ws.description = it->name;
new_ws = clone_weightsystem(wsIn);
QString vString(new_ws.description);
for (int i = 0; i < MAX_WS_INFO && ws_info[i].name; ++i) {
if (gettextFromC::tr(ws_info[i].name) == vString) {
free_weightsystem(new_ws);
new_ws.description = copy_string(ws_info[i].name);
break;
}
}
// If that doesn't change anything, do nothing
if (same_weightsystem(ws, new_ws)) {
@ -1055,12 +1096,13 @@ EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) :
EditWeight::~EditWeight()
{
free_weightsystem(new_ws);
}
void EditWeight::redo()
{
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);
emit diveListNotifier.weightEdited(dives[i], indices[i]);
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
@ -1076,7 +1118,8 @@ void EditWeight::undo()
// ***** Add Cylinder *****
AddCylinder::AddCylinder(bool currentDiveOnly) :
EditDivesBase(currentDiveOnly)
EditDivesBase(currentDiveOnly),
cyl(empty_cylinder)
{
if (dives.empty())
return;
@ -1090,6 +1133,7 @@ AddCylinder::AddCylinder(bool currentDiveOnly) :
AddCylinder::~AddCylinder()
{
free_cylinder(cyl);
}
bool AddCylinder::workToBeDone()
@ -1113,7 +1157,7 @@ void AddCylinder::redo()
for (dive *d: dives) {
int index = first_hidden_cylinder(d);
indexes.push_back(index);
add_cylinder(&d->cylinders, index, cyl);
add_cylinder(&d->cylinders, index, clone_cylinder(cyl));
update_cylinder_related_info(d);
emit diveListNotifier.cylinderAdded(d, index);
invalidate_dive_cache(d); // Ensure that dive is written in git_save()
@ -1122,14 +1166,14 @@ void AddCylinder::redo()
static bool same_cylinder_type(const cylinder_t &cyl1, const cylinder_t &cyl2)
{
return std::tie(cyl1.cylinder_use, cyl1.type.description) ==
std::tie(cyl2.cylinder_use, cyl2.type.description);
return same_string(cyl1.type.description, cyl2.type.description) &&
cyl1.cylinder_use == cyl2.cylinder_use;
}
static bool same_cylinder_size(const cylinder_t &cyl1, const cylinder_t &cyl2)
{
return std::tie(cyl1.type.size.mliter, cyl1.type.workingpressure.mbar) ==
std::tie(cyl2.type.size.mliter, cyl2.type.workingpressure.mbar);
return cyl1.type.size.mliter == cyl2.type.size.mliter &&
cyl1.type.workingpressure.mbar == cyl2.type.workingpressure.mbar;
}
// Flags for comparing cylinders
@ -1142,7 +1186,7 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt
EditDivesBase(currentDiveOnly)
{
// Get the old cylinder, bail if index is invalid
if (!current || index < 0 || index >= static_cast<int>(current->cylinders.size())) {
if (!current || index < 0 || index >= current->cylinders.nr) {
dives.clear();
return;
}
@ -1154,12 +1198,12 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt
cyl.reserve(dives.size());
for (dive *d: dives) {
if (index >= static_cast<int>(d->cylinders.size()))
if (index >= d->cylinders.nr)
continue;
if (nonProtectedOnly && is_cylinder_prot(d, index))
continue;
// We checked that the cylinder exists above.
const cylinder_t &cylinder = d->cylinders[index];
const cylinder_t &cylinder = *get_cylinder(d, index);
if (d != current &&
(!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
@ -1171,13 +1215,15 @@ 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
// a cylinder to several dives as the spot will potentially be different in different dives
indexes.push_back(index);
cyl.push_back(cylinder);
cyl.push_back(clone_cylinder(cylinder));
}
dives = std::move(divesNew);
}
EditCylinderBase::~EditCylinderBase()
{
for (cylinder_t c: cyl)
free_cylinder(c);
}
bool EditCylinderBase::workToBeDone()
@ -1198,9 +1244,9 @@ RemoveCylinder::RemoveCylinder(int index, bool currentDiveOnly) :
void RemoveCylinder::undo()
{
for (size_t i = 0; i < dives.size(); ++i) {
std::vector<int> mapping = get_cylinder_map_for_add(dives[i]->cylinders.size(), indexes[i]);
add_cylinder(&dives[i]->cylinders, indexes[i], cyl[i]);
cylinder_renumber(*dives[i], &mapping[0]);
std::vector<int> mapping = get_cylinder_map_for_add(dives[i]->cylinders.nr, indexes[i]);
add_cylinder(&dives[i]->cylinders, indexes[i], clone_cylinder(cyl[i]));
cylinder_renumber(dives[i], &mapping[0]);
update_cylinder_related_info(dives[i]);
emit diveListNotifier.cylinderAdded(dives[i], indexes[i]);
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
@ -1210,9 +1256,9 @@ void RemoveCylinder::undo()
void RemoveCylinder::redo()
{
for (size_t i = 0; i < dives.size(); ++i) {
std::vector<int> mapping = get_cylinder_map_for_remove(dives[i]->cylinders.size(), indexes[i]);
std::vector<int> mapping = get_cylinder_map_for_remove(dives[i]->cylinders.nr, 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]);
emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]);
invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save()
@ -1245,12 +1291,15 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn,
else
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
for (int i = 0; i < (int)indexes.size(); ++i) {
switch (type) {
case EditCylinderType::TYPE:
free((void *)cyl[i].type.description);
cyl[i].type = cylIn.type;
cyl[i].type.description = cylIn.type.description;
cyl[i].type.description = copy_qstring(description);
cyl[i].cylinder_use = cylIn.cylinder_use;
break;
case EditCylinderType::PRESSURE:
@ -1261,7 +1310,7 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn,
cyl[i].gasmix = cylIn.gasmix;
cyl[i].bestmix_o2 = cylIn.bestmix_o2;
cyl[i].bestmix_he = cylIn.bestmix_he;
sanitize_gasmix(cyl[i].gasmix);
sanitize_gasmix(&cyl[i].gasmix);
break;
}
}
@ -1270,8 +1319,7 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn,
void EditCylinder::redo()
{
for (size_t i = 0; i < dives.size(); ++i) {
const std::string &name = cyl[i].type.description;
set_tank_info_data(tank_info_table, name, cyl[i].type.size, cyl[i].type.workingpressure);
set_tank_info_data(&tank_info_table, cyl[i].type.description, cyl[i].type.size, cyl[i].type.workingpressure);
std::swap(*get_cylinder(dives[i], indexes[i]), cyl[i]);
update_cylinder_related_info(dives[i]);
emit diveListNotifier.cylinderEdited(dives[i], indexes[i]);
@ -1296,13 +1344,13 @@ EditSensors::EditSensors(int toCylinderIn, int fromCylinderIn, int dcNr)
void EditSensors::mapSensors(int toCyl, int fromCyl)
{
for (auto &sample: dc->samples) {
for (int i = 0; i < dc->samples; ++i) {
for (int s = 0; s < MAX_SENSORS; ++s) {
if (sample.pressure[s].mbar && sample.sensor[s] == fromCyl)
sample.sensor[s] = toCyl;
if (dc->sample[i].pressure[s].mbar && dc->sample[i].sensor[s] == fromCyl)
dc->sample[i].sensor[s] = toCyl;
// In case the cylinder we are moving to has a sensor attached, move it to the other cylinder
else if (sample.pressure[s].mbar && sample.sensor[s] == toCyl)
sample.sensor[s] = fromCyl;
else if (dc->sample[i].pressure[s].mbar && dc->sample[i].sensor[s] == toCyl)
dc->sample[i].sensor[s] = fromCyl;
}
}
emit diveListNotifier.diveComputerEdited(dc);
@ -1361,9 +1409,9 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s
changedFields |= DiveField::ATM_PRESS;
if (oldDive->dive_site != newDive->dive_site)
changedFields |= DiveField::DIVESITE;
if (oldDive->diveguide != newDive->diveguide)
if (!same_string(oldDive->diveguide, newDive->diveguide))
changedFields |= DiveField::DIVEGUIDE;
if (oldDive->buddy != newDive->buddy)
if (!same_string(oldDive->buddy, newDive->buddy))
changedFields |= DiveField::BUDDY;
if (oldDive->rating != newDive->rating)
changedFields |= DiveField::RATING;
@ -1377,13 +1425,13 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s
changedFields |= DiveField::SURGE;
if (oldDive->chill != newDive->chill)
changedFields |= DiveField::CHILL;
if (oldDive->suit != newDive->suit)
if (!same_string(oldDive->suit, newDive->suit))
changedFields |= DiveField::SUIT;
if (taglist_get_tagstring(oldDive->tags) != taglist_get_tagstring(newDive->tags)) // This is cheating. Do we have a taglist comparison function?
if (taglist_get_tagstring(oldDive->tag_list) != taglist_get_tagstring(newDive->tag_list)) // This is cheating. Do we have a taglist comparison function?
changedFields |= DiveField::TAGS;
if (oldDive->dcs[0].divemode != newDive->dcs[0].divemode)
if (oldDive->dc.divemode != newDive->dc.divemode)
changedFields |= DiveField::MODE;
if (oldDive->notes != newDive->notes)
if (!same_string(oldDive->notes, newDive->notes))
changedFields |= DiveField::NOTES;
if (oldDive->salinity != newDive->salinity)
changedFields |= DiveField::SALINITY;
@ -1394,9 +1442,9 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s
void EditDive::undo()
{
if (siteToRemove) {
auto res = divelog.sites.pull(siteToRemove);
siteToAdd = std::move(res.ptr);
emit diveListNotifier.diveSiteDeleted(siteToRemove, res.idx); // Inform frontend of removed dive site.
int idx = unregister_dive_site(siteToRemove);
siteToAdd.reset(siteToRemove);
emit diveListNotifier.diveSiteDeleted(siteToRemove, idx); // Inform frontend of removed dive site.
}
exchangeDives();
@ -1406,9 +1454,9 @@ void EditDive::undo()
void EditDive::redo()
{
if (siteToAdd) {
auto res = divelog.sites.register_site(std::move(siteToAdd)); // Return ownership to backend.
siteToRemove = res.ptr;
emit diveListNotifier.diveSiteAdded(siteToRemove, res.idx); // Inform frontend of new dive site.
siteToRemove = siteToAdd.get();
int idx = register_dive_site(siteToAdd.release()); // Return ownership to backend.
emit diveListNotifier.diveSiteAdded(siteToRemove, idx); // Inform frontend of new dive site.
}
exchangeDives();
@ -1433,7 +1481,7 @@ void EditDive::exchangeDives()
std::swap(*newDive, *oldDive);
fulltext_register(oldDive);
if (newDiveSite)
newDiveSite->add_dive(oldDive);
add_dive_to_dive_site(oldDive, newDiveSite);
newDiveSite = oldDiveSite; // remember the previous dive site
invalidate_dive_cache(oldDive);
@ -1441,12 +1489,12 @@ void EditDive::exchangeDives()
QVector<dive *> dives = { oldDive };
timestamp_t delta = oldDive->when - newDive->when;
if (delta != 0) {
divelog.dives.sort();
divelog.trips.sort();
sort_dive_table(divelog.dives);
sort_trip_table(divelog.trips);
if (newDive->divetrip != oldDive->divetrip)
qWarning("Command::EditDive::redo(): This command does not support moving between trips!");
if (oldDive->divetrip)
newDive->divetrip->sort_dives(); // Keep the trip-table in order
sort_dive_table(&newDive->divetrip->dives); // Keep the trip-table in order
emit diveListNotifier.divesTimeChanged(delta, dives);
}

View File

@ -88,7 +88,7 @@ private:
// 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.
template <DiveField::Flags ID, std::string dive::*PTR>
template <DiveField::Flags ID, char *dive::*PTR>
class EditStringSetter : public EditTemplate<QString, ID> {
private:
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.
class EditDiveSiteNew : public EditDiveSite {
public:
std::unique_ptr<dive_site> diveSiteToAdd;
OwningDiveSitePtr diveSiteToAdd;
struct dive_site *diveSiteToRemove;
EditDiveSiteNew(const QString &newName, bool currentDiveOnly);
void undo() override;
@ -289,19 +289,19 @@ public:
struct PasteState {
dive *d;
dive_site *divesite;
std::string notes;
std::string diveguide;
std::string buddy;
std::string suit;
QString notes;
QString diveguide;
QString buddy;
QString suit;
int rating;
int wavesize;
int visibility;
int current;
int surge;
int chill;
tag_list tags;
cylinder_table cylinders;
weightsystem_table weightsystems;
tag_entry *tags;
struct cylinder_table cylinders;
struct weightsystem_table weightsystems;
int number;
timestamp_t when;
@ -329,7 +329,7 @@ class ReplanDive : public Base {
depth_t maxdepth, meandepth;
struct cylinder_table cylinders;
struct divecomputer dc;
std::string notes;
char *notes;
pressure_t surface_pressure;
duration_t duration;
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
private:
dive *oldDive; // Dive that is going to be overwritten
std::unique_ptr<dive> newDive; // New data
OwningDivePtr newDive; // New data
dive_site *newDiveSite;
int changedFields;
dive_site *siteToRemove;
std::unique_ptr<dive_site> siteToAdd;
OwningDiveSitePtr siteToAdd;
dive_site *siteToEdit;
location_t dsLocation;

View File

@ -41,12 +41,13 @@ void EditTripBase::redo()
// ***** Location *****
void EditTripLocation::set(dive_trip *t, const QString &s) const
{
t->location = s.toStdString();
free(t->location);
t->location = copy_qstring(s);
}
QString EditTripLocation::data(dive_trip *t) const
{
return QString::fromStdString(t->location);
return QString(t->location);
}
QString EditTripLocation::fieldName() const
@ -62,12 +63,13 @@ TripField EditTripLocation::fieldId() const
// ***** Notes *****
void EditTripNotes::set(dive_trip *t, const QString &s) const
{
t->notes = s.toStdString();
free(t->notes);
t->notes = copy_qstring(s);
}
QString EditTripNotes::data(dive_trip *t) const
{
return QString::fromStdString(t->notes);
return QString(t->notes);
}
QString EditTripNotes::fieldName() const

View File

@ -30,7 +30,7 @@ protected:
void redo() override;
// Get and set functions to be overriden by sub-classes.
virtual void set(dive_trip *t, const QString &) const = 0;
virtual void set(struct dive_trip *t, const QString &) 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 TripField fieldId() const = 0;

View File

@ -2,6 +2,7 @@
#include "command_event.h"
#include "core/dive.h"
#include "core/event.h"
#include "core/selection.h"
#include "core/subsurface-qt/divelistnotifier.h"
#include "core/libdivecomputer.h"
@ -34,8 +35,8 @@ void EventBase::updateDive()
setSelection({ d }, d, dcNr);
}
AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event ev) : EventBase(d, dcNr),
ev(std::move(ev))
AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr),
eventToAdd(ev)
{
}
@ -47,29 +48,32 @@ bool AddEventBase::workToBeDone()
void AddEventBase::redoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
idx = add_event_to_dc(dc, ev); // return ownership to backend
eventToRemove = eventToAdd.get();
add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend
}
void AddEventBase::undoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
ev = remove_event_from_dc(dc, idx);
remove_event_from_dc(dc, eventToRemove);
eventToAdd.reset(eventToRemove); // take ownership of event
eventToRemove = nullptr;
}
AddEventBookmark::AddEventBookmark(struct dive *d, int dcNr, int seconds) :
AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_BOOKMARK, 0, 0, "bookmark"))
AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_BOOKMARK, 0, 0, "bookmark"))
{
setText(Command::Base::tr("Add bookmark"));
}
AddEventDivemodeSwitch::AddEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode) :
AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_BOOKMARK, 0, divemode, QT_TRANSLATE_NOOP("gettextFromC", "modechange")))
AddEventBase(d, dcNr, create_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])));
}
AddEventSetpointChange::AddEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2) :
AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_PO2, 0, pO2.mbar, QT_TRANSLATE_NOOP("gettextFromC", "SP change"))),
AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_PO2, 0, pO2.mbar, QT_TRANSLATE_NOOP("gettextFromC", "SP change"))),
divemode(CCR)
{
setText(Command::Base::tr("Add set point change")); // TODO: format pO2 value in bar or psi.
@ -87,11 +91,11 @@ void AddEventSetpointChange::redoit()
std::swap(get_dive_dc(d, dcNr)->divemode, divemode);
}
RenameEvent::RenameEvent(struct dive *d, int dcNr, int idx, const std::string name) : EventBase(d, dcNr),
idx(idx),
name(std::move(name))
RenameEvent::RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name) : EventBase(d, dcNr),
eventToAdd(clone_event_rename(ev, name)),
eventToRemove(ev)
{
setText(Command::Base::tr("Rename bookmark to %1").arg(name.c_str()));
setText(Command::Base::tr("Rename bookmark to %1").arg(name));
}
bool RenameEvent::workToBeDone()
@ -102,25 +106,24 @@ bool RenameEvent::workToBeDone()
void RenameEvent::redoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
event *ev = get_event(dc, idx);
if (ev)
std::swap(ev->name, name);
swap_event(dc, eventToRemove, eventToAdd.get());
event *tmp = eventToRemove;
eventToRemove = eventToAdd.release();
eventToAdd.reset(tmp);
}
void RenameEvent::undoit()
{
// Undo and redo do the same thing - they simply swap names
// Undo and redo do the same thing - they simply swap events
redoit();
}
RemoveEvent::RemoveEvent(struct dive *d, int dcNr, int idx) : EventBase(d, dcNr),
idx(idx), cylinder(-1)
RemoveEvent::RemoveEvent(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr),
eventToRemove(ev),
cylinder(ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE ?
ev->gas.index : -1)
{
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()));
setText(Command::Base::tr("Remove %1 event").arg(ev->name));
}
bool RemoveEvent::workToBeDone()
@ -131,13 +134,16 @@ bool RemoveEvent::workToBeDone()
void RemoveEvent::redoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
ev = remove_event_from_dc(dc, idx);
remove_event_from_dc(dc, eventToRemove);
eventToAdd.reset(eventToRemove); // take ownership of event
eventToRemove = nullptr;
}
void RemoveEvent::undoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
idx = add_event_to_dc(dc, std::move(ev));
eventToRemove = eventToAdd.get();
add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend
}
void RemoveEvent::post() const
@ -159,18 +165,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
// support that anyway.
struct divecomputer *dc = get_dive_dc(d, dcNr);
// Note that we remove events in reverse order so that the indexes don't change
// meaning while removing. This should be an extremely rare case anyway.
for (int idx = static_cast<int>(dc->events.size()) - 1; idx > 0; --idx) {
const event &ev = dc->events[idx];
if (ev.time.seconds == seconds && ev.name == "gaschange")
eventsToRemove.push_back(idx);
if (std::find(cylinders.begin(), cylinders.end(), ev.gas.index) == cylinders.end())
cylinders.push_back(ev.gas.index); // cylinders might have changed their status
struct event *gasChangeEvent = dc->events;
while ((gasChangeEvent = get_next_event_mutable(gasChangeEvent, "gaschange")) != NULL) {
if (gasChangeEvent->time.seconds == seconds) {
eventsToRemove.push_back(gasChangeEvent);
int idx = gasChangeEvent->gas.index;
if (std::find(cylinders.begin(), cylinders.end(), idx) == cylinders.end())
cylinders.push_back(idx); // cylinders might have changed their status
}
gasChangeEvent = gasChangeEvent->next;
}
eventsToAdd.push_back(create_gas_switch_event(d, dc, seconds, tank));
eventsToAdd.emplace_back(create_gas_switch_event(d, dc, seconds, tank));
}
bool AddGasSwitch::workToBeDone()
@ -180,21 +186,20 @@ bool AddGasSwitch::workToBeDone()
void AddGasSwitch::redoit()
{
std::vector<event> newEventsToAdd;
std::vector<int> newEventsToRemove;
std::vector<OwningEventPtr> newEventsToAdd;
std::vector<event *> newEventsToRemove;
newEventsToAdd.reserve(eventsToRemove.size());
newEventsToRemove.reserve(eventsToAdd.size());
struct divecomputer *dc = get_dive_dc(d, dcNr);
for (int idx: eventsToRemove)
newEventsToAdd.push_back(remove_event_from_dc(dc, idx));
for (auto &ev: eventsToAdd)
newEventsToRemove.push_back(add_event_to_dc(dc, std::move(ev)));
// Make sure that events are removed in reverse order
std::sort(newEventsToRemove.begin(), newEventsToRemove.end(), std::greater<int>());
for (event *ev: eventsToRemove) {
remove_event_from_dc(dc, ev);
newEventsToAdd.emplace_back(ev); // take ownership of event
}
for (OwningEventPtr &ev: eventsToAdd) {
newEventsToRemove.push_back(ev.get());
add_event_to_dc(dc, ev.release()); // return ownership to backend
}
eventsToAdd = std::move(newEventsToAdd);
eventsToRemove = std::move(newEventsToRemove);

View File

@ -6,12 +6,15 @@
#include "command_base.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
namespace Command {
// Pointers to events are not stable, so we always store indexes.
// Events are a strange thing: they contain there own description which means
// 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 {
protected:
@ -22,7 +25,8 @@ protected:
virtual void undoit() = 0;
// Note: we store dive and the divecomputer-number instead of a pointer to the divecomputer.
// Pointers to divecomputers are not stable.
// Since one divecomputer is integrated into the dive structure, pointers to divecomputers
// are probably not stable.
struct dive *d;
int dcNr;
private:
@ -31,15 +35,15 @@ private:
class AddEventBase : public EventBase {
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:
void undoit() override;
void redoit() override;
private:
bool workToBeDone() override;
struct event ev; // for redo
int idx; // for undo
OwningEventPtr eventToAdd; // for redo
event *eventToRemove; // for undo
};
class AddEventBookmark : public AddEventBase {
@ -63,28 +67,28 @@ private:
class RenameEvent : public EventBase {
public:
RenameEvent(struct dive *d, int dcNr, int idx, const std::string name);
RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name);
private:
bool workToBeDone() override;
void undoit() override;
void redoit() override;
int idx; // for undo and redo
std::string name; // for undo and redo
OwningEventPtr eventToAdd; // for undo and redo
event *eventToRemove; // for undo and redo
};
class RemoveEvent : public EventBase {
public:
RemoveEvent(struct dive *d, int dcNr, int idx);
RemoveEvent(struct dive *d, int dcNr, struct event *ev);
private:
bool workToBeDone() override;
void undoit() override;
void redoit() override;
void post() const; // Called to fix up dives should a gas-change have happened.
event ev; // for undo
int idx; // for redo
int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch.
OwningEventPtr eventToAdd; // for undo
event *eventToRemove; // for redo
int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch.
};
class AddGasSwitch : public EventBase {
@ -96,8 +100,8 @@ private:
void redoit() override;
std::vector<int> cylinders; // cylinders that are modified
std::vector<event> eventsToAdd;
std::vector<int> eventsToRemove;
std::vector<OwningEventPtr> eventsToAdd;
std::vector<event *> eventsToRemove;
};
} // namespace Command

View File

@ -1,26 +1,23 @@
// SPDX-License-Identifier: GPL-2.0
#include "command_filter.h"
#include "core/divelog.h"
#include "core/filterpreset.h"
#include "core/filterpresettable.h"
#include "core/subsurface-qt/divelistnotifier.h"
namespace Command {
static int createFilterPreset(const std::string &name, const FilterData &data)
{
int index = divelog.filter_presets.add(name, data);
int index = filter_preset_add(name, data);
emit diveListNotifier.filterPresetAdded(index);
return index;
}
static std::pair<std::string, FilterData> removeFilterPreset(int index)
{
const filter_preset &preset = divelog.filter_presets[index];
std::string oldName = preset.name;
FilterData oldData = preset.data;
divelog.filter_presets.remove(index);
std::string oldName = filter_preset_name(index);
FilterData oldData = filter_preset_get(index);
filter_preset_delete(index);
emit diveListNotifier.filterPresetRemoved(index);
return { oldName, oldData };
}
@ -49,8 +46,7 @@ void CreateFilterPreset::undo()
RemoveFilterPreset::RemoveFilterPreset(int indexIn) : index(indexIn)
{
const std::string &name = divelog.filter_presets[index].name;
setText(Command::Base::tr("Delete filter preset %1").arg(QString::fromStdString(name)));
setText(Command::Base::tr("Delete filter preset %1").arg(QString(filter_preset_name(index).c_str())));
}
bool RemoveFilterPreset::workToBeDone()
@ -72,8 +68,7 @@ void RemoveFilterPreset::undo()
EditFilterPreset::EditFilterPreset(int indexIn, const FilterData &dataIn) :
index(indexIn), data(dataIn)
{
const std::string &name = divelog.filter_presets[index].name;
setText(Command::Base::tr("Edit filter preset %1").arg(QString::fromStdString(name)));
setText(Command::Base::tr("Edit filter preset %1").arg(QString(filter_preset_name(index).c_str())));
}
bool EditFilterPreset::workToBeDone()
@ -83,8 +78,9 @@ bool EditFilterPreset::workToBeDone()
void EditFilterPreset::redo()
{
filter_preset &preset = divelog.filter_presets[index];
std::swap(data, preset.data);
FilterData oldData = filter_preset_get(index);
filter_preset_set(index, data);
data = std::move(oldData);
}
void EditFilterPreset::undo()

View File

@ -2,16 +2,15 @@
#include "command_pictures.h"
#include "core/errorhelper.h"
#include "core/range.h"
#include "core/subsurface-qt/divelistnotifier.h"
#include "qt-models/divelocationmodel.h"
namespace Command {
static picture *dive_get_picture(dive *d, const QString &fn)
static picture *dive_get_picture(const dive *d, const QString &fn)
{
int idx = get_picture_idx(d->pictures, fn.toStdString());
return idx < 0 ? nullptr : &d->pictures[idx];
int idx = get_picture_idx(&d->pictures, qPrintable(fn));
return idx < 0 ? nullptr : &d->pictures.pictures[idx];
}
SetPictureOffset::SetPictureOffset(dive *dIn, const QString &filenameIn, offset_t offsetIn) :
@ -34,7 +33,7 @@ void SetPictureOffset::redo()
// Instead of trying to be smart, let's simply resort the picture table.
// If someone complains about speed, do our usual "smart" thing.
std::sort(d->pictures.begin(), d->pictures.end());
sort_picture_table(&d->pictures);
emit diveListNotifier.pictureOffsetChanged(d, filename, newOffset);
invalidate_dive_cache(d);
}
@ -56,9 +55,10 @@ static PictureListForDeletion filterPictureListForDeletion(const PictureListForD
PictureListForDeletion res;
res.d = p.d;
res.filenames.reserve(p.filenames.size());
for (auto &pic: p.d->pictures) {
if (range_contains(p.filenames, pic.filename))
res.filenames.push_back(pic.filename);
for (int i = 0; i < p.d->pictures.nr; ++i) {
std::string fn = p.d->pictures.pictures[i].filename;
if (std::find(p.filenames.begin(), p.filenames.end(), fn) != p.filenames.end())
res.filenames.push_back(fn);
}
return res;
}
@ -72,14 +72,14 @@ static std::vector<PictureListForAddition> removePictures(std::vector<PictureLis
PictureListForAddition toAdd;
toAdd.d = list.d;
for (const std::string &fn: list.filenames) {
int idx = get_picture_idx(list.d->pictures, fn);
int idx = get_picture_idx(&list.d->pictures, fn.c_str());
if (idx < 0) {
report_info("removePictures(): picture disappeared!");
continue; // Huh? We made sure that this can't happen by filtering out non-existent pictures.
}
filenames.push_back(QString::fromStdString(fn));
toAdd.pics.emplace_back(list.d->pictures[idx]);
list.d->pictures.erase(list.d->pictures.begin() + idx);
toAdd.pics.emplace_back(list.d->pictures.pictures[idx]);
remove_from_picture_table(&list.d->pictures, idx);
}
if (!toAdd.pics.empty())
res.push_back(toAdd);
@ -98,17 +98,17 @@ static std::vector<PictureListForDeletion> addPictures(std::vector<PictureListFo
// happen, as we checked that before.
std::vector<PictureListForDeletion> res;
for (const PictureListForAddition &list: picturesToAdd) {
QVector<picture> picsForSignal;
QVector<PictureObj> picsForSignal;
PictureListForDeletion toRemove;
toRemove.d = list.d;
for (const picture &pic: list.pics) {
int idx = get_picture_idx(list.d->pictures, pic.filename); // This should *not* already exist!
for (const PictureObj &pic: list.pics) {
int idx = get_picture_idx(&list.d->pictures, pic.filename.c_str()); // This should *not* already exist!
if (idx >= 0) {
report_info("addPictures(): picture disappeared!");
continue; // Huh? We made sure that this can't happen by filtering out existing pictures.
}
picsForSignal.push_back(pic);
add_picture(list.d->pictures, pic);
add_picture(&list.d->pictures, pic.toCore());
toRemove.filenames.push_back(pic.filename);
}
if (!toRemove.filenames.empty())
@ -164,15 +164,16 @@ AddPictures::AddPictures(const std::vector<PictureListForAddition> &pictures) :
std::sort(p.pics.begin(), p.pics.end());
// Find a picture with a location
auto it = std::find_if(p.pics.begin(), p.pics.end(), [](const picture &p) { return has_location(&p.location); });
auto it = std::find_if(p.pics.begin(), p.pics.end(), [](const PictureObj &p) { return has_location(&p.location); });
if (it != p.pics.end()) {
// There is a dive with a location, we might want to modify the dive accordingly.
struct dive_site *ds = p.d->dive_site;
if (!ds) {
// This dive doesn't yet have a dive site -> add a new dive site.
QString name = Command::Base::tr("unnamed dive site");
sitesToAdd.push_back(std::make_unique<dive_site>(qPrintable(name), it->location));
sitesToSet.push_back({ p.d, sitesToAdd.back().get() });
dive_site *ds = alloc_dive_site_with_gps(qPrintable(name), &it->location);
sitesToAdd.emplace_back(ds);
sitesToSet.push_back({ p.d, ds });
} else if (!dive_site_has_gps_location(ds)) {
// This dive has a dive site, but without coordinates. Let's add them.
sitesToEdit.push_back({ ds, it->location });
@ -200,7 +201,7 @@ void AddPictures::swapDiveSites()
unregister_dive_from_dive_site(entry.d); // the dive-site pointer in the dive is now NULL
std::swap(ds, entry.ds);
if (ds)
ds->add_dive(entry.d);
add_dive_to_dive_site(entry.d, ds);
emit diveListNotifier.divesChanged(QVector<dive *>{ entry.d }, DiveField::DIVESITE);
}
@ -217,9 +218,9 @@ void AddPictures::undo()
// Remove dive sites
for (dive_site *siteToRemove: sitesToRemove) {
auto res = divelog.sites.pull(siteToRemove);
sitesToAdd.push_back(std::move(res.ptr));
emit diveListNotifier.diveSiteDeleted(siteToRemove, res.idx); // Inform frontend of removed dive site.
int idx = unregister_dive_site(siteToRemove);
sitesToAdd.emplace_back(siteToRemove);
emit diveListNotifier.diveSiteDeleted(siteToRemove, idx); // Inform frontend of removed dive site.
}
sitesToRemove.clear();
}
@ -227,10 +228,10 @@ void AddPictures::undo()
void AddPictures::redo()
{
// Add dive sites
for (std::unique_ptr<dive_site> &siteToAdd: sitesToAdd) {
auto res = divelog.sites.register_site(std::move(siteToAdd)); // Return ownership to backend.
sitesToRemove.push_back(res.ptr);
emit diveListNotifier.diveSiteAdded(sitesToRemove.back(), res.idx); // Inform frontend of new dive site.
for (OwningDiveSitePtr &siteToAdd: sitesToAdd) {
sitesToRemove.push_back(siteToAdd.get());
int idx = register_dive_site(siteToAdd.release()); // Return ownership to backend.
emit diveListNotifier.diveSiteAdded(sitesToRemove.back(), idx); // Inform frontend of new dive site.
}
sitesToAdd.clear();

View File

@ -48,7 +48,7 @@ private:
location_t location;
};
std::vector<PictureListForAddition> picturesToAdd; // for redo
std::vector<std::unique_ptr<dive_site>> sitesToAdd; //for redo
std::vector<OwningDiveSitePtr> sitesToAdd; //for redo
std::vector<PictureListForDeletion> picturesToRemove; // for undo
std::vector<dive_site *> sitesToRemove; // for undo
std::vector<DiveSiteEntry> sitesToSet; // for redo and undo

View File

@ -17,7 +17,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
endif()
if(FTDISUPPORT)
set(SERIAL_FTDI serial_ftdi.cpp)
set(SERIAL_FTDI serial_ftdi.c)
endif()
if(BTSUPPORT)
@ -61,29 +61,29 @@ set(SUBSURFACE_CORE_LIB_SRCS
devicedetails.h
dive.cpp
dive.h
divecomputer.cpp
divecomputer.c
divecomputer.h
dive.h
divefilter.cpp
divefilter.h
divelist.cpp
divelist.c
divelist.h
divelog.cpp
divelog.h
divelogexportlogic.cpp
divelogexportlogic.h
divesite.cpp
divesite-helper.cpp
divesite.c
divesite.h
divesitetable.h
divesitehelpers.cpp
divesitehelpers.h
downloadfromdcthread.cpp
downloadfromdcthread.h
event.cpp
event.c
event.h
eventtype.cpp
eventtype.h
equipment.cpp
equipment.c
equipment.h
errorhelper.cpp
exif.cpp
@ -95,16 +95,14 @@ set(SUBSURFACE_CORE_LIB_SRCS
filterconstraint.h
filterpreset.cpp
filterpreset.h
filterpresettable.cpp
filterpresettable.h
format.cpp
format.h
fulltext.cpp
fulltext.h
gas.cpp
gas.c
gas.h
gas-model.cpp
gaspressures.cpp
gas-model.c
gaspressures.c
gaspressures.h
gettext.h
gettextfromc.cpp
@ -133,18 +131,21 @@ set(SUBSURFACE_CORE_LIB_SRCS
metadata.h
metrics.cpp
metrics.h
ostctools.cpp
ostctools.c
owning_ptrs.h
parse-gpx.cpp
parse-xml.cpp
parse.cpp
parse.h
picture.cpp
picture.c
picture.h
pictureobj.cpp
pictureobj.h
planner.cpp
planner.h
plannernotes.cpp
pref.h
pref.cpp
pref.c
profile.cpp
profile.h
qt-gui.h
@ -157,17 +158,18 @@ set(SUBSURFACE_CORE_LIB_SRCS
save-git.cpp
save-html.cpp
save-html.h
save-profiledata.cpp
save-profiledata.c
save-xml.cpp
selection.cpp
selection.h
sha1.cpp
sha1.c
sha1.h
statistics.cpp
ssrf.h
statistics.c
statistics.h
string-format.h
string-format.cpp
strtod.cpp
strtod.c
subsurface-float.h
subsurface-string.cpp
subsurface-string.h
@ -177,23 +179,23 @@ set(SUBSURFACE_CORE_LIB_SRCS
subsurfacesysinfo.h
tag.cpp
tag.h
taxonomy.cpp
taxonomy.c
taxonomy.h
time.cpp
trip.cpp
timer.c
timer.h
trip.c
trip.h
triptable.cpp
triptable.h
uemis-downloader.cpp
uemis.cpp
uemis.c
uemis.h
units.h
units.cpp
units.c
uploadDiveShare.cpp
uploadDiveShare.h
uploadDiveLogsDE.cpp
uploadDiveLogsDE.h
version.cpp
version.c
version.h
videoframeextractor.cpp
videoframeextractor.h

View File

@ -41,12 +41,14 @@ static std::string make_default_filename()
return system_default_path() + "/subsurface.xml";
}
extern "C" {
const char android_system_divelist_default_font[] = "Roboto";
const char *system_divelist_default_font = android_system_divelist_default_font;
double system_divelist_default_font_size = -1;
int get_usb_fd(uint16_t idVendor, uint16_t idProduct);
void subsurface_OS_pref_setup()
void subsurface_OS_pref_setup(void)
{
}
@ -56,13 +58,13 @@ bool subsurface_ignore_font(const char *font)
return false;
}
const char *system_default_directory()
const char *system_default_directory(void)
{
static const std::string path = system_default_path();
return path.c_str();
}
const char *system_default_filename()
const char *system_default_filename(void)
{
static const std::string fn = make_default_filename();
return fn.c_str();
@ -156,10 +158,12 @@ int get_usb_fd(uint16_t idVendor, uint16_t idProduct)
}
JNIEXPORT void JNICALL
Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_setUsbDevice(JNIEnv *,
jobject,
Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_setUsbDevice(JNIEnv *env,
jobject obj,
jobject javaUsbDevice)
{
Q_UNUSED (obj)
Q_UNUSED (env)
QAndroidJniObject usbDevice(javaUsbDevice);
if (usbDevice.isValid()) {
android_usb_serial_device_descriptor descriptor = getDescriptor(usbDevice);
@ -173,10 +177,12 @@ Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_setUsbDevice(JNIEnv *
}
JNIEXPORT void JNICALL
Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_restartDownload(JNIEnv *,
jobject,
Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_restartDownload(JNIEnv *env,
jobject obj,
jobject javaUsbDevice)
{
Q_UNUSED (obj)
Q_UNUSED (env)
QAndroidJniObject usbDevice(javaUsbDevice);
if (usbDevice.isValid()) {
android_usb_serial_device_descriptor descriptor = getDescriptor(usbDevice);
@ -231,12 +237,12 @@ int subsurface_zip_close(struct zip *zip)
}
/* win32 console */
void subsurface_console_init()
void subsurface_console_init(void)
{
/* NOP */
}
void subsurface_console_exit()
void subsurface_console_exit(void)
{
/* NOP */
}
@ -245,6 +251,7 @@ bool subsurface_user_is_root()
{
return false;
}
}
/* called from QML manager */
void checkPendingIntents()

View File

@ -200,7 +200,7 @@ void CheckCloudConnection::gotContinent(QNetworkReply *reply)
}
// helper to be used from C code
bool canReachCloudServer(struct git_info *info)
extern "C" bool canReachCloudServer(struct git_info *info)
{
if (verbose)
qWarning() << "Cloud storage: checking connection to cloud server" << info->url.c_str();

View File

@ -4,6 +4,7 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "ssrf.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -444,7 +445,7 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
unsigned int ndl = 0;
unsigned int in_deco = 0, deco_ceiling = 0, deco_time = 0;
struct divecomputer *dc = &dive->dcs[0];
struct divecomputer *dc = &dive->dc;
struct sample *sample;
// Initialize stat variables
@ -494,6 +495,10 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
while (offset + config.sample_size < size) {
s = samples + offset;
// Start with an empty sample
sample = prepare_sample(dc);
sample->time.seconds = sample_cnt * profile_period;
// Check for event
if (s[0] & 0x80) {
cochran_dive_event(dc, s, sample_cnt * profile_period, &in_deco, &deco_ceiling, &deco_time);
@ -501,10 +506,6 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
continue;
}
// Start with an empty sample
sample = prepare_sample(dc);
sample->time.seconds = sample_cnt * profile_period;
// Depth is in every sample
depth_sample = (double)(s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
depth += depth_sample;
@ -591,6 +592,8 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
sample->sensor[0] = 0;
sample->pressure[0].mbar = lrint(psi * PSI / 100);
finish_sample(dc);
offset += config.sample_size;
sample_cnt++;
}
@ -601,11 +604,13 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
const unsigned char *in, unsigned size,
struct dive_table &table)
struct dive_table *table)
{
unsigned char *buf = (unsigned char *)malloc(size);
struct dive *dive;
struct divecomputer *dc;
struct tm tm = {0};
uint32_t csum[5];
double max_depth, avg_depth, min_temp;
unsigned int duration = 0, corrupt_dive = 0;
@ -663,8 +668,8 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
puts("\nSample Data\n");
#endif
auto dive = std::make_unique<struct dive>();
dc = &dive->dcs[0];
dive = alloc_dive();
dc = &dive->dc;
unsigned char *log = (buf + 0x4914);
@ -672,22 +677,24 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
case TYPE_GEMINI:
case TYPE_COMMANDER:
if (config.type == TYPE_GEMINI) {
cylinder_t cyl = empty_cylinder;
dc->model = "Gemini";
dc->deviceid = buf[0x18c] * 256 + buf[0x18d]; // serial no
cylinder_t cyl = default_cylinder(dive.get());
fill_default_cylinder(dive, &cyl);
cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256
+ log[CMD_O2_PERCENT + 1]) * 10;
cyl.gasmix.he.permille = 0;
add_cylinder(&dive->cylinders, 0, std::move(cyl));
add_cylinder(&dive->cylinders, 0, cyl);
} else {
dc->model = "Commander";
dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
for (g = 0; g < 2; g++) {
cylinder_t cyl = default_cylinder(dive.get());
cylinder_t cyl = empty_cylinder;
fill_default_cylinder(dive, &cyl);
cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256
+ log[CMD_O2_PERCENT + g * 2 + 1]) * 10;
cyl.gasmix.he.permille = 0;
add_cylinder(&dive->cylinders, g, std::move(cyl));
add_cylinder(&dive->cylinders, g, cyl);
}
}
@ -712,7 +719,8 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
* (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588) * 1000);
dc->salinity = 10000 + 150 * log[CMD_WATER_CONDUCTIVITY];
dc->diveid = SHA1_uint32(log + CMD_NUMBER, 2);
SHA1(log + CMD_NUMBER, 2, (unsigned char *)csum);
dc->diveid = csum[0];
if (log[CMD_MAX_DEPTH] == 0xff && log[CMD_MAX_DEPTH + 1] == 0xff)
corrupt_dive = 1;
@ -725,14 +733,15 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
dc->model = "EMC";
dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
for (g = 0; g < 4; g++) {
cylinder_t cyl = default_cylinder(dive.get());
cylinder_t cyl = empty_cylinder;
fill_default_cylinder(dive, &cyl);
cyl.gasmix.o2.permille =
(log[EMC_O2_PERCENT + g * 2] / 256
+ log[EMC_O2_PERCENT + g * 2 + 1]) * 10;
cyl.gasmix.he.permille =
(log[EMC_HE_PERCENT + g * 2] / 256
+ log[EMC_HE_PERCENT + g * 2 + 1]) * 10;
add_cylinder(&dive->cylinders, g, std::move(cyl));
add_cylinder(&dive->cylinders, g, cyl);
}
tm.tm_year = log[EMC_YEAR];
@ -756,7 +765,8 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
* (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588) * 1000);
dc->salinity = 10000 + 150 * (log[EMC_WATER_CONDUCTIVITY] & 0x3);
dc->diveid = SHA1_uint32(log + EMC_NUMBER, 2);
SHA1(log + EMC_NUMBER, 2, (unsigned char *)csum);
dc->diveid = csum[0];
if (log[EMC_MAX_DEPTH] == 0xff && log[EMC_MAX_DEPTH + 1] == 0xff)
corrupt_dive = 1;
@ -772,7 +782,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
if (sample_pre_offset < sample_end_offset && sample_end_offset != 0xffffffff)
sample_size = sample_end_offset - sample_pre_offset;
cochran_parse_samples(dive.get(), buf + 0x4914, buf + 0x4914
cochran_parse_samples(dive, buf + 0x4914, buf + 0x4914
+ config.logbook_size, sample_size,
&duration, &max_depth, &avg_depth, &min_temp);
@ -784,7 +794,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
dc->duration.seconds = duration;
}
table.record_dive(std::move(dive));
record_dive_to_table(dive, table);
free(buf);
}

View File

@ -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 5
enum color_index_t {
typedef enum {
/* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */
SAC_1,
SAC_2,
@ -145,7 +145,7 @@ enum color_index_t {
CALC_CEILING_DEEP,
TISSUE_PERCENTAGE,
DURATION_LINE
};
} color_index_t;
QColor getColor(const color_index_t i, bool isGrayscale = false);
QColor getSacColor(int sac, int diveSac);

View File

@ -68,14 +68,16 @@ static QString writeGasDetails(gas g)
bool ConfigureDiveComputer::saveXMLBackup(const QString &fileName, const DeviceDetails &details, device_data_t *data)
{
QString xml = "";
QString vendor = data->vendor;
QString product = data->product;
QXmlStreamWriter writer(&xml);
writer.setAutoFormatting(true);
writer.writeStartDocument();
writer.writeStartElement("DiveComputerSettingsBackup");
writer.writeStartElement("DiveComputer");
writer.writeTextElement("Vendor", QString::fromStdString(data->vendor));
writer.writeTextElement("Product", QString::fromStdString(data->product));
writer.writeTextElement("Vendor", vendor);
writer.writeTextElement("Product", product);
writer.writeEndElement();
writer.writeStartElement("Settings");
writer.writeTextElement("CustomText", details.customText);

View File

@ -15,11 +15,11 @@
#include "units.h"
#include "device.h"
#include "file.h"
#include "format.h"
#include "divesite.h"
#include "dive.h"
#include "divelog.h"
#include "errorhelper.h"
#include "ssrf.h"
#include "tag.h"
static unsigned int two_bytes_to_int(unsigned char x, unsigned char y)
@ -104,23 +104,21 @@ static int read_file_header(unsigned char *buffer)
* Returns libdc's equivalent model number (also from g_models) or zero if
* 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;
int i = 0;
while (model != g_models[i].model_num && g_models[i].model_num != 0xEE)
i++;
dev_data.model = g_models[i].name;
dev_data.vendor = (const char *)malloc(strlen(g_models[i].name) + 1);
dev_data.vendor.clear();
for (const char *s = g_models[i].name; isalpha(*s); ++s)
dev_data.vendor += *s;
dev_data.product = strchr(g_models[i].name, ' ') + 1;
dev_data->model = copy_string(g_models[i].name);
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->product = copy_string(strchr(g_models[i].name, ' ') + 1);
d = get_descriptor(g_models[i].type, g_models[i].libdc_num);
if (d)
dev_data.descriptor = d;
dev_data->descriptor = d;
else
return 0;
return g_models[i].libdc_num;
@ -131,11 +129,14 @@ static int dtrak_prepare_data(int model, device_data_t &dev_data)
* 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.
*/
static std::string cyl_type_by_size(int size)
static const char *cyl_type_by_size(int size)
{
auto it = std::find_if(tank_info_table.begin(), tank_info_table.end(),
[size] (const tank_info &info) { return info.ml == size; });
return it != tank_info_table.end() ? it->name : std::string();
for (int i = 0; i < tank_info_table.nr; ++i) {
const struct tank_info *ti = &tank_info_table.infos[i];
if (ti->ml == size)
return ti->name;
}
return "";
}
/*
@ -160,19 +161,21 @@ 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)
{
int rc, profile_length, libdc_model;
char *tmp_notes_str = NULL;
unsigned char *tmp_string1 = NULL,
*locality = NULL,
*dive_point = NULL,
*compl_buffer,
*membuf = runner;
char buffer[1024];
unsigned char tmp_1byte;
unsigned int tmp_2bytes;
unsigned long tmp_4bytes;
std::string tmp_notes_str;
struct dive_site *ds;
char is_nitrox = 0, is_O2 = 0, is_SCR = 0;
device_data_t devdata;
devdata.log = log;
device_data_t *devdata = (device_data_t *)calloc(1, sizeof(device_data_t));
devdata->log = log;
/*
* Parse byte to byte till next dive entry
@ -192,7 +195,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
* Next, Time in minutes since 00:00
*/
read_bytes(2);
dt_dive->dcs[0].when = dt_dive->when = (timestamp_t)date_time_to_ssrfc(tmp_4bytes, tmp_2bytes);
dt_dive->dc.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
@ -210,13 +213,11 @@ 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
* Locality and Dive points.
*/
{
std::string buffer2 = std::string((char *)locality) + " " + (char *)dive_point;
struct dive_site *ds = log->sites.get_by_name(buffer2);
if (!ds)
ds = log->sites.create(buffer2);
ds->add_dive(dt_dive);
}
snprintf(buffer, sizeof(buffer), "%s, %s", locality, dive_point);
ds = get_dive_site_by_name(buffer, log->sites);
if (!ds)
ds = create_dive_site(buffer, log->sites);
add_dive_to_dive_site(dt_dive, ds);
free(locality);
locality = NULL;
free(dive_point);
@ -240,19 +241,19 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
read_bytes(1);
switch (tmp_1byte) {
case 1:
dt_dive->dcs[0].surface_pressure.mbar = 1013;
dt_dive->dc.surface_pressure.mbar = 1013;
break;
case 2:
dt_dive->dcs[0].surface_pressure.mbar = 932;
dt_dive->dc.surface_pressure.mbar = 932;
break;
case 3:
dt_dive->dcs[0].surface_pressure.mbar = 828;
dt_dive->dc.surface_pressure.mbar = 828;
break;
case 4:
dt_dive->dcs[0].surface_pressure.mbar = 735;
dt_dive->dc.surface_pressure.mbar = 735;
break;
default:
dt_dive->dcs[0].surface_pressure.mbar = 1013;
dt_dive->dc.surface_pressure.mbar = 1013;
}
/*
@ -260,32 +261,32 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(2);
if (tmp_2bytes != 0x7FFF)
dt_dive->dcs[0].surfacetime.seconds = (uint32_t) tmp_2bytes * 60;
dt_dive->dc.surfacetime.seconds = (uint32_t) tmp_2bytes * 60;
/*
* Weather, values table, 0 to 6
* Subsurface don't have this record but we can use tags
*/
dt_dive->tags.clear();
dt_dive->tag_list = NULL;
read_bytes(1);
switch (tmp_1byte) {
case 1:
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "clear"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "clear")));
break;
case 2:
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "misty"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "misty")));
break;
case 3:
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "fog"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "fog")));
break;
case 4:
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "rain"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "rain")));
break;
case 5:
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "storm"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "storm")));
break;
case 6:
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "snow"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "snow")));
break;
default:
// unknown, do nothing
@ -297,7 +298,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(2);
if (tmp_2bytes != 0x7FFF)
dt_dive->dcs[0].airtemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
dt_dive->dc.airtemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
/*
* Dive suit, values table, 0 to 6
@ -305,22 +306,22 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
read_bytes(1);
switch (tmp_1byte) {
case 1:
dt_dive->suit = "No suit";
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "No suit"));
break;
case 2:
dt_dive->suit = "Shorty";
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Shorty"));
break;
case 3:
dt_dive->suit = "Combi";
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Combi"));
break;
case 4:
dt_dive->suit = "Wet suit";
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Wet suit"));
break;
case 5:
dt_dive->suit = "Semidry suit";
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Semidry suit"));
break;
case 6:
dt_dive->suit = "Dry suit";
dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Dry suit"));
break;
default:
// unknown, do nothing
@ -334,14 +335,14 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(2);
if (tmp_2bytes != 0x7FFF) {
cylinder_t cyl;
cylinder_t cyl = empty_cylinder;
cyl.type.size.mliter = tmp_2bytes * 10;
cyl.type.description = cyl_type_by_size(tmp_2bytes * 10);
cyl.start.mbar = 200000;
cyl.gasmix.he.permille = 0;
cyl.gasmix.o2.permille = 210;
cyl.manually_added = true;
dt_dive->cylinders.push_back(std::move(cyl));
add_cloned_cylinder(&dt_dive->cylinders, cyl);
}
/*
@ -349,14 +350,14 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(2);
if (tmp_2bytes != 0x7FFF)
dt_dive->maxdepth.mm = dt_dive->dcs[0].maxdepth.mm = (int32_t)tmp_2bytes * 10;
dt_dive->maxdepth.mm = dt_dive->dc.maxdepth.mm = (int32_t)tmp_2bytes * 10;
/*
* Dive time in minutes.
*/
read_bytes(2);
if (tmp_2bytes != 0x7FFF)
dt_dive->duration.seconds = dt_dive->dcs[0].duration.seconds = (uint32_t)tmp_2bytes * 60;
dt_dive->duration.seconds = dt_dive->dc.duration.seconds = (uint32_t)tmp_2bytes * 60;
/*
* Minimum water temperature in C*100. If unknown, set it to 0K which
@ -364,7 +365,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(2);
if (tmp_2bytes != 0x7fff)
dt_dive->watertemp.mkelvin = dt_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
dt_dive->watertemp.mkelvin = dt_dive->dc.watertemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
else
dt_dive->watertemp.mkelvin = 0;
@ -372,7 +373,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
* Air used in bar*100.
*/
read_bytes(2);
if (tmp_2bytes != 0x7FFF && dt_dive->cylinders.size() > 0)
if (tmp_2bytes != 0x7FFF && dt_dive->cylinders.nr > 0)
get_cylinder(dt_dive, 0)->gas_used.mliter = lrint(get_cylinder(dt_dive, 0)->type.size.mliter * (tmp_2bytes / 100.0));
/*
@ -381,30 +382,30 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(1);
if (bit_set(tmp_1byte, 2))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "no stop"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "no stop")));
if (bit_set(tmp_1byte, 3))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "deco"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "deco")));
if (bit_set(tmp_1byte, 4))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "single ascent"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "single ascent")));
if (bit_set(tmp_1byte, 5))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "multiple ascent"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "multiple ascent")));
if (bit_set(tmp_1byte, 6))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "fresh water"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "fresh water")));
if (bit_set(tmp_1byte, 7))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "salt water"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "salt water")));
/*
* Dive Type 2 - Bit table, use tags again
*/
read_bytes(1);
if (bit_set(tmp_1byte, 0)) {
taglist_add_tag(dt_dive->tags, "nitrox");
taglist_add_tag(&dt_dive->tag_list, strdup("nitrox"));
is_nitrox = 1;
}
if (bit_set(tmp_1byte, 1)) {
taglist_add_tag(dt_dive->tags, "rebreather");
taglist_add_tag(&dt_dive->tag_list, strdup("rebreather"));
is_SCR = 1;
dt_dive->dcs[0].divemode = PSCR;
dt_dive->dc.divemode = PSCR;
}
/*
@ -412,36 +413,36 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
*/
read_bytes(1);
if (bit_set(tmp_1byte, 0))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "sight seeing"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "sight seeing")));
if (bit_set(tmp_1byte, 1))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "club dive"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "club dive")));
if (bit_set(tmp_1byte, 2))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "instructor"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "instructor")));
if (bit_set(tmp_1byte, 3))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "instruction"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "instruction")));
if (bit_set(tmp_1byte, 4))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "night"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "night")));
if (bit_set(tmp_1byte, 5))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "cave"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "cave")));
if (bit_set(tmp_1byte, 6))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "ice"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "ice")));
if (bit_set(tmp_1byte, 7))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "search"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "search")));
/*
* Dive Activity 2 - Bit table, use tags again
*/
read_bytes(1);
if (bit_set(tmp_1byte, 0))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "wreck"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "wreck")));
if (bit_set(tmp_1byte, 1))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "river"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "river")));
if (bit_set(tmp_1byte, 2))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "drift"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "drift")));
if (bit_set(tmp_1byte, 3))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "photo"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "photo")));
if (bit_set(tmp_1byte, 4))
taglist_add_tag(dt_dive->tags, translate("gettextFromC", "other"));
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "other")));
/*
* Other activities - String 1st byte = long
@ -450,9 +451,10 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
read_bytes(1);
if (tmp_1byte != 0) {
read_string(tmp_string1);
tmp_notes_str= format_string_std("%s: %s\n",
translate("gettextFromC", "Other activities"),
snprintf(buffer, sizeof(buffer), "%s: %s\n",
QT_TRANSLATE_NOOP("gettextFromC", "Other activities"),
tmp_string1);
tmp_notes_str = strdup(buffer);
free(tmp_string1);
}
@ -462,7 +464,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
read_bytes(1);
if (tmp_1byte != 0) {
read_string(tmp_string1);
dt_dive->buddy = (const char *)tmp_string1;
dt_dive->buddy = strdup((char *)tmp_string1);
free(tmp_string1);
}
@ -472,12 +474,15 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
read_bytes(1);
if (tmp_1byte != 0) {
read_string(tmp_string1);
dt_dive->notes = format_string_std("%s%s:\n%s",
tmp_notes_str.c_str(),
translate("gettextFromC", "Datatrak/Wlog notes"),
int len = snprintf(buffer, sizeof(buffer), "%s%s:\n%s",
tmp_notes_str ? tmp_notes_str : "",
QT_TRANSLATE_NOOP("gettextFromC", "Datatrak/Wlog notes"),
tmp_string1);
dt_dive->notes = (char *)calloc((len +1), 1);
memcpy(dt_dive->notes, buffer, len);
free(tmp_string1);
}
free(tmp_notes_str);
/*
* Alarms 1 and Alarms2 - Bit tables - Not in Subsurface, we use the profile
@ -515,7 +520,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
libdc_model = dtrak_prepare_data(tmp_1byte, devdata);
if (!libdc_model)
report_error(translate("gettextFromC", "[Warning] Manual dive # %d\n"), dt_dive->number);
dt_dive->dcs[0].model = devdata.model;
dt_dive->dc.model = copy_string(devdata->model);
/*
* Air usage, unknown use. Probably allows or deny manually entering gas
@ -538,16 +543,16 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
compl_buffer = (unsigned char *) calloc(18 + profile_length, 1);
rc = dt_libdc_buffer(membuf, profile_length, libdc_model, compl_buffer);
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 {
report_error(translate("gettextFromC", "[Error] Out of memory for dive %d. Abort parsing."), dt_dive->number);
free(compl_buffer);
goto bail;
}
if (is_nitrox && dt_dive->cylinders.size() > 0)
if (is_nitrox && dt_dive->cylinders.nr > 0)
get_cylinder(dt_dive, 0)->gasmix.o2.permille =
lrint(membuf[23] & 0x0F ? 20.0 + 2 * (membuf[23] & 0x0F) : 21.0) * 10;
if (is_O2 && dt_dive->cylinders.size() > 0)
if (is_O2 && dt_dive->cylinders.nr > 0)
get_cylinder(dt_dive, 0)->gasmix.o2.permille = membuf[23] * 10;
free(compl_buffer);
}
@ -557,16 +562,19 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct
* Initialize some dive data not supported by Datatrak/WLog
*/
if (!libdc_model)
dt_dive->dcs[0].deviceid = 0;
dt_dive->dc.deviceid = 0;
else
dt_dive->dcs[0].deviceid = 0xffffffff;
if (!is_SCR && dt_dive->cylinders.size() > 0) {
dt_dive->dc.deviceid = 0xffffffff;
dt_dive->dc.next = NULL;
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)->gas_used.mliter / get_cylinder(dt_dive, 0)->type.size.mliter) * 1000);
}
free(devdata);
return (char *)membuf;
bail:
free(locality);
free(devdata);
return NULL;
}
@ -599,7 +607,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco
pos_viz = offset + 258,
pos_tank_init = offset + 266,
pos_suit = offset + 268;
char *wlog_notes = NULL, *wlog_suit = NULL;
char *wlog_notes = NULL, *wlog_suit = NULL, *buffer = NULL;
unsigned char *runner = (unsigned char *) wl_mem.data();
/*
@ -611,16 +619,24 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco
(void)memcpy(wlog_notes_temp, runner + offset, NOTES_LENGTH);
wlog_notes = to_utf8((unsigned char *) wlog_notes_temp);
}
if (wlog_notes)
dt_dive->notes += wlog_notes;
if (dt_dive->notes && wlog_notes) {
buffer = (char *)calloc (strlen(dt_dive->notes) + strlen(wlog_notes) + 1, 1);
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
*/
tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]);
if (tmp != 0x7fff) {
weightsystem_t ws = { {tmp * 10}, translate("gettextFromC", "unknown"), false };
dt_dive->weightsystems.push_back(std::move(ws));
weightsystem_t ws = { {tmp * 10}, QT_TRANSLATE_NOOP("gettextFromC", "unknown"), false };
add_cloned_weightsystem(&dt_dive->weightsystems, ws);
}
/*
@ -654,7 +670,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);
}
if (wlog_suit)
dt_dive->suit = wlog_suit;
dt_dive->suit = copy_string(wlog_suit);
free(wlog_suit);
}
@ -687,24 +703,25 @@ int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log)
runner = mem.data();
JUMP(runner, 12);
// Sequential parsing. Abort if received NULL from dt_dive_parser.
// Secuential parsing. Abort if received NULL from dt_dive_parser.
while ((i < numdives) && (runner < maxbuf)) {
auto ptdive = std::make_unique<dive>();
struct dive *ptdive = alloc_dive();
runner = dt_dive_parser((unsigned char *)runner, ptdive.get(), log, maxbuf);
runner = dt_dive_parser((unsigned char *)runner, ptdive, log, maxbuf);
if (!wl_mem.empty())
wlog_compl_parser(wl_mem, ptdive.get(), i);
wlog_compl_parser(wl_mem, ptdive, i);
if (runner == NULL) {
report_error("%s", translate("gettextFromC", "Error: no dive"));
free(ptdive);
rc = 1;
goto out;
} else {
log->dives.record_dive(std::move(ptdive));
record_dive_to_table(ptdive, log->dives);
}
i++;
}
out:
log->dives.sort();
sort_dive_table(log->dives);
return rc;
bail:
return 1;

View File

@ -21,6 +21,7 @@
#include <assert.h>
#include "deco.h"
#include "ssrf.h"
#include "dive.h"
#include "gas.h"
#include "subsurface-string.h"
@ -215,7 +216,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;
}
double tissue_tolerance_calc(struct deco_state *ds, const struct dive *dive, double pressure, bool in_planner)
extern "C" double tissue_tolerance_calc(struct deco_state *ds, const struct dive *dive, double pressure, bool in_planner)
{
int ci = -1;
double ret_tolerance_limit_ambient_pressure = 0.0;
@ -322,7 +323,7 @@ static double calc_surface_phase(double surface_pressure, double he_pressure, do
return 0;
}
void vpmb_start_gradient(struct deco_state *ds)
extern "C" void vpmb_start_gradient(struct deco_state *ds)
{
int ci;
@ -332,7 +333,7 @@ void vpmb_start_gradient(struct deco_state *ds)
}
}
void vpmb_next_gradient(struct deco_state *ds, double deco_time, double surface_pressure, bool in_planner)
extern "C" void vpmb_next_gradient(struct deco_state *ds, double deco_time, double surface_pressure, bool in_planner)
{
int ci;
double n2_b, n2_c;
@ -378,7 +379,7 @@ static double solve_cubic(double A, double B, double C)
}
void nuclear_regeneration(struct deco_state *ds, double time)
extern "C" void nuclear_regeneration(struct deco_state *ds, double time)
{
time /= 60.0;
int ci;
@ -410,7 +411,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
void calc_crushing_pressure(struct deco_state *ds, double pressure)
extern "C" void calc_crushing_pressure(struct deco_state *ds, double pressure)
{
int ci;
double gradient;
@ -442,11 +443,12 @@ void calc_crushing_pressure(struct deco_state *ds, double pressure)
}
/* add period_in_seconds at the given pressure and gas to the deco calculation */
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)
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)
{
int ci;
struct gas_pressures pressures;
bool icd = false;
gas_pressures pressures = fill_pressures(pressure - ((in_planner && (decoMode(true) == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE),
fill_pressures(&pressures, pressure - ((in_planner && (decoMode(true) == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE),
gasmix, (double) ccpo2 / 1000.0, divemode);
for (ci = 0; ci < 16; ci++) {
@ -474,7 +476,7 @@ void add_segment(struct deco_state *ds, double pressure, struct gasmix gasmix, i
}
#if DECO_CALC_DEBUG
void dump_tissues(struct deco_state *ds)
extern "C" void dump_tissues(struct deco_state *ds)
{
int ci;
printf("N2 tissues:");
@ -487,7 +489,7 @@ void dump_tissues(struct deco_state *ds)
}
#endif
void clear_vpmb_state(struct deco_state *ds)
extern "C" void clear_vpmb_state(struct deco_state *ds)
{
int ci;
for (ci = 0; ci < 16; ci++) {
@ -499,11 +501,11 @@ void clear_vpmb_state(struct deco_state *ds)
ds->max_bottom_ceiling_pressure.mbar = 0;
}
void clear_deco(struct deco_state *ds, double surface_pressure, bool in_planner)
extern "C" void clear_deco(struct deco_state *ds, double surface_pressure, bool in_planner)
{
int ci;
*ds = deco_state();
memset(ds, 0, sizeof(*ds));
clear_vpmb_state(ds);
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;
@ -543,7 +545,7 @@ void deco_state_cache::restore(struct deco_state *target, bool keep_vpmb_state)
*target = *data;
}
int deco_allowed_depth(double tissues_tolerance, double surface_pressure, const struct dive *dive, bool smooth)
extern "C" int deco_allowed_depth(double tissues_tolerance, double surface_pressure, const struct dive *dive, bool smooth)
{
int depth;
double pressure_delta;
@ -562,7 +564,7 @@ int deco_allowed_depth(double tissues_tolerance, double surface_pressure, const
return depth;
}
void set_gf(short gflow, short gfhigh)
extern "C" void set_gf(short gflow, short gfhigh)
{
if (gflow != -1)
buehlmann_config.gf_low = (double)gflow / 100.0;
@ -570,7 +572,7 @@ void set_gf(short gflow, short gfhigh)
buehlmann_config.gf_high = (double)gfhigh / 100.0;
}
void set_vpmb_conservatism(short conservatism)
extern "C" void set_vpmb_conservatism(short conservatism)
{
if (conservatism < 0)
vpmb_config.conservatism = 0;
@ -580,7 +582,7 @@ void set_vpmb_conservatism(short conservatism)
vpmb_config.conservatism = conservatism;
}
double get_gf(struct deco_state *ds, double ambpressure_bar, const struct dive *dive)
extern "C" 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 gf_low = buehlmann_config.gf_low;
@ -594,7 +596,7 @@ double get_gf(struct deco_state *ds, double ambpressure_bar, const struct dive *
return gf;
}
double regressiona(const struct deco_state *ds)
extern "C" double regressiona(const struct deco_state *ds)
{
if (ds->sum1 > 1) {
double avxy = ds->sumxy / ds->sum1;
@ -607,7 +609,7 @@ double regressiona(const struct deco_state *ds)
return 0.0;
}
double regressionb(const struct deco_state *ds)
extern "C" double regressionb(const struct deco_state *ds)
{
if (ds->sum1)
return ds->sumy / ds->sum1 - ds->sumx * regressiona(ds) / ds->sum1;
@ -615,14 +617,14 @@ double regressionb(const struct deco_state *ds)
return 0.0;
}
void reset_regression(struct deco_state *ds)
extern "C" void reset_regression(struct deco_state *ds)
{
ds->sum1 = 0;
ds->sumxx = ds->sumx = 0L;
ds->sumy = ds->sumxy = 0.0;
}
void update_regression(struct deco_state *ds, const struct dive *dive)
extern "C" void update_regression(struct deco_state *ds, const struct dive *dive)
{
if (!ds->plot_depth)
return;

View File

@ -5,43 +5,46 @@
#include "units.h"
#include "gas.h"
#include "divemode.h"
#include <memory>
#ifdef __cplusplus
extern "C" {
#endif
struct dive;
struct divecomputer;
struct decostop;
struct deco_state {
double tissue_n2_sat[16] = {};
double tissue_he_sat[16] = {};
double tolerated_by_tissue[16] = {};
double tissue_inertgas_saturation[16] = {};
double buehlmann_inertgas_a[16] = {};
double buehlmann_inertgas_b[16] = {};
double tissue_n2_sat[16];
double tissue_he_sat[16];
double tolerated_by_tissue[16];
double tissue_inertgas_saturation[16];
double buehlmann_inertgas_a[16];
double buehlmann_inertgas_b[16];
double max_n2_crushing_pressure[16] = {};
double max_he_crushing_pressure[16] = {};
double max_n2_crushing_pressure[16];
double max_he_crushing_pressure[16];
double crushing_onset_tension[16] = {}; // total inert gas tension in the t* moment
double n2_regen_radius[16] = {}; // rs
double he_regen_radius[16] = {};
double max_ambient_pressure = 0.0; // last moment we were descending
double crushing_onset_tension[16]; // total inert gas tension in the t* moment
double n2_regen_radius[16]; // rs
double he_regen_radius[16];
double max_ambient_pressure; // last moment we were descending
double bottom_n2_gradient[16] = {};
double bottom_he_gradient[16] = {};
double bottom_n2_gradient[16];
double bottom_he_gradient[16];
double initial_n2_gradient[16] = {};
double initial_he_gradient[16] = {};
double initial_n2_gradient[16];
double initial_he_gradient[16];
pressure_t first_ceiling_pressure;
pressure_t max_bottom_ceiling_pressure;
int ci_pointing_to_guiding_tissue = 0;
double gf_low_pressure_this_dive = 0.0;
int deco_time = 0;
bool icd_warning = false;
int sum1 = 0;
long sumx = 0, sumxx = 0;
double sumy = 0, sumxy = 0;
int plot_depth = 0;
int ci_pointing_to_guiding_tissue;
double gf_low_pressure_this_dive;
int deco_time;
bool icd_warning;
int sum1;
long sumx, sumxx;
double sumy, sumxy;
int plot_depth;
};
extern const double buehlmann_N2_t_halflife[];
@ -67,6 +70,12 @@ extern double regressionb(const struct deco_state *ds);
extern void reset_regression(struct deco_state *ds);
extern void update_regression(struct deco_state *ds, const struct dive *dive);
#ifdef __cplusplus
}
// C++ only functions
#include <memory>
struct deco_state_cache {
// Test if there is cached data
operator bool () {
@ -78,4 +87,6 @@ private:
std::unique_ptr<deco_state> data;
};
#endif
#endif // DECO_H

View File

@ -8,7 +8,7 @@
#include "selection.h"
#include "core/settings/qPrefDiveComputer.h"
fingerprint_table fingerprints;
struct fingerprint_table fingerprint_table;
static bool same_device(const device &dev1, const device &dev2)
{
@ -18,35 +18,42 @@ static bool same_device(const device &dev1, const device &dev2)
bool device::operator<(const device &a) const
{
return std::tie(model, serialNumber) < std::tie(a.model, a.serialNumber);
int diff;
diff = model.compare(a.model);
if (diff)
return diff < 0;
return serialNumber < a.serialNumber;
}
const struct device *get_device_for_dc(const device_table &table, const struct divecomputer *dc)
extern "C" const struct device *get_device_for_dc(const struct device_table *table, const struct divecomputer *dc)
{
if (dc->model.empty() || dc->serial.empty())
if (!dc->model || !dc->serial)
return NULL;
const std::vector<device> &dcs = table->devices;
device dev { dc->model, dc->serial };
auto it = std::lower_bound(table.begin(), table.end(), dev);
return it != table.end() && same_device(*it, dev) ? &*it : NULL;
auto it = std::lower_bound(dcs.begin(), dcs.end(), dev);
return it != dcs.end() && same_device(*it, dev) ? &*it : NULL;
}
int get_or_add_device_for_dc(device_table &table, const struct divecomputer *dc)
extern "C" int get_or_add_device_for_dc(struct device_table *table, const struct divecomputer *dc)
{
if (dc->model.empty() || dc->serial.empty())
if (!dc->model || !dc->serial)
return -1;
const struct device *dev = get_device_for_dc(table, dc);
if (dev) {
auto it = std::lower_bound(table.begin(), table.end(), *dev);
return it - table.begin();
auto it = std::lower_bound(table->devices.begin(), table->devices.end(), *dev);
return it - table->devices.begin();
}
return create_device_node(table, dc->model, dc->serial, std::string());
return create_device_node(table, dc->model, dc->serial, "");
}
bool device_exists(const device_table &table, const struct device &dev)
extern "C" bool device_exists(const struct device_table *device_table, const struct device *dev)
{
auto it = std::lower_bound(table.begin(), table.end(), dev);
return it != table.end() && same_device(*it, dev);
auto it = std::lower_bound(device_table->devices.begin(), device_table->devices.end(), *dev);
return it != device_table->devices.end() && same_device(*it, *dev);
}
void device::showchanges(const std::string &n) const
@ -59,7 +66,7 @@ void device::showchanges(const std::string &n) const
}
}
int create_device_node(device_table &dcs, const std::string &m, const std::string &s, const std::string &n)
static int addDC(std::vector<device> &dcs, const std::string &m, const std::string &s, const std::string &n)
{
if (m.empty() || s.empty())
return -1;
@ -79,122 +86,220 @@ int create_device_node(device_table &dcs, const std::string &m, const std::strin
}
}
int add_to_device_table(device_table &device_table, const struct device &dev)
extern "C" int create_device_node(struct device_table *device_table, const char *model, const char *serial, const char *nickname)
{
return create_device_node(device_table, dev.model, dev.serialNumber, dev.nickName);
return addDC(device_table->devices, model ?: "", serial ?: "", nickname ?: "");
}
int remove_device(device_table &table, const struct device &dev)
extern "C" int add_to_device_table(struct device_table *device_table, const struct device *dev)
{
auto it = std::lower_bound(table.begin(), table.end(), dev);
if (it != table.end() && same_device(*it, dev)) {
int idx = it - table.begin();
table.erase(it);
return create_device_node(device_table, dev->model.c_str(), dev->serialNumber.c_str(), dev->nickName.c_str());
}
extern "C" int remove_device(struct device_table *device_table, const struct device *dev)
{
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;
} else {
return -1;
}
}
void remove_from_device_table(device_table &table, int idx)
extern "C" void remove_from_device_table(struct device_table *device_table, int idx)
{
if (idx < 0 || idx >= (int)table.size())
if (idx < 0 || idx >= (int)device_table->devices.size())
return;
table.erase(table.begin() + idx);
device_table->devices.erase(device_table->devices.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. */
bool device_used_by_selected_dive(const struct device &dev)
extern "C" bool device_used_by_selected_dive(const struct device *dev)
{
for (dive *d: getDiveSelection()) {
for (auto &dc: d->dcs) {
if (dc.deviceid == dev.deviceId)
struct divecomputer *dc;
for_each_dc (d, dc) {
if (dc->deviceid == dev->deviceId)
return true;
}
}
return false;
}
int is_default_dive_computer_device(const char *name)
extern "C" int is_default_dive_computer_device(const char *name)
{
return qPrefDiveComputer::device() == name;
}
std::string get_dc_nickname(const struct divecomputer *dc)
const char *get_dc_nickname(const struct divecomputer *dc)
{
const device *existNode = get_device_for_dc(divelog.devices, dc);
if (existNode && !existNode->nickName.empty())
return existNode->nickName;
return existNode->nickName.c_str();
else
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
bool fingerprint_record::operator<(const fingerprint_record &a) const
{
return std::tie(model, serial) < std::tie(a.model, a.serial);
if (model == a.model)
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
// so we can't bail on the serial number being 0
std::pair<int, const unsigned char *> get_fingerprint_data(const fingerprint_table &table, uint32_t model, uint32_t serial)
extern "C" unsigned int get_fingerprint_data(const struct fingerprint_table *table, uint32_t model, uint32_t serial, const unsigned char **fp_out)
{
if (model == 0)
return { 0, nullptr };
if (model == 0 || fp_out == nullptr)
return 0;
struct fingerprint_record fpr = { model, serial };
auto it = std::lower_bound(table.begin(), table.end(), fpr);
if (it != table.end() && it->model == model && it->serial == serial) {
auto it = std::lower_bound(table->fingerprints.begin(), table->fingerprints.end(), fpr);
if (it != table->fingerprints.end() && it->model == model && it->serial == serial) {
// 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
if (has_dive(it->fdeviceid, it->fdiveid))
return { it->fsize, it->raw_data.get() };
if (has_dive(it->fdeviceid, it->fdiveid)) {
*fp_out = it->raw_data;
return it->fsize;
}
}
return { 0, nullptr };
return 0;
}
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)
extern "C" void create_fingerprint_node(struct fingerprint_table *table, uint32_t model, uint32_t serial,
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
auto raw_data = std::make_unique<unsigned char []>(fsize);
std::copy(raw_data_in, raw_data_in + fsize, raw_data.get());
unsigned char *raw_data = (unsigned char *)malloc(fsize);
if (!raw_data)
return;
memcpy(raw_data, raw_data_in, fsize);
struct fingerprint_record fpr = { model, serial, std::move(raw_data), fsize, fdeviceid, fdiveid };
auto it = std::lower_bound(table.begin(), table.end(), fpr);
if (it != table.end() && it->model == model && it->serial == serial) {
struct fingerprint_record fpr = { model, serial, raw_data, fsize, fdeviceid, fdiveid };
auto it = std::lower_bound(table->fingerprints.begin(), table->fingerprints.end(), fpr);
if (it != table->fingerprints.end() && it->model == model && it->serial == serial) {
// 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
// can update the existing entry; first we free the memory for the stored raw data
free(it->raw_data);
it->fdeviceid = fdeviceid;
it->fdiveid = fdiveid;
it->raw_data = std::move(fpr.raw_data);
it->raw_data = raw_data;
it->fsize = fsize;
} else {
// insert a new one
table.insert(it, std::move(fpr));
table->fingerprints.insert(it, fpr);
}
}
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)
extern "C" 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)
{
QByteArray raw = QByteArray::fromHex(hex_data.c_str());
QByteArray raw = QByteArray::fromHex(hex_data);
create_fingerprint_node(table, model, serial,
(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)
{
return d <= 9 ? d + '0' : d - 10 + 'a';
}
std::string fingerprint_record::get_data() const
std::string fp_get_data(struct fingerprint_table *table, unsigned int i)
{
std::string res(fsize * 2, ' ');
for (unsigned int i = 0; i < fsize; ++i) {
res[2 * i] = to_hex_digit((raw_data[i] >> 4) & 0xf);
res[2 * i + 1] = to_hex_digit(raw_data[i] & 0xf);
if (!table || i >= table->fingerprints.size())
return std::string();
struct fingerprint_record *fpr = &table->fingerprints[i];
std::string res(fpr->fsize * 2, ' ');
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;
}

View File

@ -3,13 +3,73 @@
#define DEVICE_H
#include <stdint.h>
#include <memory>
#include <string>
#include <vector>
#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 <vector>
struct device {
bool operator<(const device &a) const;
void showchanges(const std::string &n) const;
@ -19,48 +79,28 @@ struct device {
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 {
bool operator<(const fingerprint_record &a) const;
uint32_t model; // model and libdivecomputer serial number to
uint32_t serial; // look up the fingerprint
std::unique_ptr<unsigned char[]> raw_data; // fingerprint data as provided by libdivecomputer
unsigned char *raw_data; // fingerprint data as provided by libdivecomputer
unsigned int fsize; // size of raw fingerprint data
unsigned int fdeviceid; // corresponding deviceid
unsigned int fdiveid; // corresponding diveid
std::string get_data() const; // As hex-string
};
using fingerprint_table = std::vector<fingerprint_record>;
struct device_table {
// Keep the dive computers in a vector sorted by (model, serial)
std::vector<device> devices;
};
// global device table
extern fingerprint_table fingerprints;
struct fingerprint_table {
// Keep the fingerprint records in a vector sorted by (model, serial) - these are uint32_t here
std::vector<fingerprint_record> fingerprints;
};
// 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);
std::string fp_get_data(struct fingerprint_table *table, unsigned int i);
#endif
#endif // DEVICE_H

File diff suppressed because it is too large Load Diff

View File

@ -7,13 +7,14 @@
#include "divemode.h"
#include "divecomputer.h"
#include "equipment.h"
#include "picture.h" // TODO: remove
#include "tag.h"
#include "picture.h"
#include <array>
#include <memory>
#include <string>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
extern int last_xml_version;
@ -21,72 +22,44 @@ extern const char *divemode_text_ui[];
extern const char *divemode_text[];
struct dive_site;
struct dive_site_table;
struct dive_table;
struct dive_trip;
struct full_text_cache;
struct event;
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_trip *divetrip = nullptr;
timestamp_t when = 0;
struct dive_site *dive_site = nullptr;
std::string notes;
std::string diveguide, buddy;
std::string suit;
cylinder_table cylinders;
weightsystem_table weightsystems;
int number = 0;
int rating = 0;
int wavesize = 0, current = 0, visibility = 0, surge = 0, chill = 0; /* 0 - 5 star ratings */
int sac = 0, otu = 0, cns = 0, maxcns = 0;
struct dive_trip *divetrip;
timestamp_t when;
struct dive_site *dive_site;
char *notes;
char *diveguide, *buddy;
struct cylinder_table cylinders;
struct weightsystem_table weightsystems;
char *suit;
int number;
int rating;
int wavesize, current, visibility, surge, chill; /* 0 - 5 star ratings */
int sac, otu, cns, maxcns;
/* Calculated based on dive computer data */
temperature_t mintemp, maxtemp, watertemp, airtemp;
depth_t maxdepth, meandepth;
pressure_t surface_pressure;
duration_t duration;
int salinity = 0; // kg per 10000 l
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;
int salinity; // kg per 10000 l
int user_salinity; // water density reflecting a user-specified type
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 */
@ -99,8 +72,8 @@ extern void invalidate_dive_cache(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 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 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);
/* when selectively copying dive information, which parts should be copied? */
struct dive_components {
@ -139,19 +112,39 @@ 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 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 std::string get_dive_country(const struct dive *dive);
extern std::string get_dive_location(const struct dive *dive);
extern const char *get_dive_country(const struct dive *dive);
extern const char *get_dive_location(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 const struct divecomputer *get_dive_dc(const struct dive *dive, int nr);
extern const struct divecomputer *get_dive_dc_const(const struct dive *dive, int nr);
extern timestamp_t dive_endtime(const struct dive *dive);
extern void set_git_prefs(const char *prefs);
extern std::unique_ptr<dive> clone_make_first_dc(const struct dive &d, int dc_number);
extern std::unique_ptr<dive> clone_delete_divecomputer(const struct dive &d, int dc_number);
extern std::array<std::unique_ptr<dive>, 2> split_divecomputer(const struct dive &src, int num);
extern struct dive *make_first_dc(const struct dive *d, int dc_number);
extern struct 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);
/*
* 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 int dive_has_gps_location(const struct dive *dive);
extern location_t dive_get_gps_location(const struct dive *d);
@ -160,53 +153,51 @@ 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_logic(const char *filename, bool select_only, bool anonymize);
extern int save_dive(FILE *f, const struct dive &dive, bool anonymize);
extern int save_dive(FILE *f, 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 save_dive_sites_logic(const char *filename, const struct dive_site *sites[], int nr_sites, bool anonymize);
struct membuffer;
extern void save_one_dive_to_mb(struct membuffer *b, const struct dive &dive, bool anonymize);
extern void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize);
extern void subsurface_console_init();
extern void subsurface_console_exit();
extern bool subsurface_user_is_root();
extern void subsurface_console_init(void);
extern void subsurface_console_exit(void);
extern bool subsurface_user_is_root(void);
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 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 struct std::unique_ptr<dive> move_dive(struct dive *s);
extern struct dive *move_dive(struct dive *s);
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_ptr(const struct dive *a, const struct dive *b);
extern bool dive_less_than(const struct dive *a, const struct dive *b);
extern bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b);
extern struct dive *fixup_dive(struct dive *dive);
extern pressure_t calculate_surface_pressure(const struct dive *dive);
extern pressure_t un_fixup_surface_pressure(const struct dive *d);
extern int get_dive_salinity(const struct dive *dive);
extern int dive_getUniqID();
extern std::array<std::unique_ptr<dive>, 2> split_dive(const struct dive &dive);
extern std::array<std::unique_ptr<dive>, 2> split_dive_at_time(const struct dive &dive, duration_t time);
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 int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2);
extern int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2);
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);
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 bool is_cylinder_used(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 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 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 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 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);
/* UI related protopypes */
@ -215,17 +206,32 @@ extern void invalidate_dive_cache(struct dive *dc);
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 */
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);
#ifdef __cplusplus
}
/* Make pointers to dive and dive_trip "Qt metatypes" so that they can be passed through
* QVariants and through QML.
*/
#include <QObject>
#include <string>
Q_DECLARE_METATYPE(struct dive *);
extern std::string existing_filename;
#endif
#endif // DIVE_H

570
core/divecomputer.c Normal file
View File

@ -0,0 +1,570 @@
// 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);
}

View File

@ -1,399 +0,0 @@
// 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;
}

View File

@ -4,11 +4,12 @@
#include "divemode.h"
#include "units.h"
#include <string>
#include <vector>
#ifdef __cplusplus
extern "C" {
#endif
struct extra_data;
struct event;
struct sample;
/* Is this header the correct place? */
@ -28,40 +29,44 @@ struct sample;
* A deviceid or diveid of zero is assumed to be "no ID".
*/
struct divecomputer {
timestamp_t when = 0;
timestamp_t when;
duration_t duration, surfacetime, last_manual_time;
depth_t maxdepth, meandepth;
temperature_t airtemp, watertemp;
pressure_t surface_pressure;
enum divemode_t divemode = OC; // dive computer type: OC(default) or CCR
uint8_t no_o2sensors = 0; // rebreathers: number of O2 sensors used
int salinity = 0; // kg per 10000 l
std::string model, serial, fw_version;
uint32_t deviceid = 0, diveid = 0;
// Note: ve store samples, events and extra_data in std::vector<>s.
// This means that pointers to these items are *not* stable.
std::vector<struct sample> samples;
std::vector<struct event> events;
std::vector<struct extra_data> extra_data;
divecomputer();
~divecomputer();
divecomputer(const divecomputer &);
divecomputer(divecomputer &&);
divecomputer &operator=(const divecomputer &);
enum divemode_t divemode; // dive computer type: OC(default) or CCR
uint8_t no_o2sensors; // rebreathers: number of O2 sensors used
int salinity; // kg per 10000 l
const char *model, *serial, *fw_version;
uint32_t deviceid, diveid;
int samples, alloc_samples;
struct sample *sample;
struct event *events;
struct extra_data *extra_data;
struct divecomputer *next;
};
extern void fake_dc(struct divecomputer *dc);
extern void free_dc(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 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 void append_sample(const struct sample &sample, 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 struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name);
extern struct event remove_event_from_dc(struct divecomputer *dc, int idx);
struct event *get_event(struct divecomputer *dc, int idx);
extern void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value);
extern void finish_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 unsigned int dc_airtemp(const struct divecomputer *dc);
extern unsigned int dc_watertemp(const struct divecomputer *dc);
extern void copy_events(const struct divecomputer *s, struct divecomputer *d);
extern void swap_event(struct divecomputer *dc, struct event *from, struct event *to);
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 bool is_dc_planner(const struct divecomputer *dc);
extern void make_planner_dc(struct divecomputer *dc);
@ -70,6 +75,10 @@ extern bool is_dc_manually_added_dive(const 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) */
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

View File

@ -5,7 +5,6 @@
#include "divelog.h"
#include "gettextfromc.h"
#include "qthelper.h"
#include "range.h"
#include "selection.h"
#include "subsurface-qt/divelistnotifier.h"
#if !defined(SUBSURFACE_MOBILE) && !defined(SUBSURFACE_DOWNLOADER)
@ -62,7 +61,7 @@ ShownChange DiveFilter::update(const QVector<dive *> &dives) const
std::vector<dive *> removeFromSelection;
for (dive *d: dives) {
// There are three modes: divesite, fulltext, normal
bool newStatus = doDS ? range_contains(dive_sites, d->dive_site) :
bool newStatus = doDS ? dive_sites.contains(d->dive_site) :
doFullText ? fulltext_dive_matches(d, filterData.fullText, filterData.fulltextStringMode) && showDive(d) :
showDive(d);
updateDiveStatus(d, newStatus, res, removeFromSelection);
@ -74,8 +73,10 @@ ShownChange DiveFilter::update(const QVector<dive *> &dives) const
void DiveFilter::reset()
{
shown_dives = static_cast<int>(divelog.dives.size());
for (auto &d: divelog.dives)
int i;
dive *d;
shown_dives = divelog.dives->nr;
for_each_dive(i, d)
d->hidden_by_filter = false;
updateAll();
}
@ -83,24 +84,26 @@ void DiveFilter::reset()
ShownChange DiveFilter::updateAll() const
{
ShownChange res;
int i;
dive *d;
std::vector<dive *> selection = getDiveSelection();
std::vector<dive *> removeFromSelection;
// There are three modes: divesite, fulltext, normal
if (diveSiteMode()) {
for (auto &d: divelog.dives) {
bool newStatus = range_contains(dive_sites, d->dive_site);
updateDiveStatus(d.get(), newStatus, res, removeFromSelection);
for_each_dive(i, d) {
bool newStatus = dive_sites.contains(d->dive_site);
updateDiveStatus(d, newStatus, res, removeFromSelection);
}
} else if (filterData.fullText.doit()) {
FullTextResult ft = fulltext_find_dives(filterData.fullText, filterData.fulltextStringMode);
for (auto &d: divelog.dives) {
bool newStatus = ft.dive_matches(d.get()) && showDive(d.get());
updateDiveStatus(d.get(), newStatus, res, removeFromSelection);
for_each_dive(i, d) {
bool newStatus = ft.dive_matches(d) && showDive(d);
updateDiveStatus(d, newStatus, res, removeFromSelection);
}
} else {
for (auto &d: divelog.dives) {
bool newStatus = showDive(d.get());
updateDiveStatus(d.get(), newStatus, res, removeFromSelection);
for_each_dive(i, d) {
bool newStatus = showDive(d);
updateDiveStatus(d, newStatus, res, removeFromSelection);
}
}
updateSelection(selection, std::vector<dive *>(), removeFromSelection);
@ -139,7 +142,7 @@ bool DiveFilter::showDive(const struct dive *d) const
}
#if !defined(SUBSURFACE_MOBILE) && !defined(SUBSURFACE_DOWNLOADER)
void DiveFilter::startFilterDiveSites(std::vector<dive_site *> ds)
void DiveFilter::startFilterDiveSites(QVector<dive_site *> ds)
{
if (++diveSiteRefCount > 1) {
setFilterDiveSite(std::move(ds));
@ -166,7 +169,7 @@ void DiveFilter::stopFilterDiveSites()
#endif
}
void DiveFilter::setFilterDiveSite(std::vector<dive_site *> ds)
void DiveFilter::setFilterDiveSite(QVector<dive_site *> ds)
{
// If the filter didn't change, return early to avoid a full
// map reload. For a well-defined comparison, sort the vector first.
@ -182,7 +185,7 @@ void DiveFilter::setFilterDiveSite(std::vector<dive_site *> ds)
MainWindow::instance()->diveList->expandAll();
}
const std::vector<dive_site *> &DiveFilter::filteredDiveSites() const
const QVector<dive_site *> &DiveFilter::filteredDiveSites() const
{
return dive_sites;
}
@ -200,7 +203,7 @@ bool DiveFilter::diveSiteMode() const
QString DiveFilter::shownText() const
{
size_t num = divelog.dives.size();
int num = divelog.dives->nr;
if (diveSiteMode() || filterData.validFilter())
return gettextFromC::tr("%L1/%L2 shown").arg(shown_dives).arg(num);
else
@ -226,9 +229,11 @@ std::vector<dive *> DiveFilter::visibleDives() const
std::vector<dive *> res;
res.reserve(shown_dives);
for (auto &d: divelog.dives) {
int i;
dive *d;
for_each_dive(i, d) {
if (!d->hidden_by_filter)
res.push_back(d.get());
res.push_back(d);
}
return res;
}

View File

@ -45,9 +45,9 @@ public:
bool diveSiteMode() const; // returns true if we're filtering on dive site (on mobile always returns false)
std::vector<dive *> visibleDives() const;
#ifndef SUBSURFACE_MOBILE
const std::vector<dive_site *> &filteredDiveSites() const;
void startFilterDiveSites(std::vector<dive_site *> ds);
void setFilterDiveSite(std::vector<dive_site *> ds);
const QVector<dive_site *> &filteredDiveSites() const;
void startFilterDiveSites(QVector<dive_site *> ds);
void setFilterDiveSite(QVector<dive_site *> ds);
void stopFilterDiveSites();
#endif
void setFilter(const FilterData &data);
@ -62,7 +62,7 @@ private:
void updateDiveStatus(dive *d, bool newStatus, ShownChange &change,
std::vector<dive *> &removeFromSelection) const;
std::vector<dive_site *> dive_sites;
QVector<dive_site *> dive_sites;
FilterData filterData;
mutable int shown_dives;

File diff suppressed because it is too large Load Diff

View File

@ -2,29 +2,29 @@
#ifndef DIVELIST_H
#define DIVELIST_H
#include "triptable.h"
#include "divesitetable.h"
#include "units.h"
#include <memory>
#include <vector>
#ifdef __cplusplus
extern "C" {
#endif
struct dive;
struct divelog;
struct device;
struct trip_table;
struct dive_site_table;
struct device_table;
struct deco_state;
int comp_dives(const struct dive &a, const struct dive &b);
int comp_dives_ptr(const struct dive *a, const struct dive *b);
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);
struct dive_table {
int nr, allocated;
struct dive **dives;
};
static const struct dive_table empty_dive_table = { 0, 0, (struct dive **)0 };
/* this is used for both git and xml format */
#define DATAFORMAT_VERSION 3
extern void sort_dive_table(struct dive_table *table);
extern void update_cylinder_related_info(struct dive *);
extern int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner);
@ -35,29 +35,37 @@ extern void process_loaded_dives();
#define IMPORT_IS_DOWNLOADED (1 << 1)
#define IMPORT_MERGE_ALL_TRIPS (1 << 2)
#define IMPORT_ADD_TO_NEW_TRIP (1 << 3)
extern void add_imported_dives(struct divelog &log, int flags);
struct process_imported_dives_result {
dive_table dives_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 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 trip_table *trips_to_add, struct dive_site_table *sites_to_add,
struct device_table *devices_to_add);
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 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 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 int comp_dives(const struct dive *a, const struct dive *b);
int get_min_datafile_version();
void reset_min_datafile_version();
void report_datafile_version(int version);
int get_dive_id_closest_to(timestamp_t when);
void clear_dive_file_data();
void clear_dive_table(struct dive_table *table);
struct dive *register_dive(std::unique_ptr<dive> d);
void move_dive_table(struct dive_table *src, struct dive_table *dst);
struct dive *unregister_dive(int idx);
extern bool has_dive(unsigned int deviceid, unsigned int diveid);
#ifdef __cplusplus
}
#endif
#endif // DIVELIST_H

View File

@ -3,89 +3,96 @@
#include "divelist.h"
#include "divesite.h"
#include "device.h"
#include "dive.h"
#include "errorhelper.h"
#include "filterpreset.h"
#include "filterpresettable.h"
#include "trip.h"
struct divelog divelog;
divelog::divelog() = default;
divelog::~divelog() = default;
divelog::divelog(divelog &&) = default;
struct divelog &divelog::operator=(divelog &&) = default;
// We can't use smart pointers, since this is used from C
// and it would be bold to presume that std::unique_ptr<>
// and a plain pointer have the same memory layout.
divelog::divelog() :
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
* dive log and the trip, but doesn't deal with updating dive trips, etc */
void divelog::delete_single_dive(int idx)
void delete_single_dive(struct divelog *log, int idx)
{
if (idx < 0 || static_cast<size_t>(idx) >= dives.size()) {
report_info("Warning: deleting non-existing dive with index %d", idx);
if (idx < 0 || idx > log->dives->nr) {
report_info("Warning: deleting unexisting dive with index %d", idx);
return;
}
struct dive *dive = dives[idx].get();
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);
struct dive *dive = log->dives->dives[idx];
remove_dive_from_trip(dive, log->trips);
unregister_dive_from_dive_site(dive);
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();
delete_dive_from_table(log->dives, idx);
}
void divelog::clear()
{
dives.clear();
sites.clear();
trips.clear();
devices.clear();
filter_presets.clear();
}
/* check if we have a trip right before / after this dive */
bool divelog::is_trip_before_after(const struct dive *dive, bool before) const
{
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;
while (dives->nr > 0)
delete_single_dive(this, dives->nr - 1);
while (sites->nr)
delete_dive_site(get_dive_site(0, sites), sites);
if (trips->nr != 0) {
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)
{
log->clear();
}

View File

@ -1,36 +1,43 @@
// SPDX-License-Identifier: GPL-2.0
// A structure that contains all the data we store in divelog files
// A structure that contains all the values we save in a divelog file
#ifndef DIVELOG_H
#define DIVELOG_H
#include "divelist.h"
#include "divesitetable.h"
#include "filterpresettable.h"
#include "triptable.h"
struct dive_table;
struct trip_table;
struct dive_site_table;
struct device_table;
struct filter_preset_table;
#include <vector>
struct device;
#include <stdbool.h>
struct divelog {
dive_table dives;
trip_table trips;
dive_site_table sites;
std::vector<device> devices;
filter_preset_table filter_presets;
bool autogroup = false;
struct dive_table *dives;
struct trip_table *trips;
struct dive_site_table *sites;
struct device_table *devices;
struct filter_preset_table *filter_presets;
bool autogroup;
#ifdef __cplusplus
void clear();
divelog();
~divelog();
divelog(divelog &&); // move constructor (argument is consumed).
divelog &operator=(divelog &&); // move assignment (argument is consumed).
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;
divelog(divelog &&log); // move constructor (argument is consumed).
divelog &operator=(divelog &&log); // move assignment (argument is consumed).
#endif
};
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

View File

@ -75,42 +75,45 @@ static void exportHTMLstatistics(const QString filename, struct htmlExportSettin
QFile file(filename);
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
stats_summary_auto_free stats;
stats_t total_stats;
stats_summary stats = calculate_stats_summary(hes.selectedOnly);
calculate_stats_summary(&stats, hes.selectedOnly);
total_stats.selection_size = 0;
total_stats.total_time.seconds = 0;
int i = 0;
out << "divestat=[";
if (hes.yearlyStatistics) {
for (const auto &s: stats.stats_yearly) {
while (stats.stats_yearly != NULL && stats.stats_yearly[i].period) {
out << "{";
out << "\"YEAR\":\"" << s.period << "\",";
out << "\"DIVES\":\"" << s.selection_size << "\",";
out << "\"TOTAL_TIME\":\"" << get_dive_duration_string(s.total_time.seconds,
out << "\"YEAR\":\"" << stats.stats_yearly[i].period << "\",";
out << "\"DIVES\":\"" << stats.stats_yearly[i].selection_size << "\",";
out << "\"TOTAL_TIME\":\"" << get_dive_duration_string(stats.stats_yearly[i].total_time.seconds,
gettextFromC::tr("h"), gettextFromC::tr("min"), gettextFromC::tr("sec"), " ") << "\",";
out << "\"AVERAGE_TIME\":\"" << formatMinutes(s.total_time.seconds / s.selection_size) << "\",";
out << "\"SHORTEST_TIME\":\"" << formatMinutes(s.shortest_time.seconds) << "\",";
out << "\"LONGEST_TIME\":\"" << formatMinutes(s.longest_time.seconds) << "\",";
out << "\"AVG_DEPTH\":\"" << get_depth_string(s.avg_depth) << "\",";
out << "\"MIN_DEPTH\":\"" << get_depth_string(s.min_depth) << "\",";
out << "\"MAX_DEPTH\":\"" << get_depth_string(s.max_depth) << "\",";
out << "\"AVG_SAC\":\"" << get_volume_string(s.avg_sac) << "\",";
out << "\"MIN_SAC\":\"" << get_volume_string(s.min_sac) << "\",";
out << "\"MAX_SAC\":\"" << get_volume_string(s.max_sac) << "\",";
if (s.combined_count) {
out << "\"AVERAGE_TIME\":\"" << formatMinutes(stats.stats_yearly[i].total_time.seconds / stats.stats_yearly[i].selection_size) << "\",";
out << "\"SHORTEST_TIME\":\"" << formatMinutes(stats.stats_yearly[i].shortest_time.seconds) << "\",";
out << "\"LONGEST_TIME\":\"" << formatMinutes(stats.stats_yearly[i].longest_time.seconds) << "\",";
out << "\"AVG_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].avg_depth) << "\",";
out << "\"MIN_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].min_depth) << "\",";
out << "\"MAX_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].max_depth) << "\",";
out << "\"AVG_SAC\":\"" << get_volume_string(stats.stats_yearly[i].avg_sac) << "\",";
out << "\"MIN_SAC\":\"" << get_volume_string(stats.stats_yearly[i].min_sac) << "\",";
out << "\"MAX_SAC\":\"" << get_volume_string(stats.stats_yearly[i].max_sac) << "\",";
if (stats.stats_yearly[i].combined_count) {
temperature_t avg_temp;
avg_temp.mkelvin = s.combined_temp.mkelvin / s.combined_count;
avg_temp.mkelvin = stats.stats_yearly[i].combined_temp.mkelvin / stats.stats_yearly[i].combined_count;
out << "\"AVG_TEMP\":\"" << get_temperature_string(avg_temp) << "\",";
} else {
out << "\"AVG_TEMP\":\"0.0\",";
}
out << "\"MIN_TEMP\":\"" << (s.min_temp.mkelvin == 0 ? 0 : get_temperature_string(s.min_temp)) << "\",";
out << "\"MAX_TEMP\":\"" << (s.max_temp.mkelvin == 0 ? 0 : get_temperature_string(s.max_temp)) << "\",";
out << "\"MIN_TEMP\":\"" << (stats.stats_yearly[i].min_temp.mkelvin == 0 ? 0 : get_temperature_string(stats.stats_yearly[i].min_temp)) << "\",";
out << "\"MAX_TEMP\":\"" << (stats.stats_yearly[i].max_temp.mkelvin == 0 ? 0 : get_temperature_string(stats.stats_yearly[i].max_temp)) << "\",";
out << "},";
total_stats.selection_size += s.selection_size;
total_stats.total_time.seconds += s.total_time.seconds;
total_stats.selection_size += stats.stats_yearly[i].selection_size;
total_stats.total_time.seconds += stats.stats_yearly[i].total_time.seconds;
i++;
}
exportHTMLstatisticsTotal(out, &total_stats);
}

53
core/divesite-helper.cpp Normal file
View File

@ -0,0 +1,53 @@
// 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 Normal file
View File

@ -0,0 +1,401 @@
// 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, &copy->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;
}

View File

@ -1,257 +0,0 @@
// 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;
}

View File

@ -2,39 +2,88 @@
#ifndef DIVESITE_H
#define DIVESITE_H
#include "divelist.h"
#include "taxonomy.h"
#include "units.h"
#include "taxonomy.h"
#include "divelist.h"
#include <stdlib.h>
#ifdef __cplusplus
#include <QString>
#include <QObject>
extern "C" {
#else
#include <stdbool.h>
#endif
struct dive_site
{
uint32_t uuid = 0;
std::string name;
std::vector<dive *> dives;
uint32_t uuid;
char *name;
struct dive_table dives;
location_t location;
std::string description;
std::string notes;
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);
char *description;
char *notes;
struct taxonomy_data taxonomy;
};
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);
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 */
#include <QObject>
Q_DECLARE_METATYPE(dive_site *);
#endif
#endif // DIVESITE_H

View File

@ -84,14 +84,14 @@ taxonomy_data reverseGeoLookup(degrees_t latitude, degrees_t longitude)
QString url;
QJsonObject obj;
taxonomy_data taxonomy;
taxonomy_data taxonomy = { 0, 0 };
// 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);
obj = doAsyncRESTGetRequest(url, 5000); // 5 secs. timeout
QVariantMap oceanName = obj.value("ocean").toVariant().toMap();
if (oceanName["name"].isValid())
taxonomy_set_category(taxonomy, TC_OCEAN, oceanName["name"].toString().toStdString(), taxonomy_origin::GEOCODED);
taxonomy_set_category(&taxonomy, TC_OCEAN, qPrintable(oceanName["name"].toString()), taxonomy_origin::GEOCODED);
// 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);
@ -110,16 +110,16 @@ taxonomy_data reverseGeoLookup(degrees_t latitude, degrees_t longitude)
for (int idx = TC_COUNTRY; idx < TC_NR_CATEGORIES; idx++) {
if (firstData[taxonomy_api_names[idx]].isValid()) {
QString value = firstData[taxonomy_api_names[idx]].toString();
taxonomy_set_category(taxonomy, (taxonomy_category)idx, value.toStdString(), taxonomy_origin::GEOCODED);
taxonomy_set_category(&taxonomy, (taxonomy_category)idx, qPrintable(value), taxonomy_origin::GEOCODED);
}
}
std::string l3 = taxonomy_get_value(taxonomy, TC_ADMIN_L3);
std::string lt = taxonomy_get_value(taxonomy, TC_LOCALNAME);
if (!l3.empty() && !lt.empty()) {
const char *l3 = taxonomy_get_value(&taxonomy, TC_ADMIN_L3);
const char *lt = taxonomy_get_value(&taxonomy, TC_LOCALNAME);
if (empty_string(l3) && !empty_string(lt)) {
// 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,
// 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 {
report_error("geonames.org did not provide reverse lookup information");

View File

@ -1,27 +0,0 @@
// 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

View File

@ -1,10 +1,8 @@
#include "downloadfromdcthread.h"
#include "core/errorhelper.h"
#include "core/format.h"
#include "core/libdivecomputer.h"
#include "core/qthelper.h"
#include "core/range.h"
#include "core/uemis.h"
#include "core/settings/qPrefDiveComputer.h"
#include "core/divelist.h"
#if defined(Q_OS_ANDROID)
@ -17,6 +15,16 @@ static QHash<QString, QStringList> mobileProductList; // BT, BLE or FTDI support
QMap<QString, dc_descriptor_t *> descriptorLookup;
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()
{
QString current = qPrefDiveComputer::vendor() + " - " + qPrefDiveComputer::product() + " - " + qPrefDiveComputer::device();
@ -81,7 +89,7 @@ void DownloadThread::run()
auto internalData = m_data->internalData();
internalData->descriptor = descriptorLookup[m_data->vendor().toLower() + m_data->product().toLower()];
internalData->log = &log;
internalData->btname = m_data->devBluetoothName().toStdString();
internalData->btname = strdup(m_data->devBluetoothName().toUtf8());
if (!internalData->descriptor) {
report_info("No download possible when DC type is unknown");
return;
@ -97,28 +105,27 @@ void DownloadThread::run()
report_info("Starting download from %s", qPrintable(getTransportString(transports)));
report_info("downloading %s dives", internalData->force_download ? "all" : "only new");
log.clear();
clear_divelog(&log);
Q_ASSERT(internalData->log != nullptr);
std::string errorText;
const char *errorText;
import_thread_cancelled = false;
error.clear();
if (internalData->vendor == "Uemis")
if (!strcmp(internalData->vendor, "Uemis"))
errorText = do_uemis_import(internalData);
else
errorText = do_libdivecomputer_import(internalData);
if (!errorText.empty()) {
error = format_string_std(errorText.c_str(), internalData->devname.c_str(),
internalData->vendor.c_str(), internalData->product.c_str());
report_info("Finishing download thread: %s", error.c_str());
if (errorText) {
error = str_error(errorText, internalData->devname, internalData->vendor, internalData->product);
report_info("Finishing download thread: %s", qPrintable(error));
} else {
if (log.dives.empty())
error = tr("No new dives downloaded from dive computer").toStdString();
report_info("Finishing download thread: %d dives downloaded", static_cast<int>(log.dives.size()));
if (!log.dives->nr)
error = tr("No new dives downloaded from dive computer");
report_info("Finishing download thread: %d dives downloaded", log.dives->nr);
}
qPrefDiveComputer::set_vendor(internalData->vendor.c_str());
qPrefDiveComputer::set_product(internalData->product.c_str());
qPrefDiveComputer::set_device(internalData->devname.c_str());
qPrefDiveComputer::set_vendor(internalData->vendor);
qPrefDiveComputer::set_product(internalData->product);
qPrefDiveComputer::set_device(internalData->devname);
qPrefDiveComputer::set_device_name(m_data->devBluetoothName());
updateRememberedDCs();
@ -202,6 +209,7 @@ void show_computer_list()
DCDeviceData::DCDeviceData()
{
memset(&data, 0, sizeof(data));
data.log = nullptr;
data.diveid = 0;
#if defined(BT_SUPPORT)
@ -233,8 +241,9 @@ QStringList DCDeviceData::getProductListFromVendor(const QString &vendor)
return productList[vendor];
}
int DCDeviceData::getMatchingAddress(const QString &, const QString &product)
int DCDeviceData::getMatchingAddress(const QString &vendor, const QString &product)
{
Q_UNUSED(vendor)
return connectionListModel.indexOf(product);
}
@ -245,17 +254,17 @@ DCDeviceData *DownloadThread::data()
QString DCDeviceData::vendor() const
{
return QString::fromStdString(data.vendor);
return data.vendor;
}
QString DCDeviceData::product() const
{
return QString::fromStdString(data.product);
return data.product;
}
QString DCDeviceData::devName() const
{
return QString::fromStdString(data.devname);
return data.devname;
}
QString DCDeviceData::devBluetoothName() const
@ -290,12 +299,12 @@ bool DCDeviceData::syncTime() const
void DCDeviceData::setVendor(const QString &vendor)
{
data.vendor = vendor.toStdString();
data.vendor = copy_qstring(vendor);
}
void DCDeviceData::setProduct(const QString &product)
{
data.product = product.toStdString();
data.product = copy_qstring(product);
}
void DCDeviceData::setDevName(const QString &devName)
@ -312,11 +321,11 @@ void DCDeviceData::setDevName(const QString &devName)
QString back = devName.mid(idx1 + 1, idx2 - idx1 - 1);
QString newDevName = back.indexOf(':') >= 0 ? back : front;
qWarning() << "Found invalid bluetooth device" << devName << "corrected to" << newDevName << ".";
data.devname = newDevName.toStdString();
data.devname = copy_qstring(newDevName);
return;
}
}
data.devname = devName.toStdString();
data.devname = copy_qstring(devName);
}
#if defined(Q_OS_ANDROID)

View File

@ -74,7 +74,7 @@ public:
void run() override;
DCDeviceData *data();
std::string error;
QString error;
struct divelog log;
private:

591
core/equipment.c Normal file
View File

@ -0,0 +1,591 @@
// 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

View File

@ -1,516 +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.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

View File

@ -4,85 +4,95 @@
#include "gas.h"
#include <memory>
#include <string>
#include <vector>
#ifdef __cplusplus
extern "C" {
#endif
struct dive;
enum cylinderuse {OC_GAS, DILUENT, OXYGEN, NOT_USED, NUM_GAS_USE}; // The different uses for cylinders
extern const char *cylinderuse_text[NUM_GAS_USE];
struct cylinder_type_t
typedef struct
{
volume_t size;
pressure_t workingpressure;
std::string description; /* "LP85", "AL72", "AL80", "HP100+" or whatever */
};
const char *description; /* "LP85", "AL72", "AL80", "HP100+" or whatever */
} cylinder_type_t;
struct cylinder_t
typedef struct
{
cylinder_type_t type;
struct gasmix gasmix = gasmix_air;
struct gasmix gasmix;
pressure_t start, end, sample_start, sample_end;
depth_t depth;
bool manually_added = false;
bool manually_added;
volume_t gas_used;
volume_t deco_gas_used;
enum cylinderuse cylinder_use = OC_GAS;
bool bestmix_o2 = false;
bool bestmix_he = false;
enum cylinderuse cylinder_use;
bool bestmix_o2;
bool bestmix_he;
} cylinder_t;
cylinder_t();
~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 };
/* 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;
};
/* 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
typedef struct
{
weight_t weight;
std::string description; /* "integrated", "belt", "ankle" */
bool auto_filled = false; /* weight was automatically derived from the type */
const char *description; /* "integrated", "belt", "ankle" */
bool auto_filled; /* weight was automatically derived from the type */
} weightsystem_t;
weightsystem_t();
weightsystem_t(weight_t w, std::string desc, bool auto_filled);
~weightsystem_t();
};
static const weightsystem_t empty_weightsystem = { { 0 }, 0, false };
/* 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>;
* *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)
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 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 *get_cylinder(struct dive *d, int idx);
extern const cylinder_t *get_cylinder(const struct dive *d, int idx);
extern void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl);
extern cylinder_t *get_cylinder(const 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 void remove_cylinder(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 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 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 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 void add_default_cylinder(struct dive *dive);
extern int first_hidden_cylinder(const struct dive *d);
@ -91,34 +101,44 @@ extern void dump_cylinders(struct dive *dive, bool verbose);
#endif
/* Weightsystem table functions */
extern void add_to_weightsystem_table(weightsystem_table *, int idx, weightsystem_t ws);
extern void clear_weightsystem_table(struct weightsystem_table *);
extern void add_to_weightsystem_table(struct weightsystem_table *, int idx, weightsystem_t ws);
/* 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 append_cylinder(struct cylinder_table *, cylinder_t cyl);
void get_gas_string(struct gasmix gasmix, char *text, int len);
const char *gasname(struct gasmix gasmix);
struct ws_info {
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;
typedef struct tank_info {
const char *name;
int cuft, ml, psi, bar;
} tank_info_t;
struct tank_info_table {
int nr, allocated;
struct tank_info *infos;
};
extern std::vector<tank_info> tank_info_table;
extern struct tank_info *get_tank_info(std::vector<tank_info> &table, const std::string &name);
extern void set_tank_info_data(std::vector<tank_info> &table, const std::string &name, volume_t size, pressure_t working_pressure);
extern std::pair<volume_t, pressure_t> extract_tank_info(const struct tank_info &info);
extern std::pair<volume_t, pressure_t> get_tank_info_data(const std::vector<tank_info> &table, const std::string &name);
extern void add_cylinder_description(const cylinder_type_t &);
extern void reset_tank_info_table(std::vector<tank_info> &table);
extern struct tank_info_table tank_info_table;
extern void reset_tank_info_table(struct tank_info_table *table);
extern void clear_tank_info_table(struct tank_info_table *table);
extern void add_tank_info_metric(struct tank_info_table *table, const char *name, int ml, int bar);
extern void add_tank_info_imperial(struct tank_info_table *table, const char *name, int cuft, int psi);
extern void extract_tank_info(const struct tank_info *info, volume_t *size, pressure_t *working_pressure);
extern bool get_tank_info_data(struct tank_info_table *table, const char *name, volume_t *size, pressure_t *pressure);
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

View File

@ -1,44 +1,50 @@
// SPDX-License-Identifier: GPL-2.0
#include "errorhelper.h"
#include "format.h"
#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 "membuffer.h"
#if !defined(Q_OS_ANDROID) && !defined(__ANDROID__)
#define LOG_MSG(fmt, s) fprintf(stderr, fmt, s)
#define LOG_MSG(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
#else
#include <android/log.h>
#define LOG_MSG(fmt, s) __android_log_print(ANDROID_LOG_INFO, "Subsurface", fmt, s);
#define LOG_MSG(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "Subsurface", fmt, ##__VA_ARGS__);
#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;
void report_info(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
std::string s = vformat_string_std(fmt, args);
va_end(args);
LOG_MSG("INFO: %s\n", s.c_str());
struct membufferpp buf;
VA_BUF(&buf, fmt);
strip_mb(&buf);
LOG_MSG("INFO: %s\n", mb_cstring(&buf));
}
static void (*error_cb)(std::string) = NULL;
static void (*error_cb)(char *) = NULL;
int report_error(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
std::string s = vformat_string_std(fmt, args);
va_end(args);
LOG_MSG("ERROR: %s\n", s.c_str());
struct membufferpp buf;
VA_BUF(&buf, fmt);
strip_mb(&buf);
LOG_MSG("ERROR: %s\n", mb_cstring(&buf));
/* if there is no error callback registered, don't produce errors */
if (error_cb)
error_cb(std::move(s));
if (!error_cb)
return -1;
error_cb(detach_cstring(&buf));
return -1;
}
void set_error_cb(void(*cb)(std::string))
void set_error_cb(void(*cb)(char *))
{
error_cb = cb;
}

View File

@ -4,7 +4,9 @@
// error reporting functions
#include <string>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __GNUC__
#define __printf(x, y) __attribute__((__format__(__printf__, x, y)))
@ -15,6 +17,11 @@
extern int verbose;
extern int __printf(1, 2) report_error(const char *fmt, ...);
extern void __printf(1, 2) report_info(const char *fmt, ...);
extern void set_error_cb(void(*cb)(std::string s)); // Callback takes ownership of passed string
extern void set_error_cb(void(*cb)(char *)); // Callback takes ownership of passed string
#ifdef __cplusplus
}
#endif
#endif

118
core/event.c Normal file
View File

@ -0,0 +1,118 @@
// 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;
}
}

View File

@ -1,112 +0,0 @@
// 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);
}

View File

@ -6,10 +6,11 @@
#include "gas.h"
#include "units.h"
#include <string>
#include <libdivecomputer/parser.h>
struct divecomputer;
#ifdef __cplusplus
extern "C" {
#endif
enum event_severity {
EVENT_SEVERITY_NONE = 0,
@ -22,8 +23,8 @@ enum event_severity {
* 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.
*/
struct event {
struct event *next;
duration_t time;
int type;
/* This is the annoying libdivecomputer format. */
@ -41,53 +42,26 @@ struct event {
struct gasmix mix;
} gas;
};
bool deleted; // used internally in the parser and in fixup_dive().
bool hidden;
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;
char name[];
};
class event_loop
{
std::string name;
size_t idx;
public:
event_loop(const char *name);
struct event *next(struct divecomputer &dc); // nullptr -> end
const struct event *next(const struct divecomputer &dc); // nullptr -> end
};
extern int event_is_gaschange(const struct event *ev);
extern bool event_is_divemodechange(const struct event *ev);
extern struct event *clone_event(const struct event *src_ev);
extern void free_events(struct event *ev);
extern struct event *create_event(unsigned int time, int type, int flags, int value, const char *name);
extern struct event *clone_event_rename(const struct event *ev, const char *name);
extern bool same_event(const struct event *a, const struct event *b);
extern enum event_severity get_event_severity(const struct event *ev);
/* Get gasmixes at increasing timestamps. */
class gasmix_loop {
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);
};
/* Since C doesn't have parameter-based overloading, two versions of get_next_event. */
extern const struct event *get_next_event(const struct event *event, const char *name);
extern struct event *get_next_event_mutable(struct event *event, const char *name);
/* Get divemodes at increasing timestamps. */
class divemode_loop {
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);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -14,7 +14,7 @@ struct event_type {
bool plot;
event_type(const struct event *ev) :
name(ev->name),
severity(ev->get_severity()),
severity(get_event_severity(ev)),
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;
}
void clear_event_types()
extern "C" void clear_event_types()
{
event_types.clear();
}
void remember_event_type(const struct event *ev)
extern "C" void remember_event_type(const struct event *ev)
{
if (ev->name.empty())
if (empty_string(ev->name))
return;
event_type type(ev);
if (std::find(event_types.begin(), event_types.end(), type) != event_types.end())
@ -42,33 +42,33 @@ void remember_event_type(const struct event *ev)
event_types.push_back(std::move(type));
}
bool is_event_type_hidden(const struct event *ev)
extern "C" bool is_event_type_hidden(const struct event *ev)
{
auto it = std::find(event_types.begin(), event_types.end(), ev);
return it != event_types.end() && !it->plot;
}
void hide_event_type(const struct event *ev)
extern "C" void hide_event_type(const struct event *ev)
{
auto it = std::find(event_types.begin(), event_types.end(), ev);
if (it != event_types.end())
it->plot = false;
}
void show_all_event_types()
extern "C" void show_all_event_types()
{
for (event_type &e: event_types)
e.plot = true;
}
void show_event_type(int idx)
extern "C" void show_event_type(int idx)
{
if (idx < 0 || idx >= (int)event_types.size())
return;
event_types[idx].plot = true;
}
bool any_event_types_hidden()
extern "C" bool any_event_types_hidden()
{
return std::any_of(event_types.begin(), event_types.end(),
[] (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);
}
QString event_type_name(const event &ev)
QString event_type_name(const event *ev)
{
if (ev.name.empty())
if (!ev || empty_string(ev->name))
return QString();
QString name = QString::fromStdString(ev.name);
return event_type_name(std::move(name), ev.get_severity());
QString name = QString::fromUtf8(ev->name);
return event_type_name(std::move(name), get_event_severity(ev));
}
QString event_type_name(int idx)

View File

@ -3,18 +3,29 @@
#ifndef EVENTNAME_H
#define EVENTNAME_H
#include <vector>
#include <QString>
#ifdef __cplusplus
extern "C" {
#endif
extern void clear_event_types();
extern void clear_event_types(void);
extern void remember_event_type(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 show_all_event_types();
extern void show_event_type(int idx);
extern bool any_event_types_hidden();
#ifdef __cplusplus
}
// C++-only functions
#include <vector>
#include <QString>
extern std::vector<int> hidden_event_types();
extern QString event_type_name(const event &ev);
extern QString event_type_name(int idx);
QString event_type_name(const event *ev);
QString event_type_name(int idx);
#endif
#endif

View File

@ -2,11 +2,10 @@
#ifndef EXTRADATA_H
#define EXTRADATA_H
#include <string>
struct extra_data {
std::string key;
std::string value;
const char *key;
const char *value;
struct extra_data *next;
};
#endif

View File

@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
#include "ssrf.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
@ -77,7 +78,7 @@ static void zip_read(struct zip_file *file, const char *filename, struct divelog
(void) parse_xml_buffer(filename, mem.data(), read, log, NULL);
}
int try_to_open_zip(const char *filename, struct divelog *log)
extern "C" int try_to_open_zip(const char *filename, struct divelog *log)
{
int success = 0;
/* Grr. libzip needs to re-open the file, it can't take a buffer */
@ -267,7 +268,7 @@ bool remote_repo_uptodate(const char *filename, struct git_info *info)
return false;
}
int parse_file(const char *filename, struct divelog *log)
extern "C" int parse_file(const char *filename, struct divelog *log)
{
struct git_info info;
const char *fmt;

View File

@ -6,12 +6,13 @@
#include <sys/stat.h>
#include <stdio.h>
#include <vector>
#include <utility>
struct divelog;
struct zip;
#ifdef __cplusplus
extern "C" {
#endif
extern void ostctools_import(const char *file, struct divelog *log);
extern int parse_file(const char *filename, struct divelog *log);
@ -27,9 +28,22 @@ extern int subsurface_access(const char *path, int mode);
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 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_liquivision(const char *filename, std::string &mem, struct divelog *log);
extern int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log);
#endif
#endif // FILE_H

View File

@ -134,87 +134,87 @@ static const range_mode_description *get_range_mode_description(enum filter_cons
return nullptr;
}
static enum filter_constraint_type filter_constraint_type_from_string(const std::string &s)
static enum filter_constraint_type filter_constraint_type_from_string(const char *s)
{
for (const auto &desc: type_descriptions) {
if (desc.token == s)
if (same_string(desc.token, s))
return desc.type;
}
report_error("unknown filter constraint type: %s", s.c_str());
report_error("unknown filter constraint type: %s", s);
return FILTER_CONSTRAINT_DATE;
}
static enum filter_constraint_string_mode filter_constraint_string_mode_from_string(const std::string &s)
static enum filter_constraint_string_mode filter_constraint_string_mode_from_string(const char *s)
{
for (const auto &desc: string_mode_descriptions) {
if (desc.token == s)
if (same_string(desc.token, s))
return desc.mode;
}
report_error("unknown filter constraint string mode: %s", s.c_str());
report_error("unknown filter constraint string mode: %s", s);
return FILTER_CONSTRAINT_EXACT;
}
static enum filter_constraint_range_mode filter_constraint_range_mode_from_string(const std::string &s)
static enum filter_constraint_range_mode filter_constraint_range_mode_from_string(const char *s)
{
for (const auto &desc: range_mode_descriptions) {
if (desc.token == s)
if (same_string(desc.token, s))
return desc.mode;
}
report_error("unknown filter constraint range mode: %s", s.c_str());
report_error("unknown filter constraint range mode: %s", s);
return FILTER_CONSTRAINT_EQUAL;
}
const char *filter_constraint_type_to_string(enum filter_constraint_type type)
extern "C" const char *filter_constraint_type_to_string(enum filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc ? desc->token : "unknown";
}
const char *filter_constraint_string_mode_to_string(enum filter_constraint_string_mode mode)
extern "C" const char *filter_constraint_string_mode_to_string(enum filter_constraint_string_mode mode)
{
const string_mode_description *desc = get_string_mode_description(mode);
return desc ? desc->token : "unknown";
}
const char *filter_constraint_range_mode_to_string(enum filter_constraint_range_mode mode)
extern "C" const char *filter_constraint_range_mode_to_string(enum filter_constraint_range_mode mode)
{
const range_mode_description *desc = get_range_mode_description(mode);
return desc ? desc->token : "unknown";
}
int filter_constraint_type_to_index(enum filter_constraint_type type)
extern "C" int filter_constraint_type_to_index(enum filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc ? desc - type_descriptions : -1;
}
int filter_constraint_string_mode_to_index(enum filter_constraint_string_mode mode)
extern "C" int filter_constraint_string_mode_to_index(enum filter_constraint_string_mode mode)
{
const string_mode_description *desc = get_string_mode_description(mode);
return desc ? desc - string_mode_descriptions : -1;
}
int filter_constraint_range_mode_to_index(enum filter_constraint_range_mode mode)
extern "C" int filter_constraint_range_mode_to_index(enum filter_constraint_range_mode mode)
{
const range_mode_description *desc = get_range_mode_description(mode);
return desc ? desc - range_mode_descriptions : -1;
}
enum filter_constraint_type filter_constraint_type_from_index(int index)
extern "C" enum filter_constraint_type filter_constraint_type_from_index(int index)
{
if (index >= 0 && index < (int)std::size(type_descriptions))
return type_descriptions[index].type;
return (enum filter_constraint_type)-1;
}
enum filter_constraint_string_mode filter_constraint_string_mode_from_index(int index)
extern "C" enum filter_constraint_string_mode filter_constraint_string_mode_from_index(int index)
{
if (index >= 0 && index < (int)std::size(string_mode_descriptions))
return string_mode_descriptions[index].mode;
return (enum filter_constraint_string_mode)-1;
}
enum filter_constraint_range_mode filter_constraint_range_mode_from_index(int index)
extern "C" enum filter_constraint_range_mode filter_constraint_range_mode_from_index(int index)
{
if (index >= 0 && index < (int)std::size(range_mode_descriptions))
return range_mode_descriptions[index].mode;
@ -390,7 +390,7 @@ QStringList filter_contraint_multiple_choice_translated(enum filter_constraint_t
return QStringList();
}
bool filter_constraint_is_string(filter_constraint_type type)
extern "C" bool filter_constraint_is_string(filter_constraint_type type)
{
// 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
@ -398,7 +398,7 @@ bool filter_constraint_is_string(filter_constraint_type type)
return filter_constraint_has_string_mode(type);
}
bool filter_constraint_is_timestamp(filter_constraint_type type)
extern "C" bool filter_constraint_is_timestamp(filter_constraint_type type)
{
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);
}
bool filter_constraint_has_string_mode(enum filter_constraint_type type)
extern "C" bool filter_constraint_has_string_mode(enum filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc && desc->has_string_mode;
}
bool filter_constraint_has_range_mode(enum filter_constraint_type type)
extern "C" bool filter_constraint_has_range_mode(enum filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc && desc->has_range_mode;
}
bool filter_constraint_is_star(filter_constraint_type type)
extern "C" bool filter_constraint_is_star(filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc && desc->is_star_widget;
}
bool filter_constraint_has_date_widget(filter_constraint_type type)
extern "C" bool filter_constraint_has_date_widget(filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc && desc->has_date;
}
bool filter_constraint_has_time_widget(filter_constraint_type type)
extern "C" bool filter_constraint_has_time_widget(filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc && desc->has_time;
}
int filter_constraint_num_decimals(enum filter_constraint_type type)
extern "C" int filter_constraint_num_decimals(enum filter_constraint_type type)
{
const type_description *desc = get_type_description(type);
return desc ? desc->decimal_places : 1;
@ -446,7 +446,7 @@ int filter_constraint_num_decimals(enum filter_constraint_type type)
// String constraints are valid if there is at least one term.
// Other constraints are always valid.
bool filter_constraint_is_valid(const struct filter_constraint *constraint)
extern "C" bool filter_constraint_is_valid(const struct filter_constraint *constraint)
{
if (!filter_constraint_is_string(constraint->type))
return true;
@ -551,14 +551,14 @@ filter_constraint::filter_constraint(const filter_constraint &c) :
data.numerical_range = c.data.numerical_range;
}
filter_constraint::filter_constraint(const std::string &type_in, const std::string &string_mode_in,
const std::string &range_mode_in, bool negate_in, const std::string &s_in) :
filter_constraint::filter_constraint(const char *type_in, const char *string_mode_in,
const char *range_mode_in, bool negate_in, const char *s_in) :
type(filter_constraint_type_from_string(type_in)),
string_mode(FILTER_CONSTRAINT_STARTS_WITH),
range_mode(FILTER_CONSTRAINT_GREATER),
negate(negate_in)
{
QString s = QString::fromStdString(s_in);
QString s(s_in);
if (filter_constraint_has_string_mode(type))
string_mode = filter_constraint_string_mode_from_string(string_mode_in);
if (filter_constraint_has_range_mode(type))
@ -622,22 +622,22 @@ filter_constraint::~filter_constraint()
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)) {
std::string from_s = format_datetime(c.data.timestamp_range.from);
std::string to_s = format_datetime(c.data.timestamp_range.to);
if (filter_constraint_is_timestamp(c->type)) {
std::string from_s = format_datetime(c->data.timestamp_range.from);
std::string to_s = format_datetime(c->data.timestamp_range.to);
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 ",".
// That is currently not supported by the UI, but one day we might
// have to escape the strings.
return c.data.string_list->join(",").toStdString();
} else if (filter_constraint_is_multiple_choice(c.type)) {
return std::to_string(c.data.multiple_choice);
return c->data.string_list->join(",").toStdString();
} else if (filter_constraint_is_multiple_choice(c->type)) {
return std::to_string(c->data.multiple_choice);
} else {
return std::to_string(c.data.numerical_range.from) + ',' +
std::to_string(c.data.numerical_range.to);
return std::to_string(c->data.numerical_range.from) + ',' +
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)
{
QStringList dive_tags;
for (const divetag *tag: d->tags)
dive_tags.push_back(QString::fromStdString(tag->name).trimmed());
dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dcs[0].divemode]).trimmed());
for (const tag_entry *tag = d->tag_list; tag; tag = tag->next)
dive_tags.push_back(QString::fromStdString(tag->tag->name).trimmed());
dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dc.divemode]).trimmed());
return check(c, dive_tags);
}
static bool has_people(const filter_constraint &c, const struct dive *d)
{
QStringList dive_people;
for (const QString &s: QString::fromStdString(d->buddy).split(",", SKIP_EMPTY))
for (const QString &s: QString(d->buddy).split(",", SKIP_EMPTY))
dive_people.push_back(s.trimmed());
for (const QString &s: QString::fromStdString(d->diveguide).split(",", SKIP_EMPTY))
for (const QString &s: QString(d->diveguide).split(",", SKIP_EMPTY))
dive_people.push_back(s.trimmed());
return check(c, dive_people);
}
@ -838,10 +838,10 @@ static bool has_locations(const filter_constraint &c, const struct dive *d)
{
QStringList diveLocations;
if (d->divetrip)
diveLocations.push_back(QString::fromStdString(d->divetrip->location).trimmed());
diveLocations.push_back(QString(d->divetrip->location).trimmed());
if (d->dive_site)
diveLocations.push_back(QString::fromStdString(d->dive_site->name).trimmed());
diveLocations.push_back(QString(d->dive_site->name).trimmed());
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)
{
QStringList weightsystemTypes;
for (auto &ws: d->weightsystems)
weightsystemTypes.push_back(QString::fromStdString(ws.description));
for (int i = 0; i < d->weightsystems.nr; ++i)
weightsystemTypes.push_back(d->weightsystems.weightsystems[i].description);
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)
{
QStringList cylinderTypes;
for (const cylinder_t &cyl: d->cylinders)
cylinderTypes.push_back(QString::fromStdString(cyl.type.description));
for (int i = 0; i < d->cylinders.nr; ++i)
cylinderTypes.push_back(d->cylinders.cylinders[i].type.description);
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)
{
QStringList diveSuits;
if (!d->suit.empty())
diveSuits.push_back(QString::fromStdString(d->suit));
if (d->suit)
diveSuits.push_back(QString(d->suit));
return check(c, diveSuits);
}
static bool has_notes(const filter_constraint &c, const struct dive *d)
{
QStringList diveNotes;
if (!d->notes.empty())
diveNotes.push_back(QString::fromStdString(d->notes));
if (d->notes)
diveNotes.push_back(QString(d->notes));
return check(c, diveNotes);
}
@ -905,15 +905,22 @@ 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)
{
return std::any_of(d->cylinders.begin(), d->cylinders.end(), [&c](auto &cyl)
{ return cyl.type.size.mliter &&
check_numerical_range(c, cyl.type.size.mliter); });
for (int i = 0; i < d->cylinders.nr; ++i) {
const cylinder_t &cyl = d->cylinders.cylinders[i];
if (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)
{
return std::any_of(d->cylinders.begin(), d->cylinders.end(), [&c, &component](auto &cyl)
{ return check_numerical_range(c, get_gas_component_fraction(cyl.gasmix, component).permille); });
for (int i = 0; i < d->cylinders.nr; ++i) {
const cylinder_t &cyl = d->cylinders.cylinders[i];
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)
@ -952,12 +959,12 @@ static bool check_datetime_range(const filter_constraint &c, const struct dive *
// where the given timestamp is during that dive.
return time_during_dive_with_offset(d, c.data.timestamp_range.from, 0) != c.negate;
case FILTER_CONSTRAINT_LESS:
return (d->endtime() <= c.data.timestamp_range.to) != c.negate;
return (dive_endtime(d) <= c.data.timestamp_range.to) != c.negate;
case FILTER_CONSTRAINT_GREATER:
return (d->when >= c.data.timestamp_range.from) != c.negate;
case FILTER_CONSTRAINT_RANGE:
return (d->when >= c.data.timestamp_range.from &&
d->endtime() <= c.data.timestamp_range.to) != c.negate;
dive_endtime(d) <= c.data.timestamp_range.to) != c.negate;
}
return false;
}
@ -972,14 +979,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
// that run past midnight. We might want to special case that.
return (seconds_since_midnight(d->when) <= from &&
seconds_since_midnight(d->endtime()) >= from) != negate;
seconds_since_midnight(dive_endtime(d)) >= from) != negate;
case FILTER_CONSTRAINT_LESS:
return (seconds_since_midnight(d->endtime()) <= to) != negate;
return (seconds_since_midnight(dive_endtime(d)) <= to) != negate;
case FILTER_CONSTRAINT_GREATER:
return (seconds_since_midnight(d->when) >= from) != negate;
case FILTER_CONSTRAINT_RANGE:
return (seconds_since_midnight(d->when) >= from &&
seconds_since_midnight(d->endtime()) <= to) != negate;
seconds_since_midnight(dive_endtime(d)) <= to) != negate;
}
return false;
}
@ -1067,11 +1074,11 @@ bool filter_constraint_match_dive(const filter_constraint &c, const struct dive
case FILTER_CONSTRAINT_SAC:
return check_numerical_range_non_zero(c, d->sac);
case FILTER_CONSTRAINT_LOGGED:
return d->is_logged() != c.negate;
return is_logged(d) != c.negate;
case FILTER_CONSTRAINT_PLANNED:
return d->is_planned() != c.negate;
return is_planned(d) != c.negate;
case FILTER_CONSTRAINT_DIVE_MODE:
return check_multiple_choice(c, (int)d->dcs[0].divemode); // should we be smarter and check all DCs?
return check_multiple_choice(c, (int)d->dc.divemode); // should we be smarter and check all DCs?
case FILTER_CONSTRAINT_TAGS:
return has_tags(c, d);
case FILTER_CONSTRAINT_PEOPLE:

View File

@ -5,10 +5,16 @@
#define FILTER_CONSTRAINT_H
#include "units.h"
#include <QStringList>
struct dive;
#ifdef __cplusplus
#include <QStringList>
extern "C" {
#else
typedef void QStringList;
#endif
enum filter_constraint_type {
FILTER_CONSTRAINT_DATE,
FILTER_CONSTRAINT_DATE_TIME,
@ -76,14 +82,16 @@ struct filter_constraint {
QStringList *string_list;
uint64_t multiple_choice; // bit-field for multiple choice lists. currently, we support 64 items, extend if needed.
} data;
#ifdef __cplusplus
// For C++, define constructors, assignment operators and destructor to make our lives easier.
filter_constraint(filter_constraint_type type);
filter_constraint(const std::string &type, const std::string &string_mode,
const std::string &range_mode, bool negate, const std::string &data); // from parser data
filter_constraint(const char *type, const char *string_mode,
const char *range_mode, bool negate, const char *data); // from parser data
filter_constraint(const filter_constraint &);
filter_constraint &operator=(const filter_constraint &);
~filter_constraint();
bool operator==(const filter_constraint &f2) const;
#endif
};
extern const char *filter_constraint_type_to_string(enum filter_constraint_type);
@ -109,6 +117,12 @@ extern bool filter_constraint_has_time_widget(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);
#ifdef __cplusplus
}
#endif
// C++ only functions
#ifdef __cplusplus
QString filter_constraint_type_to_string_translated(enum filter_constraint_type);
QString filter_constraint_negate_to_string_translated(bool negate);
QString filter_constraint_string_mode_to_string_translated(enum filter_constraint_string_mode);
@ -137,6 +151,8 @@ 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_multiple_choice(filter_constraint &c, uint64_t);
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

View File

@ -4,14 +4,24 @@
#include "qthelper.h"
#include "subsurface-string.h"
std::string filter_preset::fulltext_query() const
static filter_preset_table &global_table()
{
return data.fullText.originalQuery.toStdString();
return *divelog.filter_presets;
}
const char *filter_preset::fulltext_mode() const
extern "C" int filter_presets_count(void)
{
switch (data.fulltextStringMode) {
return (int)global_table().size();
}
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:
case StringFilterMode::SUBSTRING:
return "substring";
@ -22,19 +32,98 @@ const char *filter_preset::fulltext_mode() const
}
}
void filter_preset::set_fulltext(const std::string fulltext, const std::string &fulltext_string_mode)
extern "C" void filter_preset_set_fulltext(struct filter_preset *preset, const char *fulltext, const char *fulltext_string_mode)
{
if (fulltext_string_mode == "substring")
data.fulltextStringMode = StringFilterMode::SUBSTRING;
else if (fulltext_string_mode == "startswith")
data.fulltextStringMode = StringFilterMode::STARTSWITH;
else // if (fulltext_string_mode == "exact"))
data.fulltextStringMode = StringFilterMode::EXACT;
data.fullText = QString::fromStdString(std::move(fulltext));
if (same_string(fulltext_string_mode, "substring"))
preset->data.fulltextStringMode = StringFilterMode::SUBSTRING;
else if (same_string(fulltext_string_mode, "startswith"))
preset->data.fulltextStringMode = StringFilterMode::STARTSWITH;
else // if (same_string(fulltext_string_mode, "exact"))
preset->data.fulltextStringMode = StringFilterMode::EXACT;
preset->data.fullText = fulltext;
}
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)
extern "C" int filter_preset_constraint_count(int preset)
{
data.constraints.emplace_back(type, string_mode, range_mode, negate, constraint_data);
return (int)global_table()[preset].data.constraints.size();
}
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);
}

View File

@ -1,23 +1,75 @@
// 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
#define FILTER_PRESETS_H
#include "divefilter.h"
#include <string>
struct dive;
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 {
std::string name;
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

View File

@ -1,53 +0,0 @@
// 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);
}

View File

@ -1,25 +0,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.
#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

View File

@ -353,27 +353,20 @@ std::string casprintf_loc(const char *cformat, ...)
return std::string(utf8.constData(), utf8.size());
}
std::string format_string_std(const char *fmt, ...)
std::string __printf(1, 2) format_string_std(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
std::string res = vformat_string_std(fmt, ap);
size_t stringsize = vsnprintf(NULL, 0, fmt, 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)
return std::string();
std::string res;
res.resize(stringsize); // Pointless clearing, oh my.
// This overwrites the terminal null-byte of std::string.
// That's probably "undefined behavior". Oh my.
va_start(ap, fmt);
vsnprintf(res.data(), stringsize + 1, fmt, ap);
va_end(ap);
return res;
}

View File

@ -7,11 +7,12 @@
#define __printf(x, y)
#endif
#ifdef __cplusplus
#include <QString>
__printf(1, 2) QString qasprintf_loc(const char *cformat, ...);
__printf(1, 0) QString vqasprintf_loc(const char *cformat, va_list ap);
__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, ...);
#endif
#endif

View File

@ -10,6 +10,11 @@
#include <QLocale>
#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
class FullText {
std::map<QString, std::vector<dive *>> words; // Dives that belong to each word
@ -30,6 +35,8 @@ static FullText self;
// C-interface functions
extern "C" {
void fulltext_register(struct dive *d)
{
self.registerDive(d);
@ -50,6 +57,8 @@ void fulltext_populate()
self.populate();
}
} // extern "C"
// C++-only interface functions
FullTextResult fulltext_find_dives(const FullTextQuery &q, StringFilterMode mode)
{
@ -114,27 +123,31 @@ static void tokenize(QString s, std::vector<QString> &res)
static std::vector<QString> getWords(const dive *d)
{
std::vector<QString> res;
tokenize(QString::fromStdString(d->notes), res);
tokenize(QString::fromStdString(d->diveguide), res);
tokenize(QString::fromStdString(d->buddy), res);
tokenize(QString::fromStdString(d->suit), res);
for (const divetag *tag: d->tags)
tokenize(QString::fromStdString(tag->name), res);
for (auto &cyl: d->cylinders)
tokenize(QString::fromStdString(cyl.type.description), res);
for (auto &ws: d->weightsystems)
tokenize(QString::fromStdString(ws.description), res);
tokenize(QString(d->notes), res);
tokenize(QString(d->diveguide), res);
tokenize(QString(d->buddy), res);
tokenize(QString(d->suit), res);
for (const tag_entry *tag = d->tag_list; tag; tag = tag->next)
tokenize(QString::fromStdString(tag->tag->name), res);
for (int i = 0; i < d->cylinders.nr; ++i) {
const cylinder_t &cyl = *get_cylinder(d, i);
tokenize(QString(cyl.type.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
// take the tokens from a cache.
if (d->dive_site) {
tokenize(QString::fromStdString(d->dive_site->name), res);
std::string country = taxonomy_get_country(d->dive_site->taxonomy);
if (!country.empty())
tokenize(country.c_str(), res);
tokenize(d->dive_site->name, res);
const char *country = taxonomy_get_country(&d->dive_site->taxonomy);
if (country)
tokenize(country, res);
}
// TODO: We should index trips separately!
if (d->divetrip)
tokenize(QString::fromStdString(d->divetrip->location), res);
tokenize(d->divetrip->location, res);
return res;
}
@ -143,9 +156,11 @@ void FullText::populate()
// 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("start processing"));
for (auto &d: divelog.dives)
registerDive(d.get());
uiNotification(QObject::tr("%1 dives processed").arg(divelog.dives.size()));
int i;
dive *d;
for_each_dive(i, d)
registerDive(d);
uiNotification(QObject::tr("%1 dives processed").arg(divelog.dives->nr));
}
void FullText::registerDive(struct dive *d)
@ -153,7 +168,7 @@ void FullText::registerDive(struct dive *d)
if (d->full_text)
unregisterWords(d, d->full_text->words);
else
d->full_text = std::make_unique<full_text_cache>();
d->full_text = new full_text_cache;
d->full_text->words = getWords(d);
registerWords(d, d->full_text->words);
}
@ -163,13 +178,18 @@ void FullText::unregisterDive(struct dive *d)
if (!d->full_text)
return;
unregisterWords(d, d->full_text->words);
d->full_text.reset();
delete d->full_text;
d->full_text = nullptr;
}
void FullText::unregisterAll()
{
for (auto &d: divelog.dives)
d->full_text.reset();
int i;
dive *d;
for_each_dive(i, d) {
delete d->full_text;
d->full_text = nullptr;
}
words.clear();
}

View File

@ -5,30 +5,43 @@
// issues such as COW semantics and UTF-16 encoding, it provides
// platform independence and reasonable performance. Therefore,
// 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
#define FULLTEXT_H
#include <QString>
#include <vector>
// 1) The C-accessible interface
#ifdef __cplusplus
extern "C" {
#endif
struct full_text_cache;
struct dive;
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_all(); // Unregisters 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 {
SUBSTRING = 0,
STARTSWITH = 1,
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
struct FullTextQuery {
std::vector<QString> words;
@ -50,3 +63,4 @@ FullTextResult fulltext_find_dives(const FullTextQuery &q, StringFilterMode);
bool fulltext_dive_matches(const struct dive *d, const FullTextQuery &q, StringFilterMode);
#endif
#endif

View File

@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/* gas-model.cpp */
/* gas-model.c */
/* gas compressibility model */
#include <algorithm> // std::clamp
#include <stdio.h>
#include <stdlib.h>
#include "dive.h"
@ -47,20 +46,23 @@ double gas_compressibility_factor(struct gasmix gas, double bar)
-8.83632921053e-08,
+5.33304543646e-11
};
int o2, he;
double Z;
/*
* The curve fitting range is only [0,500] bar.
* Anything else is way out of range for cylinder
* pressures.
*/
bar = std::clamp(bar, 0.0, 500.0);
if (bar < 0) bar = 0;
if (bar > 500) bar = 500;
int o2 = get_o2(gas);
int he = get_he(gas);
o2 = get_o2(gas);
he = get_he(gas);
double Z = virial_m1(o2_coefficients, bar) * o2 +
virial_m1(he_coefficients, bar) * he +
virial_m1(n2_coefficients, bar) * (1000 - o2 - he);
Z = virial_m1(o2_coefficients, bar) * o2 +
virial_m1(he_coefficients, bar) * he +
virial_m1(n2_coefficients, bar) * (1000 - o2 - he);
/*
* We add the 1.0 at the very end - the linear mixing of the

View File

@ -5,7 +5,6 @@
#include "gettext.h"
#include <stdio.h>
#include <string.h>
#include <QtGlobal> // for QT_TRANSLATE_NOOP
/* 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
@ -40,20 +39,20 @@ int same_gasmix(struct gasmix a, struct gasmix 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;
o2 = get_o2(mix);
he = get_he(mix);
o2 = get_o2(*mix);
he = get_he(*mix);
/* Regular air: leave empty */
if (!he) {
if (!o2)
return;
/* 20.8% to 21% O2 is just air */
if (gasmix_is_air(mix)) {
mix.o2.permille = 0;
if (gasmix_is_air(*mix)) {
mix->o2.permille = 0;
return;
}
}
@ -62,7 +61,7 @@ void sanitize_gasmix(struct gasmix &mix)
if (o2 <= 1000 && he <= 1000 && o2 + he <= 1000)
return;
report_info("Odd gasmix: %u O2 %u He", o2, he);
mix = gasmix_air;
memset(mix, 0, sizeof(*mix));
}
int gasmix_distance(struct gasmix a, struct gasmix b)
@ -116,43 +115,42 @@ int pscr_o2(const double amb_pressure, struct gasmix mix)
* 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
* 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.
* 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.
*/
gas_pressures fill_pressures(const double amb_pressure, struct gasmix mix, double po2, enum divemode_t divemode)
void fill_pressures(struct gas_pressures *pressures, const double amb_pressure, struct gasmix mix, double po2, enum divemode_t divemode)
{
struct gas_pressures pressures;
if ((divemode != OC) && po2) { // This is a rebreather dive where pressures.o2 is defined
if ((divemode != OC) && po2) { // This is a rebreather dive where pressures->o2 is defined
if (po2 >= amb_pressure) {
pressures.o2 = amb_pressure;
pressures.n2 = pressures.he = 0.0;
pressures->o2 = amb_pressure;
pressures->n2 = pressures->he = 0.0;
} else {
pressures.o2 = po2;
pressures->o2 = po2;
if (get_o2(mix) == 1000) {
pressures.he = pressures.n2 = 0;
pressures->he = pressures->n2 = 0;
} else {
pressures.he = (amb_pressure - pressures.o2) * (double)get_he(mix) / (1000 - get_o2(mix));
pressures.n2 = amb_pressure - pressures.o2 - pressures.he;
pressures->he = (amb_pressure - pressures->o2) * (double)get_he(mix) / (1000 - get_o2(mix));
pressures->n2 = amb_pressure - pressures->o2 - pressures->he;
}
}
} else {
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) {
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->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));
} else {
pressures.he = pressures.n2 = 0;
pressures->he = pressures->n2 = 0;
}
} else {
// 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.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->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->n2 = get_n2(mix) / 1000.0 * amb_pressure;
}
}
return pressures;
}
enum gastype gasmix_to_type(struct gasmix mix)

View File

@ -5,6 +5,12 @@
#include "divemode.h"
#include "units.h"
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
enum gas_component { N2, HE, O2 };
// o2 == 0 && he == 0 -> air
@ -55,13 +61,13 @@ static inline int get_n2(struct gasmix mix)
int pscr_o2(const double amb_pressure, struct gasmix mix);
struct gas_pressures {
double o2 = 0.0, n2 = 0.0, he = 0.0;
double o2, n2, he;
};
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 fraction_t get_gas_component_fraction(struct gasmix mix, enum gas_component component);
extern gas_pressures fill_pressures(double amb_pressure, struct gasmix mix, double po2, enum divemode_t dctype);
extern void fill_pressures(struct gas_pressures *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_invalid(struct gasmix mix);
@ -69,4 +75,8 @@ extern enum gastype gasmix_to_type(struct gasmix mix);
extern const char *gastype_name(enum gastype type);
extern fraction_t make_fraction(int f);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/* gaspressures.cpp
* ----------------
/* gaspressures.c
* ---------------
* This file contains the routines to calculate the gas pressures in the cylinders.
* The functions below support the code in profile.cpp.
* The high-level function is populate_pressure_information(), called by function
@ -10,8 +10,15 @@
* populate_pressure_information() -> calc_pressure_time()
* -> fill_missing_tank_pressures() -> fill_missing_segment_pressures()
* -> 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 "event.h"
#include "profile.h"
@ -19,29 +26,23 @@
#include "pref.h"
#include <stdlib.h>
#include <vector>
/*
* simple structure to track the beginning and end tank pressure as
* well as the integral of depth over time spent while we have no
* pressure reading from the tank */
struct pr_track_t {
typedef struct pr_track_struct pr_track_t;
struct pr_track_struct {
int start;
int end;
int t_start;
int t_end;
int pressure_time;
pr_track_t(int start, int t_start) :
start(start),
end(0),
t_start(t_start),
t_end(t_start),
pressure_time(0)
{
}
pr_track_t *next;
};
struct pr_interpolate_t {
typedef struct pr_interpolate_struct pr_interpolate_t;
struct pr_interpolate_struct {
int start;
int end;
int pressure_time;
@ -50,17 +51,61 @@ struct pr_interpolate_t {
enum interpolation_strategy {SAC, TIME, CONSTANT};
#ifdef DEBUG_PR_TRACK
static void dump_pr_track(int cyl, std::vector<pr_track_t> &track_pr)
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
static void dump_pr_track(int cyl, pr_track_t *track_pr)
{
pr_track_t *list;
printf("cyl%d:\n", cyl);
for (const auto &item: track_pr) {
list = track_pr;
while (list) {
printf(" start %f end %f t_start %d:%02d t_end %d:%02d pt %d\n",
mbar_to_PSI(item.start),
mbar_to_PSI(item.end),
FRACTION_TUPLE(item.t_start, 60),
FRACTION_TUPLE(item.t_end, 60),
item.pressure_time);
mbar_to_PSI(list->start),
mbar_to_PSI(list->end),
FRACTION_TUPLE(list->t_start, 60),
FRACTION_TUPLE(list->t_end, 60),
list->pressure_time);
list = list->next;
}
}
#endif
@ -83,22 +128,24 @@ static void dump_pr_track(int cyl, std::vector<pr_track_t> &track_pr)
* segments according to how big of a time_pressure area
* they have.
*/
static void fill_missing_segment_pressures(std::vector<pr_track_t> &list, enum interpolation_strategy strategy)
static void fill_missing_segment_pressures(pr_track_t *list, enum interpolation_strategy strategy)
{
for (auto it = list.begin(); it != list.end(); ++it) {
int start = it->start, end;
double magic;
while (list) {
int start = list->start, end;
pr_track_t *tmp = list;
int pt_sum = 0, pt = 0;
auto tmp = it;
for (;;) {
pt_sum += tmp->pressure_time;
end = tmp->end;
if (end)
break;
end = start;
if (std::next(tmp) == list.end())
if (!tmp->next)
break;
++tmp;
tmp = tmp->next;
}
if (!start)
@ -112,34 +159,37 @@ static void fill_missing_segment_pressures(std::vector<pr_track_t> &list, enum i
*
* Now dole out the pressures relative to pressure-time.
*/
it->start = start;
list->start = start;
tmp->end = end;
switch (strategy) {
case SAC:
for (;;) {
int pressure;
pt += it->pressure_time;
pt += list->pressure_time;
pressure = start;
if (pt_sum)
pressure -= lrint((start - end) * (double)pt / pt_sum);
it->end = pressure;
if (it == tmp)
list->end = pressure;
if (list == tmp)
break;
++it;
it->start = pressure;
list = list->next;
list->start = pressure;
}
break;
case TIME:
if (it->t_end && (tmp->t_start - tmp->t_end)) {
double magic = (it->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end);
it->end = lrint(start - (start - end) * magic);
if (list->t_end && (tmp->t_start - tmp->t_end)) {
magic = (list->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end);
list->end = lrint(start - (start - end) * magic);
} else {
it->end = start;
list->end = start;
}
break;
case CONSTANT:
it->end = start;
list->end = start;
}
/* Ok, we've done that set of segments */
list = list->next;
}
}
@ -152,39 +202,42 @@ void dump_pr_interpolate(int i, pr_interpolate_t interpolate_pr)
#endif
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;
pr_interpolate_t interpolate;
static struct pr_interpolate_struct get_pr_interpolate_data(pr_track_t *segment, struct plot_info *pi, int cur)
{ // cur = index to pi->entry corresponding to t_end of segment;
struct pr_interpolate_struct interpolate;
int i;
struct plot_data *entry;
interpolate.start = segment.start;
interpolate.end = segment.end;
interpolate.start = segment->start;
interpolate.end = segment->end;
interpolate.acc_pressure_time = 0;
interpolate.pressure_time = 0;
for (i = 0; i < pi.nr; i++) {
const plot_data &entry = pi.entry[i];
for (i = 0; i < pi->nr; i++) {
entry = pi->entry + i;
if (entry.sec < segment.t_start)
if (entry->sec < segment->t_start)
continue;
interpolate.pressure_time += entry.pressure_time;
if (entry.sec >= segment.t_end)
interpolate.pressure_time += entry->pressure_time;
if (entry->sec >= segment->t_end)
break;
if (i <= cur)
interpolate.acc_pressure_time += entry.pressure_time;
interpolate.acc_pressure_time += entry->pressure_time;
}
return interpolate;
}
static void fill_missing_tank_pressures(const struct dive *dive, struct plot_info &pi, std::vector<pr_track_t> &track_pr, int cyl)
static void fill_missing_tank_pressures(const struct dive *dive, struct plot_info *pi, pr_track_t *track_pr, int cyl)
{
int i;
struct plot_data *entry;
pr_interpolate_t interpolate = { 0, 0, 0, 0 };
pr_track_t *last_segment = NULL;
int cur_pr;
enum interpolation_strategy strategy;
/* no segment where this cylinder is used */
if (track_pr.empty())
if (!track_pr)
return;
if (get_cylinder(dive, cyl)->cylinder_use == OC_GAS)
@ -192,7 +245,7 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf
else
strategy = TIME;
fill_missing_segment_pressures(track_pr, strategy); // Interpolate the missing tank pressure values ..
cur_pr = track_pr[0].start; // in the pr_track_t lists of structures
cur_pr = track_pr->start; // in the pr_track_t lists of structures
// and keep the starting pressure for each cylinder.
#ifdef DEBUG_PR_TRACK
dump_pr_track(cyl, track_pr);
@ -208,44 +261,47 @@ 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
* at time 0 we need to process the second of them here, therefore i=1 */
auto last_segment = track_pr.end();
for (i = 1; i < pi.nr; i++) { // For each point on the profile:
const struct plot_data &entry = pi.entry[i];
for (i = 1; i < pi->nr; i++) { // For each point on the profile:
double magic;
pr_track_t *segment;
int pressure;
int pressure = get_plot_pressure(pi, i, cyl);
entry = pi->entry + i;
if (pressure) { // If there is a valid pressure value,
last_segment = track_pr.end(); // get rid of interpolation data,
cur_pr = pressure; // set current pressure
continue; // and skip to next point.
pressure = get_plot_pressure(pi, i, cyl);
if (pressure) { // If there is a valid pressure value,
last_segment = NULL; // get rid of interpolation data,
cur_pr = pressure; // set current pressure
continue; // and skip to next point.
}
// If there is NO valid pressure value..
// Find the pressure segment corresponding to this entry..
auto it = track_pr.begin();
while (it != track_pr.end() && it->t_end < entry.sec) // Find the track_pr with end time..
++it; // ..that matches the plot_info time (entry.sec)
segment = track_pr;
while (segment && segment->t_end < entry->sec) // Find the track_pr with end time..
segment = segment->next; // ..that matches the plot_info time (entry->sec)
// After last segment? All done.
if (it == track_pr.end())
if (!segment)
break;
// Before first segment, or between segments.. Go on, no interpolation.
if (it->t_start > entry.sec)
if (segment->t_start > entry->sec)
continue;
if (!it->pressure_time) { // Empty segment?
if (!segment->pressure_time) { // Empty segment?
set_plot_pressure_data(pi, i, SENSOR_PR, cyl, cur_pr);
// Just use our current pressure
continue; // and skip to next point.
}
// If there is a valid segment but no tank pressure ..
if (it == last_segment) {
interpolate.acc_pressure_time += entry.pressure_time;
if (segment == last_segment) {
interpolate.acc_pressure_time += entry->pressure_time;
} else {
// Set up an interpolation structure
interpolate = get_pr_interpolate_data(*it, pi, i);
last_segment = it;
interpolate = get_pr_interpolate_data(segment, pi, i);
last_segment = segment;
}
if(get_cylinder(dive, cyl)->cylinder_use == OC_GAS) {
@ -253,19 +309,20 @@ 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 (interpolate.pressure_time) {
/* Overall pressure change over total pressure-time for this segment*/
double magic = (interpolate.end - interpolate.start) / (double)interpolate.pressure_time;
magic = (interpolate.end - interpolate.start) / (double)interpolate.pressure_time;
/* Use that overall pressure change to update the current pressure */
cur_pr = lrint(interpolate.start + magic * interpolate.acc_pressure_time);
}
} else {
double magic = (interpolate.end - interpolate.start) / (it->t_end - it->t_start);
cur_pr = lrint(it->start + magic * (entry.sec - it->t_start));
magic = (interpolate.end - interpolate.start) / (segment->t_end - segment->t_start);
cur_pr = lrint(segment->start + magic * (entry->sec - segment->t_start));
}
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?
* We're calculating the integral of pressure over time by
@ -277,10 +334,10 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf
* scale pressures, so it ends up being a unitless scaling
* factor.
*/
static inline int calc_pressure_time(const struct dive *dive, const struct plot_data &a, const struct plot_data &b)
static inline int calc_pressure_time(const struct dive *dive, struct plot_data *a, struct plot_data *b)
{
int time = b.sec - a.sec;
int depth = (a.depth + b.depth) / 2;
int time = b->sec - a->sec;
int depth = (a->depth + b->depth) / 2;
if (depth <= SURFACE_THRESHOLD)
return 0;
@ -290,33 +347,36 @@ static inline int calc_pressure_time(const struct dive *dive, const struct plot_
#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():
static void debug_print_pressures(struct plot_info &pi)
static void debug_print_pressures(struct plot_info *pi)
{
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));
}
#endif
/* 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
* pressures (pressure==0). For each missing item (node) of tank pressure it creates a pr_track_t structure
* pressures (pressure==0). For each missing item (node) of tank pressure it creates a pr_track_alloc structure
* that represents a segment on the dive profile and that contains tank pressures. There is a linked list of
* pr_track_t structures for each cylinder. These pr_track_t structures ultimately allow for filling
* pr_track_alloc structures for each cylinder. These pr_track_alloc 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
* calculates the summed pressure-time value for the duration of the dive and stores these in the pr_track_t
* calculates the summed pressure-time value for the duration of the dive and stores these * in the pr_track_alloc
* 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;
const cylinder_t *cylinder = get_cylinder(dive, sensor);
std::vector<pr_track_t> track;
size_t current = std::string::npos;
cylinder_t *cylinder = get_cylinder(dive, sensor);
pr_track_t *track = NULL;
pr_track_t *current = NULL;
const struct event *ev, *b_ev;
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 };
if (sensor < 0 || static_cast<size_t>(sensor) >= dive->cylinders.size())
if (sensor < 0 || sensor >= dive->cylinders.nr)
return;
/* if we have no pressure data whatsoever, this is pointless, so let's just return */
@ -326,7 +386,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
/* Get a rough range of where we have any pressures at all */
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);
if (!pressure)
@ -349,31 +409,34 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
* itself has a gas change event.
*/
cyl = sensor;
event_loop loop_gas("gaschange");
const struct event *ev = has_gaschange_event(dive, dc, sensor) ?
loop_gas.next(*dc) : nullptr;
divemode_loop loop_mode(*dc);
ev = NULL;
if (has_gaschange_event(dive, dc, sensor))
ev = get_next_event(dc->events, "gaschange");
b_ev = get_next_event(dc->events, "modechange");
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 time = entry.sec;
int time = entry->sec;
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)
cyl = sensor;
ev = loop_gas.next(*dc);
ev = get_next_event(ev->next, "gaschange");
}
divemode_t dmode = loop_mode.next(time);
while (b_ev && b_ev->time.seconds <= time) { // Keep existing divemode, then
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 != std::string::npos) { // calculate pressure-time, taking into account the dive mode for this specific segment.
entry.pressure_time = (int)(calc_pressure_time(dive, pi.entry[i - 1], entry) * gasfactor[dmode] + 0.5);
track[current].pressure_time += entry.pressure_time;
track[current].t_end = entry.sec;
if (current) { // 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);
current->pressure_time += entry->pressure_time;
current->t_end = entry->sec;
if (pressure)
track[current].end = pressure;
current->end = pressure;
}
// We have a final pressure for 'current'
@ -381,7 +444,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
// current pressure track entry and continue
// until we get back to this cylinder.
if (cyl != sensor) {
current = std::string::npos;
current = NULL;
set_plot_pressure_data(pi, i, SENSOR_PR, sensor, 0);
continue;
}
@ -390,7 +453,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
// continue with or without a tracking entry. Mark any
// existing tracking entry as non-dense, and remember
// to fill in interpolated data.
if (current != std::string::npos && !pressure) {
if (current && !pressure) {
missing_pr = 1;
dense = 0;
continue;
@ -399,7 +462,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
// If we already have a pressure tracking entry, and
// it has not had any missing samples, just continue
// using it - there's nothing to interpolate yet.
if (current != std::string::npos && dense)
if (current && dense)
continue;
// We need to start a new tracking entry, either
@ -408,15 +471,18 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
// missing entries that need to be interpolated.
// Or maybe we didn't have a previous one at all,
// and this is the first pressure entry.
track.emplace_back(pressure, entry.sec);
current = track.size() - 1;
current = pr_track_alloc(pressure, entry->sec);
track = list_add(track, current);
dense = 1;
}
if (missing_pr)
if (missing_pr) {
fill_missing_tank_pressures(dive, pi, track, sensor);
}
#ifdef PRINT_PRESSURES_DEBUG
debug_print_pressures(pi);
#endif
list_free(track);
}

View File

@ -2,6 +2,13 @@
#ifndef GASPRESSURES_H
#define GASPRESSURES_H
void populate_pressure_information(const struct dive *, const struct divecomputer *, struct plot_info &, int);
#ifdef __cplusplus
extern "C" {
#endif
void populate_pressure_information(const struct dive *, const struct divecomputer *, struct plot_info *, int);
#ifdef __cplusplus
}
#endif
#endif // GASPRESSURES_H

View File

@ -2,10 +2,22 @@
#ifndef MYGETTEXT_H
#define MYGETTEXT_H
const char *trGettext(const char *);
#ifdef __cplusplus
extern "C" const char *trGettext(const char *);
static inline const char *translate(const char *, const char *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

View File

@ -6,7 +6,7 @@
static QHash<QByteArray, QByteArray> translationCache;
static QMutex lock;
const char *trGettext(const char *text)
extern "C" const char *trGettext(const char *text)
{
QByteArray key(text);
QMutexLocker l(&lock);

View File

@ -4,7 +4,7 @@
#include <QCoreApplication>
const char *trGettext(const char *text);
extern "C" const char *trGettext(const char *text);
class gettextFromC {
Q_DECLARE_TR_FUNCTIONS(gettextFromC)

View File

@ -4,6 +4,7 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "ssrf.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
@ -57,7 +58,7 @@ static bool includes_string_caseinsensitive(const char *haystack, const char *ne
return 0;
}
void set_git_update_cb(int(*cb)(const char *))
extern "C" void set_git_update_cb(int(*cb)(const char *))
{
update_progress_cb = cb;
}
@ -68,7 +69,7 @@ void set_git_update_cb(int(*cb)(const char *))
// 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
// to understand which parts of the process take how much time.
int git_storage_update_progress(const char *text)
extern "C" int git_storage_update_progress(const char *text)
{
int ret = 0;
if (update_progress_cb)
@ -134,6 +135,9 @@ std::string normalize_cloud_name(const std::string &remote_in)
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
// 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
@ -144,11 +148,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
// collisions for "repo1 branch" vs "repo 1branch".
SHA1 sha;
sha.update(remote);
sha.update("", 1);
sha.update(branch);
auto hash = sha.hash();
SHA1_Init(&ctx);
SHA1_Update(&ctx, remote.c_str(), remote.size());
SHA1_Update(&ctx, "", 1);
SHA1_Update(&ctx, branch.c_str(), branch.size());
SHA1_Final(hash, &ctx);
return format_string_std("%s/cloudstorage/%02x%02x%02x%02x%02x%02x%02x%02x",
system_default_directory(),
hash[0], hash[1], hash[2], hash[3],
@ -240,7 +244,7 @@ static bool exceeded_auth_attempts()
return false;
}
int credential_ssh_cb(git_cred **out,
extern "C" int credential_ssh_cb(git_cred **out,
const char *,
const char *,
unsigned int allowed_types,
@ -272,7 +276,7 @@ int credential_ssh_cb(git_cred **out,
return GIT_EUSER;
}
int credential_https_cb(git_cred **out,
extern "C" int credential_https_cb(git_cred **out,
const char *,
const char *,
unsigned int,
@ -287,7 +291,7 @@ int credential_https_cb(git_cred **out,
return git_cred_userpass_plaintext_new(out, username, password);
}
int certificate_check_cb(git_cert *cert, int valid, const char *host, void *)
extern "C" int certificate_check_cb(git_cert *cert, int valid, const char *host, void *)
{
if (verbose)
report_info("git storage: certificate callback for host %s with validity %d\n", host, valid);
@ -338,7 +342,7 @@ static int update_remote(struct git_info *info, git_remote *origin, git_referenc
return 0;
}
int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree);
extern "C" 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)
{
@ -346,7 +350,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_index *merged_index;
git_merge_options merge_options;
membuffer msg;
struct membufferpp msg;
if (verbose) {
char outlocal[41], outremote[41];

View File

@ -4,12 +4,14 @@
#include "git2.h"
#include "filterpreset.h"
#include <string>
struct dive_log;
struct git_oid;
struct git_repository;
struct divelog;
#ifdef __cplusplus
extern "C" {
#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_U2 "ssrf-cloud-u2.subsurface-divelog.org" // secondary (older) server in the US
@ -22,12 +24,21 @@ enum remote_transport { RT_LOCAL, RT_HTTPS, RT_SSH, RT_OTHER };
extern bool git_local_only;
extern bool git_remote_sync_successful;
extern void clear_git_id();
extern void clear_git_id(void);
extern void set_git_id(const struct git_oid *);
void set_git_update_cb(int(*)(const char *));
int git_storage_update_progress(const char *text);
int get_authorship(git_repository *repo, git_signature **authorp);
#ifdef __cplusplus
}
#include <string>
struct git_oid;
struct git_repository;
struct divelog;
struct git_info {
std::string url;
std::string branch;
@ -52,4 +63,6 @@ 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 git_create_local_repo(const std::string &filename);
#endif
#endif // GITACCESS_H

View File

@ -96,7 +96,7 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const
// For io error or video, return early with the appropriate dummy-icon.
if (type == MEDIATYPE_IO_ERROR)
return { failImage, MEDIATYPE_IO_ERROR, duration_t() };
return { failImage, MEDIATYPE_IO_ERROR, zero_duration };
else if (type == MEDIATYPE_VIDEO)
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,
// we pass 0 as the duration.
if (hasVideoFileExtension(filename))
return fetchVideoThumbnail(filename, originalFilename, duration_t());
return fetchVideoThumbnail(filename, originalFilename, zero_duration);
// 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.
@ -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
// 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));
return { QImage(), MEDIATYPE_STILL_LOADING, duration_t() };
return { QImage(), MEDIATYPE_STILL_LOADING, zero_duration };
}
return { QImage(), MEDIATYPE_IO_ERROR, duration_t() };
return { QImage(), MEDIATYPE_IO_ERROR, zero_duration };
}
// 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.
// 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.
Thumbnail thumbnail { QImage(), MEDIATYPE_IO_ERROR, duration_t() };
Thumbnail thumbnail { QImage(), MEDIATYPE_IO_ERROR, zero_duration };
if (localFilename != filename)
thumbnail = fetchImage(localFilename, filename, tryDownload);
@ -187,7 +187,7 @@ Thumbnailer::Thumbnail Thumbnailer::getPictureThumbnailFromStream(QDataStream &s
{
QImage res;
stream >> res;
return { std::move(res), MEDIATYPE_PICTURE, duration_t() };
return { std::move(res), MEDIATYPE_PICTURE, zero_duration };
}
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,
// no more than 10000 pictures).
if (stream.status() != QDataStream::Ok || duration > 36000 || numPics > 10000)
return { QImage(), MEDIATYPE_VIDEO, duration_t() };
return { QImage(), MEDIATYPE_VIDEO, zero_duration };
// 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
@ -240,7 +240,7 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture
{
QString filename = thumbnailFileName(picture_filename);
if (filename.isEmpty())
return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
return { QImage(), MEDIATYPE_UNKNOWN, zero_duration };
QFile file(filename);
if (prefs.auto_recalculate_thumbnails) {
@ -254,13 +254,13 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture
if (pictureTime.isValid() && thumbnailTime.isValid() && thumbnailTime < pictureTime) {
// Both files exist, have valid timestamps and thumbnail was calculated before picture.
// Return an empty thumbnail to signal recalculation of the thumbnail
return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
return { QImage(), MEDIATYPE_UNKNOWN, zero_duration };
}
}
}
if (!file.open(QIODevice::ReadOnly))
return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
return { QImage(), MEDIATYPE_UNKNOWN, zero_duration };
QDataStream stream(&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) {
case MEDIATYPE_PICTURE: return getPictureThumbnailFromStream(stream);
case MEDIATYPE_VIDEO: return getVideoThumbnailFromStream(stream, picture_filename);
case MEDIATYPE_UNKNOWN: return { unknownImage, MEDIATYPE_UNKNOWN, duration_t() };
default: return { QImage(), MEDIATYPE_UNKNOWN, duration_t() };
case MEDIATYPE_UNKNOWN: return { unknownImage, MEDIATYPE_UNKNOWN, zero_duration };
default: return { QImage(), MEDIATYPE_UNKNOWN, zero_duration };
}
}
@ -319,7 +319,7 @@ Thumbnailer::Thumbnail Thumbnailer::fetchVideoThumbnail(const QString &filename,
return { videoImage, MEDIATYPE_VIDEO, duration };
} else {
// Video-thumbnailing is disabled. Write a thumbnail without picture.
return addVideoThumbnailToCache(originalFilename, duration, QImage(), duration_t());
return addVideoThumbnailToCache(originalFilename, duration, QImage(), zero_duration);
}
}
@ -337,7 +337,7 @@ Thumbnailer::Thumbnail Thumbnailer::addPictureThumbnailToCache(const QString &pi
stream << thumbnail;
file.commit();
}
return { thumbnail, MEDIATYPE_PICTURE, duration_t() };
return { thumbnail, MEDIATYPE_PICTURE, zero_duration };
}
Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &picture_filename)
@ -348,7 +348,7 @@ Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &pi
QDataStream stream(&file);
stream << (quint32)MEDIATYPE_UNKNOWN;
}
return { unknownImage, MEDIATYPE_UNKNOWN, duration_t() };
return { unknownImage, MEDIATYPE_UNKNOWN, zero_duration };
}
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
// add to the thumbnail cache as a video image with unknown thumbnail.
addVideoThumbnailToCache(filename, duration, QImage(), duration_t());
addVideoThumbnailToCache(filename, duration, QImage(), zero_duration);
QMutexLocker l(&lock);
workingOn.remove(filename);
}
@ -435,7 +435,7 @@ void Thumbnailer::imageDownloaded(QString filename)
void Thumbnailer::imageDownloadFailed(QString filename)
{
emit thumbnailChanged(filename, failImage, duration_t());
emit thumbnailChanged(filename, failImage, zero_duration);
QMutexLocker l(&lock);
workingOn.remove(filename);
}

View File

@ -5,6 +5,7 @@
#endif
#include <stdlib.h>
#include "ssrf.h"
#include "dive.h"
#include "divesite.h"
#include "errorhelper.h"
@ -28,7 +29,7 @@ static int cobalt_profile_sample(void *param, int, char **data, char **)
if (data[1])
state->cur_sample->depth.mm = atoi(data[1]);
if (data[2])
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL));
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));
sample_end(state);
return 0;
@ -63,7 +64,7 @@ static int cobalt_buddies(void *param, int, char **data, char **)
struct parser_state *state = (struct parser_state *)param;
if (data[0])
utf8_string_std(data[0], &state->cur_dive->buddy);
utf8_string(data[0], &state->cur_dive->buddy);
return 0;
}
@ -80,8 +81,8 @@ static int cobalt_visibility(void *, int, char **, char **)
static int cobalt_location(void *param, int, char **data, char **)
{
std::string *location = (std::string *)param;
*location = data[0] ? data[0] : NULL;
char **location = (char **)param;
*location = data[0] ? strdup(data[0]) : NULL;
return 0;
}
@ -91,7 +92,7 @@ static int cobalt_dive(void *param, int, char **data, char **)
int retval = 0;
struct parser_state *state = (struct parser_state *)param;
sqlite3 *handle = state->sql_handle;
std::string location, location_site;
char *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_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";
@ -106,7 +107,7 @@ static int cobalt_dive(void *param, int, char **data, char **)
state->cur_dive->when = (time_t)(atol(data[1]));
if (data[4])
utf8_string_std(data[4], &state->cur_dive->notes);
utf8_string(data[4], &state->cur_dive->notes);
/* data[5] should have information on Units used, but I cannot
* parse it at all based on the sample log I have received. The
@ -118,13 +119,13 @@ static int cobalt_dive(void *param, int, char **data, char **)
/* Cobalt stores the pressures, not the depth */
if (data[6])
state->cur_dive->dcs[0].maxdepth.mm = atoi(data[6]);
state->cur_dive->dc.maxdepth.mm = atoi(data[6]);
if (data[7])
state->cur_dive->dcs[0].duration.seconds = atoi(data[7]);
state->cur_dive->dc.duration.seconds = atoi(data[7]);
if (data[8])
state->cur_dive->dcs[0].surface_pressure.mbar = atoi(data[8]);
state->cur_dive->dc.surface_pressure.mbar = atoi(data[8]);
/*
* TODO: the deviceid hash should be calculated here.
*/
@ -133,15 +134,15 @@ static int cobalt_dive(void *param, int, char **data, char **)
if (data[9]) {
utf8_string_std(data[9], &state->cur_settings.dc.serial_nr);
state->cur_settings.dc.deviceid = atoi(data[9]);
state->cur_settings.dc.model = "Cobalt import";
state->cur_settings.dc.model = strdup("Cobalt import");
}
dc_settings_end(state);
settings_end(state);
if (data[9]) {
state->cur_dive->dcs[0].deviceid = atoi(data[9]);
state->cur_dive->dcs[0].model = "Cobalt import";
state->cur_dive->dc.deviceid = atoi(data[9]);
state->cur_dive->dc.model = strdup("Cobalt import");
}
snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder_template, state->cur_dive->number);
@ -179,10 +180,19 @@ static int cobalt_dive(void *param, int, char **data, char **)
return 1;
}
if (!location.empty() && !location_site.empty()) {
std::string tmp = location + " / " + location_site;
state->log->sites.find_or_create(tmp)->add_dive(state->cur_dive.get());
if (location && location_site) {
char *tmp = (char *)malloc(strlen(location) + strlen(location_site) + 4);
if (!tmp) {
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);
retval = sqlite3_exec(handle, get_buffer, &cobalt_profile_sample, state, NULL);
@ -196,7 +206,8 @@ static int cobalt_dive(void *param, int, char **data, char **)
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;
struct parser_state state;

View File

@ -5,6 +5,7 @@
#include "dive.h"
#include "errorhelper.h"
#include "ssrf.h"
#include "subsurface-string.h"
#include "divelist.h"
#include "divelog.h"
@ -269,7 +270,7 @@ static int parse_dan_format(const char *filename, struct xml_params *params, str
return ret;
}
int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log)
extern "C" int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log)
{
int ret;
std::string mem;
@ -401,6 +402,7 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log)
char *header[8];
int i, time;
timestamp_t date;
struct dive *dive;
struct divecomputer *dc;
for (i = 0; i < 8; i++) {
@ -415,10 +417,10 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log)
if (!date)
return 0;
auto dive = std::make_unique<struct dive>();
dive = alloc_dive();
dive->when = date;
dive->number = atoi(header[1]);
dc = &dive->dcs[0];
dc = &dive->dc;
time = 0;
for (;;) {
@ -436,6 +438,7 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log)
sample = prepare_sample(dc);
sample->time.seconds = time;
add_sample_data(sample, type, val);
finish_sample(dc);
time++;
dc->duration.seconds = time;
@ -443,7 +446,7 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log)
break;
p = end + 1;
}
log->dives.record_dive(std::move(dive));
record_dive_to_table(dive, log->dives);
return 1;
}
@ -494,7 +497,9 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
bool has_depth = false, has_setpoint = false, has_ndl = false;
char *lineptr;
int prev_time = 0;
cylinder_t cyl = empty_cylinder;
struct dive *dive;
struct divecomputer *dc;
struct tm cur_tm;
@ -508,40 +513,34 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
cur_tm.tm_min = mm;
cur_tm.tm_sec = ss;
auto dive = std::make_unique<struct dive>();
dive = alloc_dive();
dive->when = utc_mktime(&cur_tm);;
dive->dcs[0].model = "Poseidon MkVI Discovery";
dive->dc.model = strdup("Poseidon MkVI Discovery");
value = parse_mkvi_value(memtxt.data(), "Rig Serial number");
dive->dcs[0].deviceid = atoi(value.c_str());
dive->dcs[0].divemode = CCR;
dive->dcs[0].no_o2sensors = 2;
dive->dc.deviceid = atoi(value.c_str());
dive->dc.divemode = CCR;
dive->dc.no_o2sensors = 2;
{
cylinder_t cyl;
cyl.cylinder_use = OXYGEN;
cyl.type.size.mliter = 3000;
cyl.type.workingpressure.mbar = 200000;
cyl.type.description = "3l Mk6";
cyl.gasmix.o2.permille = 1000;
cyl.manually_added = true;
cyl.bestmix_o2 = 0;
cyl.bestmix_he = 0;
dive->cylinders.push_back(std::move(cyl));
}
cyl.cylinder_use = OXYGEN;
cyl.type.size.mliter = 3000;
cyl.type.workingpressure.mbar = 200000;
cyl.type.description = "3l Mk6";
cyl.gasmix.o2.permille = 1000;
cyl.manually_added = true;
cyl.bestmix_o2 = 0;
cyl.bestmix_he = 0;
add_cloned_cylinder(&dive->cylinders, cyl);
{
cylinder_t cyl;
cyl.cylinder_use = DILUENT;
cyl.type.size.mliter = 3000;
cyl.type.workingpressure.mbar = 200000;
cyl.type.description = "3l Mk6";
value = parse_mkvi_value(memtxt.data(), "Helium percentage");
he = atoi(value.c_str());
value = parse_mkvi_value(memtxt.data(), "Nitrogen percentage");
cyl.gasmix.o2.permille = (100 - atoi(value.c_str()) - he) * 10;
cyl.gasmix.he.permille = he * 10;
dive->cylinders.push_back(std::move(cyl));
}
cyl.cylinder_use = DILUENT;
cyl.type.size.mliter = 3000;
cyl.type.workingpressure.mbar = 200000;
cyl.type.description = "3l Mk6";
value = parse_mkvi_value(memtxt.data(), "Helium percentage");
he = atoi(value.c_str());
value = parse_mkvi_value(memtxt.data(), "Nitrogen percentage");
cyl.gasmix.o2.permille = (100 - atoi(value.c_str()) - he) * 10;
cyl.gasmix.he.permille = he * 10;
add_cloned_cylinder(&dive->cylinders, cyl);
lineptr = strstr(memtxt.data(), "Dive started at");
while (!empty_string(lineptr) && (lineptr = strchr(lineptr, '\n'))) {
@ -552,9 +551,9 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
std::string value = parse_mkvi_value(lineptr, key.c_str());
if (value.empty())
break;
add_extra_data(&dive->dcs[0], key, value);
add_extra_data(&dive->dc, key.c_str(), value.c_str());
}
dc = &dive->dcs[0];
dc = &dive->dc;
/*
* Read samples from the CSV file. A sample contains all the lines with same timestamp. The CSV file has
@ -574,8 +573,10 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
*/
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);
}
lineptr = memcsv.data();
for (;;) {
struct sample *sample;
@ -745,11 +746,12 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log)
add_sample_data(sample, POSEIDON_SETPOINT, prev_setpoint);
if (!has_ndl && prev_ndl >= 0)
add_sample_data(sample, POSEIDON_NDL, prev_ndl);
finish_sample(dc);
if (!lineptr || !*lineptr)
break;
}
log->dives.record_dive(std::move(dive));
record_dive_to_table(dive, log->dives);
return 1;
} else {
return 0;

View File

@ -21,6 +21,10 @@ enum csv_format {
#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 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);
@ -28,4 +32,8 @@ 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_manual_file(const char *filename, struct xml_params *params, struct divelog *log);
#ifdef __cplusplus
}
#endif
#endif // IMPORTCSV_H

View File

@ -4,6 +4,7 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "ssrf.h"
#include "dive.h"
#include "divesite.h"
#include "sample.h"
@ -55,8 +56,6 @@ static int divinglog_cylinder(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;
int sinterval = 0;
@ -130,14 +129,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->pressure[0].mbar = pressure * 100;
state->cur_sample->rbt.seconds = rbt;
if (oldcyl != tank && tank >= 0 && static_cast<size_t>(tank) < state->cur_dive->cylinders.size()) {
struct gasmix mix = get_cylinder(state->cur_dive.get(), tank)->gasmix;
if (oldcyl != tank && tank >= 0 && tank < state->cur_dive->cylinders.nr) {
struct gasmix mix = get_cylinder(state->cur_dive, tank)->gasmix;
int o2 = get_o2(mix);
int he = get_he(mix);
event_start(state);
state->cur_event.time.seconds = time;
state->cur_event.name = "gaschange"s;
strcpy(state->cur_event.name, "gaschange");
o2 = (o2 + 5) / 10;
he = (he + 5) / 10;
@ -212,8 +211,8 @@ static int divinglog_profile(void *param, int, char **data, char **)
* Count the number of o2 sensors
*/
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->dcs[0].no_o2sensors = state->cur_sample->o2sensor[0].mbar ? 1 : 0 +
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)) {
state->cur_dive->dc.no_o2sensors = state->cur_sample->o2sensor[0].mbar ? 1 : 0 +
state->cur_sample->o2sensor[1].mbar ? 1 : 0 +
state->cur_sample->o2sensor[2].mbar ? 1 : 0;
}
@ -224,7 +223,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
if (ptr1[6] - '0') {
event_start(state);
state->cur_event.time.seconds = time;
state->cur_event.name = "rbt"s;
strcpy(state->cur_event.name, "rbt");
event_end(state);
}
@ -232,7 +231,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
if (ptr1[7] - '0') {
event_start(state);
state->cur_event.time.seconds = time;
state->cur_event.name = "ascent"s;
strcpy(state->cur_event.name, "ascent");
event_end(state);
}
@ -240,7 +239,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
if (ptr1[8] - '0') {
event_start(state);
state->cur_event.time.seconds = time;
state->cur_event.name = "violation"s;
strcpy(state->cur_event.name, "violation");
event_end(state);
}
@ -248,7 +247,7 @@ static int divinglog_profile(void *param, int, char **data, char **)
if (ptr1[9] - '0') {
event_start(state);
state->cur_event.time.seconds = time;
state->cur_event.name = "workload"s;
strcpy(state->cur_event.name, "workload");
event_end(state);
}
@ -276,22 +275,22 @@ static int divinglog_dive(void *param, int, char **data, char **)
state->cur_dive->when = (time_t)(atol(data[1]));
if (data[2])
state->log->sites.find_or_create(std::string(data[2]))->add_dive(state->cur_dive.get());
add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(data[2], state->log->sites));
if (data[3])
utf8_string_std(data[3], &state->cur_dive->buddy);
utf8_string(data[3], &state->cur_dive->buddy);
if (data[4])
utf8_string_std(data[4], &state->cur_dive->notes);
utf8_string(data[4], &state->cur_dive->notes);
if (data[5])
state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[5], NULL) * 1000);
state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[5], NULL, 0) * 1000);
if (data[6])
state->cur_dive->dcs[0].duration.seconds = atoi(data[6]) * 60;
state->cur_dive->dc.duration.seconds = atoi(data[6]) * 60;
if (data[7])
utf8_string_std(data[7], &state->cur_dive->diveguide);
utf8_string(data[7], &state->cur_dive->diveguide);
if (data[8])
state->cur_dive->airtemp.mkelvin = C_to_mkelvin(atol(data[8]));
@ -301,11 +300,11 @@ static int divinglog_dive(void *param, int, char **data, char **)
if (data[10]) {
weightsystem_t ws = { { atoi(data[10]) * 1000 }, translate("gettextFromC", "unknown"), false };
state->cur_dive->weightsystems.push_back(std::move(ws));
add_cloned_weightsystem(&state->cur_dive->weightsystems, ws);
}
if (data[11])
state->cur_dive->suit = data[11];
state->cur_dive->suit = strdup(data[11]);
/* Divinglog has following visibility options: good, medium, bad */
if (data[14]) {
@ -330,9 +329,9 @@ static int divinglog_dive(void *param, int, char **data, char **)
dc_settings_start(state);
if (data[12]) {
state->cur_dive->dcs[0].model = data[12];
state->cur_dive->dc.model = strdup(data[12]);
} else {
state->cur_settings.dc.model = "Divinglog import";
state->cur_settings.dc.model = strdup("Divinglog import");
}
snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder0_template, diveid);
@ -355,10 +354,10 @@ static int divinglog_dive(void *param, int, char **data, char **)
case '0':
break;
case '1':
state->cur_dive->dcs[0].divemode = PSCR;
state->cur_dive->dc.divemode = PSCR;
break;
case '2':
state->cur_dive->dcs[0].divemode = CCR;
state->cur_dive->dc.divemode = CCR;
break;
}
}
@ -367,9 +366,9 @@ static int divinglog_dive(void *param, int, char **data, char **)
settings_end(state);
if (data[12]) {
state->cur_dive->dcs[0].model = data[12];
state->cur_dive->dc.model = strdup(data[12]);
} else {
state->cur_dive->dcs[0].model = "Divinglog import";
state->cur_dive->dc.model = strdup("Divinglog import");
}
snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, diveid);
@ -385,7 +384,7 @@ static int divinglog_dive(void *param, int, char **data, char **)
}
int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
extern "C" int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
struct parser_state state;

View File

@ -6,6 +6,7 @@
#include <stdlib.h>
#include "qthelper.h"
#include "ssrf.h"
#include "dive.h"
#include "sample.h"
#include "subsurface-string.h"
@ -26,12 +27,11 @@
*/
static int seac_gaschange(void *param, sqlite3_stmt *sqlstmt)
{
using namespace std::string_literals;
struct parser_state *state = (struct parser_state *)param;
event_start(state);
state->cur_event.time.seconds = sqlite3_column_int(sqlstmt, 1);
state->cur_event.name = "gaschange"s;
strcpy(state->cur_event.name, "gaschange");
state->cur_event.gas.mix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4);
event_end(state);
@ -70,7 +70,7 @@ static int seac_dive(void *param, int, char **data, char **)
state->cur_dive->number = atoi(data[0]);
// Create first cylinder
cylinder_t *curcyl = get_or_create_cylinder(state->cur_dive.get(), 0);
cylinder_t *curcyl = get_or_create_cylinder(state->cur_dive, 0);
// Get time and date
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]) {
switch (atoi(data[6])) {
case 1:
state->cur_dive->dcs[0].divemode = OC;
state->cur_dive->dc.divemode = OC;
break;
// Gauge Mode
case 2:
state->cur_dive->dcs[0].divemode = UNDEF_COMP_TYPE;
state->cur_dive->dc.divemode = UNDEF_COMP_TYPE;
break;
case 3:
state->cur_dive->dcs[0].divemode = FREEDIVE;
state->cur_dive->dc.divemode = FREEDIVE;
break;
default:
if (verbose) {
@ -150,12 +150,12 @@ static int seac_dive(void *param, int, char **data, char **)
// 9 = comments from seac app
if (data[9]) {
utf8_string_std(data[9], &state->cur_dive->notes);
utf8_string(data[9], &state->cur_dive->notes);
}
// 10 = dive duration
if (data[10]) {
state->cur_dive->dcs[0].duration.seconds = atoi(data[10]);
state->cur_dive->dc.duration.seconds = atoi(data[10]);
}
// 8 = water_type
@ -181,7 +181,7 @@ static int seac_dive(void *param, int, char **data, char **)
if (data[11]) {
state->cur_dive->dcs[0].maxdepth.mm = 10 * atoi(data[11]);
state->cur_dive->dc.maxdepth.mm = 10 * atoi(data[11]);
}
// Create sql_stmt type to query DB
@ -205,24 +205,27 @@ static int seac_dive(void *param, int, char **data, char **)
settings_start(state);
dc_settings_start(state);
utf8_string_std(data[1], &state->cur_dive->dcs[0].serial);
utf8_string_std(data[12],&state->cur_dive->dcs[0].fw_version);
state->cur_dive->dcs[0].model = "Seac Action";
// These dc values are const char *, therefore we have to cast.
// Will be fixed by converting to std::string
utf8_string(data[1], (char **)&state->cur_dive->dc.serial);
utf8_string(data[12], (char **)&state->cur_dive->dc.fw_version);
state->cur_dive->dc.model = strdup("Seac Action");
state->cur_dive->dcs[0].deviceid = calculate_string_hash(data[1]);
state->cur_dive->dc.deviceid = calculate_string_hash(data[1]);
add_extra_data(&state->cur_dive->dcs[0], "GF-Lo", (const char*)sqlite3_column_text(sqlstmt, 9));
add_extra_data(&state->cur_dive->dcs[0], "GF-Hi", (const char*)sqlite3_column_text(sqlstmt, 10));
add_extra_data(&state->cur_dive->dc, "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));
dc_settings_end(state);
settings_end(state);
if (data[11]) {
state->cur_dive->dcs[0].maxdepth.mm = 10 * atoi(data[11]);
state->cur_dive->dc.maxdepth.mm = 10 * atoi(data[11]);
}
curcyl->gasmix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4);
// Track gasses to tell when switch occurs
lastgas = curcyl->gasmix;
curgas = curcyl->gasmix;
@ -238,7 +241,7 @@ static int seac_dive(void *param, int, char **data, char **)
seac_gaschange(state, sqlstmt);
lastgas = curgas;
cylnum ^= 1; // Only need to toggle between two cylinders
curcyl = get_or_create_cylinder(state->cur_dive.get(), cylnum);
curcyl = get_or_create_cylinder(state->cur_dive, cylnum);
curcyl->gasmix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4);
}
state->cur_sample->stopdepth.mm = 10 * sqlite3_column_int(sqlstmt, 5);
@ -261,7 +264,7 @@ static int seac_dive(void *param, int, char **data, char **)
* The callback function performs another SQL query on the other
* table, to read in the sample values.
*/
int parse_seac_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
extern "C" int parse_seac_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
char *err = NULL;

View File

@ -4,6 +4,7 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "ssrf.h"
#include "dive.h"
#include "sample.h"
#include "subsurface-string.h"
@ -22,8 +23,8 @@ static int shearwater_cylinders(void *param, int, char **data, char **)
struct parser_state *state = (struct parser_state *)param;
cylinder_t *cyl;
int o2 = lrint(permissive_strtod(data[0], NULL) * 1000);
int he = lrint(permissive_strtod(data[1], NULL) * 1000);
int o2 = lrint(strtod_flags(data[0], NULL, 0) * 1000);
int he = lrint(strtod_flags(data[1], NULL, 0) * 1000);
/* Shearwater allows entering only 99%, not 100%
* so assume 99% to be pure oxygen */
@ -49,8 +50,8 @@ static int shearwater_changes(void *param, int columns, char **data, char **)
if (!data[0] || !data[1] || !data[2]) {
return 2;
}
int o2 = lrint(permissive_strtod(data[1], NULL) * 1000);
int he = lrint(permissive_strtod(data[2], NULL) * 1000);
int o2 = lrint(strtod_flags(data[1], NULL, 0) * 1000);
int he = lrint(strtod_flags(data[2], NULL, 0) * 1000);
/* Shearwater allows entering only 99%, not 100%
* so assume 99% to be pure oxygen */
@ -58,20 +59,24 @@ static int shearwater_changes(void *param, int columns, char **data, char **)
o2 = 1000;
// Find the cylinder index
auto it = std::find_if(state->cur_dive->cylinders.begin(), state->cur_dive->cylinders.end(),
[o2, he](auto &cyl)
{ return cyl.gasmix.o2.permille == o2 && cyl.gasmix.he.permille == he; });
if (it == state->cur_dive->cylinders.end()) {
int index;
bool found = false;
for (index = 0; index < state->cur_dive->cylinders.nr; ++index) {
const cylinder_t *cyl = get_cylinder(state->cur_dive, index);
if (cyl->gasmix.o2.permille == o2 && cyl->gasmix.he.permille == he) {
found = true;
break;
}
}
if (!found) {
// Cylinder not found, creating a new one
cyl = cylinder_start(state);
cyl->gasmix.o2.permille = o2;
cyl->gasmix.he.permille = he;
cylinder_end(state);
it = std::prev(state->cur_dive->cylinders.end());
}
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());
add_gas_switch_event(state->cur_dive, get_dc(state), state->sample_rate ? atoi(data[0]) / state->sample_rate * 10 : atoi(data[0]), index);
return 0;
}
@ -96,11 +101,12 @@ static int shearwater_profile_sample(void *param, int, char **data, char **)
if (data[1])
state->cur_sample->depth.mm = state->metric ? lrint(permissive_strtod(data[1], NULL) * 1000) : feet_to_mm(permissive_strtod(data[1], NULL));
state->cur_sample->depth.mm = state->metric ? lrint(strtod_flags(data[1], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[1], NULL, 0));
if (data[2])
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])
state->cur_sample->setpoint.mbar = lrint(permissive_strtod(data[3], NULL) * 1000);
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));
if (data[3]) {
state->cur_sample->setpoint.mbar = lrint(strtod_flags(data[3], NULL, 0) * 1000);
}
if (data[4])
state->cur_sample->ndl.seconds = atoi(data[4]) * 60;
if (data[5])
@ -155,11 +161,11 @@ static int shearwater_ai_profile_sample(void *param, int, char **data, char **)
state->cur_sample->time.seconds = atoi(data[0]);
if (data[1])
state->cur_sample->depth.mm = state->metric ? lrint(permissive_strtod(data[1], NULL) * 1000) : feet_to_mm(permissive_strtod(data[1], NULL));
state->cur_sample->depth.mm = state->metric ? lrint(strtod_flags(data[1], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[1], NULL, 0));
if (data[2])
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL));
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));
if (data[3]) {
state->cur_sample->setpoint.mbar = lrint(permissive_strtod(data[3], NULL) * 1000);
state->cur_sample->setpoint.mbar = lrint(strtod_flags(data[3], NULL, 0) * 1000);
}
if (data[4])
state->cur_sample->ndl.seconds = atoi(data[4]) * 60;
@ -209,7 +215,7 @@ static int shearwater_mode(void *param, int, char **data, char **)
struct parser_state *state = (struct parser_state *)param;
if (data[0])
state->cur_dive->dcs[0].divemode = atoi(data[0]) == 0 ? CCR : OC;
state->cur_dive->dc.divemode = atoi(data[0]) == 0 ? CCR : OC;
return 0;
}
@ -234,23 +240,23 @@ static int shearwater_dive(void *param, int, char **data, char **)
long int dive_id = atol(data[11]);
if (data[2])
add_dive_site(data[2], state->cur_dive.get(), state);
add_dive_site(data[2], state->cur_dive, state);
if (data[3])
utf8_string_std(data[3], &state->cur_dive->buddy);
utf8_string(data[3], &state->cur_dive->buddy);
if (data[4])
utf8_string_std(data[4], &state->cur_dive->notes);
utf8_string(data[4], &state->cur_dive->notes);
state->metric = atoi(data[5]) == 1 ? 0 : 1;
/* TODO: verify that metric calculation is correct */
if (data[6])
state->cur_dive->dcs[0].maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL));
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));
if (data[7])
state->cur_dive->dcs[0].duration.seconds = atoi(data[7]) * 60;
state->cur_dive->dc.duration.seconds = atoi(data[7]) * 60;
if (data[8])
state->cur_dive->dcs[0].surface_pressure.mbar = atoi(data[8]);
state->cur_dive->dc.surface_pressure.mbar = atoi(data[8]);
/*
* TODO: the deviceid hash should be calculated here.
*/
@ -261,13 +267,13 @@ static int shearwater_dive(void *param, int, char **data, char **)
if (data[10]) {
switch (atoi(data[10])) {
case 2:
state->cur_settings.dc.model = "Shearwater Petrel/Perdix";
state->cur_settings.dc.model = strdup("Shearwater Petrel/Perdix");
break;
case 4:
state->cur_settings.dc.model = "Shearwater Predator";
state->cur_settings.dc.model = strdup("Shearwater Predator");
break;
default:
state->cur_settings.dc.model = "Shearwater import";
state->cur_settings.dc.model = strdup("Shearwater import");
break;
}
}
@ -280,13 +286,13 @@ static int shearwater_dive(void *param, int, char **data, char **)
if (data[10]) {
switch (atoi(data[10])) {
case 2:
state->cur_dive->dcs[0].model = "Shearwater Petrel/Perdix";
state->cur_dive->dc.model = strdup("Shearwater Petrel/Perdix");
break;
case 4:
state->cur_dive->dcs[0].model = "Shearwater Predator";
state->cur_dive->dc.model = strdup("Shearwater Predator");
break;
default:
state->cur_dive->dcs[0].model = "Shearwater import";
state->cur_dive->dc.model = strdup("Shearwater import");
break;
}
}
@ -364,23 +370,23 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
state->sample_rate = 0;
if (data[2])
add_dive_site(data[2], state->cur_dive.get(), state);
add_dive_site(data[2], state->cur_dive, state);
if (data[3])
utf8_string_std(data[3], &state->cur_dive->buddy);
utf8_string(data[3], &state->cur_dive->buddy);
if (data[4])
utf8_string_std(data[4], &state->cur_dive->notes);
utf8_string(data[4], &state->cur_dive->notes);
state->metric = atoi(data[5]) == 1 ? 0 : 1;
/* TODO: verify that metric calculation is correct */
if (data[6])
state->cur_dive->dcs[0].maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL));
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));
if (data[7])
state->cur_dive->dcs[0].duration.seconds = atoi(data[7]);
state->cur_dive->dc.duration.seconds = atoi(data[7]);
if (data[8])
state->cur_dive->dcs[0].surface_pressure.mbar = atoi(data[8]);
state->cur_dive->dc.surface_pressure.mbar = atoi(data[8]);
/*
* TODO: the deviceid hash should be calculated here.
*/
@ -391,13 +397,13 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
if (data[10]) {
switch (atoi(data[10])) {
case 2:
state->cur_settings.dc.model = "Shearwater Petrel/Perdix";
state->cur_settings.dc.model = strdup("Shearwater Petrel/Perdix");
break;
case 4:
state->cur_settings.dc.model = "Shearwater Predator";
state->cur_settings.dc.model = strdup("Shearwater Predator");
break;
default:
state->cur_settings.dc.model = "Shearwater import";
state->cur_settings.dc.model = strdup("Shearwater import");
break;
}
}
@ -410,13 +416,13 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
if (data[10]) {
switch (atoi(data[10])) {
case 2:
state->cur_dive->dcs[0].model = "Shearwater Petrel/Perdix";
state->cur_dive->dc.model = strdup("Shearwater Petrel/Perdix");
break;
case 4:
state->cur_dive->dcs[0].model = "Shearwater Predator";
state->cur_dive->dc.model = strdup("Shearwater Predator");
break;
default:
state->cur_dive->dcs[0].model = "Shearwater import";
state->cur_dive->dc.model = strdup("Shearwater import");
break;
}
}
@ -467,7 +473,7 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
return SQLITE_OK;
}
int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
extern "C" int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
struct parser_state state;
@ -490,7 +496,7 @@ int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *, int,
return 0;
}
int parse_shearwater_cloud_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
extern "C" int parse_shearwater_cloud_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
struct parser_state state;

View File

@ -4,6 +4,7 @@
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "ssrf.h"
#include "dive.h"
#include "parse.h"
#include "sample.h"
@ -20,7 +21,6 @@
static int dm4_events(void *param, int, char **data, char **)
{
using namespace std::string_literals;
struct parser_state *state = (struct parser_state *)param;
event_start(state);
@ -31,108 +31,108 @@ static int dm4_events(void *param, int, char **data, char **)
switch (atoi(data[2])) {
case 1:
/* 1 Mandatory Safety Stop */
state->cur_event.name = "safety stop (mandatory)"s;
strcpy(state->cur_event.name, "safety stop (mandatory)");
break;
case 3:
/* 3 Deco */
/* What is Subsurface's term for going to
* deco? */
state->cur_event.name = "deco"s;
strcpy(state->cur_event.name, "deco");
break;
case 4:
/* 4 Ascent warning */
state->cur_event.name = "ascent"s;
strcpy(state->cur_event.name, "ascent");
break;
case 5:
/* 5 Ceiling broken */
state->cur_event.name = "violation"s;
strcpy(state->cur_event.name, "violation");
break;
case 6:
/* 6 Mandatory safety stop ceiling error */
state->cur_event.name = "violation"s;
strcpy(state->cur_event.name, "violation");
break;
case 7:
/* 7 Below deco floor */
state->cur_event.name = "below floor"s;
strcpy(state->cur_event.name, "below floor");
break;
case 8:
/* 8 Dive time alarm */
state->cur_event.name = "divetime"s;
strcpy(state->cur_event.name, "divetime");
break;
case 9:
/* 9 Depth alarm */
state->cur_event.name = "maxdepth"s;
strcpy(state->cur_event.name, "maxdepth");
break;
case 10:
/* 10 OLF 80% */
case 11:
/* 11 OLF 100% */
state->cur_event.name = "OLF"s;
strcpy(state->cur_event.name, "OLF");
break;
case 12:
/* 12 High pO₂ */
state->cur_event.name = "PO2"s;
strcpy(state->cur_event.name, "PO2");
break;
case 13:
/* 13 Air time */
state->cur_event.name = "airtime"s;
strcpy(state->cur_event.name, "airtime");
break;
case 17:
/* 17 Ascent warning */
state->cur_event.name = "ascent"s;
strcpy(state->cur_event.name, "ascent");
break;
case 18:
/* 18 Ceiling error */
state->cur_event.name = "ceiling"s;
strcpy(state->cur_event.name, "ceiling");
break;
case 19:
/* 19 Surfaced */
state->cur_event.name = "surface"s;
strcpy(state->cur_event.name, "surface");
break;
case 20:
/* 20 Deco */
state->cur_event.name = "deco"s;
strcpy(state->cur_event.name, "deco");
break;
case 22:
case 32:
/* 22 Mandatory safety stop violation */
/* 32 Deep stop violation */
state->cur_event.name = "violation"s;
strcpy(state->cur_event.name, "violation");
break;
case 30:
/* Tissue level warning */
state->cur_event.name = "tissue warning"s;
strcpy(state->cur_event.name, "tissue warning");
break;
case 37:
/* Tank pressure alarm */
state->cur_event.name = "tank pressure"s;
strcpy(state->cur_event.name, "tank pressure");
break;
case 257:
/* 257 Dive active */
/* This seems to be given after surface when
* descending again. */
state->cur_event.name = "surface"s;
strcpy(state->cur_event.name, "surface");
break;
case 258:
/* 258 Bookmark */
if (data[3]) {
state->cur_event.name = "heading"s;
strcpy(state->cur_event.name, "heading");
state->cur_event.value = atoi(data[3]);
} else {
state->cur_event.name = "bookmark"s;
strcpy(state->cur_event.name, "bookmark");
}
break;
case 259:
/* Deep stop */
state->cur_event.name = "Deep stop"s;
strcpy(state->cur_event.name, "Deep stop");
break;
case 260:
/* Deep stop */
state->cur_event.name = "Deep stop cleared"s;
strcpy(state->cur_event.name, "Deep stop cleared");
break;
case 266:
/* Mandatory safety stop activated */
state->cur_event.name = "safety stop (mandatory)"s;
strcpy(state->cur_event.name, "safety stop (mandatory)");
break;
case 267:
/* 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 */
break;
default:
state->cur_event.name = "unknown"s;
strcpy(state->cur_event.name, "unknown");
state->cur_event.value = atoi(data[2]);
break;
}
@ -155,7 +155,7 @@ static int dm4_tags(void *param, int, char **data, char **)
struct parser_state *state = (struct parser_state *)param;
if (data[0])
taglist_add_tag(state->cur_dive->tags, data[0]);
taglist_add_tag(&state->cur_dive->tag_list, data[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]));
if (data[2])
utf8_string_std(data[2], &state->cur_dive->notes);
utf8_string(data[2], &state->cur_dive->notes);
/*
* 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])
state->cur_dive->duration.seconds = atoi(data[3]);
if (data[15])
state->cur_dive->dcs[0].duration.seconds = atoi(data[15]);
state->cur_dive->dc.duration.seconds = atoi(data[15]);
/*
* 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);
if (data[6])
state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000);
state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[6], NULL, 0) * 1000);
if (data[8])
state->cur_dive->dcs[0].airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
state->cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
if (data[9])
state->cur_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
state->cur_dive->dc.watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
/*
* 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)
cyl->end.mbar = (atoi(data[11]));
if (data[12])
cyl->type.size.mliter = lrint((permissive_strtod(data[12], NULL)) * 1000);
cyl->type.size.mliter = lrint((strtod_flags(data[12], NULL, 0)) * 1000);
if (data[13])
cyl->type.workingpressure.mbar = (atoi(data[13]));
if (data[20])
@ -237,7 +237,7 @@ static int dm4_dive(void *param, int, char **data, char **)
cylinder_end(state);
if (data[14])
state->cur_dive->dcs[0].surface_pressure.mbar = (atoi(data[14]) * 1000);
state->cur_dive->dc.surface_pressure.mbar = (atoi(data[14]) * 1000);
interval = data[16] ? atoi(data[16]) : 0;
profileBlob = (float *)data[17];
@ -249,7 +249,7 @@ static int dm4_dive(void *param, int, char **data, char **)
if (profileBlob)
state->cur_sample->depth.mm = lrintf(profileBlob[i] * 1000.0f);
else
state->cur_sample->depth.mm = state->cur_dive->dcs[0].maxdepth.mm;
state->cur_sample->depth.mm = state->cur_dive->dc.maxdepth.mm;
if (data[18] && data[18][0])
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;
}
int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
extern "C" int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
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
* value is 0 (and using metric units). So we just use
* the same 12 liters when size is not available */
if (permissive_strtod(data[6], NULL) == 0.0 && cyl->start.mbar)
if (strtod_flags(data[6], NULL, 0) == 0.0 && cyl->start.mbar)
cyl->type.size.mliter = 12000;
else
cyl->type.size.mliter = lrint((permissive_strtod(data[6], NULL)) * 1000);
cyl->type.size.mliter = lrint((strtod_flags(data[6], NULL, 0)) * 1000);
}
if (data[2])
cyl->gasmix.o2.permille = atoi(data[2]) * 10;
@ -329,20 +329,19 @@ static int dm5_cylinders(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;
event_start(state);
if (data[0])
state->cur_event.time.seconds = atoi(data[0]);
if (data[1]) {
state->cur_event.name = "gaschange"s;
state->cur_event.value = lrint(permissive_strtod(data[1], NULL));
strcpy(state->cur_event.name, "gaschange");
state->cur_event.value = lrint(strtod_flags(data[1], NULL, 0));
}
/* He part of the mix */
if (data[2])
state->cur_event.value += lrint(permissive_strtod(data[2], NULL)) << 16;
state->cur_event.value += lrint(strtod_flags(data[2], NULL, 0)) << 16;
event_end(state);
return 0;
@ -367,12 +366,12 @@ static int dm5_dive(void *param, int, char **data, char **)
state->cur_dive->when = (time_t)(atol(data[1]));
if (data[2])
utf8_string_std(data[2], &state->cur_dive->notes);
utf8_string(data[2], &state->cur_dive->notes);
if (data[3])
state->cur_dive->duration.seconds = atoi(data[3]);
if (data[15])
state->cur_dive->dcs[0].duration.seconds = atoi(data[15]);
state->cur_dive->dc.duration.seconds = atoi(data[15]);
/*
* TODO: the deviceid hash should be calculated here.
@ -390,28 +389,30 @@ static int dm5_dive(void *param, int, char **data, char **)
settings_end(state);
if (data[6])
state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000);
state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[6], NULL, 0) * 1000);
if (data[8])
state->cur_dive->dcs[0].airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
state->cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
if (data[9])
state->cur_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
state->cur_dive->dc.watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
if (data[4]) {
state->cur_dive->dcs[0].deviceid = atoi(data[4]);
state->cur_dive->dc.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])
utf8_string_std(data[5], &state->cur_dive->dcs[0].model);
utf8_string(data[5], (char **)&state->cur_dive->dc.model);
if (data[25]) {
switch(atoi(data[25])) {
case 1:
state->cur_dive->dcs[0].divemode = OC;
state->cur_dive->dc.divemode = OC;
break;
case 5:
state->cur_dive->dcs[0].divemode = CCR;
state->cur_dive->dc.divemode = CCR;
break;
default:
state->cur_dive->dcs[0].divemode = OC;
state->cur_dive->dc.divemode = OC;
break;
}
}
@ -424,7 +425,7 @@ static int dm5_dive(void *param, int, char **data, char **)
}
if (data[14])
state->cur_dive->dcs[0].surface_pressure.mbar = (atoi(data[14]) / 100);
state->cur_dive->dc.surface_pressure.mbar = (atoi(data[14]) / 100);
interval = data[16] ? atoi(data[16]) : 0;
@ -512,7 +513,7 @@ static int dm5_dive(void *param, int, char **data, char **)
if (profileBlob)
state->cur_sample->depth.mm = lrintf(profileBlob[i] * 1000.0f);
else
state->cur_sample->depth.mm = state->cur_dive->dcs[0].maxdepth.mm;
state->cur_sample->depth.mm = state->cur_dive->dc.maxdepth.mm;
if (data[18] && data[18][0])
state->cur_sample->temperature.mkelvin = C_to_mkelvin(tempBlob[i]);
@ -548,7 +549,7 @@ static int dm5_dive(void *param, int, char **data, char **)
return SQLITE_OK;
}
int parse_dm5_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
extern "C" int parse_dm5_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log)
{
int retval;
char *err = NULL;

View File

@ -32,11 +32,13 @@ static std::string make_default_filename()
return system_default_path() + "/subsurface.xml";
}
extern "C" {
const char mac_system_divelist_default_font[] = "Arial";
const char *system_divelist_default_font = mac_system_divelist_default_font;
double system_divelist_default_font_size = -1.0;
void subsurface_OS_pref_setup()
void subsurface_OS_pref_setup(void)
{
// nothing
}
@ -47,13 +49,13 @@ bool subsurface_ignore_font(const char*)
return false;
}
const char *system_default_directory()
const char *system_default_directory(void)
{
static const std::string path = system_default_path();
return path.c_str();
}
const char *system_default_filename()
const char *system_default_filename(void)
{
static const std::string fn = make_default_filename();
return fn.c_str();
@ -107,12 +109,12 @@ int subsurface_zip_close(struct zip *zip)
}
/* win32 console */
void subsurface_console_init()
void subsurface_console_init(void)
{
/* NOP */
}
void subsurface_console_exit()
void subsurface_console_exit(void)
{
/* NOP */
}
@ -121,3 +123,4 @@ bool subsurface_user_is_root()
{
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,6 @@
#include <stdint.h>
#include <stdio.h>
#include <string>
/* libdivecomputer */
@ -21,42 +20,47 @@
#define dc_usb_storage_open(stream, context, devname) (DC_STATUS_UNSUPPORTED)
#endif
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
struct dive;
struct divelog;
struct devices;
struct device_data_t {
dc_descriptor_t *descriptor = nullptr;
std::string vendor, product, devname;
std::string model, btname;
unsigned char *fingerprint = nullptr;
unsigned int fsize = 0, fdeviceid = 0, fdiveid = 0;
struct dc_event_devinfo_t devinfo = { };
uint32_t diveid = 0;
dc_device_t *device = nullptr;
dc_context_t *context = nullptr;
dc_iostream_t *iostream = nullptr;
bool force_download = false;
bool libdc_log = false;
bool libdc_dump = false;
bool bluetooth_mode = false;
bool sync_time = false;
FILE *libdc_logfile = nullptr;
struct divelog *log = nullptr;
void *androidUsbDeviceDescriptor = nullptr;
device_data_t();
~device_data_t();
};
typedef struct {
dc_descriptor_t *descriptor;
const char *vendor, *product, *devname;
const char *model, *btname;
unsigned char *fingerprint;
unsigned int fsize, fdeviceid, fdiveid;
struct dc_event_devinfo_t devinfo;
uint32_t diveid;
dc_device_t *device;
dc_context_t *context;
dc_iostream_t *iostream;
bool force_download;
bool libdc_log;
bool libdc_dump;
bool bluetooth_mode;
bool sync_time;
FILE *libdc_logfile;
struct divelog *log;
void *androidUsbDeviceDescriptor;
} device_data_t;
const char *errmsg (dc_status_t rc);
std::string do_libdivecomputer_import(device_data_t *data);
const char *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);
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);
extern int import_thread_cancelled;
extern std::string progress_bar_text;
extern void (*progress_callback)(const std::string &text);
extern const char *progress_bar_text;
extern void (*progress_callback)(const char *text);
extern double progress_bar_fraction;
dc_status_t ble_packet_open(dc_iostream_t **iostream, dc_context_t *context, const char* devaddr, void *userdata);
@ -68,7 +72,13 @@ dc_status_t divecomputer_device_open(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 dumpfile_name;
#endif
#endif // LIBDIVECOMPUTER_H

View File

@ -2,6 +2,7 @@
#include <string.h>
#include <stdlib.h>
#include "ssrf.h"
#include "divesite.h"
#include "dive.h"
#include "divelog.h"
@ -131,23 +132,25 @@ static int handle_event_ver3(int code, const unsigned char *ps, unsigned int ps_
return skip;
}
static void parse_dives(int log_version, const unsigned char *buf, unsigned int buf_size, struct dive_table &table, dive_site_table &sites)
static void parse_dives(int log_version, const unsigned char *buf, unsigned int buf_size, struct dive_table *table, struct dive_site_table *sites)
{
unsigned int ptr = 0;
unsigned char model;
struct dive *dive;
struct divecomputer *dc;
struct sample *sample;
while (ptr < buf_size) {
int i;
auto dive = std::make_unique<struct dive>();
dive = alloc_dive();
memset(&sensor_ids, 0, sizeof(sensor_ids));
dc = &dive->dcs[0];
dc = &dive->dc;
/* Just the main cylinder until we can handle the buddy cylinder porperly */
for (i = 0; i < 1; i++) {
cylinder_t cyl = default_cylinder(dive.get());
cylinder_t cyl = empty_cylinder;
fill_default_cylinder(dive, &cyl);
add_cylinder(&dive->cylinders, i, cyl);
}
@ -155,17 +158,17 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
model = *(buf + ptr);
switch (model) {
case 0:
dc->model = "Xen";
dc->model = strdup("Xen");
break;
case 1:
case 2:
dc->model = "Xeo";
dc->model = strdup("Xeo");
break;
case 4:
dc->model = "Lynx";
dc->model = strdup("Lynx");
break;
default:
dc->model = "Liquivision";
dc->model = strdup("Liquivision");
break;
}
ptr++;
@ -188,7 +191,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
/* Store the location only if we have one */
if (!location.empty())
sites.find_or_create(location)->add_dive(dive.get());
add_dive_to_dive_site(dive, find_or_create_dive_site_with_name(location.c_str(), sites));
ptr += len + 4 + place_len;
@ -199,7 +202,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
// Blank notes are better than the default text
std::string notes((char *)buf + ptr, len);
if (!starts_with(notes, "Comment ..."))
dive->notes = notes;
dive->notes = strdup(notes.c_str());
ptr += len;
dive->id = array_uint32_le(buf + ptr);
@ -333,6 +336,7 @@ 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->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);
finish_sample(dc);
break;
} else if (event.time > sample_time) {
@ -340,6 +344,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
sample->time.seconds = sample_time;
sample->depth.mm = depth_mm;
sample->temperature.mkelvin = temp_mk;
finish_sample(dc);
d++;
continue;
@ -348,6 +353,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
sample->depth.mm = depth_mm;
sample->temperature.mkelvin = temp_mk;
add_sample_pressure(sample, event.pressure.sensor, event.pressure.mbar);
finish_sample(dc);
d++;
break;
@ -366,6 +372,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
sample->temperature.mkelvin = last_temp + (temp_mk - last_temp)
* ((int)event.time - (int)last_time) / sample_interval;
}
finish_sample(dc);
break;
}
@ -380,6 +387,7 @@ 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->temperature.mkelvin =
C_to_mkelvin((float)array_uint16_le(ts + d * 2) / 10);
finish_sample(dc);
}
if (log_version == 3 && model == 4) {
@ -407,11 +415,17 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int
}
// End dive
table.record_dive(std::move(dive));
record_dive_to_table(dive, table);
dive = NULL;
// Advance ptr for next dive
ptr += ps_ptr + 4;
} // 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)

View File

@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
#include "ssrf.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
@ -37,8 +38,8 @@ std::string saved_git_id;
struct git_parser_state {
git_repository *repo = nullptr;
struct divecomputer *active_dc = nullptr;
std::unique_ptr<dive> active_dive;
std::unique_ptr<dive_trip> active_trip;
struct dive *active_dive = nullptr;
dive_trip_t *active_trip = nullptr;
std::string fulltext_mode;
std::string fulltext_query;
std::string filter_constraint_type;
@ -46,7 +47,7 @@ struct git_parser_state {
std::string filter_constraint_range_mode;
bool filter_constraint_negate = false;
std::string filter_constraint_data;
struct picture active_pic;
struct picture active_pic = { 0 };
struct dive_site *active_site = nullptr;
std::unique_ptr<filter_preset> active_filter;
struct divelog *log = nullptr;
@ -171,16 +172,16 @@ static int get_hex(const char *line)
static void parse_dive_gps(char *line, struct git_parser_state *state)
{
location_t location;
struct dive_site *ds = get_dive_site_for_dive(state->active_dive.get());
struct dive_site *ds = get_dive_site_for_dive(state->active_dive);
parse_location(line, &location);
if (!ds) {
ds = state->log->sites.get_by_gps(&location);
ds = get_dive_site_by_gps(&location, state->log->sites);
if (!ds)
ds = state->log->sites.create(std::string(), location);
ds->add_dive(state->active_dive.get());
ds = create_dive_site_with_gps("", &location, state->log->sites);
add_dive_to_dive_site(state->active_dive, ds);
} else {
if (dive_site_has_gps_location(ds) && ds->location != location) {
if (dive_site_has_gps_location(ds) && !same_location(&ds->location, &location)) {
std::string coords = printGPSCoordsC(&location);
// we have a dive site that already has GPS coordinates
// note 1: there will be much less copying once the core
@ -188,8 +189,10 @@ static void parse_dive_gps(char *line, struct git_parser_state *state)
// note 2: we could include the first newline in the
// translation string, but that would be weird and cause
// a new string.
ds->notes += '\n';
ds->notes += format_string_std(translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords.c_str());
std::string new_text = std::string(ds->notes) + '\n' +
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;
}
@ -207,43 +210,54 @@ static std::string get_first_converted_string(struct git_parser_state *state)
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)
{
std::string name = get_first_converted_string(state);
struct dive_site *ds = get_dive_site_for_dive(state->active_dive.get());
struct dive_site *ds = get_dive_site_for_dive(state->active_dive);
if (!ds) {
ds = state->log->sites.get_by_name(name);
ds = get_dive_site_by_name(name.c_str(), state->log->sites);
if (!ds)
ds = state->log->sites.create(name);
ds->add_dive(state->active_dive.get());
ds = create_dive_site(name.c_str(), state->log->sites);
add_dive_to_dive_site(state->active_dive, ds);
} else {
// we already had a dive site linked to the dive
if (ds->name.empty()) {
ds->name = name.c_str();
if (empty_string(ds->name)) {
free(ds->name); // empty_string could mean pointer to a 0-byte!
ds->name = strdup(name.c_str());
} else {
// and that dive site had a name. that's weird - if our name is different, add it to the notes
if (ds->name == name) {
ds->notes += '\n';
ds->notes += format_string_std(translate("gettextFromC", "additional name for site: %s\n"), name.c_str());
if (!same_string(ds->name, name.c_str())) {
std::string new_string = std::string(ds->notes) + '\n' +
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)
{ state->active_dive->diveguide = get_first_converted_string(state); }
{ state->active_dive->diveguide = get_first_converted_string_c(state); }
static void parse_dive_buddy(char *, struct git_parser_state *state)
{ state->active_dive->buddy = get_first_converted_string(state); }
{ state->active_dive->buddy = get_first_converted_string_c(state); }
static void parse_dive_suit(char *, struct git_parser_state *state)
{ state->active_dive->suit = get_first_converted_string(state); }
{ state->active_dive->suit = get_first_converted_string_c(state); }
static void parse_dive_notes(char *, struct git_parser_state *state)
{ state->active_dive->notes = get_first_converted_string(state); }
{ state->active_dive->notes = get_first_converted_string_c(state); }
static void parse_dive_divesiteid(char *line, struct git_parser_state *state)
{ state->log->sites.get_by_uuid(get_hex(line))->add_dive(state->active_dive.get()); }
{ add_dive_to_dive_site(state->active_dive, get_dive_site_by_uuid(get_hex(line), state->log->sites)); }
/*
* We can have multiple tags.
@ -252,7 +266,7 @@ static void parse_dive_tags(char *, struct git_parser_state *state)
{
for (const std::string &tag: state->converted_strings) {
if (!tag.empty())
taglist_add_tag(state->active_dive->tags, tag.c_str());
taglist_add_tag(&state->active_dive->tag_list, tag.c_str());
}
}
@ -300,13 +314,13 @@ static void parse_dive_invalid(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(state); }
{ state->active_site->description = get_first_converted_string_c(state); }
static void parse_site_name(char *, struct git_parser_state *state)
{ state->active_site->name = get_first_converted_string(state); }
{ state->active_site->name = get_first_converted_string_c(state); }
static void parse_site_notes(char *, struct git_parser_state *state)
{ state->active_site->notes = get_first_converted_string(state); }
{ state->active_site->notes = get_first_converted_string_c(state); }
static void parse_site_gps(char *line, struct git_parser_state *state)
{
@ -318,8 +332,8 @@ static void parse_site_geo(char *line, struct git_parser_state *state)
int origin;
int category;
sscanf(line, "cat %d origin %d \"", &category, &origin);
taxonomy_set_category(state->active_site->taxonomy, (taxonomy_category)category,
get_first_converted_string(state), (taxonomy_origin)origin);
taxonomy_set_category(&state->active_site->taxonomy, (taxonomy_category)category,
get_first_converted_string(state).c_str(), (taxonomy_origin)origin);
}
static std::string pop_cstring(struct git_parser_state *state, const char *err)
@ -376,7 +390,7 @@ static void parse_cylinder_keyvalue(void *_cylinder, const char *key, const std:
return;
}
if (!strcmp(key, "description")) {
cylinder->type.description = value;
cylinder->type.description = strdup(value.c_str());
return;
}
if (!strcmp(key, "o2")) {
@ -420,7 +434,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)
{
cylinder_t cylinder;
cylinder_t cylinder = empty_cylinder;
for (;;) {
char c;
@ -431,9 +445,9 @@ static void parse_dive_cylinder(char *line, struct git_parser_state *state)
line = parse_keyvalue_entry(parse_cylinder_keyvalue, &cylinder, line, state);
}
if (cylinder.cylinder_use == OXYGEN)
state->o2pressure_sensor = static_cast<int>(state->active_dive->cylinders.size());
state->o2pressure_sensor = state->active_dive->cylinders.nr;
state->active_dive->cylinders.push_back(std::move(cylinder));
add_cylinder(&state->active_dive->cylinders, state->active_dive->cylinders.nr, cylinder);
}
static void parse_weightsystem_keyvalue(void *_ws, const char *key, const std::string &value)
@ -444,7 +458,7 @@ static void parse_weightsystem_keyvalue(void *_ws, const char *key, const std::s
return;
}
if (!strcmp(key, "description")) {
ws->description = value;
ws->description = strdup(value.c_str());
return;
}
report_error("Unknown weightsystem key/value pair (%s/%s)", key, value.c_str());
@ -452,7 +466,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)
{
weightsystem_t ws;
weightsystem_t ws = empty_weightsystem;
for (;;) {
char c;
@ -463,7 +477,7 @@ static void parse_dive_weightsystem(char *line, struct git_parser_state *state)
line = parse_keyvalue_entry(parse_weightsystem_keyvalue, &ws, line, state);
}
state->active_dive->weightsystems.push_back(std::move(ws));
add_to_weightsystem_table(&state->active_dive->weightsystems, state->active_dive->weightsystems.nr, ws);
}
static int match_action(char *line, void *data,
@ -642,7 +656,7 @@ static char *parse_sample_unit(struct sample *sample, double val, char *unit)
*/
static int sanitize_sensor_id(const struct dive *d, int nr)
{
return d && nr >= 0 && static_cast<size_t>(nr) < d->cylinders.size() ? nr : NO_SENSOR;
return d && nr >= 0 && nr < d->cylinders.nr ? nr : NO_SENSOR;
}
/*
@ -666,14 +680,13 @@ static int sanitize_sensor_id(const struct dive *d, int nr)
static struct sample *new_sample(struct git_parser_state *state)
{
struct sample *sample = prepare_sample(state->active_dc);
size_t num_samples = state->active_dc->samples.size();
if (num_samples >= 2) {
*sample = state->active_dc->samples[num_samples - 2];
if (sample != state->active_dc->sample) {
memcpy(sample, sample - 1, sizeof(struct sample));
sample->pressure[0].mbar = 0;
sample->pressure[1].mbar = 0;
} else {
sample->sensor[0] = sanitize_sensor_id(state->active_dive.get(), !state->o2pressure_sensor);
sample->sensor[1] = sanitize_sensor_id(state->active_dive.get(), state->o2pressure_sensor);
sample->sensor[0] = sanitize_sensor_id(state->active_dive, !state->o2pressure_sensor);
sample->sensor[1] = sanitize_sensor_id(state->active_dive, state->o2pressure_sensor);
}
return sample;
}
@ -709,6 +722,7 @@ static void sample_parser(char *line, struct git_parser_state *state)
line = parse_sample_unit(sample, val, line);
}
}
finish_sample(state->active_dc);
}
static void parse_dc_airtemp(char *line, struct git_parser_state *state)
@ -741,7 +755,7 @@ static void parse_dc_meandepth(char *line, struct git_parser_state *state)
{ state->active_dc->meandepth = get_depth(line); }
static void parse_dc_model(char *, struct git_parser_state *state)
{ state->active_dc->model = get_first_converted_string(state); }
{ state->active_dc->model = get_first_converted_string_c(state); }
static void parse_dc_numberofoxygensensors(char *line, struct git_parser_state *state)
{ state->active_dc->no_o2sensors = get_index(line); }
@ -781,7 +795,7 @@ static int get_divemode(const char *divemodestring) {
struct parse_event {
std::string name;
int has_divemode = false;
struct event ev;
struct event ev = { 0 };
};
static void parse_event_keyvalue(void *_parse, const char *key, const std::string &value)
@ -819,13 +833,14 @@ static void parse_dc_keyvalue(char *line, struct git_parser_state *state)
if (state->converted_strings.size() != 2)
return;
add_extra_data(state->active_dc, state->converted_strings[0], state->converted_strings[1]);
add_extra_data(state->active_dc, state->converted_strings[0].c_str(), state->converted_strings[1].c_str());
}
static void parse_dc_event(char *line, struct git_parser_state *state)
{
int m, s = 0;
struct parse_event p;
struct event *ev;
m = strtol(line, &line, 10);
if (*line == ':')
@ -845,17 +860,17 @@ static void parse_dc_event(char *line, struct git_parser_state *state)
if (p.has_divemode && p.name != "modechange")
p.name = "modechange";
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());
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.
* Better to mark them being CCR on import so no need for special treatments elsewhere on
* the code.
*/
if (p.ev.time.seconds == 0 && p.ev.type == SAMPLE_EVENT_PO2 && p.ev.value && state->active_dc->divemode==OC)
if (ev && p.ev.time.seconds == 0 && p.ev.type == SAMPLE_EVENT_PO2 && p.ev.value && state->active_dc->divemode==OC)
state->active_dc->divemode = CCR;
if (ev->is_gaschange()) {
if (ev && event_is_gaschange(ev)) {
/*
* We subtract one here because "0" is "no index",
* and the parsing will add one for actual cylinder
@ -876,10 +891,10 @@ static void parse_trip_time(char *, struct git_parser_state *)
{ }
static void parse_trip_location(char *, struct git_parser_state *state)
{ state->active_trip->location = get_first_converted_string(state); }
{ state->active_trip->location = get_first_converted_string_c(state); }
static void parse_trip_notes(char *, struct git_parser_state *state)
{ state->active_trip->notes = get_first_converted_string(state); }
{ state->active_trip->notes = get_first_converted_string_c(state); }
static void parse_settings_autogroup(char *, struct git_parser_state *state)
{
@ -1029,13 +1044,13 @@ static void parse_settings_fingerprint(char *line, struct git_parser_state *stat
}
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());
create_fingerprint_node_from_hex(fingerprints, fph.model, fph.serial,
fph.hex_data, fph.fdeviceid, fph.fdiveid);
create_fingerprint_node_from_hex(&fingerprint_table, fph.model, fph.serial,
fph.hex_data.c_str(), fph.fdeviceid, fph.fdiveid);
}
static void parse_picture_filename(char *, struct git_parser_state *state)
{
state->active_pic.filename = get_first_converted_string(state);
state->active_pic.filename = get_first_converted_string_c(state);
}
static void parse_picture_gps(char *line, struct git_parser_state *state)
@ -1170,10 +1185,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);
}
state->active_filter->add_constraint(state->filter_constraint_type,
state->filter_constraint_string_mode,
state->filter_constraint_range_mode,
state->filter_constraint_negate, state->filter_constraint_data);
filter_preset_add_constraint(state->active_filter.get(), state->filter_constraint_type.c_str(),
state->filter_constraint_string_mode.c_str(),
state->filter_constraint_range_mode.c_str(),
state->filter_constraint_negate, state->filter_constraint_data.c_str());
state->filter_constraint_type.clear();
state->filter_constraint_string_mode.clear();
state->filter_constraint_range_mode.clear();
@ -1207,14 +1222,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);
}
state->active_filter.get()->set_fulltext(std::move(state->fulltext_query), state->fulltext_mode);
filter_preset_set_fulltext(state->active_filter.get(), state->fulltext_query.c_str(), state->fulltext_mode.c_str());
state->fulltext_mode.clear();
state->fulltext_query.clear();
}
static void parse_filter_preset_name(char *, struct git_parser_state *state)
{
state->active_filter->name = get_first_converted_string(state);
filter_preset_set_name(state->active_filter.get(), get_first_converted_string_c(state));
}
/* These need to be sorted! */
@ -1368,27 +1383,33 @@ 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)
{
auto &trip = state->active_trip;
dive_trip_t *trip = state->active_trip;
if (trip)
state->log->trips.put(std::move(trip));
if (trip) {
state->active_trip = NULL;
insert_trip(trip, state->log->trips);
}
}
static void finish_active_dive(struct git_parser_state *state)
{
if (state->active_dive)
state->log->dives.record_dive(std::move(state->active_dive));
struct dive *dive = 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)
{
state->active_dive = std::make_unique<dive>();
state->active_dive = alloc_dive();
/* We'll fill in more data from the dive file */
state->active_dive->when = when;
if (state->active_trip)
state->active_trip->add_dive(state->active_dive.get());
add_dive_to_trip(state->active_dive, state->active_trip);
}
static bool validate_date(int yyyy, int mm, int dd)
@ -1418,7 +1439,7 @@ static int dive_trip_directory(const char *root, const char *name, struct git_pa
if (!validate_date(yyyy, mm, dd))
return GIT_WALK_SKIP;
finish_active_trip(state);
state->active_trip = std::make_unique<dive_trip>();
state->active_trip = alloc_trip();
return GIT_WALK_OK;
}
@ -1628,12 +1649,17 @@ static git_blob *git_tree_entry_blob(git_repository *repo, const git_tree_entry
static struct divecomputer *create_new_dc(struct dive *dive)
{
struct divecomputer *dc = &dive->dcs.back();
struct divecomputer *dc = &dive->dc;
while (dc->next)
dc = dc->next;
/* Did we already fill that in? */
if (!dc->samples.empty() || !dc->model.empty() || dc->when) {
dive->dcs.emplace_back();
dc = &dive->dcs.back();
if (dc->samples || dc->model || dc->when) {
struct divecomputer *newdc = (divecomputer *)calloc(1, sizeof(*newdc));
if (!newdc)
return NULL;
dc->next = newdc;
dc = newdc;
}
dc->when = dive->when;
dc->duration = dive->duration;
@ -1653,7 +1679,7 @@ static int parse_divecomputer_entry(struct git_parser_state *state, const git_tr
if (!blob)
return report_error("Unable to read divecomputer file");
state->active_dc = create_new_dc(state->active_dive.get());
state->active_dc = create_new_dc(state->active_dive);
for_each_line(blob, divecomputer_parser, state);
git_blob_free(blob);
state->active_dc = NULL;
@ -1668,12 +1694,13 @@ 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)
{
struct dive *dive = state->active_dive;
git_blob *blob = git_tree_entry_blob(state->repo, entry);
if (!blob)
return report_error("Unable to read dive file");
if (*suffix)
state->active_dive->number = atoi(suffix + 1);
state->active_dive->weightsystems.clear();
dive->number = atoi(suffix + 1);
clear_weightsystem_table(&state->active_dive->weightsystems);
state->o2pressure_sensor = 1;
for_each_line(blob, dive_parser, state);
git_blob_free(blob);
@ -1685,7 +1712,7 @@ static int parse_site_entry(struct git_parser_state *state, const git_tree_entry
if (*suffix == '\0')
return report_error("Dive site without uuid");
uint32_t uuid = strtoul(suffix, NULL, 16);
state->active_site = state->log->sites.alloc_or_get(uuid);
state->active_site = alloc_or_get_dive_site(uuid, state->log->sites);
git_blob *blob = git_tree_entry_blob(state->repo, entry);
if (!blob)
return report_error("Unable to read dive site file");
@ -1741,12 +1768,12 @@ static int parse_picture_entry(struct git_parser_state *state, const git_tree_en
state->active_pic.offset.seconds = offset;
for_each_line(blob, picture_parser, state);
add_picture(state->active_dive->pictures, std::move(state->active_pic));
add_picture(&state->active_dive->pictures, state->active_pic);
git_blob_free(blob);
/* add_picture took ownership of the data -
* clear out our copy just to be sure. */
state->active_pic = picture();
state->active_pic = empty_picture;
return 0;
}
@ -1761,7 +1788,7 @@ static int parse_filter_preset(struct git_parser_state *state, const git_tree_en
git_blob_free(blob);
state->log->filter_presets.add(*state->active_filter);
add_filter_preset_to_table(state->active_filter.get(), state->log->filter_presets);
state->active_filter.reset();
return 0;
@ -1769,8 +1796,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)
{
auto &dive = state->active_dive;
auto &trip = state->active_trip;
struct dive *dive = state->active_dive;
dive_trip_t *trip = state->active_trip;
const char *name = git_tree_entry_name(entry);
if (verbose > 1)
report_info("git load handling file %s\n", name);
@ -1800,7 +1827,7 @@ static int walk_tree_file(const char *root, const git_tree_entry *entry, struct
return parse_settings_entry(state, entry);
break;
}
report_error("Unknown file %s%s (%p %p)", root, name, dive.get(), trip.get());
report_error("Unknown file %s%s (%p %p)", root, name, dive, trip);
return GIT_WALK_SKIP;
}
@ -1823,12 +1850,12 @@ static int load_dives_from_tree(git_repository *repo, git_tree *tree, struct git
return 0;
}
void clear_git_id()
extern "C" void clear_git_id(void)
{
saved_git_id.clear();
}
void set_git_id(const struct git_oid *id)
extern "C" void set_git_id(const struct git_oid *id)
{
char git_id_buffer[GIT_OID_HEXSZ + 1];

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/* macos.c */
/* implements Mac OS X specific functions */
#include "ssrf.h"
#include <stdlib.h>
#include <dirent.h>
#include <fnmatch.h>
@ -47,11 +48,13 @@ static std::string make_default_filename()
return system_default_path() + "/" + user + ".xml";
}
extern "C" {
const char mac_system_divelist_default_font[] = "Arial";
const char *system_divelist_default_font = mac_system_divelist_default_font;
double system_divelist_default_font_size = -1.0;
void subsurface_OS_pref_setup()
void subsurface_OS_pref_setup(void)
{
// nothing
}
@ -62,13 +65,13 @@ bool subsurface_ignore_font(const char *)
return false;
}
const char *system_default_directory()
const char *system_default_directory(void)
{
static const std::string path = system_default_path();
return path.c_str();
}
const char *system_default_filename()
const char *system_default_filename(void)
{
static const std::string fn = make_default_filename();
return fn.c_str();
@ -182,12 +185,12 @@ int subsurface_zip_close(struct zip *zip)
}
/* win32 console */
void subsurface_console_init()
void subsurface_console_init(void)
{
/* NOP */
}
void subsurface_console_exit()
void subsurface_console_exit(void)
{
/* NOP */
}
@ -196,3 +199,5 @@ bool subsurface_user_is_root()
{
return geteuid() == 0;
}
}

Some files were not shown because too many files have changed in this diff Show More