2017-04-27 18:18:03 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2017-03-11 20:08:31 +00:00
|
|
|
#ifdef __clang__
|
2016-03-09 18:19:38 +00:00
|
|
|
// Clang has a bug on zero-initialization of C structs.
|
|
|
|
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
2017-03-11 20:08:31 +00:00
|
|
|
#endif
|
2016-03-09 18:19:38 +00:00
|
|
|
|
2011-09-01 23:27:52 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <errno.h>
|
2013-10-19 17:35:36 +00:00
|
|
|
#include <unistd.h>
|
Initial implementation of git save format
This saves the dive data into a git object repository instead of a
single XML file.
We create a git object tree with each dive as a separate file,
hierarchically by trip and date.
NOTE 1: This largely duplicates the XML saving code, because trying to
share it seemed just too painful: the logic is very similar, but the
details of the actual strings end up differing sufficiently that there
are tons of trivial differences.
The git save format is line-based with minimal quoting, while XML quotes
everything with either "<..\>" or using single quotes around attributes.
NOTE 2: You currently need a dummy "file" to save to, which points to
the real save location: the git repository and branch to be used. We
should make this a config thing, but for testing, do something like
this:
echo git /home/torvalds/scuba:linus > git-test
to create that git information file, and when you use "Save To" and
specify "git-test" as the file to save to, subsurface will use the new
git save logic to save to the branch "linus" in the repository found at
"/home/torvalds/scuba".
NOTE 3: The git save format uses just the git object directory, it does
*not* check out the result in any git working tree or index. So after
you do a save, you can do
git log -p linus
to see what actually happened in that branch, but it will not affect any
actual checked-out state in the repository.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-03-06 21:28:39 +00:00
|
|
|
#include <fcntl.h>
|
2011-09-01 23:27:52 +00:00
|
|
|
|
2020-05-01 11:43:52 +00:00
|
|
|
#include "dive.h"
|
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
|
|
|
#include "divelog.h"
|
2019-03-04 22:20:29 +00:00
|
|
|
#include "divesite.h"
|
2019-08-05 17:41:15 +00:00
|
|
|
#include "errorhelper.h"
|
2020-10-25 12:28:55 +00:00
|
|
|
#include "extradata.h"
|
2020-06-08 21:07:46 +00:00
|
|
|
#include "filterconstraint.h"
|
|
|
|
#include "filterpreset.h"
|
2020-10-25 12:28:55 +00:00
|
|
|
#include "sample.h"
|
2018-05-11 15:25:41 +00:00
|
|
|
#include "subsurface-string.h"
|
2020-05-01 12:07:59 +00:00
|
|
|
#include "subsurface-time.h"
|
2019-05-31 14:09:14 +00:00
|
|
|
#include "trip.h"
|
2013-01-09 20:07:09 +00:00
|
|
|
#include "device.h"
|
2020-10-25 08:14:16 +00:00
|
|
|
#include "event.h"
|
2019-08-05 18:07:10 +00:00
|
|
|
#include "file.h"
|
2014-01-16 02:03:11 +00:00
|
|
|
#include "membuffer.h"
|
2020-04-10 07:42:14 +00:00
|
|
|
#include "picture.h"
|
2015-06-13 15:01:06 +00:00
|
|
|
#include "git-access.h"
|
2018-02-24 22:28:13 +00:00
|
|
|
#include "qthelper.h"
|
2019-04-06 19:05:18 +00:00
|
|
|
#include "gettext.h"
|
2019-05-30 16:29:36 +00:00
|
|
|
#include "tag.h"
|
2020-10-17 18:15:23 +00:00
|
|
|
#include "xmlparams.h"
|
2011-09-01 23:27:52 +00:00
|
|
|
|
2011-09-02 02:56:04 +00:00
|
|
|
/*
|
|
|
|
* We're outputting utf8 in xml.
|
|
|
|
* We need to quote the characters <, >, &.
|
|
|
|
*
|
2011-09-02 03:28:17 +00:00
|
|
|
* Technically I don't think we'd necessarily need to quote the control
|
|
|
|
* characters, but at least libxml2 doesn't like them. It doesn't even
|
|
|
|
* allow them quoted. So we just skip them and replace them with '?'.
|
|
|
|
*
|
2012-08-27 20:19:06 +00:00
|
|
|
* If we do this for attributes, we need to quote the quotes we use too.
|
2011-09-02 02:56:04 +00:00
|
|
|
*/
|
2014-01-16 02:03:11 +00:00
|
|
|
static void quote(struct membuffer *b, const char *text, int is_attribute)
|
2011-09-02 02:56:04 +00:00
|
|
|
{
|
2014-06-02 17:10:54 +00:00
|
|
|
int is_html = 0;
|
|
|
|
put_quoted(b, text, is_attribute, is_html);
|
2011-09-02 02:56:04 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
static void show_utf8(struct membuffer *b, const char *text, const char *pre, const char *post, int is_attribute)
|
2011-09-02 02:56:04 +00:00
|
|
|
{
|
|
|
|
int len;
|
2014-05-06 22:27:49 +00:00
|
|
|
|
|
|
|
if (!text)
|
|
|
|
return;
|
2014-05-07 16:28:26 +00:00
|
|
|
/* remove leading and trailing space */
|
2014-07-10 19:37:29 +00:00
|
|
|
/* We need to combine isascii() with isspace(),
|
|
|
|
* because we can only trust isspace() with 7-bit ascii,
|
|
|
|
* on windows for example */
|
|
|
|
while (isascii(*text) && isspace(*text))
|
2014-05-06 22:27:49 +00:00
|
|
|
text++;
|
|
|
|
len = strlen(text);
|
|
|
|
if (!len)
|
|
|
|
return;
|
2014-07-10 19:37:29 +00:00
|
|
|
while (len && isascii(text[len - 1]) && isspace(text[len - 1]))
|
2014-05-06 22:27:49 +00:00
|
|
|
len--;
|
2024-02-28 05:36:48 +00:00
|
|
|
std::string cleaned(text, len);
|
2014-01-16 02:03:11 +00:00
|
|
|
put_string(b, pre);
|
2024-02-28 05:36:48 +00:00
|
|
|
quote(b, cleaned.c_str(), is_attribute);
|
2014-01-16 02:03:11 +00:00
|
|
|
put_string(b, post);
|
2011-09-02 02:56:04 +00:00
|
|
|
}
|
|
|
|
|
2024-02-28 05:36:48 +00:00
|
|
|
static void blankout(std::string &s)
|
2018-09-10 12:47:56 +00:00
|
|
|
{
|
2024-02-28 05:36:48 +00:00
|
|
|
for(char &c: s) {
|
|
|
|
switch (c) {
|
2018-09-10 12:47:56 +00:00
|
|
|
case 'A'...'Z':
|
2024-02-28 05:36:48 +00:00
|
|
|
c = 'X';
|
2018-09-10 12:47:56 +00:00
|
|
|
break;
|
|
|
|
case 'a'...'z':
|
2024-02-28 05:36:48 +00:00
|
|
|
c = 'x';
|
2018-09-10 12:47:56 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void show_utf8_blanked(struct membuffer *b, const char *text, const char *pre, const char *post, int is_attribute, bool anonymize)
|
|
|
|
{
|
|
|
|
if (!text)
|
|
|
|
return;
|
2024-02-28 05:36:48 +00:00
|
|
|
std::string copy(text);
|
2018-09-10 12:47:56 +00:00
|
|
|
|
|
|
|
if (anonymize)
|
|
|
|
blankout(copy);
|
2024-02-28 05:36:48 +00:00
|
|
|
show_utf8(b, copy.c_str(), pre, post, is_attribute);
|
2018-09-10 12:47:56 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
static void save_depths(struct membuffer *b, struct divecomputer *dc)
|
2011-09-05 16:39:55 +00:00
|
|
|
{
|
|
|
|
/* What's the point of this dive entry again? */
|
2013-01-23 18:25:31 +00:00
|
|
|
if (!dc->maxdepth.mm && !dc->meandepth.mm)
|
2011-09-05 16:39:55 +00:00
|
|
|
return;
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
put_string(b, " <depth");
|
|
|
|
put_depth(b, dc->maxdepth, " max='", " m'");
|
|
|
|
put_depth(b, dc->meandepth, " mean='", " m'");
|
|
|
|
put_string(b, " />\n");
|
2011-09-05 16:39:55 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
static void save_dive_temperature(struct membuffer *b, struct dive *dive)
|
2013-02-14 17:44:18 +00:00
|
|
|
{
|
2013-11-29 20:05:21 +00:00
|
|
|
if (!dive->airtemp.mkelvin && !dive->watertemp.mkelvin)
|
2013-02-14 23:18:48 +00:00
|
|
|
return;
|
2024-05-27 15:09:48 +00:00
|
|
|
if (dive->airtemp.mkelvin == dc_airtemp(dive).mkelvin && dive->watertemp.mkelvin == dc_watertemp(dive).mkelvin)
|
2013-02-14 23:18:48 +00:00
|
|
|
return;
|
2013-02-14 17:44:18 +00:00
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
put_string(b, " <divetemperature");
|
2024-05-27 15:09:48 +00:00
|
|
|
if (dive->airtemp.mkelvin != dc_airtemp(dive).mkelvin)
|
2014-01-16 02:03:11 +00:00
|
|
|
put_temperature(b, dive->airtemp, " air='", " C'");
|
2024-05-27 15:09:48 +00:00
|
|
|
if (dive->watertemp.mkelvin != dc_watertemp(dive).mkelvin)
|
2014-01-16 02:03:11 +00:00
|
|
|
put_temperature(b, dive->watertemp, " water='", " C'");
|
|
|
|
put_string(b, "/>\n");
|
2013-02-14 17:44:18 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
static void save_temperatures(struct membuffer *b, struct divecomputer *dc)
|
2011-09-05 16:39:55 +00:00
|
|
|
{
|
2013-01-23 18:25:31 +00:00
|
|
|
if (!dc->airtemp.mkelvin && !dc->watertemp.mkelvin)
|
2011-09-05 16:39:55 +00:00
|
|
|
return;
|
2014-01-16 02:03:11 +00:00
|
|
|
put_string(b, " <temperature");
|
|
|
|
put_temperature(b, dc->airtemp, " air='", " C'");
|
|
|
|
put_temperature(b, dc->watertemp, " water='", " C'");
|
|
|
|
put_string(b, " />\n");
|
2011-09-05 16:39:55 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
static void save_airpressure(struct membuffer *b, struct divecomputer *dc)
|
2012-11-12 19:57:49 +00:00
|
|
|
{
|
2013-01-23 18:25:31 +00:00
|
|
|
if (!dc->surface_pressure.mbar)
|
2012-11-12 19:57:49 +00:00
|
|
|
return;
|
2014-01-16 02:03:11 +00:00
|
|
|
put_string(b, " <surface");
|
|
|
|
put_pressure(b, dc->surface_pressure, " pressure='", " bar'");
|
|
|
|
put_string(b, " />\n");
|
2012-11-12 19:57:49 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
static void save_salinity(struct membuffer *b, struct divecomputer *dc)
|
2012-11-12 19:57:49 +00:00
|
|
|
{
|
2023-03-27 17:35:24 +00:00
|
|
|
if (!dc->salinity)
|
2012-11-12 19:57:49 +00:00
|
|
|
return;
|
2014-01-16 02:03:11 +00:00
|
|
|
put_string(b, " <water");
|
|
|
|
put_salinity(b, dc->salinity, " salinity='", " g/l'");
|
|
|
|
put_string(b, " />\n");
|
2012-11-12 19:57:49 +00:00
|
|
|
}
|
|
|
|
|
2018-09-10 12:47:56 +00:00
|
|
|
static void save_overview(struct membuffer *b, struct dive *dive, bool anonymize)
|
2011-09-01 23:27:52 +00:00
|
|
|
{
|
2022-02-12 13:03:18 +00:00
|
|
|
show_utf8_blanked(b, dive->diveguide, " <divemaster>", "</divemaster>\n", 0, anonymize);
|
2018-09-10 12:47:56 +00:00
|
|
|
show_utf8_blanked(b, dive->buddy, " <buddy>", "</buddy>\n", 0, anonymize);
|
|
|
|
show_utf8_blanked(b, dive->notes, " <notes>", "</notes>\n", 0, anonymize);
|
|
|
|
show_utf8_blanked(b, dive->suit, " <suit>", "</suit>\n", 0, anonymize);
|
2011-09-01 23:27:52 +00:00
|
|
|
}
|
|
|
|
|
2018-08-16 17:10:10 +00:00
|
|
|
static void put_gasmix(struct membuffer *b, struct gasmix mix)
|
2014-08-17 18:26:21 +00:00
|
|
|
{
|
2018-08-16 17:10:10 +00:00
|
|
|
int o2 = mix.o2.permille;
|
|
|
|
int he = mix.he.permille;
|
2014-08-17 18:26:21 +00:00
|
|
|
|
|
|
|
if (o2) {
|
2024-05-01 09:01:06 +00:00
|
|
|
put_format(b, " o2='%u.%u%%'", FRACTION_TUPLE(o2, 10));
|
2014-08-17 18:26:21 +00:00
|
|
|
if (he)
|
2024-05-01 09:01:06 +00:00
|
|
|
put_format(b, " he='%u.%u%%'", FRACTION_TUPLE(he, 10));
|
2014-08-17 18:26:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
static void save_cylinder_info(struct membuffer *b, struct dive *dive)
|
2011-09-01 23:27:52 +00:00
|
|
|
{
|
Fix missing save of (almost empty) cylinder information
If we have no explicit cylinder info at all (it's normal air, no size or
working pressure information, and no beginning/end pressure information),
we don't save the cylinders in question because that would be redundant.
Such non-saved cylinders may still show up in the equipment list because
there may be implicit mention of them elsewhere, notably due to sample
data, so not saving them is the right thing to do - there is nothing to
save.
However, we missed one case: if there were other cylinders that *did* have
explicit information in it following such an uninteresting cylinder, we do
need to save the cylinder information for the useless case - if only in
order to be able to save the non-useless information for subsequent
cylinders.
This patch does that. Now, if you had an air-filled cylinder with no
information as your first cylinder, and a 51% nitrox as your second one,
it will save that information as
<cylinder />
<cylinder o2='51.0%' />
rather than dropping the cylinder information entirely.
This bug has been there for a long time, and was hidden by the fact that
normally you'd fill in cylinder descriptions etc after importing new
dives. It also used to be that we saved the cylinder beginning/end
pressure even if that was generated from the sample data, so if you
imported from a air-integrated computer and had samples for that cylinder,
we used to save it even though it was technically redundant.
We stopped saving redundant air sample information in commit 0089dd8819b7
("Don't save cylinder start/end pressures unless set by hand").
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Removed start and end in save_cylinder_info(). These two variables are no
longer used.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-09-21 21:06:57 +00:00
|
|
|
int i, nr;
|
2011-09-01 23:27:52 +00:00
|
|
|
|
Fix missing save of (almost empty) cylinder information
If we have no explicit cylinder info at all (it's normal air, no size or
working pressure information, and no beginning/end pressure information),
we don't save the cylinders in question because that would be redundant.
Such non-saved cylinders may still show up in the equipment list because
there may be implicit mention of them elsewhere, notably due to sample
data, so not saving them is the right thing to do - there is nothing to
save.
However, we missed one case: if there were other cylinders that *did* have
explicit information in it following such an uninteresting cylinder, we do
need to save the cylinder information for the useless case - if only in
order to be able to save the non-useless information for subsequent
cylinders.
This patch does that. Now, if you had an air-filled cylinder with no
information as your first cylinder, and a 51% nitrox as your second one,
it will save that information as
<cylinder />
<cylinder o2='51.0%' />
rather than dropping the cylinder information entirely.
This bug has been there for a long time, and was hidden by the fact that
normally you'd fill in cylinder descriptions etc after importing new
dives. It also used to be that we saved the cylinder beginning/end
pressure even if that was generated from the sample data, so if you
imported from a air-integrated computer and had samples for that cylinder,
we used to save it even though it was technically redundant.
We stopped saving redundant air sample information in commit 0089dd8819b7
("Don't save cylinder start/end pressures unless set by hand").
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Removed start and end in save_cylinder_info(). These two variables are no
longer used.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-09-21 21:06:57 +00:00
|
|
|
nr = nr_cylinders(dive);
|
|
|
|
|
|
|
|
for (i = 0; i < nr; i++) {
|
2019-08-04 20:13:49 +00:00
|
|
|
cylinder_t *cylinder = get_cylinder(dive, i);
|
2011-09-04 03:31:18 +00:00
|
|
|
int volume = cylinder->type.size.mliter;
|
2011-09-04 20:34:22 +00:00
|
|
|
const char *description = cylinder->type.description;
|
2019-07-14 17:40:04 +00:00
|
|
|
int use = cylinder->cylinder_use;
|
2011-09-01 23:27:52 +00:00
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " <cylinder");
|
Don't save cylinder working pressure
It was a mistake to save it - and I did it just because other dive
managers did. It's a totally nonsensical measure, and nobody cares.
The only thing that matters is the size of the cylinder, and the
*actual* pressures. Those give actual air consumption numbers, and are
meaningful and unambiguous.
So the "working pressure" for a cylinder is pointless except for two
things:
- if you don't know the actual physical size, you need the "working
pressure" along with the air size (eg "85 cuft") in order to compute
the physical size. So we do use the working pressure on *input* from
systems that report cylinder sizes that way.
- People may well want to know what kind of cylinder they were diving,
and again, you can make a good guess about this from the working
pressure. So saving information like "HP100+" for the cylinder would
be a good thing.
But notice how in neither case do we actually want to save the working
pressure itself. And in fact saving it actually makes the output format
ambiguous: if we give both size and working pressure, what does 'size'
mean? Is it physical size in liters, or air size in cu ft?
So saving working pressure is just wrong. Get rid of it.
I'm going to add some kind of "cylinder description" thing, which we can
save instead (and perhaps guess standard cylinders from input like the
working pressure from dive logs that don't do this sanely - which is all
of them, as far as I can tell).
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-09-04 18:23:41 +00:00
|
|
|
if (volume)
|
2014-01-16 02:03:11 +00:00
|
|
|
put_milli(b, " size='", volume, " l'");
|
|
|
|
put_pressure(b, cylinder->type.workingpressure, " workpressure='", " bar'");
|
|
|
|
show_utf8(b, description, " description='", "'", 1);
|
2018-08-16 17:10:10 +00:00
|
|
|
put_gasmix(b, cylinder->gasmix);
|
2014-11-15 17:40:53 +00:00
|
|
|
put_pressure(b, cylinder->start, " start='", " bar'");
|
|
|
|
put_pressure(b, cylinder->end, " end='", " bar'");
|
2019-07-14 17:40:04 +00:00
|
|
|
if (use > OC_GAS && use < NUM_GAS_USE)
|
|
|
|
show_utf8(b, cylinderuse_text[use], " use='", "'", 1);
|
2017-11-27 17:20:21 +00:00
|
|
|
if (cylinder->depth.mm != 0)
|
|
|
|
put_milli(b, " depth='", cylinder->depth.mm, " m'");
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " />\n");
|
2011-09-01 23:27:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
static void save_weightsystem_info(struct membuffer *b, struct dive *dive)
|
2011-12-24 03:41:16 +00:00
|
|
|
{
|
2013-11-27 21:59:17 +00:00
|
|
|
int i, nr;
|
2011-12-24 03:41:16 +00:00
|
|
|
|
2013-11-27 21:59:17 +00:00
|
|
|
nr = nr_weightsystems(dive);
|
|
|
|
|
|
|
|
for (i = 0; i < nr; i++) {
|
2019-06-26 15:21:03 +00:00
|
|
|
weightsystem_t ws = dive->weightsystems.weightsystems[i];
|
|
|
|
int grams = ws.weight.grams;
|
|
|
|
const char *description = ws.description;
|
2011-12-24 03:41:16 +00:00
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " <weightsystem");
|
|
|
|
put_milli(b, " weight='", grams, " kg'");
|
|
|
|
show_utf8(b, description, " description='", "'", 1);
|
|
|
|
put_format(b, " />\n");
|
2011-12-24 03:41:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Start using the actual cylinder data for gas switch events
Now that gas switch events always have indices into the cylinder table,
start using that to look up the gas mix from the cylinders rather than
from the gas switch event itself. In other words, the cylinder index is
now the primary data for gas switch events.
This means that now as you change the cylinder information, the gas
switch events will automatically update to reflect those changes.
Note that on loading data from the outside (either from a xml file, from
a git/cloud account, or from a dive computer), we may or may not
initially have an index for the gas change event. The external data may
be from an older version of subsurface, or it may be from a
libdivecomputer download that just doesn't give index data at all.
In that case, we will do:
- if there is no index, but there is explicit gas mix information, we
will look up the index based on that gas mix, picking the cylinder
that has the closest mix.
- if there isn't even explicit gas mix data, so we only have the event
value from libdivecomputer, we will turn that value into a gasmix,
and use that to look up the cylinder index as above.
- if no valid cylinder information is available at all, gas switch
events will just be dropped.
When saving the data, we now always save the cylinder index, and the gas
mix associated with that cylinder (that gas mix will be ignored on load,
since the index is the primary, but it makes the event much easier to
read).
It is worth noting we do not modify the libdivecomputer value, even if
the gasmix has changed, so that remains as a record of the original
download.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-02 21:07:06 +00:00
|
|
|
static void show_integer(struct membuffer *b, int value, const char *pre, const char *post)
|
|
|
|
{
|
|
|
|
put_format(b, " %s%d%s", pre, value, post);
|
|
|
|
}
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
static void show_index(struct membuffer *b, int value, const char *pre, const char *post)
|
2011-09-23 01:02:54 +00:00
|
|
|
{
|
|
|
|
if (value)
|
Start using the actual cylinder data for gas switch events
Now that gas switch events always have indices into the cylinder table,
start using that to look up the gas mix from the cylinders rather than
from the gas switch event itself. In other words, the cylinder index is
now the primary data for gas switch events.
This means that now as you change the cylinder information, the gas
switch events will automatically update to reflect those changes.
Note that on loading data from the outside (either from a xml file, from
a git/cloud account, or from a dive computer), we may or may not
initially have an index for the gas change event. The external data may
be from an older version of subsurface, or it may be from a
libdivecomputer download that just doesn't give index data at all.
In that case, we will do:
- if there is no index, but there is explicit gas mix information, we
will look up the index based on that gas mix, picking the cylinder
that has the closest mix.
- if there isn't even explicit gas mix data, so we only have the event
value from libdivecomputer, we will turn that value into a gasmix,
and use that to look up the cylinder index as above.
- if no valid cylinder information is available at all, gas switch
events will just be dropped.
When saving the data, we now always save the cylinder index, and the gas
mix associated with that cylinder (that gas mix will be ignored on load,
since the index is the primary, but it makes the event much easier to
read).
It is worth noting we do not modify the libdivecomputer value, even if
the gasmix has changed, so that remains as a record of the original
download.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-02 21:07:06 +00:00
|
|
|
show_integer(b, value, pre, post);
|
2011-09-23 01:02:54 +00:00
|
|
|
}
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
static void save_sample(struct membuffer *b, const struct sample &sample, struct sample &old, int o2sensor)
|
2011-09-01 23:27:52 +00:00
|
|
|
{
|
Add support for loading and saving multiple pressure samples
This does both the XML and the git save format, because the changes
really are the same, even if the actual format differs in some details.
See how the two "save_samples()" routines both do the same basic setup,
for example.
This is fairly straightforward, with the possible exception of the odd
sensor = sample->sensor[0];
default in the git pressure loading code.
That line just means that if we do *not* have an explicit cylinder index
for the pressure reading, we will always end up filling in the new
pressure as the first pressure (because the cylinder index will match the
first sensor slot).
So that makes the "add_sample_pressure()" case always do the same thing it
used to do for the legacy case: fill in the first slot. The actual sensor
index may later change, since the legacy format has a "sensor=X" key value
pair that sets the sensor, but it will also use the first sensor slot,
making it all do exactly what it used to do.
And on the other hand, if we're loading new-style data with cylinder
pressure and sensor index together, we just end up using the new semantics
for add_sample_pressure(), which tries to keep the same slot for the same
sensor, but does the right thing if we already have other pressure values.
The XML code has no such issues at all, since it can't share the cases
anyway, and we need to have different node names for the different sensor
values and cannot just have multiple "pressure" entries. Have I mentioned
how much I despise XML lately?
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-26 02:10:03 +00:00
|
|
|
int idx;
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
put_format(b, " <sample time='%u:%02u min'", FRACTION_TUPLE(sample.time.seconds, 60));
|
|
|
|
put_milli(b, " depth='", sample.depth.mm, " m'");
|
|
|
|
if (sample.temperature.mkelvin && sample.temperature.mkelvin != old.temperature.mkelvin) {
|
|
|
|
put_temperature(b, sample.temperature, " temp='", " C'");
|
|
|
|
old.temperature = sample.temperature;
|
2015-01-03 05:29:40 +00:00
|
|
|
}
|
First step in cleaning up cylinder pressure sensor logic
This clarifies/changes the meaning of our "cylinderindex" entry in our
samples. It has been rather confused, because different dive computers
have done things differently, and the naming really hasn't helped.
There are two totally different - and independent - cylinder "indexes":
- the pressure sensor index, which indicates which cylinder the sensor
data is from.
- the "active cylinder" index, which indicates which cylinder we actually
breathe from.
These two values really are totally independent, and have nothing
what-so-ever to do with each other. The sensor index may well be fixed:
many dive computers only support a single pressure sensor (whether
wireless or wired), and the sensor index is thus always zero.
Other dive computers may support multiple pressure sensors, and the gas
switch event may - or may not - indicate that the sensor changed too. A
dive computer might give the sensor data for *all* cylinders it can read,
regardless of which one is the one we're actively breathing. In fact, some
dive computers might give sensor data for not just *your* cylinder, but
your buddies.
This patch renames "cylinderindex" in the samples as "sensor", making it
quite clear that it's about which sensor index the pressure data in the
sample is about.
The way we figure out which is the currently active gas is with an
explicit has change event. If a computer (like the Uemis Zurich) joins the
two concepts together, then a sensor change should also create a gas
switch event. This patch also changes the Uemis importer to do that.
Finally, it should be noted that the plot info works totally separately
from the sample data, and is about what we actually *display*, not about
the sample pressures etc. In the plot info, the "cylinderindex" does in
fact mean the currently active cylinder, and while it is initially set to
match the sensor information from the samples, we then walk the gas change
events and fix it up - and if the active cylinder differs from the sensor
cylinder, we clear the sensor data.
[Dirk Hohndel: this conflicted with some of my recent changes - I think
I merged things correctly...]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-31 04:00:51 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We only show sensor information for samples with pressure, and only if it
|
|
|
|
* changed from the previous sensor we showed.
|
|
|
|
*/
|
Add support for loading and saving multiple pressure samples
This does both the XML and the git save format, because the changes
really are the same, even if the actual format differs in some details.
See how the two "save_samples()" routines both do the same basic setup,
for example.
This is fairly straightforward, with the possible exception of the odd
sensor = sample->sensor[0];
default in the git pressure loading code.
That line just means that if we do *not* have an explicit cylinder index
for the pressure reading, we will always end up filling in the new
pressure as the first pressure (because the cylinder index will match the
first sensor slot).
So that makes the "add_sample_pressure()" case always do the same thing it
used to do for the legacy case: fill in the first slot. The actual sensor
index may later change, since the legacy format has a "sensor=X" key value
pair that sets the sensor, but it will also use the first sensor slot,
making it all do exactly what it used to do.
And on the other hand, if we're loading new-style data with cylinder
pressure and sensor index together, we just end up using the new semantics
for add_sample_pressure(), which tries to keep the same slot for the same
sensor, but does the right thing if we already have other pressure values.
The XML code has no such issues at all, since it can't share the cases
anyway, and we need to have different node names for the different sensor
values and cannot just have multiple "pressure" entries. Have I mentioned
how much I despise XML lately?
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-26 02:10:03 +00:00
|
|
|
for (idx = 0; idx < MAX_SENSORS; idx++) {
|
2024-05-19 10:38:38 +00:00
|
|
|
pressure_t p = sample.pressure[idx];
|
|
|
|
int sensor = sample.sensor[idx];
|
Add support for loading and saving multiple pressure samples
This does both the XML and the git save format, because the changes
really are the same, even if the actual format differs in some details.
See how the two "save_samples()" routines both do the same basic setup,
for example.
This is fairly straightforward, with the possible exception of the odd
sensor = sample->sensor[0];
default in the git pressure loading code.
That line just means that if we do *not* have an explicit cylinder index
for the pressure reading, we will always end up filling in the new
pressure as the first pressure (because the cylinder index will match the
first sensor slot).
So that makes the "add_sample_pressure()" case always do the same thing it
used to do for the legacy case: fill in the first slot. The actual sensor
index may later change, since the legacy format has a "sensor=X" key value
pair that sets the sensor, but it will also use the first sensor slot,
making it all do exactly what it used to do.
And on the other hand, if we're loading new-style data with cylinder
pressure and sensor index together, we just end up using the new semantics
for add_sample_pressure(), which tries to keep the same slot for the same
sensor, but does the right thing if we already have other pressure values.
The XML code has no such issues at all, since it can't share the cases
anyway, and we need to have different node names for the different sensor
values and cannot just have multiple "pressure" entries. Have I mentioned
how much I despise XML lately?
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-26 02:10:03 +00:00
|
|
|
|
2021-09-13 18:57:56 +00:00
|
|
|
if (sensor == NO_SENSOR)
|
|
|
|
continue;
|
|
|
|
|
Add support for loading and saving multiple pressure samples
This does both the XML and the git save format, because the changes
really are the same, even if the actual format differs in some details.
See how the two "save_samples()" routines both do the same basic setup,
for example.
This is fairly straightforward, with the possible exception of the odd
sensor = sample->sensor[0];
default in the git pressure loading code.
That line just means that if we do *not* have an explicit cylinder index
for the pressure reading, we will always end up filling in the new
pressure as the first pressure (because the cylinder index will match the
first sensor slot).
So that makes the "add_sample_pressure()" case always do the same thing it
used to do for the legacy case: fill in the first slot. The actual sensor
index may later change, since the legacy format has a "sensor=X" key value
pair that sets the sensor, but it will also use the first sensor slot,
making it all do exactly what it used to do.
And on the other hand, if we're loading new-style data with cylinder
pressure and sensor index together, we just end up using the new semantics
for add_sample_pressure(), which tries to keep the same slot for the same
sensor, but does the right thing if we already have other pressure values.
The XML code has no such issues at all, since it can't share the cases
anyway, and we need to have different node names for the different sensor
values and cannot just have multiple "pressure" entries. Have I mentioned
how much I despise XML lately?
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-26 02:10:03 +00:00
|
|
|
if (!p.mbar)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Legacy o2pressure format? */
|
|
|
|
if (o2sensor >= 0) {
|
|
|
|
if (sensor == o2sensor) {
|
|
|
|
put_pressure(b, p, " o2pressure='", " bar'");
|
|
|
|
continue;
|
|
|
|
}
|
2017-09-14 18:06:46 +00:00
|
|
|
put_pressure(b, p, " pressure='", " bar'");
|
2024-05-19 10:38:38 +00:00
|
|
|
if (sensor != old.sensor[0]) {
|
Add support for loading and saving multiple pressure samples
This does both the XML and the git save format, because the changes
really are the same, even if the actual format differs in some details.
See how the two "save_samples()" routines both do the same basic setup,
for example.
This is fairly straightforward, with the possible exception of the odd
sensor = sample->sensor[0];
default in the git pressure loading code.
That line just means that if we do *not* have an explicit cylinder index
for the pressure reading, we will always end up filling in the new
pressure as the first pressure (because the cylinder index will match the
first sensor slot).
So that makes the "add_sample_pressure()" case always do the same thing it
used to do for the legacy case: fill in the first slot. The actual sensor
index may later change, since the legacy format has a "sensor=X" key value
pair that sets the sensor, but it will also use the first sensor slot,
making it all do exactly what it used to do.
And on the other hand, if we're loading new-style data with cylinder
pressure and sensor index together, we just end up using the new semantics
for add_sample_pressure(), which tries to keep the same slot for the same
sensor, but does the right thing if we already have other pressure values.
The XML code has no such issues at all, since it can't share the cases
anyway, and we need to have different node names for the different sensor
values and cannot just have multiple "pressure" entries. Have I mentioned
how much I despise XML lately?
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-26 02:10:03 +00:00
|
|
|
put_format(b, " sensor='%d'", sensor);
|
2024-05-19 10:38:38 +00:00
|
|
|
old.sensor[0] = sensor;
|
Add support for loading and saving multiple pressure samples
This does both the XML and the git save format, because the changes
really are the same, even if the actual format differs in some details.
See how the two "save_samples()" routines both do the same basic setup,
for example.
This is fairly straightforward, with the possible exception of the odd
sensor = sample->sensor[0];
default in the git pressure loading code.
That line just means that if we do *not* have an explicit cylinder index
for the pressure reading, we will always end up filling in the new
pressure as the first pressure (because the cylinder index will match the
first sensor slot).
So that makes the "add_sample_pressure()" case always do the same thing it
used to do for the legacy case: fill in the first slot. The actual sensor
index may later change, since the legacy format has a "sensor=X" key value
pair that sets the sensor, but it will also use the first sensor slot,
making it all do exactly what it used to do.
And on the other hand, if we're loading new-style data with cylinder
pressure and sensor index together, we just end up using the new semantics
for add_sample_pressure(), which tries to keep the same slot for the same
sensor, but does the right thing if we already have other pressure values.
The XML code has no such issues at all, since it can't share the cases
anyway, and we need to have different node names for the different sensor
values and cannot just have multiple "pressure" entries. Have I mentioned
how much I despise XML lately?
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-26 02:10:03 +00:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The new-style format is much simpler: the sensor is always encoded */
|
|
|
|
put_format(b, " pressure%d=", sensor);
|
|
|
|
put_pressure(b, p, "'", " bar'");
|
First step in cleaning up cylinder pressure sensor logic
This clarifies/changes the meaning of our "cylinderindex" entry in our
samples. It has been rather confused, because different dive computers
have done things differently, and the naming really hasn't helped.
There are two totally different - and independent - cylinder "indexes":
- the pressure sensor index, which indicates which cylinder the sensor
data is from.
- the "active cylinder" index, which indicates which cylinder we actually
breathe from.
These two values really are totally independent, and have nothing
what-so-ever to do with each other. The sensor index may well be fixed:
many dive computers only support a single pressure sensor (whether
wireless or wired), and the sensor index is thus always zero.
Other dive computers may support multiple pressure sensors, and the gas
switch event may - or may not - indicate that the sensor changed too. A
dive computer might give the sensor data for *all* cylinders it can read,
regardless of which one is the one we're actively breathing. In fact, some
dive computers might give sensor data for not just *your* cylinder, but
your buddies.
This patch renames "cylinderindex" in the samples as "sensor", making it
quite clear that it's about which sensor index the pressure data in the
sample is about.
The way we figure out which is the currently active gas is with an
explicit has change event. If a computer (like the Uemis Zurich) joins the
two concepts together, then a sensor change should also create a gas
switch event. This patch also changes the Uemis importer to do that.
Finally, it should be noted that the plot info works totally separately
from the sample data, and is about what we actually *display*, not about
the sample pressures etc. In the plot info, the "cylinderindex" does in
fact mean the currently active cylinder, and while it is initially set to
match the sensor information from the samples, we then walk the gas change
events and fix it up - and if the active cylinder differs from the sensor
cylinder, we clear the sensor data.
[Dirk Hohndel: this conflicted with some of my recent changes - I think
I merged things correctly...]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-31 04:00:51 +00:00
|
|
|
}
|
|
|
|
|
2012-12-01 21:02:30 +00:00
|
|
|
/* the deco/ndl values are stored whenever they change */
|
2024-05-19 10:38:38 +00:00
|
|
|
if (sample.ndl.seconds != old.ndl.seconds) {
|
|
|
|
put_format(b, " ndl='%u:%02u min'", FRACTION_TUPLE(sample.ndl.seconds, 60));
|
|
|
|
old.ndl = sample.ndl;
|
First step in cleaning up cylinder pressure sensor logic
This clarifies/changes the meaning of our "cylinderindex" entry in our
samples. It has been rather confused, because different dive computers
have done things differently, and the naming really hasn't helped.
There are two totally different - and independent - cylinder "indexes":
- the pressure sensor index, which indicates which cylinder the sensor
data is from.
- the "active cylinder" index, which indicates which cylinder we actually
breathe from.
These two values really are totally independent, and have nothing
what-so-ever to do with each other. The sensor index may well be fixed:
many dive computers only support a single pressure sensor (whether
wireless or wired), and the sensor index is thus always zero.
Other dive computers may support multiple pressure sensors, and the gas
switch event may - or may not - indicate that the sensor changed too. A
dive computer might give the sensor data for *all* cylinders it can read,
regardless of which one is the one we're actively breathing. In fact, some
dive computers might give sensor data for not just *your* cylinder, but
your buddies.
This patch renames "cylinderindex" in the samples as "sensor", making it
quite clear that it's about which sensor index the pressure data in the
sample is about.
The way we figure out which is the currently active gas is with an
explicit has change event. If a computer (like the Uemis Zurich) joins the
two concepts together, then a sensor change should also create a gas
switch event. This patch also changes the Uemis importer to do that.
Finally, it should be noted that the plot info works totally separately
from the sample data, and is about what we actually *display*, not about
the sample pressures etc. In the plot info, the "cylinderindex" does in
fact mean the currently active cylinder, and while it is initially set to
match the sensor information from the samples, we then walk the gas change
events and fix it up - and if the active cylinder differs from the sensor
cylinder, we clear the sensor data.
[Dirk Hohndel: this conflicted with some of my recent changes - I think
I merged things correctly...]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-31 04:00:51 +00:00
|
|
|
}
|
2024-05-19 10:38:38 +00:00
|
|
|
if (sample.tts.seconds != old.tts.seconds) {
|
|
|
|
put_format(b, " tts='%u:%02u min'", FRACTION_TUPLE(sample.tts.seconds, 60));
|
|
|
|
old.tts = sample.tts;
|
2014-07-09 20:13:36 +00:00
|
|
|
}
|
2024-05-19 10:38:38 +00:00
|
|
|
if (sample.rbt.seconds != old.rbt.seconds) {
|
|
|
|
put_format(b, " rbt='%u:%02u min'", FRACTION_TUPLE(sample.rbt.seconds, 60));
|
|
|
|
old.rbt = sample.rbt;
|
2019-07-14 17:43:38 +00:00
|
|
|
}
|
2024-05-19 10:38:38 +00:00
|
|
|
if (sample.in_deco != old.in_deco) {
|
|
|
|
put_format(b, " in_deco='%d'", sample.in_deco ? 1 : 0);
|
|
|
|
old.in_deco = sample.in_deco;
|
First step in cleaning up cylinder pressure sensor logic
This clarifies/changes the meaning of our "cylinderindex" entry in our
samples. It has been rather confused, because different dive computers
have done things differently, and the naming really hasn't helped.
There are two totally different - and independent - cylinder "indexes":
- the pressure sensor index, which indicates which cylinder the sensor
data is from.
- the "active cylinder" index, which indicates which cylinder we actually
breathe from.
These two values really are totally independent, and have nothing
what-so-ever to do with each other. The sensor index may well be fixed:
many dive computers only support a single pressure sensor (whether
wireless or wired), and the sensor index is thus always zero.
Other dive computers may support multiple pressure sensors, and the gas
switch event may - or may not - indicate that the sensor changed too. A
dive computer might give the sensor data for *all* cylinders it can read,
regardless of which one is the one we're actively breathing. In fact, some
dive computers might give sensor data for not just *your* cylinder, but
your buddies.
This patch renames "cylinderindex" in the samples as "sensor", making it
quite clear that it's about which sensor index the pressure data in the
sample is about.
The way we figure out which is the currently active gas is with an
explicit has change event. If a computer (like the Uemis Zurich) joins the
two concepts together, then a sensor change should also create a gas
switch event. This patch also changes the Uemis importer to do that.
Finally, it should be noted that the plot info works totally separately
from the sample data, and is about what we actually *display*, not about
the sample pressures etc. In the plot info, the "cylinderindex" does in
fact mean the currently active cylinder, and while it is initially set to
match the sensor information from the samples, we then walk the gas change
events and fix it up - and if the active cylinder differs from the sensor
cylinder, we clear the sensor data.
[Dirk Hohndel: this conflicted with some of my recent changes - I think
I merged things correctly...]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-31 04:00:51 +00:00
|
|
|
}
|
2024-05-19 10:38:38 +00:00
|
|
|
if (sample.stoptime.seconds != old.stoptime.seconds) {
|
|
|
|
put_format(b, " stoptime='%u:%02u min'", FRACTION_TUPLE(sample.stoptime.seconds, 60));
|
|
|
|
old.stoptime = sample.stoptime;
|
First step in cleaning up cylinder pressure sensor logic
This clarifies/changes the meaning of our "cylinderindex" entry in our
samples. It has been rather confused, because different dive computers
have done things differently, and the naming really hasn't helped.
There are two totally different - and independent - cylinder "indexes":
- the pressure sensor index, which indicates which cylinder the sensor
data is from.
- the "active cylinder" index, which indicates which cylinder we actually
breathe from.
These two values really are totally independent, and have nothing
what-so-ever to do with each other. The sensor index may well be fixed:
many dive computers only support a single pressure sensor (whether
wireless or wired), and the sensor index is thus always zero.
Other dive computers may support multiple pressure sensors, and the gas
switch event may - or may not - indicate that the sensor changed too. A
dive computer might give the sensor data for *all* cylinders it can read,
regardless of which one is the one we're actively breathing. In fact, some
dive computers might give sensor data for not just *your* cylinder, but
your buddies.
This patch renames "cylinderindex" in the samples as "sensor", making it
quite clear that it's about which sensor index the pressure data in the
sample is about.
The way we figure out which is the currently active gas is with an
explicit has change event. If a computer (like the Uemis Zurich) joins the
two concepts together, then a sensor change should also create a gas
switch event. This patch also changes the Uemis importer to do that.
Finally, it should be noted that the plot info works totally separately
from the sample data, and is about what we actually *display*, not about
the sample pressures etc. In the plot info, the "cylinderindex" does in
fact mean the currently active cylinder, and while it is initially set to
match the sensor information from the samples, we then walk the gas change
events and fix it up - and if the active cylinder differs from the sensor
cylinder, we clear the sensor data.
[Dirk Hohndel: this conflicted with some of my recent changes - I think
I merged things correctly...]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-31 04:00:51 +00:00
|
|
|
}
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
if (sample.stopdepth.mm != old.stopdepth.mm) {
|
|
|
|
put_milli(b, " stopdepth='", sample.stopdepth.mm, " m'");
|
|
|
|
old.stopdepth = sample.stopdepth;
|
First step in cleaning up cylinder pressure sensor logic
This clarifies/changes the meaning of our "cylinderindex" entry in our
samples. It has been rather confused, because different dive computers
have done things differently, and the naming really hasn't helped.
There are two totally different - and independent - cylinder "indexes":
- the pressure sensor index, which indicates which cylinder the sensor
data is from.
- the "active cylinder" index, which indicates which cylinder we actually
breathe from.
These two values really are totally independent, and have nothing
what-so-ever to do with each other. The sensor index may well be fixed:
many dive computers only support a single pressure sensor (whether
wireless or wired), and the sensor index is thus always zero.
Other dive computers may support multiple pressure sensors, and the gas
switch event may - or may not - indicate that the sensor changed too. A
dive computer might give the sensor data for *all* cylinders it can read,
regardless of which one is the one we're actively breathing. In fact, some
dive computers might give sensor data for not just *your* cylinder, but
your buddies.
This patch renames "cylinderindex" in the samples as "sensor", making it
quite clear that it's about which sensor index the pressure data in the
sample is about.
The way we figure out which is the currently active gas is with an
explicit has change event. If a computer (like the Uemis Zurich) joins the
two concepts together, then a sensor change should also create a gas
switch event. This patch also changes the Uemis importer to do that.
Finally, it should be noted that the plot info works totally separately
from the sample data, and is about what we actually *display*, not about
the sample pressures etc. In the plot info, the "cylinderindex" does in
fact mean the currently active cylinder, and while it is initially set to
match the sensor information from the samples, we then walk the gas change
events and fix it up - and if the active cylinder differs from the sensor
cylinder, we clear the sensor data.
[Dirk Hohndel: this conflicted with some of my recent changes - I think
I merged things correctly...]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-31 04:00:51 +00:00
|
|
|
}
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
if (sample.cns != old.cns) {
|
|
|
|
put_format(b, " cns='%u%%'", sample.cns);
|
|
|
|
old.cns = sample.cns;
|
First step in cleaning up cylinder pressure sensor logic
This clarifies/changes the meaning of our "cylinderindex" entry in our
samples. It has been rather confused, because different dive computers
have done things differently, and the naming really hasn't helped.
There are two totally different - and independent - cylinder "indexes":
- the pressure sensor index, which indicates which cylinder the sensor
data is from.
- the "active cylinder" index, which indicates which cylinder we actually
breathe from.
These two values really are totally independent, and have nothing
what-so-ever to do with each other. The sensor index may well be fixed:
many dive computers only support a single pressure sensor (whether
wireless or wired), and the sensor index is thus always zero.
Other dive computers may support multiple pressure sensors, and the gas
switch event may - or may not - indicate that the sensor changed too. A
dive computer might give the sensor data for *all* cylinders it can read,
regardless of which one is the one we're actively breathing. In fact, some
dive computers might give sensor data for not just *your* cylinder, but
your buddies.
This patch renames "cylinderindex" in the samples as "sensor", making it
quite clear that it's about which sensor index the pressure data in the
sample is about.
The way we figure out which is the currently active gas is with an
explicit has change event. If a computer (like the Uemis Zurich) joins the
two concepts together, then a sensor change should also create a gas
switch event. This patch also changes the Uemis importer to do that.
Finally, it should be noted that the plot info works totally separately
from the sample data, and is about what we actually *display*, not about
the sample pressures etc. In the plot info, the "cylinderindex" does in
fact mean the currently active cylinder, and while it is initially set to
match the sensor information from the samples, we then walk the gas change
events and fix it up - and if the active cylinder differs from the sensor
cylinder, we clear the sensor data.
[Dirk Hohndel: this conflicted with some of my recent changes - I think
I merged things correctly...]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-31 04:00:51 +00:00
|
|
|
}
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
if ((sample.o2sensor[0].mbar) && (sample.o2sensor[0].mbar != old.o2sensor[0].mbar)) {
|
|
|
|
put_milli(b, " sensor1='", sample.o2sensor[0].mbar, " bar'");
|
|
|
|
old.o2sensor[0] = sample.o2sensor[0];
|
2014-11-17 19:04:36 +00:00
|
|
|
}
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
if ((sample.o2sensor[1].mbar) && (sample.o2sensor[1].mbar != old.o2sensor[1].mbar)) {
|
|
|
|
put_milli(b, " sensor2='", sample.o2sensor[1].mbar, " bar'");
|
|
|
|
old.o2sensor[1] = sample.o2sensor[1];
|
2014-11-17 19:04:36 +00:00
|
|
|
}
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
if ((sample.o2sensor[2].mbar) && (sample.o2sensor[2].mbar != old.o2sensor[2].mbar)) {
|
|
|
|
put_milli(b, " sensor3='", sample.o2sensor[2].mbar, " bar'");
|
|
|
|
old.o2sensor[2] = sample.o2sensor[2];
|
2014-11-17 19:04:36 +00:00
|
|
|
}
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
if ((sample.o2sensor[3].mbar) && (sample.o2sensor[3].mbar != old.o2sensor[3].mbar)) {
|
|
|
|
put_milli(b, " sensor4='", sample.o2sensor[3].mbar, " bar'");
|
|
|
|
old.o2sensor[3] = sample.o2sensor[3];
|
2024-01-20 23:35:44 +00:00
|
|
|
}
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
if ((sample.o2sensor[4].mbar) && (sample.o2sensor[4].mbar != old.o2sensor[4].mbar)) {
|
|
|
|
put_milli(b, " sensor5='", sample.o2sensor[4].mbar, " bar'");
|
|
|
|
old.o2sensor[4] = sample.o2sensor[4];
|
2024-01-20 23:35:44 +00:00
|
|
|
}
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
if ((sample.o2sensor[5].mbar) && (sample.o2sensor[5].mbar != old.o2sensor[5].mbar)) {
|
|
|
|
put_milli(b, " sensor6='", sample.o2sensor[5].mbar, " bar'");
|
|
|
|
old.o2sensor[5] = sample.o2sensor[5];
|
2024-01-20 23:35:44 +00:00
|
|
|
}
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
if (sample.setpoint.mbar != old.setpoint.mbar) {
|
|
|
|
put_milli(b, " po2='", sample.setpoint.mbar, " bar'");
|
|
|
|
old.setpoint = sample.setpoint;
|
First step in cleaning up cylinder pressure sensor logic
This clarifies/changes the meaning of our "cylinderindex" entry in our
samples. It has been rather confused, because different dive computers
have done things differently, and the naming really hasn't helped.
There are two totally different - and independent - cylinder "indexes":
- the pressure sensor index, which indicates which cylinder the sensor
data is from.
- the "active cylinder" index, which indicates which cylinder we actually
breathe from.
These two values really are totally independent, and have nothing
what-so-ever to do with each other. The sensor index may well be fixed:
many dive computers only support a single pressure sensor (whether
wireless or wired), and the sensor index is thus always zero.
Other dive computers may support multiple pressure sensors, and the gas
switch event may - or may not - indicate that the sensor changed too. A
dive computer might give the sensor data for *all* cylinders it can read,
regardless of which one is the one we're actively breathing. In fact, some
dive computers might give sensor data for not just *your* cylinder, but
your buddies.
This patch renames "cylinderindex" in the samples as "sensor", making it
quite clear that it's about which sensor index the pressure data in the
sample is about.
The way we figure out which is the currently active gas is with an
explicit has change event. If a computer (like the Uemis Zurich) joins the
two concepts together, then a sensor change should also create a gas
switch event. This patch also changes the Uemis importer to do that.
Finally, it should be noted that the plot info works totally separately
from the sample data, and is about what we actually *display*, not about
the sample pressures etc. In the plot info, the "cylinderindex" does in
fact mean the currently active cylinder, and while it is initially set to
match the sensor information from the samples, we then walk the gas change
events and fix it up - and if the active cylinder differs from the sensor
cylinder, we clear the sensor data.
[Dirk Hohndel: this conflicted with some of my recent changes - I think
I merged things correctly...]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-31 04:00:51 +00:00
|
|
|
}
|
2024-05-19 10:38:38 +00:00
|
|
|
if (sample.heartbeat != old.heartbeat) {
|
|
|
|
show_index(b, sample.heartbeat, "heartbeat='", "'");
|
|
|
|
old.heartbeat = sample.heartbeat;
|
2017-09-10 05:43:54 +00:00
|
|
|
}
|
2024-05-19 10:38:38 +00:00
|
|
|
if (sample.bearing.degrees != old.bearing.degrees) {
|
|
|
|
show_index(b, sample.bearing.degrees, "bearing='", "'");
|
|
|
|
old.bearing.degrees = sample.bearing.degrees;
|
2017-09-10 05:43:00 +00:00
|
|
|
}
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " />\n");
|
2011-09-01 23:27:52 +00:00
|
|
|
}
|
|
|
|
|
2024-05-25 06:16:57 +00:00
|
|
|
static void save_one_event(struct membuffer *b, struct dive *dive, const struct event &ev)
|
2011-09-23 01:02:54 +00:00
|
|
|
{
|
2024-05-25 06:16:57 +00:00
|
|
|
put_format(b, " <event time='%d:%02d min'", FRACTION_TUPLE(ev.time.seconds, 60));
|
|
|
|
show_index(b, ev.type, "type='", "'");
|
|
|
|
show_index(b, ev.flags, "flags='", "'");
|
|
|
|
if (ev.name == "modechange")
|
|
|
|
show_utf8(b, divemode_text[ev.value], " divemode='", "'",1);
|
2018-04-07 12:56:37 +00:00
|
|
|
else
|
2024-05-25 06:16:57 +00:00
|
|
|
show_index(b, ev.value, "value='", "'");
|
|
|
|
show_utf8(b, ev.name.c_str(), " name='", "'", 1);
|
2024-05-25 18:12:10 +00:00
|
|
|
if (ev.is_gaschange()) {
|
2018-08-16 11:35:14 +00:00
|
|
|
struct gasmix mix = get_gasmix_from_event(dive, ev);
|
2024-05-25 06:16:57 +00:00
|
|
|
if (ev.gas.index >= 0)
|
|
|
|
show_integer(b, ev.gas.index, "cylinder='", "'");
|
2018-08-16 17:10:10 +00:00
|
|
|
put_gasmix(b, mix);
|
2014-08-17 18:26:21 +00:00
|
|
|
}
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " />\n");
|
2011-09-23 01:02:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-05-25 06:16:57 +00:00
|
|
|
static void save_events(struct membuffer *b, struct dive *dive, const struct divecomputer *dc)
|
2011-09-23 01:02:54 +00:00
|
|
|
{
|
2024-05-25 06:16:57 +00:00
|
|
|
for (auto &ev: dc->events)
|
Start using the actual cylinder data for gas switch events
Now that gas switch events always have indices into the cylinder table,
start using that to look up the gas mix from the cylinders rather than
from the gas switch event itself. In other words, the cylinder index is
now the primary data for gas switch events.
This means that now as you change the cylinder information, the gas
switch events will automatically update to reflect those changes.
Note that on loading data from the outside (either from a xml file, from
a git/cloud account, or from a dive computer), we may or may not
initially have an index for the gas change event. The external data may
be from an older version of subsurface, or it may be from a
libdivecomputer download that just doesn't give index data at all.
In that case, we will do:
- if there is no index, but there is explicit gas mix information, we
will look up the index based on that gas mix, picking the cylinder
that has the closest mix.
- if there isn't even explicit gas mix data, so we only have the event
value from libdivecomputer, we will turn that value into a gasmix,
and use that to look up the cylinder index as above.
- if no valid cylinder information is available at all, gas switch
events will just be dropped.
When saving the data, we now always save the cylinder index, and the gas
mix associated with that cylinder (that gas mix will be ignored on load,
since the index is the primary, but it makes the event much easier to
read).
It is worth noting we do not modify the libdivecomputer value, even if
the gasmix has changed, so that remains as a record of the original
download.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-02 21:07:06 +00:00
|
|
|
save_one_event(b, dive, ev);
|
2011-09-23 01:02:54 +00:00
|
|
|
}
|
|
|
|
|
Get rid of crazy empty tag_list element at the start
So this is totally unrelated to the git repository format, except for
the fact that I noticed it while writing the git saving code.
The subsurface divetag list handling is being stupid, and has a
initial dummy entry at the head of the list for no good reason.
I say "no good reason", because there *is* a reason for it: it allows
code to avoid the special case of empty list and adding entries to
before the first entry etc etc. But that reason is a really *bad*
reason, because it's valid only because people don't understand basic
list manipulation and pointers to pointers.
So get rid of the dummy element, and do things right instead - by
passing a *pointer* to the list, instead of the list. And then when
traversing the list and looking for a place to insert things, don't go
to the next entry - just update the "pointer to pointer" to point to
the address of the next entry. Each entry in a C linked list is no
different than the list itself, so you can use the pointer to the
pointer to the next entry as a pointer to the list.
This is a pet peeve of mine. The real beauty of pointers can never be
understood unless you understand the indirection they allow. People
who grew up with Pascal and were corrupted by that mindset are
mentally stunted. Niklaus Wirth has a lot to answer for!
But never fear. You too can overcome that mental limitation, it just
needs some brain exercise. Reading this patch may help. In particular,
contemplate the new "taglist_add_divetag()".
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-03-10 17:18:13 +00:00
|
|
|
static void save_tags(struct membuffer *b, struct tag_entry *entry)
|
2013-04-09 20:06:30 +00:00
|
|
|
{
|
Get rid of crazy empty tag_list element at the start
So this is totally unrelated to the git repository format, except for
the fact that I noticed it while writing the git saving code.
The subsurface divetag list handling is being stupid, and has a
initial dummy entry at the head of the list for no good reason.
I say "no good reason", because there *is* a reason for it: it allows
code to avoid the special case of empty list and adding entries to
before the first entry etc etc. But that reason is a really *bad*
reason, because it's valid only because people don't understand basic
list manipulation and pointers to pointers.
So get rid of the dummy element, and do things right instead - by
passing a *pointer* to the list, instead of the list. And then when
traversing the list and looking for a place to insert things, don't go
to the next entry - just update the "pointer to pointer" to point to
the address of the next entry. Each entry in a C linked list is no
different than the list itself, so you can use the pointer to the
pointer to the next entry as a pointer to the list.
This is a pet peeve of mine. The real beauty of pointers can never be
understood unless you understand the indirection they allow. People
who grew up with Pascal and were corrupted by that mindset are
mentally stunted. Niklaus Wirth has a lot to answer for!
But never fear. You too can overcome that mental limitation, it just
needs some brain exercise. Reading this patch may help. In particular,
contemplate the new "taglist_add_divetag()".
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-03-10 17:18:13 +00:00
|
|
|
if (entry) {
|
|
|
|
const char *sep = " tags='";
|
|
|
|
do {
|
2024-03-26 20:06:13 +00:00
|
|
|
const struct divetag *tag = entry->tag;
|
Get rid of crazy empty tag_list element at the start
So this is totally unrelated to the git repository format, except for
the fact that I noticed it while writing the git saving code.
The subsurface divetag list handling is being stupid, and has a
initial dummy entry at the head of the list for no good reason.
I say "no good reason", because there *is* a reason for it: it allows
code to avoid the special case of empty list and adding entries to
before the first entry etc etc. But that reason is a really *bad*
reason, because it's valid only because people don't understand basic
list manipulation and pointers to pointers.
So get rid of the dummy element, and do things right instead - by
passing a *pointer* to the list, instead of the list. And then when
traversing the list and looking for a place to insert things, don't go
to the next entry - just update the "pointer to pointer" to point to
the address of the next entry. Each entry in a C linked list is no
different than the list itself, so you can use the pointer to the
pointer to the next entry as a pointer to the list.
This is a pet peeve of mine. The real beauty of pointers can never be
understood unless you understand the indirection they allow. People
who grew up with Pascal and were corrupted by that mindset are
mentally stunted. Niklaus Wirth has a lot to answer for!
But never fear. You too can overcome that mental limitation, it just
needs some brain exercise. Reading this patch may help. In particular,
contemplate the new "taglist_add_divetag()".
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-03-10 17:18:13 +00:00
|
|
|
put_string(b, sep);
|
2013-11-02 01:12:42 +00:00
|
|
|
/* If the tag has been translated, write the source to the xml file */
|
2024-03-26 20:06:13 +00:00
|
|
|
quote(b, tag->source.empty() ? tag->name.c_str() : tag->source.c_str(), 1);
|
Get rid of crazy empty tag_list element at the start
So this is totally unrelated to the git repository format, except for
the fact that I noticed it while writing the git saving code.
The subsurface divetag list handling is being stupid, and has a
initial dummy entry at the head of the list for no good reason.
I say "no good reason", because there *is* a reason for it: it allows
code to avoid the special case of empty list and adding entries to
before the first entry etc etc. But that reason is a really *bad*
reason, because it's valid only because people don't understand basic
list manipulation and pointers to pointers.
So get rid of the dummy element, and do things right instead - by
passing a *pointer* to the list, instead of the list. And then when
traversing the list and looking for a place to insert things, don't go
to the next entry - just update the "pointer to pointer" to point to
the address of the next entry. Each entry in a C linked list is no
different than the list itself, so you can use the pointer to the
pointer to the next entry as a pointer to the list.
This is a pet peeve of mine. The real beauty of pointers can never be
understood unless you understand the indirection they allow. People
who grew up with Pascal and were corrupted by that mindset are
mentally stunted. Niklaus Wirth has a lot to answer for!
But never fear. You too can overcome that mental limitation, it just
needs some brain exercise. Reading this patch may help. In particular,
contemplate the new "taglist_add_divetag()".
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-03-10 17:18:13 +00:00
|
|
|
sep = ", ";
|
|
|
|
} while ((entry = entry->next) != NULL);
|
|
|
|
put_string(b, "'");
|
2013-04-09 20:06:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-18 19:04:58 +00:00
|
|
|
static void save_extra_data(struct membuffer *b, const struct divecomputer *dc)
|
2014-11-06 18:34:19 +00:00
|
|
|
{
|
2024-05-18 19:04:58 +00:00
|
|
|
for (const auto &ed: dc->extra_data) {
|
|
|
|
if (!ed.key.empty() && !ed.value.empty()) {
|
2014-11-06 18:34:19 +00:00
|
|
|
put_string(b, " <extradata");
|
2024-05-18 19:04:58 +00:00
|
|
|
show_utf8(b, ed.key.c_str(), " key='", "'", 1);
|
|
|
|
show_utf8(b, ed.value.c_str(), " value='", "'", 1);
|
2014-11-06 18:34:19 +00:00
|
|
|
put_string(b, " />\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
static void show_date(struct membuffer *b, timestamp_t when)
|
2012-08-22 05:04:24 +00:00
|
|
|
{
|
2012-09-20 00:35:52 +00:00
|
|
|
struct tm tm;
|
|
|
|
|
2012-11-25 02:50:21 +00:00
|
|
|
utc_mkdate(when, &tm);
|
2012-08-22 05:04:24 +00:00
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " date='%04u-%02u-%02u'",
|
2016-04-28 22:13:30 +00:00
|
|
|
tm.tm_year, tm.tm_mon + 1, tm.tm_mday);
|
2017-09-10 08:09:28 +00:00
|
|
|
if (tm.tm_hour || tm.tm_min || tm.tm_sec)
|
|
|
|
put_format(b, " time='%02u:%02u:%02u'",
|
|
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
2012-11-25 02:50:21 +00:00
|
|
|
}
|
|
|
|
|
Fix up o2 pressure sensor handling at load time
Because of how we traditionally did things, the "o2pressure" parsing
depends on implicitly setting the sensor index to the last cylinder that
was marked as being used for oxygen.
We also always defaulted the primary sensor (which is used for the
diluent tank for CCR) to cylinder 0, but that doesn't work when the
oxygen tank is cylinder 0.
This gets that right at file loading time, and unifies the xml and git
sample parsing to make them match. The new defaults are:
- unless anything else is explicitly specified, the primary sensor is
associated with the first tank, and the secondary sensor is
associated with the second tank
- if we're a CCR dive, and have an explicit oxygen tank, we associate
the secondary sensor with that oxygen cylinder. The primary sensor
will be switched over to the second cylinder if the oxygen cylinder
is the first one.
This may sound backwards, but matches our traditional behavior where
the O2 pressure was the secondary pressure.
This is definitely not pretty, but it gets our historical files working
right, and is at least reasonably sensible.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-21 20:37:34 +00:00
|
|
|
static void save_samples(struct membuffer *b, struct dive *dive, struct divecomputer *dc)
|
2011-09-01 23:27:52 +00:00
|
|
|
{
|
Fix up o2 pressure sensor handling at load time
Because of how we traditionally did things, the "o2pressure" parsing
depends on implicitly setting the sensor index to the last cylinder that
was marked as being used for oxygen.
We also always defaulted the primary sensor (which is used for the
diluent tank for CCR) to cylinder 0, but that doesn't work when the
oxygen tank is cylinder 0.
This gets that right at file loading time, and unifies the xml and git
sample parsing to make them match. The new defaults are:
- unless anything else is explicitly specified, the primary sensor is
associated with the first tank, and the secondary sensor is
associated with the second tank
- if we're a CCR dive, and have an explicit oxygen tank, we associate
the secondary sensor with that oxygen cylinder. The primary sensor
will be switched over to the second cylinder if the oxygen cylinder
is the first one.
This may sound backwards, but matches our traditional behavior where
the O2 pressure was the secondary pressure.
This is definitely not pretty, but it gets our historical files working
right, and is at least reasonably sensible.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-21 20:37:34 +00:00
|
|
|
int o2sensor;
|
2024-02-28 05:36:48 +00:00
|
|
|
struct sample dummy;
|
2012-12-01 21:02:30 +00:00
|
|
|
|
2020-03-11 10:30:51 +00:00
|
|
|
/* Set up default pressure sensor indices */
|
Add support for loading and saving multiple pressure samples
This does both the XML and the git save format, because the changes
really are the same, even if the actual format differs in some details.
See how the two "save_samples()" routines both do the same basic setup,
for example.
This is fairly straightforward, with the possible exception of the odd
sensor = sample->sensor[0];
default in the git pressure loading code.
That line just means that if we do *not* have an explicit cylinder index
for the pressure reading, we will always end up filling in the new
pressure as the first pressure (because the cylinder index will match the
first sensor slot).
So that makes the "add_sample_pressure()" case always do the same thing it
used to do for the legacy case: fill in the first slot. The actual sensor
index may later change, since the legacy format has a "sensor=X" key value
pair that sets the sensor, but it will also use the first sensor slot,
making it all do exactly what it used to do.
And on the other hand, if we're loading new-style data with cylinder
pressure and sensor index together, we just end up using the new semantics
for add_sample_pressure(), which tries to keep the same slot for the same
sensor, but does the right thing if we already have other pressure values.
The XML code has no such issues at all, since it can't share the cases
anyway, and we need to have different node names for the different sensor
values and cannot just have multiple "pressure" entries. Have I mentioned
how much I despise XML lately?
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-26 02:10:03 +00:00
|
|
|
o2sensor = legacy_format_o2pressures(dive, dc);
|
|
|
|
if (o2sensor >= 0) {
|
|
|
|
dummy.sensor[0] = !o2sensor;
|
|
|
|
dummy.sensor[1] = o2sensor;
|
|
|
|
}
|
Fix up o2 pressure sensor handling at load time
Because of how we traditionally did things, the "o2pressure" parsing
depends on implicitly setting the sensor index to the last cylinder that
was marked as being used for oxygen.
We also always defaulted the primary sensor (which is used for the
diluent tank for CCR) to cylinder 0, but that doesn't work when the
oxygen tank is cylinder 0.
This gets that right at file loading time, and unifies the xml and git
sample parsing to make them match. The new defaults are:
- unless anything else is explicitly specified, the primary sensor is
associated with the first tank, and the secondary sensor is
associated with the second tank
- if we're a CCR dive, and have an explicit oxygen tank, we associate
the secondary sensor with that oxygen cylinder. The primary sensor
will be switched over to the second cylinder if the oxygen cylinder
is the first one.
This may sound backwards, but matches our traditional behavior where
the O2 pressure was the secondary pressure.
This is definitely not pretty, but it gets our historical files working
right, and is at least reasonably sensible.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-21 20:37:34 +00:00
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
for (const auto &s: dc->samples)
|
|
|
|
save_sample(b, s, dummy, o2sensor);
|
2012-12-01 21:02:30 +00:00
|
|
|
}
|
2012-11-25 04:29:14 +00:00
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer *dc)
|
2012-12-01 21:02:30 +00:00
|
|
|
{
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " <divecomputer");
|
2024-05-18 15:03:19 +00:00
|
|
|
show_utf8(b, dc->model.c_str(), " model='", "'", 1);
|
Improve profile display in planner
This patch allows the planner to save the last manually-entered
dive planner point of a dive plan. When the plan has been saved
and re-opened for edit, the time of the last-entered dive planner
point is used to ensure that dive planning continues from the same
point in the profile as was when the original dive plan was saved.
Mechanism:
1) In dive.h, create a new dc attribute dc->last_manual_time
with data type of duration_t.
2) In diveplanner.c, ensure that the last manually-entered
dive planner point is saved in dc->last_manual_time.
3) In save-xml.c, create a new XML attribute for the <divecomputer>
element, named last-manual-time. For dive plans, the element would
now look like:
<divecomputer model='planned dive' last-manual-time='31:17 min'>
4) In parse-xml.c, insert code that recognises the last-manual-time
XML attribute, reads the time value and assigns this time to
dc->last_manual_time.
5) In diveplannermodel.cpp, method DiveplannerPointModel::loadfromdive,
insert code that sets the appropriate boolean value to dp->entered
by comparing newtime (i.e. time of dp) with dc->last_manual_time.
6) Diveplannermodel.cpp also accepts profile data from normal dives in
the dive log, whether hand-entered or loaded from dive computer. It
looks like the reduction of dive points for dives with >100 points
continues to work ok.
The result is that when a dive plan is saved with manually entered
points up to e.g. 10 minutes into the dive, it can be re-opened for edit
in the dive planner and the planner re-creates the plan with manually
entered points up to 10 minutes. The rest of the points are "soft"
points, shaped by the deco calculations of the planner.
Improvements: Improve code for profile display in dive planner
This responds to #1052.
Change load-git.c and save-git.c so that the last-manual-time is
also saved in the git-format dive log.
Several stylistic changes in text for consistent C source code.
Improvement of dive planner profile display:
Do some simplification of my alterations to diveplannermodel.cpp
Two small style changes in planner.c and diveplannermodel.cpp
as requested ny @neolit123
Signed-off-by: Willem Ferguson <willemferguson@zoology.up.ac.za>
2018-01-15 12:51:47 +00:00
|
|
|
if (dc->last_manual_time.seconds)
|
|
|
|
put_duration(b, dc->last_manual_time, " last-manual-time='", " min'");
|
2012-11-25 19:44:27 +00:00
|
|
|
if (dc->deviceid)
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " deviceid='%08x'", dc->deviceid);
|
2012-11-25 19:44:27 +00:00
|
|
|
if (dc->diveid)
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " diveid='%08x'", dc->diveid);
|
2012-11-25 04:29:14 +00:00
|
|
|
if (dc->when && dc->when != dive->when)
|
2014-01-16 02:03:11 +00:00
|
|
|
show_date(b, dc->when);
|
2024-05-27 15:09:48 +00:00
|
|
|
if (dc->duration.seconds && dc->duration.seconds != dive->dcs[0].duration.seconds)
|
2014-01-16 02:03:11 +00:00
|
|
|
put_duration(b, dc->duration, " duration='", " min'");
|
2015-01-10 23:01:15 +00:00
|
|
|
if (dc->divemode != OC) {
|
2024-02-28 05:36:48 +00:00
|
|
|
int i = (int)dc->divemode;
|
|
|
|
if (i >= 0 && i < NUM_DIVEMODE)
|
|
|
|
show_utf8(b, divemode_text[i], " dctype='", "'", 1);
|
2014-11-17 19:04:36 +00:00
|
|
|
if (dc->no_o2sensors)
|
|
|
|
put_format(b," no_o2sensors='%d'", dc->no_o2sensors);
|
2014-11-16 23:11:18 +00:00
|
|
|
}
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, ">\n");
|
|
|
|
save_depths(b, dc);
|
|
|
|
save_temperatures(b, dc);
|
|
|
|
save_airpressure(b, dc);
|
|
|
|
save_salinity(b, dc);
|
|
|
|
put_duration(b, dc->surfacetime, " <surfacetime>", " min</surfacetime>\n");
|
2024-05-18 19:04:58 +00:00
|
|
|
save_extra_data(b, dc);
|
2024-05-25 06:16:57 +00:00
|
|
|
save_events(b, dive, dc);
|
Fix up o2 pressure sensor handling at load time
Because of how we traditionally did things, the "o2pressure" parsing
depends on implicitly setting the sensor index to the last cylinder that
was marked as being used for oxygen.
We also always defaulted the primary sensor (which is used for the
diluent tank for CCR) to cylinder 0, but that doesn't work when the
oxygen tank is cylinder 0.
This gets that right at file loading time, and unifies the xml and git
sample parsing to make them match. The new defaults are:
- unless anything else is explicitly specified, the primary sensor is
associated with the first tank, and the secondary sensor is
associated with the second tank
- if we're a CCR dive, and have an explicit oxygen tank, we associate
the secondary sensor with that oxygen cylinder. The primary sensor
will be switched over to the second cylinder if the oxygen cylinder
is the first one.
This may sound backwards, but matches our traditional behavior where
the O2 pressure was the secondary pressure.
This is definitely not pretty, but it gets our historical files working
right, and is at least reasonably sensible.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-07-21 20:37:34 +00:00
|
|
|
save_samples(b, dive, dc);
|
2014-01-16 02:03:11 +00:00
|
|
|
|
|
|
|
put_format(b, " </divecomputer>\n");
|
2012-11-25 02:50:21 +00:00
|
|
|
}
|
2012-09-20 00:35:52 +00:00
|
|
|
|
2014-06-03 17:26:27 +00:00
|
|
|
static void save_picture(struct membuffer *b, struct picture *pic)
|
|
|
|
{
|
|
|
|
put_string(b, " <picture filename='");
|
2014-12-09 17:37:49 +00:00
|
|
|
put_quoted(b, pic->filename, true, false);
|
2014-06-03 17:26:27 +00:00
|
|
|
put_string(b, "'");
|
2014-07-08 19:29:06 +00:00
|
|
|
if (pic->offset.seconds) {
|
|
|
|
int offset = pic->offset.seconds;
|
|
|
|
char sign = '+';
|
|
|
|
if (offset < 0) {
|
|
|
|
sign = '-';
|
|
|
|
offset = -offset;
|
|
|
|
}
|
2024-05-01 09:01:06 +00:00
|
|
|
put_format(b, " offset='%c%u:%02u min'", sign, FRACTION_TUPLE(offset, 60));
|
2014-07-08 19:29:06 +00:00
|
|
|
}
|
2018-10-20 18:12:15 +00:00
|
|
|
put_location(b, &pic->location, " gps='","'");
|
2015-02-26 13:39:42 +00:00
|
|
|
|
2014-06-03 17:26:27 +00:00
|
|
|
put_string(b, "/>\n");
|
|
|
|
}
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize)
|
2012-11-25 02:50:21 +00:00
|
|
|
{
|
2019-04-30 10:42:33 +00:00
|
|
|
pressure_t surface_pressure = un_fixup_surface_pressure(dive);
|
2011-09-01 23:27:52 +00:00
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
put_string(b, "<dive");
|
2011-09-11 18:36:33 +00:00
|
|
|
if (dive->number)
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " number='%d'", dive->number);
|
2018-11-18 10:15:32 +00:00
|
|
|
if (dive->notrip)
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " tripflag='NOTRIP'");
|
2011-12-08 04:49:22 +00:00
|
|
|
if (dive->rating)
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " rating='%d'", dive->rating);
|
2012-10-28 22:49:02 +00:00
|
|
|
if (dive->visibility)
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, " visibility='%d'", dive->visibility);
|
2019-11-29 05:08:14 +00:00
|
|
|
if (dive->wavesize)
|
|
|
|
put_format(b, " wavesize='%d'", dive->wavesize);
|
|
|
|
if (dive->current)
|
|
|
|
put_format(b, " current='%d'", dive->current);
|
|
|
|
if (dive->surge)
|
|
|
|
put_format(b, " surge='%d'", dive->surge);
|
|
|
|
if (dive->chill)
|
|
|
|
put_format(b, " chill='%d'", dive->chill);
|
2019-12-12 21:58:53 +00:00
|
|
|
if (dive->invalid)
|
2019-12-12 22:07:17 +00:00
|
|
|
put_format(b, " invalid='1'");
|
2020-05-14 16:48:09 +00:00
|
|
|
|
|
|
|
// These three are calculated, and not read when loading.
|
|
|
|
// But saving them into the XML is useful for data export.
|
|
|
|
if (dive->sac > 100)
|
2024-05-01 09:01:06 +00:00
|
|
|
put_format(b, " sac='%d.%03d l/min'", FRACTION_TUPLE(dive->sac, 1000));
|
2020-05-14 16:48:09 +00:00
|
|
|
if (dive->otu)
|
|
|
|
put_format(b, " otu='%d'", dive->otu);
|
|
|
|
if (dive->maxcns)
|
|
|
|
put_format(b, " cns='%d%%'", dive->maxcns);
|
|
|
|
|
Get rid of crazy empty tag_list element at the start
So this is totally unrelated to the git repository format, except for
the fact that I noticed it while writing the git saving code.
The subsurface divetag list handling is being stupid, and has a
initial dummy entry at the head of the list for no good reason.
I say "no good reason", because there *is* a reason for it: it allows
code to avoid the special case of empty list and adding entries to
before the first entry etc etc. But that reason is a really *bad*
reason, because it's valid only because people don't understand basic
list manipulation and pointers to pointers.
So get rid of the dummy element, and do things right instead - by
passing a *pointer* to the list, instead of the list. And then when
traversing the list and looking for a place to insert things, don't go
to the next entry - just update the "pointer to pointer" to point to
the address of the next entry. Each entry in a C linked list is no
different than the list itself, so you can use the pointer to the
pointer to the next entry as a pointer to the list.
This is a pet peeve of mine. The real beauty of pointers can never be
understood unless you understand the indirection they allow. People
who grew up with Pascal and were corrupted by that mindset are
mentally stunted. Niklaus Wirth has a lot to answer for!
But never fear. You too can overcome that mental limitation, it just
needs some brain exercise. Reading this patch may help. In particular,
contemplate the new "taglist_add_divetag()".
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-03-10 17:18:13 +00:00
|
|
|
save_tags(b, dive->tag_list);
|
2019-11-19 17:16:45 +00:00
|
|
|
if (dive->dive_site)
|
2018-10-26 15:03:54 +00:00
|
|
|
put_format(b, " divesiteid='%8x'", dive->dive_site->uuid);
|
2019-11-19 17:16:45 +00:00
|
|
|
if (dive->user_salinity)
|
|
|
|
put_salinity(b, dive->user_salinity, " watersalinity='", " g/l'");
|
2014-01-16 02:03:11 +00:00
|
|
|
show_date(b, dive->when);
|
2019-04-30 10:42:33 +00:00
|
|
|
if (surface_pressure.mbar)
|
|
|
|
put_pressure(b, surface_pressure, " airpressure='", " bar'");
|
2024-05-27 15:09:48 +00:00
|
|
|
if (dive->dcs[0].duration.seconds > 0)
|
2017-09-10 07:53:45 +00:00
|
|
|
put_format(b, " duration='%u:%02u min'>\n",
|
2024-05-27 15:09:48 +00:00
|
|
|
FRACTION_TUPLE(dive->dcs[0].duration.seconds, 60));
|
2017-09-10 07:53:45 +00:00
|
|
|
else
|
|
|
|
put_format(b, ">\n");
|
2018-09-10 12:47:56 +00:00
|
|
|
save_overview(b, dive, anonymize);
|
2014-01-16 02:03:11 +00:00
|
|
|
save_cylinder_info(b, dive);
|
|
|
|
save_weightsystem_info(b, dive);
|
|
|
|
save_dive_temperature(b, dive);
|
2012-11-25 02:50:21 +00:00
|
|
|
/* Save the dive computer data */
|
2024-05-27 15:09:48 +00:00
|
|
|
for (auto &dc: dive->dcs)
|
|
|
|
save_dc(b, dive, &dc);
|
2014-06-03 17:26:27 +00:00
|
|
|
FOR_EACH_PICTURE(dive)
|
|
|
|
save_picture(b, picture);
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, "</dive>\n");
|
|
|
|
}
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
int save_dive(FILE *f, struct dive *dive, bool anonymize)
|
2014-01-16 02:03:11 +00:00
|
|
|
{
|
2024-05-05 04:47:28 +00:00
|
|
|
membuffer buf;
|
2014-01-16 02:03:11 +00:00
|
|
|
|
2018-09-10 12:47:56 +00:00
|
|
|
save_one_dive_to_mb(&buf, dive, anonymize);
|
2014-01-16 02:03:11 +00:00
|
|
|
flush_buffer(&buf, f);
|
2014-03-14 17:11:26 +00:00
|
|
|
/* Error handling? */
|
|
|
|
return 0;
|
2011-09-01 23:27:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-10 12:47:56 +00:00
|
|
|
static void save_trip(struct membuffer *b, dive_trip_t *trip, bool anonymize)
|
Allow overlapping (and disjoint) dive trips
We used to have the rule that a dive trip has to have all dives in it in
sequential order, even though our XML file really is much more flexible,
and allows arbitrary nesting of dives within a dive trip.
Put another way, the old model had fairly inflexible rules:
- the dive array is sorted by time
- a dive trip is always a contiguous slice of this sorted array
which makes perfect sense when you think of the dive and trip list as a
physical activity by one person, but leads to various very subtle issues
in the general case when there are no guarantees that the user then uses
subsurface that way.
In particular, if you load the XML files of two divers that have
overlapping dive trips, the end result is incredibly messy, and does not
conform to the above model at all.
There's two ways to enforce such conformance:
- disallow that kind of behavior entirely.
This is actually hard. Our XML files aren't date-based, they are
based on XML nesting rules, and even a single XML file can have
nesting that violates the date ordering. With multiple XML files,
it's trivial to do in practice, and while we could just fail at
loading, the failure would have to be a hard failure that leaves the
user no way to use the data at all.
- try to "fix it up" by sorting, splitting, and combining dive trips
automatically.
Dirk had a patch to do this, but it really does destroy the actual
dive data: if you load both mine and Dirk's dive trips, you ended up
with a result that followed the above two technical rules, but that
didn't actually make any *sense*.
So this patch doesn't try to enforce the rules, and instead just changes
them to be more generic:
- the dive array is still sorted by dive time
- a dive trip is just an arbitrary collection of dives.
The relaxed rules means that mixing dives and dive trips for two people
is trivial, and we can easily handle any XML file. The dive trip is
defined by the XML nesting level, and is totally independent of any
date-based sorting.
It does require a few things:
- when we save our dive data, we have to do it hierarchically by dive
trip, not just by walking the dive array linearly.
- similarly, when we create the dive tree model, we can't just blindly
walk the array of dives one by one, we have to look up the correct
trip (parent)
- when we try to merge two dives that are adjacent (by date sorting),
we can't do it if they are in different trips.
but apart from that, nothing else really changes.
NOTE! Despite the new relaxed model, creating totally disjoing dive
trips is not all that easy (nor is there any *reason* for it to be
easty). Our GUI interfaces still are "add dive to trip above" etc, and
the automatic adding of dives to dive trips is obviously still based on
date.
So this does not really change the expected normal usage, the relaxed
data structure rules just mean that we don't need to worry about the odd
cases as much, because we can just let them be.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-30 19:00:37 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct dive *dive;
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, "<trip");
|
2018-11-11 12:09:51 +00:00
|
|
|
show_date(b, trip_date(trip));
|
2014-02-16 23:42:56 +00:00
|
|
|
show_utf8(b, trip->location, " location=\'", "\'", 1);
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, ">\n");
|
2014-02-16 23:42:56 +00:00
|
|
|
show_utf8(b, trip->notes, "<notes>", "</notes>\n", 0);
|
Allow overlapping (and disjoint) dive trips
We used to have the rule that a dive trip has to have all dives in it in
sequential order, even though our XML file really is much more flexible,
and allows arbitrary nesting of dives within a dive trip.
Put another way, the old model had fairly inflexible rules:
- the dive array is sorted by time
- a dive trip is always a contiguous slice of this sorted array
which makes perfect sense when you think of the dive and trip list as a
physical activity by one person, but leads to various very subtle issues
in the general case when there are no guarantees that the user then uses
subsurface that way.
In particular, if you load the XML files of two divers that have
overlapping dive trips, the end result is incredibly messy, and does not
conform to the above model at all.
There's two ways to enforce such conformance:
- disallow that kind of behavior entirely.
This is actually hard. Our XML files aren't date-based, they are
based on XML nesting rules, and even a single XML file can have
nesting that violates the date ordering. With multiple XML files,
it's trivial to do in practice, and while we could just fail at
loading, the failure would have to be a hard failure that leaves the
user no way to use the data at all.
- try to "fix it up" by sorting, splitting, and combining dive trips
automatically.
Dirk had a patch to do this, but it really does destroy the actual
dive data: if you load both mine and Dirk's dive trips, you ended up
with a result that followed the above two technical rules, but that
didn't actually make any *sense*.
So this patch doesn't try to enforce the rules, and instead just changes
them to be more generic:
- the dive array is still sorted by dive time
- a dive trip is just an arbitrary collection of dives.
The relaxed rules means that mixing dives and dive trips for two people
is trivial, and we can easily handle any XML file. The dive trip is
defined by the XML nesting level, and is totally independent of any
date-based sorting.
It does require a few things:
- when we save our dive data, we have to do it hierarchically by dive
trip, not just by walking the dive array linearly.
- similarly, when we create the dive tree model, we can't just blindly
walk the array of dives one by one, we have to look up the correct
trip (parent)
- when we try to merge two dives that are adjacent (by date sorting),
we can't do it if they are in different trips.
but apart from that, nothing else really changes.
NOTE! Despite the new relaxed model, creating totally disjoing dive
trips is not all that easy (nor is there any *reason* for it to be
easty). Our GUI interfaces still are "add dive to trip above" etc, and
the automatic adding of dives to dive trips is obviously still based on
date.
So this does not really change the expected normal usage, the relaxed
data structure rules just mean that we don't need to worry about the odd
cases as much, because we can just let them be.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-30 19:00:37 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Incredibly cheesy: we want to save the dives sorted, and they
|
|
|
|
* are sorted in the dive array.. So instead of using the dive
|
|
|
|
* list in the trip, we just traverse the global dive array and
|
|
|
|
* check the divetrip pointer..
|
|
|
|
*/
|
|
|
|
for_each_dive(i, dive) {
|
|
|
|
if (dive->divetrip == trip)
|
2018-09-10 12:47:56 +00:00
|
|
|
save_one_dive_to_mb(b, dive, anonymize);
|
Allow overlapping (and disjoint) dive trips
We used to have the rule that a dive trip has to have all dives in it in
sequential order, even though our XML file really is much more flexible,
and allows arbitrary nesting of dives within a dive trip.
Put another way, the old model had fairly inflexible rules:
- the dive array is sorted by time
- a dive trip is always a contiguous slice of this sorted array
which makes perfect sense when you think of the dive and trip list as a
physical activity by one person, but leads to various very subtle issues
in the general case when there are no guarantees that the user then uses
subsurface that way.
In particular, if you load the XML files of two divers that have
overlapping dive trips, the end result is incredibly messy, and does not
conform to the above model at all.
There's two ways to enforce such conformance:
- disallow that kind of behavior entirely.
This is actually hard. Our XML files aren't date-based, they are
based on XML nesting rules, and even a single XML file can have
nesting that violates the date ordering. With multiple XML files,
it's trivial to do in practice, and while we could just fail at
loading, the failure would have to be a hard failure that leaves the
user no way to use the data at all.
- try to "fix it up" by sorting, splitting, and combining dive trips
automatically.
Dirk had a patch to do this, but it really does destroy the actual
dive data: if you load both mine and Dirk's dive trips, you ended up
with a result that followed the above two technical rules, but that
didn't actually make any *sense*.
So this patch doesn't try to enforce the rules, and instead just changes
them to be more generic:
- the dive array is still sorted by dive time
- a dive trip is just an arbitrary collection of dives.
The relaxed rules means that mixing dives and dive trips for two people
is trivial, and we can easily handle any XML file. The dive trip is
defined by the XML nesting level, and is totally independent of any
date-based sorting.
It does require a few things:
- when we save our dive data, we have to do it hierarchically by dive
trip, not just by walking the dive array linearly.
- similarly, when we create the dive tree model, we can't just blindly
walk the array of dives one by one, we have to look up the correct
trip (parent)
- when we try to merge two dives that are adjacent (by date sorting),
we can't do it if they are in different trips.
but apart from that, nothing else really changes.
NOTE! Despite the new relaxed model, creating totally disjoing dive
trips is not all that easy (nor is there any *reason* for it to be
easty). Our GUI interfaces still are "add dive to trip above" etc, and
the automatic adding of dives to dive trips is obviously still based on
date.
So this does not really change the expected normal usage, the relaxed
data structure rules just mean that we don't need to worry about the odd
cases as much, because we can just let them be.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-30 19:00:37 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, "</trip>\n");
|
Allow overlapping (and disjoint) dive trips
We used to have the rule that a dive trip has to have all dives in it in
sequential order, even though our XML file really is much more flexible,
and allows arbitrary nesting of dives within a dive trip.
Put another way, the old model had fairly inflexible rules:
- the dive array is sorted by time
- a dive trip is always a contiguous slice of this sorted array
which makes perfect sense when you think of the dive and trip list as a
physical activity by one person, but leads to various very subtle issues
in the general case when there are no guarantees that the user then uses
subsurface that way.
In particular, if you load the XML files of two divers that have
overlapping dive trips, the end result is incredibly messy, and does not
conform to the above model at all.
There's two ways to enforce such conformance:
- disallow that kind of behavior entirely.
This is actually hard. Our XML files aren't date-based, they are
based on XML nesting rules, and even a single XML file can have
nesting that violates the date ordering. With multiple XML files,
it's trivial to do in practice, and while we could just fail at
loading, the failure would have to be a hard failure that leaves the
user no way to use the data at all.
- try to "fix it up" by sorting, splitting, and combining dive trips
automatically.
Dirk had a patch to do this, but it really does destroy the actual
dive data: if you load both mine and Dirk's dive trips, you ended up
with a result that followed the above two technical rules, but that
didn't actually make any *sense*.
So this patch doesn't try to enforce the rules, and instead just changes
them to be more generic:
- the dive array is still sorted by dive time
- a dive trip is just an arbitrary collection of dives.
The relaxed rules means that mixing dives and dive trips for two people
is trivial, and we can easily handle any XML file. The dive trip is
defined by the XML nesting level, and is totally independent of any
date-based sorting.
It does require a few things:
- when we save our dive data, we have to do it hierarchically by dive
trip, not just by walking the dive array linearly.
- similarly, when we create the dive tree model, we can't just blindly
walk the array of dives one by one, we have to look up the correct
trip (parent)
- when we try to merge two dives that are adjacent (by date sorting),
we can't do it if they are in different trips.
but apart from that, nothing else really changes.
NOTE! Despite the new relaxed model, creating totally disjoing dive
trips is not all that easy (nor is there any *reason* for it to be
easty). Our GUI interfaces still are "add dive to trip above" etc, and
the automatic adding of dives to dive trips is obviously still based on
date.
So this does not really change the expected normal usage, the relaxed
data structure rules just mean that we don't need to worry about the odd
cases as much, because we can just let them be.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-30 19:00:37 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 19:46:44 +00:00
|
|
|
static void save_one_device(struct membuffer *b, const struct device *d)
|
2012-12-28 16:38:47 +00:00
|
|
|
{
|
2024-05-18 15:03:19 +00:00
|
|
|
std::string model = device_get_model(d);
|
|
|
|
std::string nickname = device_get_nickname(d);
|
|
|
|
std::string serial_nr = device_get_serial(d);
|
2014-01-16 02:03:11 +00:00
|
|
|
|
Assemble the actual Suunto serial number
It turns out that the serial number returned by libdivecomputer isn't
really the serial number as interpreted by the vendor. Those tend to be
strings, but libdivecomputer gives us a 32bit number.
Some experimenting showed that for the Suunto devies tested the serial
number is encoded in that 32bit number:
It so happens that the Suunto serial number strings are strings that have
all numbers, but they aren't *one* number. They are four bytes
representing two numbers each, and the "23500027" string is actually the
four bytes 23 50 00 27 (0x17 0x32 0x00 0x1b). And libdivecomputer has
incorrectly parsed those four bytes as one number, not as the encoded
serial number string it is. So the value 389152795 is actually hex
0x1732001b, which is 0x17 0x32 0x00 0x1b, which is - 23 50 00 27.
This should be done by libdivecomputer, but hey, in the meantime this at
least shows the concept. And helps test the XML save/restore code.
It depends on the two patches that create the whole "device.c"
infrastructure, of course. With this, my dive file ends up having the
settings section look like this:
<divecomputerid model='Suunto Vyper Air' deviceid='d4629110'
serial='01201094' firmware='1.1.22'/>
<divecomputerid model='Suunto HelO2' deviceid='995dd566'
serial='23500027' firmware='1.0.4'/>
where the format of the firmware version is something I guessed at,
but it was the obvious choice (again, it's byte-based, I'm ignoring
the high byte that is zero for both of my Suuntos).
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2013-01-10 00:14:21 +00:00
|
|
|
/* Nicknames that are empty or the same as the device model are not interesting */
|
2024-05-18 15:03:19 +00:00
|
|
|
if (nickname.empty() || serial_nr.empty() || model == nickname)
|
2012-12-31 04:27:01 +00:00
|
|
|
return;
|
2012-12-28 16:38:47 +00:00
|
|
|
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, "<divecomputerid");
|
2024-05-18 15:03:19 +00:00
|
|
|
show_utf8(b, model.c_str(), " model='", "'", 1);
|
|
|
|
put_format(b, " deviceid='%08x'", calculate_string_hash(serial_nr.c_str()));
|
|
|
|
show_utf8(b, serial_nr.c_str(), " serial='", "'", 1);
|
|
|
|
show_utf8(b, nickname.c_str(), " nickname='", "'", 1);
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, "/>\n");
|
2013-01-24 19:42:20 +00:00
|
|
|
}
|
|
|
|
|
2021-10-30 23:31:29 +00:00
|
|
|
static void save_one_fingerprint(struct membuffer *b, int i)
|
|
|
|
{
|
|
|
|
put_format(b, "<fingerprint model='%08x' serial='%08x' deviceid='%08x' diveid='%08x' data='%s'/>\n",
|
|
|
|
fp_get_model(&fingerprint_table, i),
|
|
|
|
fp_get_serial(&fingerprint_table, i),
|
|
|
|
fp_get_deviceid(&fingerprint_table, i),
|
|
|
|
fp_get_diveid(&fingerprint_table, i),
|
2024-02-29 07:23:55 +00:00
|
|
|
fp_get_data(&fingerprint_table, i).c_str());
|
2021-10-30 23:31:29 +00:00
|
|
|
}
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
int save_dives(const char *filename)
|
2013-02-01 08:28:33 +00:00
|
|
|
{
|
2018-09-10 12:47:56 +00:00
|
|
|
return save_dives_logic(filename, false, false);
|
2013-02-01 08:28:33 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 21:07:46 +00:00
|
|
|
static void save_filter_presets(struct membuffer *b)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (filter_presets_count() <= 0)
|
|
|
|
return;
|
|
|
|
put_format(b, "<filterpresets>\n");
|
|
|
|
for (i = 0; i < filter_presets_count(); i++) {
|
2024-02-29 12:39:17 +00:00
|
|
|
std::string name = filter_preset_name(i);
|
2020-06-08 21:07:46 +00:00
|
|
|
put_format(b, " <filterpreset");
|
2024-02-29 12:39:17 +00:00
|
|
|
show_utf8(b, name.c_str(), " name='", "'", 1);
|
2020-06-08 21:07:46 +00:00
|
|
|
put_format(b, ">\n");
|
|
|
|
|
2024-02-29 12:39:17 +00:00
|
|
|
std::string fulltext = filter_preset_fulltext_query(i);
|
|
|
|
if (!fulltext.empty()) {
|
2020-06-08 21:07:46 +00:00
|
|
|
const char *fulltext_mode = filter_preset_fulltext_mode(i);
|
|
|
|
show_utf8(b, fulltext_mode, " <fulltext mode='", "'>", 1);
|
2024-02-29 12:39:17 +00:00
|
|
|
show_utf8(b, fulltext.c_str(), "", "</fulltext>\n", 0);
|
2020-06-08 21:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int j = 0; j < filter_preset_constraint_count(i); j++) {
|
|
|
|
const struct filter_constraint *constraint = filter_preset_constraint(i, j);
|
|
|
|
const char *type = filter_constraint_type_to_string(constraint->type);
|
|
|
|
put_format(b, " <constraint");
|
|
|
|
show_utf8(b, type, " type='", "'", 1);
|
|
|
|
if (filter_constraint_has_string_mode(constraint->type)) {
|
|
|
|
const char *mode = filter_constraint_string_mode_to_string(constraint->string_mode);
|
|
|
|
show_utf8(b, mode, " string_mode='", "'", 1);
|
|
|
|
}
|
|
|
|
if (filter_constraint_has_range_mode(constraint->type)) {
|
|
|
|
const char *mode = filter_constraint_range_mode_to_string(constraint->range_mode);
|
|
|
|
show_utf8(b, mode, " range_mode='", "'", 1);
|
|
|
|
}
|
|
|
|
if (constraint->negate)
|
|
|
|
put_format(b, " negate='1'");
|
|
|
|
put_format(b, ">");
|
2024-02-29 10:31:46 +00:00
|
|
|
std::string data = filter_constraint_data_to_string(constraint);
|
|
|
|
show_utf8(b, data.c_str(), "", "", 0);
|
2020-06-08 21:07:46 +00:00
|
|
|
put_format(b, "</constraint>\n");
|
|
|
|
}
|
|
|
|
put_format(b, " </filterpreset>\n");
|
|
|
|
}
|
|
|
|
put_format(b, "</filterpresets>\n");
|
|
|
|
}
|
|
|
|
|
2020-02-06 21:56:10 +00:00
|
|
|
static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonymize)
|
2011-09-01 23:27:52 +00:00
|
|
|
{
|
|
|
|
int i;
|
New XML format for saving dives
This patch makes the trips nest, and it also fixes the fact that you never
saved the trip notes (you could edit it, but saving would throw it away).
I did *not* change the indentation of the dives, so the trip stuff shows
up the the beginning of the line, at the same level as the <dive> and
<dives> thing. I think it's fairly readable xml, though, and we haven't
really had proper "indentation shows nesting" anyway, since the top-level
"<dives>" thing also didn't indent stuff inside of it.
Anyway, the way I wrote it, it still parses your old "INTRIP" stuff etc,
so as far as I know, it should happily read the old-style XML too. At
least it seemed to work with your xml file that already had the old-style
one (I haven't committed my divetrips, exactly because I didn't like the
new format).
It always saves in the new style, though.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-09-30 19:36:18 +00:00
|
|
|
struct dive *dive;
|
Allow overlapping (and disjoint) dive trips
We used to have the rule that a dive trip has to have all dives in it in
sequential order, even though our XML file really is much more flexible,
and allows arbitrary nesting of dives within a dive trip.
Put another way, the old model had fairly inflexible rules:
- the dive array is sorted by time
- a dive trip is always a contiguous slice of this sorted array
which makes perfect sense when you think of the dive and trip list as a
physical activity by one person, but leads to various very subtle issues
in the general case when there are no guarantees that the user then uses
subsurface that way.
In particular, if you load the XML files of two divers that have
overlapping dive trips, the end result is incredibly messy, and does not
conform to the above model at all.
There's two ways to enforce such conformance:
- disallow that kind of behavior entirely.
This is actually hard. Our XML files aren't date-based, they are
based on XML nesting rules, and even a single XML file can have
nesting that violates the date ordering. With multiple XML files,
it's trivial to do in practice, and while we could just fail at
loading, the failure would have to be a hard failure that leaves the
user no way to use the data at all.
- try to "fix it up" by sorting, splitting, and combining dive trips
automatically.
Dirk had a patch to do this, but it really does destroy the actual
dive data: if you load both mine and Dirk's dive trips, you ended up
with a result that followed the above two technical rules, but that
didn't actually make any *sense*.
So this patch doesn't try to enforce the rules, and instead just changes
them to be more generic:
- the dive array is still sorted by dive time
- a dive trip is just an arbitrary collection of dives.
The relaxed rules means that mixing dives and dive trips for two people
is trivial, and we can easily handle any XML file. The dive trip is
defined by the XML nesting level, and is totally independent of any
date-based sorting.
It does require a few things:
- when we save our dive data, we have to do it hierarchically by dive
trip, not just by walking the dive array linearly.
- similarly, when we create the dive tree model, we can't just blindly
walk the array of dives one by one, we have to look up the correct
trip (parent)
- when we try to merge two dives that are adjacent (by date sorting),
we can't do it if they are in different trips.
but apart from that, nothing else really changes.
NOTE! Despite the new relaxed model, creating totally disjoing dive
trips is not all that easy (nor is there any *reason* for it to be
easty). Our GUI interfaces still are "add dive to trip above" etc, and
the automatic adding of dives to dive trips is obviously still based on
date.
So this does not really change the expected normal usage, the relaxed
data structure rules just mean that we don't need to worry about the odd
cases as much, because we can just let them be.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-30 19:00:37 +00:00
|
|
|
dive_trip_t *trip;
|
2012-08-22 05:04:24 +00:00
|
|
|
|
2015-06-20 13:45:12 +00:00
|
|
|
put_format(b, "<divelog program='subsurface' version='%d'>\n<settings>\n", DATAFORMAT_VERSION);
|
2013-01-01 16:29:43 +00:00
|
|
|
|
|
|
|
/* save the dive computer nicknames, if any */
|
2024-05-13 17:34:43 +00:00
|
|
|
for (int i = 0; i < nr_devices(divelog.devices.get()); i++) {
|
|
|
|
const struct device *d = get_device(divelog.devices.get(), i);
|
2020-10-06 19:46:44 +00:00
|
|
|
if (!select_only || device_used_by_selected_dive(d))
|
|
|
|
save_one_device(b, d);
|
|
|
|
}
|
2021-10-30 23:31:29 +00:00
|
|
|
/* save the fingerprint data */
|
|
|
|
for (int i = 0; i < nr_fingerprints(&fingerprint_table); i++)
|
|
|
|
save_one_fingerprint(b, i);
|
|
|
|
|
2022-11-12 08:14:00 +00:00
|
|
|
if (divelog.autogroup)
|
2014-04-11 06:17:35 +00:00
|
|
|
put_format(b, " <autogroup state='1' />\n");
|
2015-02-12 05:46:02 +00:00
|
|
|
put_format(b, "</settings>\n");
|
|
|
|
|
2019-04-06 19:05:18 +00:00
|
|
|
/* save the dive sites */
|
2015-02-12 05:46:02 +00:00
|
|
|
put_format(b, "<divesites>\n");
|
2024-05-11 09:47:45 +00:00
|
|
|
for (const auto &ds: *divelog.sites) {
|
2019-04-06 19:05:18 +00:00
|
|
|
/* Don't export empty dive sites */
|
2024-05-11 13:01:37 +00:00
|
|
|
if (ds->is_empty())
|
2019-04-06 19:05:18 +00:00
|
|
|
continue;
|
2019-03-30 20:34:35 +00:00
|
|
|
/* Only write used dive sites when exporting selected dives */
|
2024-05-11 13:01:37 +00:00
|
|
|
if (select_only && !ds->is_selected())
|
2019-04-06 19:05:18 +00:00
|
|
|
continue;
|
2015-07-16 04:25:26 +00:00
|
|
|
|
2015-02-15 13:03:57 +00:00
|
|
|
put_format(b, "<site uuid='%8x'", ds->uuid);
|
2024-05-04 15:18:08 +00:00
|
|
|
show_utf8_blanked(b, ds->name.c_str(), " name='", "'", 1, anonymize);
|
2018-10-20 18:12:15 +00:00
|
|
|
put_location(b, &ds->location, " gps='", "'");
|
2024-05-04 15:18:08 +00:00
|
|
|
show_utf8_blanked(b, ds->description.c_str(), " description='", "'", 1, anonymize);
|
2015-10-04 09:17:25 +00:00
|
|
|
put_format(b, ">\n");
|
2024-05-04 15:18:08 +00:00
|
|
|
show_utf8_blanked(b, ds->notes.c_str(), " <notes>", " </notes>\n", 0, anonymize);
|
2024-05-04 11:39:04 +00:00
|
|
|
for (auto const &t: ds->taxonomy) {
|
|
|
|
if (t.category != TC_NONE && !t.value.empty()) {
|
|
|
|
put_format(b, " <geo cat='%d'", t.category);
|
|
|
|
put_format(b, " origin='%d'", t.origin);
|
|
|
|
show_utf8_blanked(b, t.value.c_str(), " value='", "'", 1, anonymize);
|
|
|
|
put_format(b, "/>\n");
|
2015-07-01 19:30:33 +00:00
|
|
|
}
|
|
|
|
}
|
2015-10-04 09:17:25 +00:00
|
|
|
put_format(b, "</site>\n");
|
2015-02-12 05:46:02 +00:00
|
|
|
}
|
|
|
|
put_format(b, "</divesites>\n<dives>\n");
|
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
|
|
|
for (i = 0; i < divelog.trips->nr; ++i)
|
|
|
|
divelog.trips->trips[i]->saved = 0;
|
Allow overlapping (and disjoint) dive trips
We used to have the rule that a dive trip has to have all dives in it in
sequential order, even though our XML file really is much more flexible,
and allows arbitrary nesting of dives within a dive trip.
Put another way, the old model had fairly inflexible rules:
- the dive array is sorted by time
- a dive trip is always a contiguous slice of this sorted array
which makes perfect sense when you think of the dive and trip list as a
physical activity by one person, but leads to various very subtle issues
in the general case when there are no guarantees that the user then uses
subsurface that way.
In particular, if you load the XML files of two divers that have
overlapping dive trips, the end result is incredibly messy, and does not
conform to the above model at all.
There's two ways to enforce such conformance:
- disallow that kind of behavior entirely.
This is actually hard. Our XML files aren't date-based, they are
based on XML nesting rules, and even a single XML file can have
nesting that violates the date ordering. With multiple XML files,
it's trivial to do in practice, and while we could just fail at
loading, the failure would have to be a hard failure that leaves the
user no way to use the data at all.
- try to "fix it up" by sorting, splitting, and combining dive trips
automatically.
Dirk had a patch to do this, but it really does destroy the actual
dive data: if you load both mine and Dirk's dive trips, you ended up
with a result that followed the above two technical rules, but that
didn't actually make any *sense*.
So this patch doesn't try to enforce the rules, and instead just changes
them to be more generic:
- the dive array is still sorted by dive time
- a dive trip is just an arbitrary collection of dives.
The relaxed rules means that mixing dives and dive trips for two people
is trivial, and we can easily handle any XML file. The dive trip is
defined by the XML nesting level, and is totally independent of any
date-based sorting.
It does require a few things:
- when we save our dive data, we have to do it hierarchically by dive
trip, not just by walking the dive array linearly.
- similarly, when we create the dive tree model, we can't just blindly
walk the array of dives one by one, we have to look up the correct
trip (parent)
- when we try to merge two dives that are adjacent (by date sorting),
we can't do it if they are in different trips.
but apart from that, nothing else really changes.
NOTE! Despite the new relaxed model, creating totally disjoing dive
trips is not all that easy (nor is there any *reason* for it to be
easty). Our GUI interfaces still are "add dive to trip above" etc, and
the automatic adding of dives to dive trips is obviously still based on
date.
So this does not really change the expected normal usage, the relaxed
data structure rules just mean that we don't need to worry about the odd
cases as much, because we can just let them be.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-30 19:00:37 +00:00
|
|
|
|
2020-06-08 21:07:46 +00:00
|
|
|
/* save the filter presets */
|
|
|
|
save_filter_presets(b);
|
|
|
|
|
2012-08-22 05:04:24 +00:00
|
|
|
/* save the dives */
|
New XML format for saving dives
This patch makes the trips nest, and it also fixes the fact that you never
saved the trip notes (you could edit it, but saving would throw it away).
I did *not* change the indentation of the dives, so the trip stuff shows
up the the beginning of the line, at the same level as the <dive> and
<dives> thing. I think it's fairly readable xml, though, and we haven't
really had proper "indentation shows nesting" anyway, since the top-level
"<dives>" thing also didn't indent stuff inside of it.
Anyway, the way I wrote it, it still parses your old "INTRIP" stuff etc,
so as far as I know, it should happily read the old-style XML too. At
least it seemed to work with your xml file that already had the old-style
one (I haven't committed my divetrips, exactly because I didn't like the
new format).
It always saves in the new style, though.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-09-30 19:36:18 +00:00
|
|
|
for_each_dive(i, dive) {
|
2013-02-01 08:28:33 +00:00
|
|
|
if (select_only) {
|
|
|
|
|
2014-02-16 23:42:56 +00:00
|
|
|
if (!dive->selected)
|
2013-02-01 08:28:33 +00:00
|
|
|
continue;
|
2018-09-10 12:47:56 +00:00
|
|
|
save_one_dive_to_mb(b, dive, anonymize);
|
Allow overlapping (and disjoint) dive trips
We used to have the rule that a dive trip has to have all dives in it in
sequential order, even though our XML file really is much more flexible,
and allows arbitrary nesting of dives within a dive trip.
Put another way, the old model had fairly inflexible rules:
- the dive array is sorted by time
- a dive trip is always a contiguous slice of this sorted array
which makes perfect sense when you think of the dive and trip list as a
physical activity by one person, but leads to various very subtle issues
in the general case when there are no guarantees that the user then uses
subsurface that way.
In particular, if you load the XML files of two divers that have
overlapping dive trips, the end result is incredibly messy, and does not
conform to the above model at all.
There's two ways to enforce such conformance:
- disallow that kind of behavior entirely.
This is actually hard. Our XML files aren't date-based, they are
based on XML nesting rules, and even a single XML file can have
nesting that violates the date ordering. With multiple XML files,
it's trivial to do in practice, and while we could just fail at
loading, the failure would have to be a hard failure that leaves the
user no way to use the data at all.
- try to "fix it up" by sorting, splitting, and combining dive trips
automatically.
Dirk had a patch to do this, but it really does destroy the actual
dive data: if you load both mine and Dirk's dive trips, you ended up
with a result that followed the above two technical rules, but that
didn't actually make any *sense*.
So this patch doesn't try to enforce the rules, and instead just changes
them to be more generic:
- the dive array is still sorted by dive time
- a dive trip is just an arbitrary collection of dives.
The relaxed rules means that mixing dives and dive trips for two people
is trivial, and we can easily handle any XML file. The dive trip is
defined by the XML nesting level, and is totally independent of any
date-based sorting.
It does require a few things:
- when we save our dive data, we have to do it hierarchically by dive
trip, not just by walking the dive array linearly.
- similarly, when we create the dive tree model, we can't just blindly
walk the array of dives one by one, we have to look up the correct
trip (parent)
- when we try to merge two dives that are adjacent (by date sorting),
we can't do it if they are in different trips.
but apart from that, nothing else really changes.
NOTE! Despite the new relaxed model, creating totally disjoing dive
trips is not all that easy (nor is there any *reason* for it to be
easty). Our GUI interfaces still are "add dive to trip above" etc, and
the automatic adding of dives to dive trips is obviously still based on
date.
So this does not really change the expected normal usage, the relaxed
data structure rules just mean that we don't need to worry about the odd
cases as much, because we can just let them be.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-30 19:00:37 +00:00
|
|
|
|
2013-02-01 08:28:33 +00:00
|
|
|
} else {
|
|
|
|
trip = dive->divetrip;
|
|
|
|
|
|
|
|
/* Bare dive without a trip? */
|
|
|
|
if (!trip) {
|
2018-09-10 12:47:56 +00:00
|
|
|
save_one_dive_to_mb(b, dive, anonymize);
|
2013-02-01 08:28:33 +00:00
|
|
|
continue;
|
|
|
|
}
|
Allow overlapping (and disjoint) dive trips
We used to have the rule that a dive trip has to have all dives in it in
sequential order, even though our XML file really is much more flexible,
and allows arbitrary nesting of dives within a dive trip.
Put another way, the old model had fairly inflexible rules:
- the dive array is sorted by time
- a dive trip is always a contiguous slice of this sorted array
which makes perfect sense when you think of the dive and trip list as a
physical activity by one person, but leads to various very subtle issues
in the general case when there are no guarantees that the user then uses
subsurface that way.
In particular, if you load the XML files of two divers that have
overlapping dive trips, the end result is incredibly messy, and does not
conform to the above model at all.
There's two ways to enforce such conformance:
- disallow that kind of behavior entirely.
This is actually hard. Our XML files aren't date-based, they are
based on XML nesting rules, and even a single XML file can have
nesting that violates the date ordering. With multiple XML files,
it's trivial to do in practice, and while we could just fail at
loading, the failure would have to be a hard failure that leaves the
user no way to use the data at all.
- try to "fix it up" by sorting, splitting, and combining dive trips
automatically.
Dirk had a patch to do this, but it really does destroy the actual
dive data: if you load both mine and Dirk's dive trips, you ended up
with a result that followed the above two technical rules, but that
didn't actually make any *sense*.
So this patch doesn't try to enforce the rules, and instead just changes
them to be more generic:
- the dive array is still sorted by dive time
- a dive trip is just an arbitrary collection of dives.
The relaxed rules means that mixing dives and dive trips for two people
is trivial, and we can easily handle any XML file. The dive trip is
defined by the XML nesting level, and is totally independent of any
date-based sorting.
It does require a few things:
- when we save our dive data, we have to do it hierarchically by dive
trip, not just by walking the dive array linearly.
- similarly, when we create the dive tree model, we can't just blindly
walk the array of dives one by one, we have to look up the correct
trip (parent)
- when we try to merge two dives that are adjacent (by date sorting),
we can't do it if they are in different trips.
but apart from that, nothing else really changes.
NOTE! Despite the new relaxed model, creating totally disjoing dive
trips is not all that easy (nor is there any *reason* for it to be
easty). Our GUI interfaces still are "add dive to trip above" etc, and
the automatic adding of dives to dive trips is obviously still based on
date.
So this does not really change the expected normal usage, the relaxed
data structure rules just mean that we don't need to worry about the odd
cases as much, because we can just let them be.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-30 19:00:37 +00:00
|
|
|
|
2013-02-01 08:28:33 +00:00
|
|
|
/* Have we already seen this trip (and thus saved this dive?) */
|
2018-07-18 06:01:25 +00:00
|
|
|
if (trip->saved)
|
2013-02-01 08:28:33 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* We haven't seen this trip before - save it and all dives */
|
2018-07-18 06:01:25 +00:00
|
|
|
trip->saved = 1;
|
2018-09-10 12:47:56 +00:00
|
|
|
save_trip(b, trip, anonymize);
|
2013-02-01 08:28:33 +00:00
|
|
|
}
|
New XML format for saving dives
This patch makes the trips nest, and it also fixes the fact that you never
saved the trip notes (you could edit it, but saving would throw it away).
I did *not* change the indentation of the dives, so the trip stuff shows
up the the beginning of the line, at the same level as the <dive> and
<dives> thing. I think it's fairly readable xml, though, and we haven't
really had proper "indentation shows nesting" anyway, since the top-level
"<dives>" thing also didn't indent stuff inside of it.
Anyway, the way I wrote it, it still parses your old "INTRIP" stuff etc,
so as far as I know, it should happily read the old-style XML too. At
least it seemed to work with your xml file that already had the old-style
one (I haven't committed my divetrips, exactly because I didn't like the
new format).
It always saves in the new style, though.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-09-30 19:36:18 +00:00
|
|
|
}
|
2014-01-16 02:03:11 +00:00
|
|
|
put_format(b, "</dives>\n</divelog>\n");
|
|
|
|
}
|
|
|
|
|
2014-02-16 21:25:02 +00:00
|
|
|
static void save_backup(const char *name, const char *ext, const char *new_ext)
|
|
|
|
{
|
|
|
|
int len = strlen(name);
|
2024-02-28 05:36:48 +00:00
|
|
|
int a = strlen(ext);
|
2014-02-16 21:25:02 +00:00
|
|
|
|
|
|
|
/* len up to and including the final '.' */
|
|
|
|
len -= a;
|
|
|
|
if (len <= 1)
|
|
|
|
return;
|
2014-02-16 23:42:56 +00:00
|
|
|
if (name[len - 1] != '.')
|
2014-02-16 21:25:02 +00:00
|
|
|
return;
|
|
|
|
/* msvc doesn't have strncasecmp, has _strnicmp instead - crazy */
|
2014-02-16 23:42:56 +00:00
|
|
|
if (strncasecmp(name + len, ext, a))
|
2014-02-16 21:25:02 +00:00
|
|
|
return;
|
|
|
|
|
2024-02-28 05:36:48 +00:00
|
|
|
std::string newname(name, len);
|
|
|
|
newname += new_ext;
|
2014-02-16 21:25:02 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ignore errors. Maybe we can't create the backup file,
|
|
|
|
* maybe no old file existed. Regardless, we'll write the
|
|
|
|
* new file.
|
|
|
|
*/
|
2024-02-28 05:36:48 +00:00
|
|
|
(void) subsurface_rename(name, newname.c_str());
|
2014-02-16 21:25:02 +00:00
|
|
|
}
|
|
|
|
|
Initial implementation of git save format
This saves the dive data into a git object repository instead of a
single XML file.
We create a git object tree with each dive as a separate file,
hierarchically by trip and date.
NOTE 1: This largely duplicates the XML saving code, because trying to
share it seemed just too painful: the logic is very similar, but the
details of the actual strings end up differing sufficiently that there
are tons of trivial differences.
The git save format is line-based with minimal quoting, while XML quotes
everything with either "<..\>" or using single quotes around attributes.
NOTE 2: You currently need a dummy "file" to save to, which points to
the real save location: the git repository and branch to be used. We
should make this a config thing, but for testing, do something like
this:
echo git /home/torvalds/scuba:linus > git-test
to create that git information file, and when you use "Save To" and
specify "git-test" as the file to save to, subsurface will use the new
git save logic to save to the branch "linus" in the repository found at
"/home/torvalds/scuba".
NOTE 3: The git save format uses just the git object directory, it does
*not* check out the result in any git working tree or index. So after
you do a save, you can do
git log -p linus
to see what actually happened in that branch, but it will not affect any
actual checked-out state in the repository.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-03-06 21:28:39 +00:00
|
|
|
static void try_to_backup(const char *filename)
|
2014-01-16 02:03:11 +00:00
|
|
|
{
|
2014-02-16 23:42:56 +00:00
|
|
|
char extension[][5] = { "xml", "ssrf", "" };
|
2014-02-16 23:19:24 +00:00
|
|
|
int i = 0;
|
|
|
|
int flen = strlen(filename);
|
2014-01-16 02:03:11 +00:00
|
|
|
|
2014-02-16 21:25:02 +00:00
|
|
|
/* Maybe we might want to make this configurable? */
|
2014-02-16 23:19:24 +00:00
|
|
|
while (extension[i][0] != '\0') {
|
|
|
|
int elen = strlen(extension[i]);
|
|
|
|
if (strcasecmp(filename + flen - elen, extension[i]) == 0) {
|
2015-06-20 13:45:12 +00:00
|
|
|
if (last_xml_version < DATAFORMAT_VERSION) {
|
2024-02-28 05:36:48 +00:00
|
|
|
std::string special_ext = std::string(extension[i]) + ".v" + std::to_string(last_xml_version);
|
|
|
|
save_backup(filename, extension[i], special_ext.c_str());
|
2015-02-13 07:35:52 +00:00
|
|
|
} else {
|
|
|
|
save_backup(filename, extension[i], "bak");
|
|
|
|
}
|
2014-02-16 23:19:24 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
Initial implementation of git save format
This saves the dive data into a git object repository instead of a
single XML file.
We create a git object tree with each dive as a separate file,
hierarchically by trip and date.
NOTE 1: This largely duplicates the XML saving code, because trying to
share it seemed just too painful: the logic is very similar, but the
details of the actual strings end up differing sufficiently that there
are tons of trivial differences.
The git save format is line-based with minimal quoting, while XML quotes
everything with either "<..\>" or using single quotes around attributes.
NOTE 2: You currently need a dummy "file" to save to, which points to
the real save location: the git repository and branch to be used. We
should make this a config thing, but for testing, do something like
this:
echo git /home/torvalds/scuba:linus > git-test
to create that git information file, and when you use "Save To" and
specify "git-test" as the file to save to, subsurface will use the new
git save logic to save to the branch "linus" in the repository found at
"/home/torvalds/scuba".
NOTE 3: The git save format uses just the git object directory, it does
*not* check out the result in any git working tree or index. So after
you do a save, you can do
git log -p linus
to see what actually happened in that branch, but it will not affect any
actual checked-out state in the repository.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-03-06 21:28:39 +00:00
|
|
|
}
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
int save_dives_logic(const char *filename, const bool select_only, bool anonymize)
|
Initial implementation of git save format
This saves the dive data into a git object repository instead of a
single XML file.
We create a git object tree with each dive as a separate file,
hierarchically by trip and date.
NOTE 1: This largely duplicates the XML saving code, because trying to
share it seemed just too painful: the logic is very similar, but the
details of the actual strings end up differing sufficiently that there
are tons of trivial differences.
The git save format is line-based with minimal quoting, while XML quotes
everything with either "<..\>" or using single quotes around attributes.
NOTE 2: You currently need a dummy "file" to save to, which points to
the real save location: the git repository and branch to be used. We
should make this a config thing, but for testing, do something like
this:
echo git /home/torvalds/scuba:linus > git-test
to create that git information file, and when you use "Save To" and
specify "git-test" as the file to save to, subsurface will use the new
git save logic to save to the branch "linus" in the repository found at
"/home/torvalds/scuba".
NOTE 3: The git save format uses just the git object directory, it does
*not* check out the result in any git working tree or index. So after
you do a save, you can do
git log -p linus
to see what actually happened in that branch, but it will not affect any
actual checked-out state in the repository.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-03-06 21:28:39 +00:00
|
|
|
{
|
2024-05-05 04:47:28 +00:00
|
|
|
membuffer buf;
|
2022-04-13 16:43:37 +00:00
|
|
|
struct git_info info;
|
Initial implementation of git save format
This saves the dive data into a git object repository instead of a
single XML file.
We create a git object tree with each dive as a separate file,
hierarchically by trip and date.
NOTE 1: This largely duplicates the XML saving code, because trying to
share it seemed just too painful: the logic is very similar, but the
details of the actual strings end up differing sufficiently that there
are tons of trivial differences.
The git save format is line-based with minimal quoting, while XML quotes
everything with either "<..\>" or using single quotes around attributes.
NOTE 2: You currently need a dummy "file" to save to, which points to
the real save location: the git repository and branch to be used. We
should make this a config thing, but for testing, do something like
this:
echo git /home/torvalds/scuba:linus > git-test
to create that git information file, and when you use "Save To" and
specify "git-test" as the file to save to, subsurface will use the new
git save logic to save to the branch "linus" in the repository found at
"/home/torvalds/scuba".
NOTE 3: The git save format uses just the git object directory, it does
*not* check out the result in any git working tree or index. So after
you do a save, you can do
git log -p linus
to see what actually happened in that branch, but it will not affect any
actual checked-out state in the repository.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-03-06 21:28:39 +00:00
|
|
|
FILE *f;
|
2017-12-27 00:12:45 +00:00
|
|
|
int error = 0;
|
Initial implementation of git save format
This saves the dive data into a git object repository instead of a
single XML file.
We create a git object tree with each dive as a separate file,
hierarchically by trip and date.
NOTE 1: This largely duplicates the XML saving code, because trying to
share it seemed just too painful: the logic is very similar, but the
details of the actual strings end up differing sufficiently that there
are tons of trivial differences.
The git save format is line-based with minimal quoting, while XML quotes
everything with either "<..\>" or using single quotes around attributes.
NOTE 2: You currently need a dummy "file" to save to, which points to
the real save location: the git repository and branch to be used. We
should make this a config thing, but for testing, do something like
this:
echo git /home/torvalds/scuba:linus > git-test
to create that git information file, and when you use "Save To" and
specify "git-test" as the file to save to, subsurface will use the new
git save logic to save to the branch "linus" in the repository found at
"/home/torvalds/scuba".
NOTE 3: The git save format uses just the git object directory, it does
*not* check out the result in any git working tree or index. So after
you do a save, you can do
git log -p linus
to see what actually happened in that branch, but it will not affect any
actual checked-out state in the repository.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-03-06 21:28:39 +00:00
|
|
|
|
2022-04-18 21:36:00 +00:00
|
|
|
if (is_git_repository(filename, &info)) {
|
|
|
|
error = git_save_dives(&info, select_only);
|
|
|
|
return error;
|
|
|
|
}
|
2014-03-15 04:01:13 +00:00
|
|
|
|
2018-09-10 12:47:56 +00:00
|
|
|
save_dives_buffer(&buf, select_only, anonymize);
|
Initial implementation of git save format
This saves the dive data into a git object repository instead of a
single XML file.
We create a git object tree with each dive as a separate file,
hierarchically by trip and date.
NOTE 1: This largely duplicates the XML saving code, because trying to
share it seemed just too painful: the logic is very similar, but the
details of the actual strings end up differing sufficiently that there
are tons of trivial differences.
The git save format is line-based with minimal quoting, while XML quotes
everything with either "<..\>" or using single quotes around attributes.
NOTE 2: You currently need a dummy "file" to save to, which points to
the real save location: the git repository and branch to be used. We
should make this a config thing, but for testing, do something like
this:
echo git /home/torvalds/scuba:linus > git-test
to create that git information file, and when you use "Save To" and
specify "git-test" as the file to save to, subsurface will use the new
git save logic to save to the branch "linus" in the repository found at
"/home/torvalds/scuba".
NOTE 3: The git save format uses just the git object directory, it does
*not* check out the result in any git working tree or index. So after
you do a save, you can do
git log -p linus
to see what actually happened in that branch, but it will not affect any
actual checked-out state in the repository.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-03-06 21:28:39 +00:00
|
|
|
|
2015-11-16 15:10:07 +00:00
|
|
|
if (same_string(filename, "-")) {
|
|
|
|
f = stdout;
|
|
|
|
} else {
|
|
|
|
try_to_backup(filename);
|
|
|
|
error = -1;
|
|
|
|
f = subsurface_fopen(filename, "w");
|
|
|
|
}
|
2014-01-16 02:03:11 +00:00
|
|
|
if (f) {
|
|
|
|
flush_buffer(&buf, f);
|
2014-03-14 17:11:26 +00:00
|
|
|
error = fclose(f);
|
2014-01-16 02:03:11 +00:00
|
|
|
}
|
2014-03-14 17:11:26 +00:00
|
|
|
if (error)
|
2019-04-06 19:05:18 +00:00
|
|
|
report_error(translate("gettextFromC", "Failed to save dives to %s (%s)"), filename, strerror(errno));
|
2014-03-14 17:11:26 +00:00
|
|
|
|
|
|
|
return error;
|
2011-09-01 23:27:52 +00:00
|
|
|
}
|
2013-10-19 17:35:36 +00:00
|
|
|
|
2020-10-17 18:15:23 +00:00
|
|
|
|
|
|
|
static int export_dives_xslt_doit(const char *filename, struct xml_params *params, bool selected, int units, const char *export_xslt, bool anonymize);
|
2018-09-10 12:47:56 +00:00
|
|
|
int export_dives_xslt(const char *filename, const bool selected, const int units, const char *export_xslt, bool anonymize)
|
2020-10-17 18:15:23 +00:00
|
|
|
{
|
|
|
|
struct xml_params *params = alloc_xml_params();
|
|
|
|
int ret = export_dives_xslt_doit(filename, params, selected, units, export_xslt, anonymize);
|
|
|
|
free_xml_params(params);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int export_dives_xslt_doit(const char *filename, struct xml_params *params, bool selected, int units, const char *export_xslt, bool anonymize)
|
2013-10-19 17:35:36 +00:00
|
|
|
{
|
|
|
|
FILE *f;
|
2024-05-05 04:47:28 +00:00
|
|
|
membuffer buf;
|
2013-10-19 17:35:36 +00:00
|
|
|
xmlDoc *doc;
|
|
|
|
xsltStylesheetPtr xslt = NULL;
|
|
|
|
xmlDoc *transformed;
|
2014-07-16 08:57:29 +00:00
|
|
|
int res = 0;
|
2014-04-26 07:55:41 +00:00
|
|
|
|
2014-12-22 01:53:18 +00:00
|
|
|
if (verbose)
|
2024-03-24 20:03:08 +00:00
|
|
|
report_info("export_dives_xslt with stylesheet %s", export_xslt);
|
2014-12-22 01:53:18 +00:00
|
|
|
|
2013-10-19 17:35:36 +00:00
|
|
|
if (!filename)
|
2014-04-26 07:55:41 +00:00
|
|
|
return report_error("No filename for export");
|
2013-10-19 17:35:36 +00:00
|
|
|
|
|
|
|
/* Save XML to file and convert it into a memory buffer */
|
2018-09-10 12:47:56 +00:00
|
|
|
save_dives_buffer(&buf, selected, anonymize);
|
2013-10-19 17:35:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse the memory buffer into XML document and
|
2014-04-26 07:55:41 +00:00
|
|
|
* transform it to selected export format, finally dumping
|
2013-10-19 17:35:36 +00:00
|
|
|
* the XML into a character buffer.
|
|
|
|
*/
|
2024-03-13 22:47:05 +00:00
|
|
|
doc = xmlReadMemory(buf.buffer, buf.len, "divelog", NULL, XML_PARSE_HUGE);
|
2014-03-14 17:11:26 +00:00
|
|
|
if (!doc)
|
|
|
|
return report_error("Failed to read XML memory");
|
2013-10-19 17:35:36 +00:00
|
|
|
|
2014-04-26 07:55:41 +00:00
|
|
|
/* Convert to export format */
|
|
|
|
xslt = get_stylesheet(export_xslt);
|
2014-03-14 17:11:26 +00:00
|
|
|
if (!xslt)
|
2014-04-26 07:55:41 +00:00
|
|
|
return report_error("Failed to open export conversion stylesheet");
|
2013-10-19 17:35:36 +00:00
|
|
|
|
2020-10-17 18:15:23 +00:00
|
|
|
xml_params_add_int(params, "units", units);
|
2014-12-31 20:09:34 +00:00
|
|
|
|
2020-10-17 18:15:23 +00:00
|
|
|
transformed = xsltApplyStylesheet(xslt, doc, xml_params_get(params));
|
2013-10-19 17:35:36 +00:00
|
|
|
xmlFreeDoc(doc);
|
|
|
|
|
2014-04-26 07:55:41 +00:00
|
|
|
/* Write the transformed export to file */
|
2013-12-19 13:00:51 +00:00
|
|
|
f = subsurface_fopen(filename, "w");
|
2014-07-16 08:57:29 +00:00
|
|
|
if (f) {
|
|
|
|
xsltSaveResultToFile(f, transformed, xslt);
|
|
|
|
fclose(f);
|
|
|
|
/* Check write errors? */
|
|
|
|
} else {
|
|
|
|
res = report_error("Failed to open %s for writing (%s)", filename, strerror(errno));
|
|
|
|
}
|
|
|
|
xsltFreeStylesheet(xslt);
|
2013-10-19 17:35:36 +00:00
|
|
|
xmlFreeDoc(transformed);
|
|
|
|
|
2014-07-16 08:57:29 +00:00
|
|
|
return res;
|
2013-10-19 17:35:36 +00:00
|
|
|
}
|
2019-04-06 19:05:18 +00:00
|
|
|
|
2019-09-21 12:10:53 +00:00
|
|
|
static void save_dive_sites_buffer(struct membuffer *b, const struct dive_site *sites[], int nr_sites, bool anonymize)
|
2019-04-06 19:05:18 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
put_format(b, "<divesites program='subsurface' version='%d'>\n", DATAFORMAT_VERSION);
|
|
|
|
|
|
|
|
/* save the dive sites */
|
2019-09-21 12:10:53 +00:00
|
|
|
for (i = 0; i < nr_sites; i++) {
|
|
|
|
const struct dive_site *ds = sites[i];
|
2019-04-06 19:05:18 +00:00
|
|
|
|
|
|
|
put_format(b, "<site uuid='%8x'", ds->uuid);
|
2024-05-04 15:18:08 +00:00
|
|
|
show_utf8_blanked(b, ds->name.c_str(), " name='", "'", 1, anonymize);
|
2019-04-06 19:05:18 +00:00
|
|
|
put_location(b, &ds->location, " gps='", "'");
|
2024-05-04 15:18:08 +00:00
|
|
|
show_utf8_blanked(b, ds->description.c_str(), " description='", "'", 1, anonymize);
|
2019-04-06 19:05:18 +00:00
|
|
|
put_format(b, ">\n");
|
2024-05-04 15:18:08 +00:00
|
|
|
show_utf8_blanked(b, ds->notes.c_str(), " <notes>", " </notes>\n", 0, anonymize);
|
2024-05-04 11:39:04 +00:00
|
|
|
for (const auto &t: ds->taxonomy) {
|
|
|
|
if (t.category != TC_NONE && !t.value.empty()) {
|
|
|
|
put_format(b, " <geo cat='%d'", t.category);
|
|
|
|
put_format(b, " origin='%d'", t.origin);
|
|
|
|
show_utf8_blanked(b, t.value.c_str(), " value='", "'", 1, anonymize);
|
|
|
|
put_format(b, "/>\n");
|
2019-04-06 19:05:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
put_format(b, "</site>\n");
|
|
|
|
}
|
|
|
|
put_format(b, "</divesites>\n");
|
|
|
|
}
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
int save_dive_sites_logic(const char *filename, const struct dive_site *sites[], int nr_sites, bool anonymize)
|
2019-04-06 19:05:18 +00:00
|
|
|
{
|
2024-05-05 04:47:28 +00:00
|
|
|
membuffer buf;
|
2019-04-06 19:05:18 +00:00
|
|
|
FILE *f;
|
|
|
|
int error = 0;
|
|
|
|
|
2019-09-21 12:10:53 +00:00
|
|
|
save_dive_sites_buffer(&buf, sites, nr_sites, anonymize);
|
2019-04-06 19:05:18 +00:00
|
|
|
|
|
|
|
if (same_string(filename, "-")) {
|
|
|
|
f = stdout;
|
|
|
|
} else {
|
|
|
|
try_to_backup(filename);
|
|
|
|
error = -1;
|
|
|
|
f = subsurface_fopen(filename, "w");
|
|
|
|
}
|
|
|
|
if (f) {
|
|
|
|
flush_buffer(&buf, f);
|
|
|
|
error = fclose(f);
|
|
|
|
}
|
|
|
|
if (error)
|
|
|
|
report_error(translate("gettextFromC", "Failed to save divesites to %s (%s)"), filename, strerror(errno));
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|