core: turn event-list of divecomputer into std::vector<>

This is a rather long commit, because it refactors lots of the event
code from pointer to value semantics: pointers to entries in an
std::vector<> are not stable, so better use indexes.

To step through the event-list at diven time stamps, add *_loop classes,
which encapsulate state that had to be manually handled before by
the caller. I'm not happy about the interface, but it tries to
mirror the one we had before.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2024-05-25 08:16:57 +02:00 committed by bstoeger
parent 8ddc960fa0
commit 27dbdd35c6
36 changed files with 644 additions and 821 deletions

View file

@ -352,14 +352,14 @@ void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO
execute(new AddEventSetpointChange(d, dcNr, seconds, pO2)); execute(new AddEventSetpointChange(d, dcNr, seconds, pO2));
} }
void renameEvent(struct dive *d, int dcNr, struct event *ev, const char *name) void renameEvent(struct dive *d, int dcNr, int idx, const std::string name)
{ {
execute(new RenameEvent(d, dcNr, ev, name)); execute(new RenameEvent(d, dcNr, idx, std::move(name)));
} }
void removeEvent(struct dive *d, int dcNr, struct event *ev) void removeEvent(struct dive *d, int dcNr, int idx)
{ {
execute(new RemoveEvent(d, dcNr, ev)); execute(new RemoveEvent(d, dcNr, idx));
} }
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank) void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank)

View file

@ -132,8 +132,8 @@ void editTripNotes(dive_trip *trip, const QString &s);
void addEventBookmark(struct dive *d, int dcNr, int seconds); void addEventBookmark(struct dive *d, int dcNr, int seconds);
void addEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode); void addEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode);
void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2); void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2);
void renameEvent(struct dive *d, int dcNr, struct event *ev, const char *name); void renameEvent(struct dive *d, int dcNr, int idx, std::string name);
void removeEvent(struct dive *d, int dcNr, struct event *ev); void removeEvent(struct dive *d, int dcNr, int idx);
void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank); void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank);
// 7) Picture (media) commands // 7) Picture (media) commands

View file

@ -3,6 +3,7 @@
#include "command_edit.h" #include "command_edit.h"
#include "core/divelist.h" #include "core/divelist.h"
#include "core/divelog.h" #include "core/divelog.h"
#include "core/event.h"
#include "core/fulltext.h" #include "core/fulltext.h"
#include "core/qthelper.h" // for copy_qstring #include "core/qthelper.h" // for copy_qstring
#include "core/sample.h" #include "core/sample.h"
@ -898,7 +899,7 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int
duration = source->duration; duration = source->duration;
dc.samples = sdc->samples; dc.samples = sdc->samples;
copy_events(sdc, &dc); dc.events = sdc->events;
setText(editProfileTypeToString(type, count) + " " + diveNumberOrDate(d)); setText(editProfileTypeToString(type, count) + " " + diveNumberOrDate(d));
} }

View file

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

View file

@ -6,15 +6,12 @@
#include "command_base.h" #include "command_base.h"
#include "core/divemode.h" #include "core/divemode.h"
#include "core/event.h"
// We put everything in a namespace, so that we can shorten names without polluting the global namespace // We put everything in a namespace, so that we can shorten names without polluting the global namespace
namespace Command { namespace Command {
// Events are a strange thing: they contain there own description which means // Pointers to events are not stable, so we always store indexes.
// that on changing the description a new object must be allocated. Moreover,
// it means that these objects can't be collected in a table.
// Therefore, the undo commands work on events as they do with dives: using
// owning pointers. See comments in command_base.h
class EventBase : public Base { class EventBase : public Base {
protected: protected:
@ -25,8 +22,7 @@ protected:
virtual void undoit() = 0; virtual void undoit() = 0;
// Note: we store dive and the divecomputer-number instead of a pointer to the divecomputer. // Note: we store dive and the divecomputer-number instead of a pointer to the divecomputer.
// Since one divecomputer is integrated into the dive structure, pointers to divecomputers // Pointers to divecomputers are not stable.
// are probably not stable.
struct dive *d; struct dive *d;
int dcNr; int dcNr;
private: private:
@ -35,15 +31,15 @@ private:
class AddEventBase : public EventBase { class AddEventBase : public EventBase {
public: public:
AddEventBase(struct dive *d, int dcNr, struct event *ev); // Takes ownership of event! AddEventBase(struct dive *d, int dcNr, struct event ev); // Takes ownership of event!
protected: protected:
void undoit() override; void undoit() override;
void redoit() override; void redoit() override;
private: private:
bool workToBeDone() override; bool workToBeDone() override;
std::unique_ptr<event> eventToAdd; // for redo struct event ev; // for redo
event *eventToRemove; // for undo int idx; // for undo
}; };
class AddEventBookmark : public AddEventBase { class AddEventBookmark : public AddEventBase {
@ -67,28 +63,28 @@ private:
class RenameEvent : public EventBase { class RenameEvent : public EventBase {
public: public:
RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name); RenameEvent(struct dive *d, int dcNr, int idx, const std::string name);
private: private:
bool workToBeDone() override; bool workToBeDone() override;
void undoit() override; void undoit() override;
void redoit() override; void redoit() override;
std::unique_ptr<event> eventToAdd; // for undo and redo int idx; // for undo and redo
event *eventToRemove; // for undo and redo std::string name; // for undo and redo
}; };
class RemoveEvent : public EventBase { class RemoveEvent : public EventBase {
public: public:
RemoveEvent(struct dive *d, int dcNr, struct event *ev); RemoveEvent(struct dive *d, int dcNr, int idx);
private: private:
bool workToBeDone() override; bool workToBeDone() override;
void undoit() override; void undoit() override;
void redoit() override; void redoit() override;
void post() const; // Called to fix up dives should a gas-change have happened. void post() const; // Called to fix up dives should a gas-change have happened.
std::unique_ptr<event> eventToAdd; // for undo event ev; // for undo
event *eventToRemove; // for redo int idx; // for redo
int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch. int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch.
}; };
class AddGasSwitch : public EventBase { class AddGasSwitch : public EventBase {
@ -100,8 +96,8 @@ private:
void redoit() override; void redoit() override;
std::vector<int> cylinders; // cylinders that are modified std::vector<int> cylinders; // cylinders that are modified
std::vector<std::unique_ptr<event>> eventsToAdd; std::vector<event> eventsToAdd;
std::vector<event *> eventsToRemove; std::vector<int> eventsToRemove;
}; };
} // namespace Command } // namespace Command

View file

@ -97,22 +97,20 @@ int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer
} }
/* warning: does not test idx for validity */ /* warning: does not test idx for validity */
struct event *create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx) struct event create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx)
{ {
/* The gas switch event format is insane for historical reasons */ /* The gas switch event format is insane for historical reasons */
struct gasmix mix = get_cylinder(dive, idx)->gasmix; struct gasmix mix = get_cylinder(dive, idx)->gasmix;
int o2 = get_o2(mix); int o2 = get_o2(mix);
int he = get_he(mix); int he = get_he(mix);
struct event *ev;
int value;
o2 = (o2 + 5) / 10; o2 = (o2 + 5) / 10;
he = (he + 5) / 10; he = (he + 5) / 10;
value = o2 + (he << 16); int value = o2 + (he << 16);
ev = create_event(seconds, he ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE, 0, value, "gaschange"); struct event ev(seconds, he ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE, 0, value, "gaschange");
ev->gas.index = idx; ev.gas.index = idx;
ev->gas.mix = mix; ev.gas.mix = mix;
return ev; return ev;
} }
@ -126,20 +124,20 @@ void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int second
report_error("Unknown cylinder index: %d", idx); report_error("Unknown cylinder index: %d", idx);
return; return;
} }
struct event *ev = create_gas_switch_event(dive, dc, seconds, idx); struct event ev = create_gas_switch_event(dive, dc, seconds, idx);
add_event_to_dc(dc, ev); add_event_to_dc(dc, std::move(ev));
} }
struct gasmix get_gasmix_from_event(const struct dive *dive, const struct event *ev) struct gasmix get_gasmix_from_event(const struct dive *dive, const struct event &ev)
{ {
if (ev && event_is_gaschange(ev)) { if (event_is_gaschange(ev)) {
int index = ev->gas.index; int index = ev.gas.index;
// FIXME: The planner uses one past cylinder-count to signify "surface air". Remove in due course. // FIXME: The planner uses one past cylinder-count to signify "surface air". Remove in due course.
if (index == dive->cylinders.nr) if (index == dive->cylinders.nr)
return gasmix_air; return gasmix_air;
if (index >= 0 && index < dive->cylinders.nr) if (index >= 0 && index < dive->cylinders.nr)
return get_cylinder(dive, index)->gasmix; return get_cylinder(dive, index)->gasmix;
return ev->gas.mix; return ev.gas.mix;
} }
return gasmix_air; return gasmix_air;
} }
@ -158,7 +156,6 @@ int dive_getUniqID()
static void copy_dc(const struct divecomputer *sdc, struct divecomputer *ddc) static void copy_dc(const struct divecomputer *sdc, struct divecomputer *ddc)
{ {
*ddc = *sdc; *ddc = *sdc;
copy_events(sdc, ddc);
} }
static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, const int mapping[]); static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, const int mapping[]);
@ -169,7 +166,7 @@ static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, con
static void copy_dc_renumber(struct dive *d, const struct divecomputer *sdc, struct divecomputer *ddc, const int cylinders_map[]) static void copy_dc_renumber(struct dive *d, const struct divecomputer *sdc, struct divecomputer *ddc, const int cylinders_map[])
{ {
for (;;) { for (;;) {
copy_dc(sdc, ddc); *ddc = *sdc;
dc_cylinder_renumber(d, ddc, cylinders_map); dc_cylinder_renumber(d, ddc, cylinders_map);
if (!sdc->next) if (!sdc->next)
break; break;
@ -243,14 +240,14 @@ void copy_dive(const struct dive *s, struct dive *d)
copy_dive_nodc(s, d); copy_dive_nodc(s, d);
// Copy the first dc explicitly, then the list of subsequent dc's // Copy the first dc explicitly, then the list of subsequent dc's
copy_dc(&s->dc, &d->dc); d->dc = s->dc;
STRUCTURED_LIST_COPY(struct divecomputer, s->dc.next, d->dc.next, copy_dc); STRUCTURED_LIST_COPY(struct divecomputer, s->dc.next, d->dc.next, copy_dc);
} }
static void copy_dive_onedc(const struct dive *s, const struct divecomputer *sdc, struct dive *d) static void copy_dive_onedc(const struct dive *s, const struct divecomputer *sdc, struct dive *d)
{ {
copy_dive_nodc(s, d); copy_dive_nodc(s, d);
copy_dc(sdc, &d->dc); d->dc = *sdc;
d->dc.next = NULL; d->dc.next = NULL;
} }
@ -312,13 +309,10 @@ void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int tim
if (!s || !d) if (!s || !d)
return; return;
const struct event *ev; for (const auto &ev: s->events) {
ev = s->events;
while (ev != NULL) {
// Don't add events the planner knows about // Don't add events the planner knows about
if (ev->time.seconds < time && !event_is_gaschange(ev) && !event_is_divemodechange(ev)) if (ev.time.seconds < time && !event_is_gaschange(ev) && !event_is_divemodechange(ev))
add_event(d, ev->time.seconds, ev->type, ev->flags, ev->value, ev->name); add_event(d, ev.time.seconds, ev.type, ev.flags, ev.value, ev.name);
ev = ev->next;
} }
} }
@ -413,7 +407,6 @@ static bool has_unknown_used_cylinders(const struct dive *dive, const struct div
const bool used_cylinders[], int num) const bool used_cylinders[], int num)
{ {
int idx; int idx;
const struct event *ev;
auto used_and_unknown = std::make_unique<bool[]>(dive->cylinders.nr); auto used_and_unknown = std::make_unique<bool[]>(dive->cylinders.nr);
std::copy(used_cylinders, used_cylinders + dive->cylinders.nr, used_and_unknown.get()); std::copy(used_cylinders, used_cylinders + dive->cylinders.nr, used_and_unknown.get());
@ -434,14 +427,14 @@ static bool has_unknown_used_cylinders(const struct dive *dive, const struct div
} }
/* And we have possible switches to other gases */ /* And we have possible switches to other gases */
ev = get_next_event(dc->events, "gaschange"); event_loop loop("gaschange");
while (ev && num > 0) { const struct event *ev;
idx = get_cylinder_index(dive, ev); while ((ev = loop.next(*dc)) != nullptr && num > 0) {
idx = get_cylinder_index(dive, *ev);
if (idx >= 0 && used_and_unknown[idx]) { if (idx >= 0 && used_and_unknown[idx]) {
used_and_unknown[idx] = false; used_and_unknown[idx] = false;
num--; num--;
} }
ev = get_next_event(ev->next, "gaschange");
} }
return num > 0; return num > 0;
@ -494,7 +487,8 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i
} }
if (dc->samples.empty()) if (dc->samples.empty())
fake_dc(dc); fake_dc(dc);
const struct event *ev = get_next_event(dc->events, "gaschange"); event_loop loop("gaschange");
const struct event *ev = loop.next(*dc);
std::vector<int> depthtime(dive->cylinders.nr, 0); std::vector<int> depthtime(dive->cylinders.nr, 0);
for (auto it = dc->samples.begin(); it != dc->samples.end(); ++it) { for (auto it = dc->samples.begin(); it != dc->samples.end(); ++it) {
int32_t time = it->time.seconds; int32_t time = it->time.seconds;
@ -502,8 +496,8 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i
/* Make sure to move the event past 'lasttime' */ /* Make sure to move the event past 'lasttime' */
while (ev && lasttime >= ev->time.seconds) { while (ev && lasttime >= ev->time.seconds) {
idx = get_cylinder_index(dive, ev); idx = get_cylinder_index(dive, *ev);
ev = get_next_event(ev->next, "gaschange"); ev = loop.next(*dc);
} }
/* Do we need to fake a midway sample at an event? */ /* Do we need to fake a midway sample at an event? */
@ -561,9 +555,9 @@ int explicit_first_cylinder(const struct dive *dive, const struct divecomputer *
if (!dive->cylinders.nr) if (!dive->cylinders.nr)
return -1; return -1;
if (dc) { if (dc) {
const struct event *ev = get_next_event(dc->events, "gaschange"); const struct event *ev = get_first_event(*dc, "gaschange");
if (ev && ((!dc->samples.empty() && ev->time.seconds == dc->samples[0].time.seconds) || ev->time.seconds <= 1)) if (ev && ((!dc->samples.empty() && ev->time.seconds == dc->samples[0].time.seconds) || ev->time.seconds <= 1))
res = get_cylinder_index(dive, ev); res = get_cylinder_index(dive, *ev);
else if (dc->divemode == CCR) else if (dc->divemode == CCR)
res = std::max(get_cylinder_idx_by_use(dive, DILUENT), res); res = std::max(get_cylinder_idx_by_use(dive, DILUENT), res);
} }
@ -575,7 +569,6 @@ int explicit_first_cylinder(const struct dive *dive, const struct divecomputer *
*/ */
void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) void update_setpoint_events(const struct dive *dive, struct divecomputer *dc)
{ {
struct event *ev;
int new_setpoint = 0; int new_setpoint = 0;
if (dc->divemode == CCR) if (dc->divemode == CCR)
@ -590,15 +583,16 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc)
// by mistake when it's actually CCR is _bad_ // by mistake when it's actually CCR is _bad_
// So we make sure, this comes from a Predator or Petrel and we only remove // So we make sure, this comes from a Predator or Petrel and we only remove
// pO2 values we would have computed anyway. // pO2 values we would have computed anyway.
const struct event *ev = get_next_event(dc->events, "gaschange"); event_loop loop("gaschange");
struct gasmix gasmix = get_gasmix_from_event(dive, ev); const struct event *ev = loop.next(*dc);
const struct event *next = get_next_event(ev, "gaschange"); struct gasmix gasmix = get_gasmix_from_event(dive, *ev);
const struct event *next = loop.next(*dc);
for (auto &sample: dc->samples) { for (auto &sample: dc->samples) {
if (next && sample.time.seconds >= next->time.seconds) { if (next && sample.time.seconds >= next->time.seconds) {
ev = next; ev = next;
gasmix = get_gasmix_from_event(dive, ev); gasmix = get_gasmix_from_event(dive, *ev);
next = get_next_event(ev, "gaschange"); next = loop.next(*dc);
} }
gas_pressures pressures = fill_pressures(lrint(calculate_depth_to_mbarf(sample.depth.mm, dc->surface_pressure, 0)), gasmix ,0, dc->divemode); gas_pressures pressures = fill_pressures(lrint(calculate_depth_to_mbarf(sample.depth.mm, dc->surface_pressure, 0)), gasmix ,0, dc->divemode);
if (abs(sample.setpoint.mbar - (int)(1000 * pressures.o2)) <= 50) if (abs(sample.setpoint.mbar - (int)(1000 * pressures.o2)) <= 50)
@ -609,7 +603,7 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc)
// an "SP change" event at t=0 is currently our marker for OC vs CCR // an "SP change" event at t=0 is currently our marker for OC vs CCR
// this will need to change to a saner setup, but for now we can just // this will need to change to a saner setup, but for now we can just
// check if such an event is there and adjust it, or add that event // check if such an event is there and adjust it, or add that event
ev = get_next_event(dc->events, "SP change"); struct event *ev = get_first_event(*dc, "SP change");
if (ev && ev->time.seconds == 0) { if (ev && ev->time.seconds == 0) {
ev->value = new_setpoint; ev->value = new_setpoint;
} else { } else {
@ -705,33 +699,17 @@ static void sanitize_cylinder_info(struct dive *dive)
} }
/* some events should never be thrown away */ /* some events should never be thrown away */
static bool is_potentially_redundant(const struct event *event) static bool is_potentially_redundant(const struct event &event)
{ {
if (event->name == "gaschange") if (event.name == "gaschange")
return false; return false;
if (event->name == "bookmark") if (event.name == "bookmark")
return false; return false;
if (event->name == "heading") if (event.name == "heading")
return false; return false;
return true; return true;
} }
/* match just by name - we compare the details in the code that uses this helper */
static struct event *find_previous_event(struct divecomputer *dc, struct event *event)
{
struct event *ev = dc->events;
struct event *previous = NULL;
if (event->name.empty())
return NULL;
while (ev && ev != event) {
if (ev->name == event->name)
previous = ev;
ev = ev->next;
}
return previous;
}
pressure_t calculate_surface_pressure(const struct dive *dive) pressure_t calculate_surface_pressure(const struct dive *dive)
{ {
const struct divecomputer *dc; const struct divecomputer *dc;
@ -848,36 +826,24 @@ static temperature_t un_fixup_airtemp(const struct dive *a)
* is no dive computer with a sample rate of more than 60 * is no dive computer with a sample rate of more than 60
* seconds... that would be pretty pointless to plot the * seconds... that would be pretty pointless to plot the
* profile with) * profile with)
*
* We first only mark the events for deletion so that we
* still know when the previous event happened.
*/ */
static void fixup_dc_events(struct divecomputer *dc) static void fixup_dc_events(struct divecomputer *dc)
{ {
struct event *event; std::vector<int> to_delete;
event = dc->events; for (auto [idx, event]: enumerated_range(dc->events)) {
while (event) { if (!is_potentially_redundant(event))
struct event *prev; continue;
if (is_potentially_redundant(event)) { for (int idx2 = idx - 1; idx2 > 0; --idx2) {
prev = find_previous_event(dc, event); const auto &prev = dc->events[idx2];
if (prev && prev->value == event->value && if (prev.name == event.name && prev.flags == event.flags &&
prev->flags == event->flags && event.time.seconds - prev.time.seconds < 61)
event->time.seconds - prev->time.seconds < 61) to_delete.push_back(idx);
event->deleted = true;
}
event = event->next;
}
event = dc->events;
while (event) {
if (event->next && event->next->deleted) {
struct event *nextnext = event->next->next;
delete event->next;
event->next = nextnext;
} else {
event = event->next;
} }
} }
// Delete from back to not invalidate indexes
for (auto it = to_delete.rbegin(); it != to_delete.rend(); ++it)
dc->events.erase(dc->events.begin() + *it);
} }
static int interpolate_depth(struct divecomputer *dc, int idx, int lastdepth, int lasttime, int now) static int interpolate_depth(struct divecomputer *dc, int idx, int lastdepth, int lasttime, int now)
@ -1046,44 +1012,44 @@ static void fixup_dive_pressures(struct dive *dive, struct divecomputer *dc)
/* /*
* Match a gas change event against the cylinders we have * Match a gas change event against the cylinders we have
*/ */
static bool validate_gaschange(struct dive *dive, struct event *event) static bool validate_gaschange(struct dive *dive, struct event &event)
{ {
int index; int index;
int o2, he, value; int o2, he, value;
/* We'll get rid of the per-event gasmix, but for now sanitize it */ /* We'll get rid of the per-event gasmix, but for now sanitize it */
if (gasmix_is_air(event->gas.mix)) if (gasmix_is_air(event.gas.mix))
event->gas.mix.o2.permille = 0; event.gas.mix.o2.permille = 0;
/* Do we already have a cylinder index for this gasmix? */ /* Do we already have a cylinder index for this gasmix? */
if (event->gas.index >= 0) if (event.gas.index >= 0)
return true; return true;
index = find_best_gasmix_match(event->gas.mix, &dive->cylinders); index = find_best_gasmix_match(event.gas.mix, &dive->cylinders);
if (index < 0 || index >= dive->cylinders.nr) if (index < 0 || index >= dive->cylinders.nr)
return false; return false;
/* Fix up the event to have the right information */ /* Fix up the event to have the right information */
event->gas.index = index; event.gas.index = index;
event->gas.mix = get_cylinder(dive, index)->gasmix; event.gas.mix = get_cylinder(dive, index)->gasmix;
/* Convert to odd libdivecomputer format */ /* Convert to odd libdivecomputer format */
o2 = get_o2(event->gas.mix); o2 = get_o2(event.gas.mix);
he = get_he(event->gas.mix); he = get_he(event.gas.mix);
o2 = (o2 + 5) / 10; o2 = (o2 + 5) / 10;
he = (he + 5) / 10; he = (he + 5) / 10;
value = o2 + (he << 16); value = o2 + (he << 16);
event->value = value; event.value = value;
if (he) if (he)
event->type = SAMPLE_EVENT_GASCHANGE2; event.type = SAMPLE_EVENT_GASCHANGE2;
return true; return true;
} }
/* Clean up event, return true if event is ok, false if it should be dropped as bogus */ /* Clean up event, return true if event is ok, false if it should be dropped as bogus */
static bool validate_event(struct dive *dive, struct event *event) static bool validate_event(struct dive *dive, struct event &event)
{ {
if (event_is_gaschange(event)) if (event_is_gaschange(event))
return validate_gaschange(dive, event); return validate_gaschange(dive, event);
@ -1092,18 +1058,11 @@ static bool validate_event(struct dive *dive, struct event *event)
static void fixup_dc_gasswitch(struct dive *dive, struct divecomputer *dc) static void fixup_dc_gasswitch(struct dive *dive, struct divecomputer *dc)
{ {
struct event **evp, *event; // erase-remove idiom
auto &events = dc->events;
evp = &dc->events; events.erase(std::remove_if(events.begin(), events.end(),
while ((event = *evp) != NULL) { [dive](auto &ev) { return !validate_event(dive, ev); }),
if (validate_event(dive, event)) { events.end());
evp = &event->next;
continue;
}
/* Delete this event and try the next one */
*evp = event->next;
}
} }
static void fixup_no_o2sensors(struct divecomputer *dc) static void fixup_no_o2sensors(struct divecomputer *dc)
@ -1434,15 +1393,15 @@ static char *merge_text(const char *a, const char *b, const char *sep)
#define SORT(a, b) \ #define SORT(a, b) \
if (a != b) \ if (a != b) \
return a < b ? -1 : 1 return a < b ? -1 : 1
#define SORT_FIELD(a, b, field) SORT(a->field, b->field) #define SORT_FIELD(a, b, field) SORT(a.field, b.field)
static int sort_event(const struct event *a, const struct event *b, int time_a, int time_b) static int sort_event(const struct event &a, const struct event &b, int time_a, int time_b)
{ {
SORT(time_a, time_b); SORT(time_a, time_b);
SORT_FIELD(a, b, type); SORT_FIELD(a, b, type);
SORT_FIELD(a, b, flags); SORT_FIELD(a, b, flags);
SORT_FIELD(a, b, value); SORT_FIELD(a, b, value);
return a->name.compare(b->name); return a.name.compare(b.name);
} }
static int same_gas(const struct event *a, const struct event *b) static int same_gas(const struct event *a, const struct event *b)
@ -1454,7 +1413,7 @@ static int same_gas(const struct event *a, const struct event *b)
return false; return false;
} }
static void event_renumber(struct event *ev, const int mapping[]); static void event_renumber(struct event &ev, const int mapping[]);
static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc, int offset, int idx); static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc, int offset, int idx);
static void merge_events(struct dive *d, struct divecomputer *res, static void merge_events(struct dive *d, struct divecomputer *res,
@ -1462,8 +1421,6 @@ static void merge_events(struct dive *d, struct divecomputer *res,
const int *cylinders_map1, const int *cylinders_map2, const int *cylinders_map1, const int *cylinders_map2,
int offset) int offset)
{ {
const struct event *a, *b;
struct event **p = &res->events;
const struct event *last_gas = NULL; const struct event *last_gas = NULL;
/* Always use positive offsets */ /* Always use positive offsets */
@ -1481,40 +1438,40 @@ static void merge_events(struct dive *d, struct divecomputer *res,
cylinders_map2 = cylinders_map_tmp; cylinders_map2 = cylinders_map_tmp;
} }
a = src1->events; auto a = src1->events.begin();
b = src2->events; auto b = src2->events.begin();
while (a || b) { while (a != src1->events.end() || b != src2->events.end()) {
int s; int s = 0;
const struct event *pick; const struct event *pick;
const int *cylinders_map; const int *cylinders_map;
int event_offset; int event_offset;
if (!b) if (b == src2->events.end())
goto pick_a; goto pick_a;
if (!a) if (a == src1->events.end())
goto pick_b; goto pick_b;
s = sort_event(a, b, a->time.seconds, b->time.seconds + offset); s = sort_event(*a, *b, a->time.seconds, b->time.seconds + offset);
/* Identical events? Just skip one of them (we skip a) */ /* Identical events? Just skip one of them (we skip a) */
if (!s) { if (!s) {
a = a->next; ++a;
continue; continue;
} }
/* Otherwise, pick the one that sorts first */ /* Otherwise, pick the one that sorts first */
if (s < 0) { if (s < 0) {
pick_a: pick_a:
pick = a; pick = &*a;
a = a->next; ++a;
event_offset = 0; event_offset = 0;
cylinders_map = cylinders_map1; cylinders_map = cylinders_map1;
} else { } else {
pick_b: pick_b:
pick = b; pick = &*b;
b = b->next; ++b;
event_offset = offset; event_offset = offset;
cylinders_map = cylinders_map2; cylinders_map = cylinders_map2;
} }
@ -1523,17 +1480,16 @@ pick_b:
* If that's a gas-change that matches the previous * If that's a gas-change that matches the previous
* gas change, we'll just skip it * gas change, we'll just skip it
*/ */
if (event_is_gaschange(pick)) { if (event_is_gaschange(*pick)) {
if (last_gas && same_gas(pick, last_gas)) if (last_gas && same_gas(pick, last_gas))
continue; continue;
last_gas = pick; last_gas = pick;
} }
/* Add it to the target list */ /* Add it to the target list */
*p = clone_event(pick); res->events.push_back(*pick);
(*p)->time.seconds += event_offset; res->events.back().time.seconds += event_offset;
event_renumber(*p, cylinders_map); event_renumber(res->events.back(), cylinders_map);
p = &(*p)->next;
} }
/* If the initial cylinder of a divecomputer was remapped, add a gas change event to that cylinder */ /* If the initial cylinder of a divecomputer was remapped, add a gas change event to that cylinder */
@ -1564,13 +1520,12 @@ static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc, in
{ {
/* if there is a gaschange event up to 30 sec after the initial event, /* if there is a gaschange event up to 30 sec after the initial event,
* refrain from adding the initial event */ * refrain from adding the initial event */
const struct event *ev = dc->events; event_loop loop("gaschange");
while(ev && (ev = get_next_event(ev, "gaschange")) != NULL) { while(auto ev = loop.next(*dc)) {
if (ev->time.seconds > offset + 30) if (ev->time.seconds > offset + 30)
break; break;
else if (ev->time.seconds > offset) else if (ev->time.seconds > offset)
return; return;
ev = ev->next;
} }
/* Old starting gas mix */ /* Old starting gas mix */
@ -1607,25 +1562,23 @@ static void renumber_last_sample(struct divecomputer *dc, const int mapping[])
sample_renumber(dc->samples.back(), prev, mapping); sample_renumber(dc->samples.back(), prev, mapping);
} }
static void event_renumber(struct event *ev, const int mapping[]) static void event_renumber(struct event &ev, const int mapping[])
{ {
if (!event_is_gaschange(ev)) if (!event_is_gaschange(ev))
return; return;
if (ev->gas.index < 0) if (ev.gas.index < 0)
return; return;
ev->gas.index = mapping[ev->gas.index]; ev.gas.index = mapping[ev.gas.index];
} }
static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, const int mapping[]) static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, const int mapping[])
{ {
struct event *ev;
/* Remap or delete the sensor indices */ /* Remap or delete the sensor indices */
for (auto [i, sample]: enumerated_range(dc->samples)) for (auto [i, sample]: enumerated_range(dc->samples))
sample_renumber(sample, i > 0 ? &dc->samples[i-1] : nullptr, mapping); sample_renumber(sample, i > 0 ? &dc->samples[i-1] : nullptr, mapping);
/* Remap the gas change indices */ /* Remap the gas change indices */
for (ev = dc->events; ev; ev = ev->next) for (auto &ev: dc->events)
event_renumber(ev, mapping); event_renumber(ev, mapping);
/* If the initial cylinder of a dive was remapped, add a gas change event to that cylinder */ /* If the initial cylinder of a dive was remapped, add a gas change event to that cylinder */
@ -2259,8 +2212,6 @@ static bool operator==(const sample &a, const sample &b)
static int same_dc(struct divecomputer *a, struct divecomputer *b) static int same_dc(struct divecomputer *a, struct divecomputer *b)
{ {
int i; int i;
const struct event *eva, *evb;
i = match_one_dc(a, b); i = match_one_dc(a, b);
if (i) if (i)
return i > 0; return i > 0;
@ -2269,15 +2220,7 @@ static int same_dc(struct divecomputer *a, struct divecomputer *b)
return 0; return 0;
if (a->samples != b->samples) if (a->samples != b->samples)
return 0; return 0;
eva = a->events; return a->events == b->events;
evb = b->events;
while (eva && evb) {
if (!same_event(eva, evb))
return 0;
eva = eva->next;
evb = evb->next;
}
return eva == evb;
} }
static int might_be_same_device(const struct divecomputer *a, const struct divecomputer *b) static int might_be_same_device(const struct divecomputer *a, const struct divecomputer *b)
@ -2337,7 +2280,7 @@ static void copy_dive_computer(struct divecomputer *res, const struct divecomput
{ {
*res = *a; *res = *a;
res->samples.clear(); res->samples.clear();
res->events = NULL; res->events.clear();
res->next = NULL; res->next = NULL;
} }
@ -2617,7 +2560,6 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou
int32_t t; int32_t t;
struct dive *d1, *d2; struct dive *d1, *d2;
struct divecomputer *dc1, *dc2; struct divecomputer *dc1, *dc2;
struct event *event, **evp;
/* if we can't find the dive in the dive list, don't bother */ /* if we can't find the dive in the dive list, don't bother */
if ((nr = get_divenr(dive)) < 0) if ((nr = get_divenr(dive)) < 0)
@ -2673,26 +2615,19 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou
sample.time.seconds -= t; sample.time.seconds -= t;
/* Remove the events past 't' from d1 */ /* Remove the events past 't' from d1 */
evp = &dc1->events; auto it = std::lower_bound(dc1->events.begin(), dc1->events.end(), t,
while ((event = *evp) != NULL && event->time.seconds < t) [] (struct event &ev, int t)
evp = &event->next; { return ev.time.seconds < t; });
*evp = NULL; dc1->events.erase(it, dc1->events.end());
while (event) {
struct event *next = event->next;
delete event;
event = next;
}
/* Remove the events before 't' from d2, and shift the rest */ /* Remove the events before 't' from d2, and shift the rest */
evp = &dc2->events; it = std::lower_bound(dc2->events.begin(), dc2->events.end(), t,
while ((event = *evp) != NULL) { [] (struct event &ev, int t)
if (event->time.seconds < t) { { return ev.time.seconds < t; });
*evp = event->next; dc2->events.erase(dc2->events.begin(), it);
delete event; for (auto &ev: dc2->events)
} else { ev.time.seconds -= t;
event->time.seconds -= t;
}
}
dc1 = dc1->next; dc1 = dc1->next;
dc2 = dc2->next; dc2 = dc2->next;
} }
@ -2952,8 +2887,7 @@ static void delete_divecomputer(struct dive *d, int num)
/* During our move to C++, copy the divecomputer instead of moving the internals. /* During our move to C++, copy the divecomputer instead of moving the internals.
* Yes, this is "inefficient", but I don't care. Will be removed anyways. */ * Yes, this is "inefficient", but I don't care. Will be removed anyways. */
struct divecomputer *fdc = d->dc.next; struct divecomputer *fdc = d->dc.next;
free_dc_contents(&d->dc); d->dc = *fdc;
copy_dc(fdc, &d->dc);
delete fdc; delete fdc;
} else { } else {
struct divecomputer *pdc = &d->dc; struct divecomputer *pdc = &d->dc;
@ -3331,40 +3265,37 @@ location_t dive_get_gps_location(const struct dive *d)
return res; return res;
} }
/* When evaluated at the time of a gasswitch, this returns the new gas */ gasmix_loop::gasmix_loop(const struct dive &d, const struct divecomputer &dc) :
struct gasmix get_gasmix(const struct dive *dive, const struct divecomputer *dc, int time, const struct event **evp, struct gasmix gasmix) dive(d), dc(dc), last(gasmix_air), loop("gaschange")
{ {
const struct event *ev = *evp;
struct gasmix res;
/* if there is no cylinder, return air */ /* if there is no cylinder, return air */
if (dive->cylinders.nr <= 0) if (dive.cylinders.nr <= 0)
return gasmix_air; return;
if (!ev) { /* on first invocation, get initial gas mix and first event (if any) */
/* on first invocation, get initial gas mix and first event (if any) */ int cyl = explicit_first_cylinder(&dive, &dc);
int cyl = explicit_first_cylinder(dive, dc); last = get_cylinder(&dive, cyl)->gasmix;
res = get_cylinder(dive, cyl)->gasmix; ev = loop.next(dc);
ev = dc ? get_next_event(dc->events, "gaschange") : NULL; }
} else {
res = gasmix; gasmix gasmix_loop::next(int time)
} {
/* if there is no cylinder, return air */
if (dive.cylinders.nr <= 0)
return last;
while (ev && ev->time.seconds <= time) { while (ev && ev->time.seconds <= time) {
res = get_gasmix_from_event(dive, ev); last = get_gasmix_from_event(&dive, *ev);
ev = get_next_event(ev->next, "gaschange"); ev = loop.next(dc);
} }
*evp = ev; return last;
return res;
} }
/* get the gas at a certain time during the dive */ /* get the gas at a certain time during the dive */
/* If there is a gasswitch at that time, it returns the new gasmix */ /* If there is a gasswitch at that time, it returns the new gasmix */
struct gasmix get_gasmix_at_time(const struct dive *d, const struct divecomputer *dc, duration_t time) struct gasmix get_gasmix_at_time(const struct dive &d, const struct divecomputer &dc, duration_t time)
{ {
const struct event *ev = NULL; return gasmix_loop(d, dc).next(time.seconds);
struct gasmix gasmix = gasmix_air;
return get_gasmix(d, dc, time.seconds, &ev, gasmix);
} }
/* Does that cylinder have any pressure readings? */ /* Does that cylinder have any pressure readings? */

View file

@ -6,11 +6,9 @@
#include "divemode.h" #include "divemode.h"
#include "divecomputer.h" #include "divecomputer.h"
#include "equipment.h" #include "equipment.h" // TODO: remove
#include "picture.h" #include "picture.h" // TODO: remove
#include <stdio.h>
#include <stdlib.h>
#include <string> #include <string>
extern int last_xml_version; extern int last_xml_version;
@ -190,10 +188,10 @@ extern void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_
extern bool is_cylinder_used(const struct dive *dive, int idx); extern bool is_cylinder_used(const struct dive *dive, int idx);
extern bool is_cylinder_prot(const struct dive *dive, int idx); extern bool is_cylinder_prot(const struct dive *dive, int idx);
extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx); extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx);
extern struct event *create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx); extern struct event create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx);
extern void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration); extern void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration);
extern int get_cylinder_index(const struct dive *dive, const struct event *ev); extern int get_cylinder_index(const struct dive *dive, const struct event &ev);
extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event *ev); extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event &ev);
extern int nr_cylinders(const struct dive *dive); extern int nr_cylinders(const struct dive *dive);
extern int nr_weightsystems(const struct dive *dive); extern int nr_weightsystems(const struct dive *dive);
extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id); extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id);
@ -207,14 +205,8 @@ extern int total_weight(const struct dive *);
extern bool is_planned(const struct dive *dive); extern bool is_planned(const struct dive *dive);
extern bool is_logged(const struct dive *dive); extern bool is_logged(const struct dive *dive);
/* Get gasmixes at increasing timestamps.
* In "evp", pass a pointer to a "struct event *" which is NULL-initialized on first invocation.
* On subsequent calls, pass the same "evp" and the "gasmix" from previous calls.
*/
extern struct gasmix get_gasmix(const struct dive *dive, const struct divecomputer *dc, int time, const struct event **evp, struct gasmix gasmix);
/* Get gasmix at a given time */ /* Get gasmix at a given time */
extern struct gasmix get_gasmix_at_time(const struct dive *dive, const struct divecomputer *dc, duration_t time); extern struct gasmix get_gasmix_at_time(const struct dive &dive, const struct divecomputer &dc, duration_t time);
extern void update_setpoint_events(const struct dive *dive, struct divecomputer *dc); extern void update_setpoint_events(const struct dive *dive, struct divecomputer *dc);

View file

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include "divecomputer.h" #include "divecomputer.h"
#include "errorhelper.h"
#include "event.h" #include "event.h"
#include "extradata.h" #include "extradata.h"
#include "pref.h" #include "pref.h"
@ -11,15 +12,8 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
divecomputer::divecomputer() divecomputer::divecomputer() = default;
{ divecomputer::~divecomputer() = default;
}
divecomputer::~divecomputer()
{
free_dc_contents(this);
}
divecomputer::divecomputer(divecomputer &&) = default; divecomputer::divecomputer(divecomputer &&) = default;
divecomputer &divecomputer::operator=(const divecomputer &) = default; divecomputer &divecomputer::operator=(const divecomputer &) = default;
@ -204,29 +198,21 @@ void fake_dc(struct divecomputer *dc)
/* Even that didn't work? Give up, there's something wrong */ /* Even that didn't work? Give up, there's something wrong */
} }
/* Find the divemode at time 'time' (in seconds) into the dive. Sequentially step through the divemode-change events, divemode_loop::divemode_loop(const struct divecomputer &dc) :
* saving the dive mode for each event. When the events occur AFTER 'time' seconds, the last stored divemode dc(dc), last(dc.divemode), loop("modechange")
* is returned. This function is self-tracking, relying on setting the event pointer 'evp' so that, in each iteration
* that calls this function, the search does not have to begin at the first event of the dive */
enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode)
{ {
const struct event *ev = *evp; /* on first invocation, get first event (if any) */
if (dc) { ev = loop.next(dc);
if (*divemode == UNDEF_COMP_TYPE) {
*divemode = dc->divemode;
ev = get_next_event(dc->events, "modechange");
}
} else {
ev = NULL;
}
while (ev && ev->time.seconds < time) {
*divemode = (enum divemode_t) ev->value;
ev = get_next_event(ev->next, "modechange");
}
*evp = ev;
return *divemode;
} }
divemode_t divemode_loop::next(int time)
{
while (ev && ev->time.seconds <= time) {
last = static_cast<divemode_t>(ev->value);
ev = loop.next(dc);
}
return last;
}
/* helper function to make it easier to work with our structures /* helper function to make it easier to work with our structures
* we don't interpolate here, just use the value from the last sample up to that time */ * we don't interpolate here, just use the value from the last sample up to that time */
@ -354,72 +340,51 @@ unsigned int dc_airtemp(const struct divecomputer *dc)
return (sum + nr / 2) / nr; return (sum + nr / 2) / nr;
} }
/* copies all events in this dive computer */ static bool operator<(const event &ev1, const event &ev2)
void copy_events(const struct divecomputer *s, struct divecomputer *d)
{ {
const struct event *ev; if (ev1.time.seconds < ev2.time.seconds)
struct event **pev; return -1;
if (!s || !d) if (ev1.time.seconds > ev2.time.seconds)
return; return 1;
ev = s->events; return ev1.name < ev2.name;
pev = &d->events;
while (ev != NULL) {
struct event *new_ev = clone_event(ev);
*pev = new_ev;
pev = &new_ev->next;
ev = ev->next;
}
*pev = NULL;
} }
void add_event_to_dc(struct divecomputer *dc, struct event *ev) int add_event_to_dc(struct divecomputer *dc, struct event ev)
{ {
struct event **p; // Do a binary search for insertion point
auto it = std::lower_bound(dc->events.begin(), dc->events.end(), ev);
p = &dc->events; int idx = it - dc->events.begin();
dc->events.insert(it, ev);
/* insert in the sorted list of events */ return idx;
while (*p && (*p)->time.seconds <= ev->time.seconds)
p = &(*p)->next;
ev->next = *p;
*p = ev;
} }
struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name) struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name)
{ {
struct event *ev = create_event(time, type, flags, value, name); struct event ev(time, type, flags, value, name);
int idx = add_event_to_dc(dc, std::move(ev));
if (!ev) return &dc->events[idx];
return NULL;
add_event_to_dc(dc, ev);
return ev;
} }
/* Substitutes an event in a divecomputer for another. No reordering is performed! */ /* Remove given event from dive computer. Returns the removed event. */
void swap_event(struct divecomputer *dc, struct event *from, struct event *to) struct event remove_event_from_dc(struct divecomputer *dc, int idx)
{ {
for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { if (idx < 0 || static_cast<size_t>(idx) > dc->events.size()) {
if (*ep == from) { report_info("removing invalid event %d", idx);
to->next = from->next; return event();
*ep = to;
from->next = NULL; // For good measure.
break;
}
} }
event res = std::move(dc->events[idx]);
dc->events.erase(dc->events.begin() + idx);
return res;
} }
/* Remove given event from dive computer. Does *not* free the event. */ struct event *get_event(struct divecomputer *dc, int idx)
void remove_event_from_dc(struct divecomputer *dc, struct event *event)
{ {
for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { if (idx < 0 || static_cast<size_t>(idx) > dc->events.size()) {
if (*ep == event) { report_info("accessing invalid event %d", idx);
*ep = event->next; return nullptr;
event->next = NULL; // For good measure.
break;
}
} }
return &dc->events[idx];
} }
void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value) void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value)
@ -463,11 +428,6 @@ int match_one_dc(const struct divecomputer *a, const struct divecomputer *b)
return a->diveid == b->diveid && a->when == b->when ? 1 : -1; return a->diveid == b->diveid && a->when == b->when ? 1 : -1;
} }
void free_dc_contents(struct divecomputer *dc)
{
free_events(dc->events);
}
static const char *planner_dc_name = "planned dive"; static const char *planner_dc_name = "planned dive";
bool is_dc_planner(const struct divecomputer *dc) bool is_dc_planner(const struct divecomputer *dc)

View file

@ -8,6 +8,7 @@
#include <vector> #include <vector>
struct extra_data; struct extra_data;
struct event;
struct sample; struct sample;
/* Is this header the correct place? */ /* Is this header the correct place? */
@ -37,8 +38,10 @@ struct divecomputer {
int salinity = 0; // kg per 10000 l int salinity = 0; // kg per 10000 l
std::string model, serial, fw_version; std::string model, serial, fw_version;
uint32_t deviceid = 0, diveid = 0; uint32_t deviceid = 0, diveid = 0;
// Note: ve store samples, events and extra_data in std::vector<>s.
// This means that pointers to these items are *not* stable.
std::vector<struct sample> samples; std::vector<struct sample> samples;
struct event *events = nullptr; std::vector<struct event> events;
std::vector<struct extra_data> extra_data; std::vector<struct extra_data> extra_data;
struct divecomputer *next = nullptr; struct divecomputer *next = nullptr;
@ -50,7 +53,6 @@ struct divecomputer {
extern void fake_dc(struct divecomputer *dc); extern void fake_dc(struct divecomputer *dc);
extern void free_dc_contents(struct divecomputer *dc); extern void free_dc_contents(struct divecomputer *dc);
extern enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode);
extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time); extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time);
extern void free_dive_dcs(struct divecomputer *dc); extern void free_dive_dcs(struct divecomputer *dc);
extern struct sample *prepare_sample(struct divecomputer *dc); extern struct sample *prepare_sample(struct divecomputer *dc);
@ -58,11 +60,10 @@ extern struct sample *add_sample(const struct sample *sample, int time, struct d
extern void fixup_dc_duration(struct divecomputer *dc); extern void fixup_dc_duration(struct divecomputer *dc);
extern unsigned int dc_airtemp(const struct divecomputer *dc); extern unsigned int dc_airtemp(const struct divecomputer *dc);
extern unsigned int dc_watertemp(const struct divecomputer *dc); extern unsigned int dc_watertemp(const struct divecomputer *dc);
extern void copy_events(const struct divecomputer *s, struct divecomputer *d); extern int add_event_to_dc(struct divecomputer *dc, struct event ev); // event structure is consumed, returns index of inserted event
extern void swap_event(struct divecomputer *dc, struct event *from, struct event *to);
extern void add_event_to_dc(struct divecomputer *dc, struct event *ev);
extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name); extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name);
extern void remove_event_from_dc(struct divecomputer *dc, struct event *event); extern struct event remove_event_from_dc(struct divecomputer *dc, int idx);
struct event *get_event(struct divecomputer *dc, int idx);
extern void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value); extern void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value);
extern uint32_t calculate_string_hash(const char *str); extern uint32_t calculate_string_hash(const char *str);
extern bool is_dc_planner(const struct divecomputer *dc); extern bool is_dc_planner(const struct divecomputer *dc);

View file

@ -76,7 +76,7 @@ int total_weight(const struct dive *dive)
static int active_o2(const struct dive *dive, const struct divecomputer *dc, duration_t time) static int active_o2(const struct dive *dive, const struct divecomputer *dc, duration_t time)
{ {
struct gasmix gas = get_gasmix_at_time(dive, dc, time); struct gasmix gas = get_gasmix_at_time(*dive, *dc, time);
return get_o2(gas); return get_o2(gas);
} }
@ -96,8 +96,8 @@ static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc,
double amb_presure = depth_to_bar(sample.depth.mm, dive); double amb_presure = depth_to_bar(sample.depth.mm, dive);
double pamb_pressure = depth_to_bar(psample.depth.mm , dive); double pamb_pressure = depth_to_bar(psample.depth.mm , dive);
if (dc->divemode == PSCR) { if (dc->divemode == PSCR) {
po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, dc, psample.time)); po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(*dive, *dc, psample.time));
po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, dc, sample.time)); po2f = pscr_o2(amb_presure, get_gasmix_at_time(*dive, *dc, sample.time));
} else { } else {
int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2. int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2.
po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment
@ -141,8 +141,8 @@ static int calculate_otu(const struct dive *dive)
double amb_presure = depth_to_bar(sample.depth.mm, dive); double amb_presure = depth_to_bar(sample.depth.mm, dive);
double pamb_pressure = depth_to_bar(psample.depth.mm , dive); double pamb_pressure = depth_to_bar(psample.depth.mm , dive);
if (dc->divemode == PSCR) { if (dc->divemode == PSCR) {
po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, dc, psample.time)); po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(*dive, *dc, psample.time));
po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, dc, sample.time)); po2f = pscr_o2(amb_presure, get_gasmix_at_time(*dive, *dc, sample.time));
} else { } else {
int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2. int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2.
po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment
@ -390,13 +390,9 @@ static int calculate_sac(const struct dive *dive)
static void add_dive_to_deco(struct deco_state *ds, struct dive *dive, bool in_planner) static void add_dive_to_deco(struct deco_state *ds, struct dive *dive, bool in_planner)
{ {
struct divecomputer *dc = &dive->dc; struct divecomputer *dc = &dive->dc;
struct gasmix gasmix = gasmix_air;
const struct event *ev = NULL, *evd = NULL;
enum divemode_t current_divemode = UNDEF_COMP_TYPE;
if (!dc)
return;
gasmix_loop loop(*dive, dive->dc);
divemode_loop loop_d(dive->dc);
for (auto [psample, sample]: pairwise_range(dc->samples)) { for (auto [psample, sample]: pairwise_range(dc->samples)) {
int t0 = psample.time.seconds; int t0 = psample.time.seconds;
int t1 = sample.time.seconds; int t1 = sample.time.seconds;
@ -404,9 +400,9 @@ static void add_dive_to_deco(struct deco_state *ds, struct dive *dive, bool in_p
for (j = t0; j < t1; j++) { for (j = t0; j < t1; j++) {
int depth = interpolate(psample.depth.mm, sample.depth.mm, j - t0, t1 - t0); int depth = interpolate(psample.depth.mm, sample.depth.mm, j - t0, t1 - t0);
gasmix = get_gasmix(dive, dc, j, &ev, gasmix); auto gasmix = loop.next(j);
add_segment(ds, depth_to_bar(depth, dive), gasmix, 1, sample.setpoint.mbar, add_segment(ds, depth_to_bar(depth, dive), gasmix, 1, sample.setpoint.mbar,
get_current_divemode(&dive->dc, j, &evd, &current_divemode), dive->sac, loop_d.next(j), dive->sac,
in_planner); in_planner);
} }
} }
@ -417,11 +413,12 @@ int get_divenr(const struct dive *dive)
int i; int i;
const struct dive *d; const struct dive *d;
// tempting as it may be, don't die when called with dive=NULL // tempting as it may be, don't die when called with dive=NULL
if (dive) if (dive) {
for_each_dive(i, d) { for_each_dive(i, d) {
if (d->id == dive->id) // don't compare pointers, we could be passing in a copy of the dive if (d->id == dive->id) // don't compare pointers, we could be passing in a copy of the dive
return i; return i;
} }
}
return -1; return -1;
} }

View file

@ -1,67 +1,24 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include "event.h" #include "event.h"
#include "divecomputer.h"
#include "eventtype.h" #include "eventtype.h"
#include "subsurface-string.h" #include "subsurface-string.h"
#include <string.h> event::event() : type(SAMPLE_EVENT_NONE), flags(0), value(0),
#include <stdlib.h> divemode(OC), hidden(false)
event::event() : next(nullptr), type(SAMPLE_EVENT_NONE), flags(0), value(0),
divemode(OC), deleted(false), hidden(false)
{ {
/* That overwrites divemode. Is this a smart thing to do? */ /* That overwrites divemode. Is this a smart thing to do? */
gas.index = -1; gas.index = -1;
gas.mix = gasmix_air; gas.mix = gasmix_air;
} }
event::~event() event::event(unsigned int time, int type, int flags, int value, const std::string &name) :
{ type(type), flags(flags), value(value), divemode(OC),
} hidden(false), name(name)
int event_is_gaschange(const struct event *ev)
{
return ev->type == SAMPLE_EVENT_GASCHANGE ||
ev->type == SAMPLE_EVENT_GASCHANGE2;
}
bool event_is_divemodechange(const struct event *ev)
{
return ev->name == "modechange";
}
struct event *clone_event(const struct event *src_ev)
{
struct event *ev;
if (!src_ev)
return NULL;
ev = new event;
*ev = *src_ev;
ev->next = NULL;
return ev;
}
void free_events(struct event *ev)
{
while (ev) {
struct event *next = ev->next;
delete ev;
ev = next;
}
}
struct event *create_event(unsigned int time, int type, int flags, int value, const std::string &name)
{ {
int gas_index = -1; int gas_index = -1;
struct event *ev; fraction_t he;
this->time.seconds = time;
ev = new event;
ev->name = name;
ev->time.seconds = time;
ev->type = type;
ev->flags = flags;
ev->value = value;
/* /*
* Expand the events into a sane format. Currently * Expand the events into a sane format. Currently
@ -70,7 +27,7 @@ struct event *create_event(unsigned int time, int type, int flags, int value, co
switch (type) { switch (type) {
case SAMPLE_EVENT_GASCHANGE2: case SAMPLE_EVENT_GASCHANGE2:
/* High 16 bits are He percentage */ /* High 16 bits are He percentage */
ev->gas.mix.he.permille = (value >> 16) * 10; he.permille = (value >> 16) * 10;
/* Extension to the GASCHANGE2 format: cylinder index in 'flags' */ /* Extension to the GASCHANGE2 format: cylinder index in 'flags' */
/* TODO: verify that gas_index < num_cylinders. */ /* TODO: verify that gas_index < num_cylinders. */
@ -79,37 +36,39 @@ struct event *create_event(unsigned int time, int type, int flags, int value, co
/* Fallthrough */ /* Fallthrough */
case SAMPLE_EVENT_GASCHANGE: case SAMPLE_EVENT_GASCHANGE:
/* Low 16 bits are O2 percentage */ /* Low 16 bits are O2 percentage */
ev->gas.mix.o2.permille = (value & 0xffff) * 10; gas.mix.he = he;
ev->gas.index = gas_index; gas.mix.o2.permille = (value & 0xffff) * 10;
gas.index = gas_index;
break; break;
} }
remember_event_type(ev); remember_event_type(this);
return ev;
} }
struct event *clone_event_rename(const struct event *ev, const std::string &name) event::~event()
{ {
return create_event(ev->time.seconds, ev->type, ev->flags, ev->value, name);
} }
bool same_event(const struct event *a, const struct event *b) bool event_is_gaschange(const struct event &ev)
{ {
if (a->time.seconds != b->time.seconds) return ev.type == SAMPLE_EVENT_GASCHANGE ||
return 0; ev.type == SAMPLE_EVENT_GASCHANGE2;
if (a->type != b->type)
return 0;
if (a->flags != b->flags)
return 0;
if (a->value != b->value)
return 0;
return a->name == b->name;
} }
extern enum event_severity get_event_severity(const struct event *ev) bool event_is_divemodechange(const struct event &ev)
{ {
switch (ev->flags & SAMPLE_FLAGS_SEVERITY_MASK) { return ev.name == "modechange";
}
bool event::operator==(const event &b) const
{
return std::tie(time.seconds, type, flags, value, name) ==
std::tie(b.time.seconds, b.type, b.flags, b.value, b.name);
}
extern enum event_severity get_event_severity(const struct event &ev)
{
switch (ev.flags & SAMPLE_FLAGS_SEVERITY_MASK) {
case SAMPLE_FLAGS_SEVERITY_INFO: case SAMPLE_FLAGS_SEVERITY_INFO:
return EVENT_SEVERITY_INFO; return EVENT_SEVERITY_INFO;
case SAMPLE_FLAGS_SEVERITY_WARN: case SAMPLE_FLAGS_SEVERITY_WARN:
@ -120,3 +79,35 @@ extern enum event_severity get_event_severity(const struct event *ev)
return EVENT_SEVERITY_NONE; return EVENT_SEVERITY_NONE;
} }
} }
event_loop::event_loop(const char *name) : name(name), idx(0)
{
}
struct event *event_loop::next(struct divecomputer &dc)
{
if (name.empty())
return nullptr;
while (idx < dc.events.size()) {
struct event &ev = dc.events[idx++];
if (ev.name == name)
return &ev;
}
return nullptr;
}
const struct event *event_loop::next(const struct divecomputer &dc)
{
return next(const_cast<divecomputer &>(dc));
}
struct event *get_first_event(struct divecomputer &dc, const std::string &name)
{
auto it = std::find_if(dc.events.begin(), dc.events.end(), [name](auto &ev) { return ev.name == name; });
return it != dc.events.end() ? &*it : nullptr;
}
const struct event *get_first_event(const struct divecomputer &dc, const std::string &name)
{
return get_first_event(const_cast<divecomputer &>(dc), name);
}

View file

@ -9,6 +9,8 @@
#include <string> #include <string>
#include <libdivecomputer/parser.h> #include <libdivecomputer/parser.h>
struct divecomputer;
enum event_severity { enum event_severity {
EVENT_SEVERITY_NONE = 0, EVENT_SEVERITY_NONE = 0,
EVENT_SEVERITY_INFO, EVENT_SEVERITY_INFO,
@ -22,7 +24,6 @@ enum event_severity {
*/ */
struct event { struct event {
struct event *next;
duration_t time; duration_t time;
int type; int type;
/* This is the annoying libdivecomputer format. */ /* This is the annoying libdivecomputer format. */
@ -40,23 +41,53 @@ struct event {
struct gasmix mix; struct gasmix mix;
} gas; } gas;
}; };
bool deleted; // used internally in the parser and in fixup_dive().
bool hidden; bool hidden;
std::string name; std::string name;
event(); event();
event(unsigned int time, int type, int flags, int value, const std::string &name);
~event(); ~event();
bool operator==(const event &b2) const;
}; };
extern int event_is_gaschange(const struct event *ev); class event_loop
extern bool event_is_divemodechange(const struct event *ev); {
extern struct event *clone_event(const struct event *src_ev); std::string name;
extern void free_events(struct event *ev); size_t idx;
extern struct event *create_event(unsigned int time, int type, int flags, int value, const std::string &name); public:
extern struct event *clone_event_rename(const struct event *ev, const std::string &name); event_loop(const char *name);
extern bool same_event(const struct event *a, const struct event *b); struct event *next(struct divecomputer &dc); // nullptr -> end
extern enum event_severity get_event_severity(const struct event *ev); const struct event *next(const struct divecomputer &dc); // nullptr -> end
};
extern const struct event *get_next_event(const struct event *event, const std::string &name); /* Get gasmixes at increasing timestamps. */
extern struct event *get_next_event(struct event *event, const std::string &name); class gasmix_loop {
const struct dive &dive;
const struct divecomputer &dc;
struct gasmix last;
event_loop loop;
const struct event *ev;
public:
gasmix_loop(const struct dive &dive, const struct divecomputer &dc);
gasmix next(int time);
};
/* Get divemodes at increasing timestamps. */
class divemode_loop {
const struct divecomputer &dc;
divemode_t last;
event_loop loop;
const struct event *ev;
public:
divemode_loop(const struct divecomputer &dc);
divemode_t next(int time);
};
extern bool event_is_gaschange(const struct event &ev);
extern bool event_is_divemodechange(const struct event &ev);
extern enum event_severity get_event_severity(const struct event &ev);
extern const struct event *get_first_event(const struct divecomputer &dc, const std::string &name);
extern struct event *get_first_event(struct divecomputer &dc, const std::string &name);
#endif #endif

View file

@ -14,7 +14,7 @@ struct event_type {
bool plot; bool plot;
event_type(const struct event *ev) : event_type(const struct event *ev) :
name(ev->name), name(ev->name),
severity(get_event_severity(ev)), severity(get_event_severity(*ev)),
plot(true) plot(true)
{ {
} }
@ -102,12 +102,12 @@ static QString event_type_name(QString name, event_severity severity)
return QStringLiteral("%1 (%2)").arg(name, severity_name); return QStringLiteral("%1 (%2)").arg(name, severity_name);
} }
QString event_type_name(const event *ev) QString event_type_name(const event &ev)
{ {
if (!ev || ev->name.empty()) if (ev.name.empty())
return QString(); return QString();
QString name = QString::fromStdString(ev->name); QString name = QString::fromStdString(ev.name);
return event_type_name(std::move(name), get_event_severity(ev)); return event_type_name(std::move(name), get_event_severity(ev));
} }

View file

@ -14,7 +14,7 @@ extern void show_all_event_types();
extern void show_event_type(int idx); extern void show_event_type(int idx);
extern bool any_event_types_hidden(); extern bool any_event_types_hidden();
extern std::vector<int> hidden_event_types(); extern std::vector<int> hidden_event_types();
extern QString event_type_name(const event *ev); extern QString event_type_name(const event &ev);
extern QString event_type_name(int idx); extern QString event_type_name(int idx);
#endif #endif

View file

@ -313,9 +313,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
cylinder_t *cylinder = get_cylinder(dive, sensor); cylinder_t *cylinder = get_cylinder(dive, sensor);
std::vector<pr_track_t> track; std::vector<pr_track_t> track;
size_t current = std::string::npos; size_t current = std::string::npos;
const struct event *ev, *b_ev;
int missing_pr = 0, dense = 1; int missing_pr = 0, dense = 1;
enum divemode_t dmode = dc->divemode;
const double gasfactor[5] = {1.0, 0.0, prefs.pscr_ratio/1000.0, 1.0, 1.0 }; const double gasfactor[5] = {1.0, 0.0, prefs.pscr_ratio/1000.0, 1.0, 1.0 };
if (sensor < 0 || sensor >= dive->cylinders.nr) if (sensor < 0 || sensor >= dive->cylinders.nr)
@ -351,10 +349,10 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
* itself has a gas change event. * itself has a gas change event.
*/ */
cyl = sensor; cyl = sensor;
ev = NULL; event_loop loop_gas("gaschange");
if (has_gaschange_event(dive, dc, sensor)) const struct event *ev = has_gaschange_event(dive, dc, sensor) ?
ev = get_next_event(dc->events, "gaschange"); loop_gas.next(*dc) : nullptr;
b_ev = get_next_event(dc->events, "modechange"); divemode_loop loop_mode(*dc);
for (int i = first; i <= last; i++) { for (int i = first; i <= last; i++) {
struct plot_data &entry = pi.entry[i]; struct plot_data &entry = pi.entry[i];
@ -362,16 +360,13 @@ void populate_pressure_information(const struct dive *dive, const struct divecom
int time = entry.sec; int time = entry.sec;
while (ev && ev->time.seconds <= time) { // Find 1st gaschange event after while (ev && ev->time.seconds <= time) { // Find 1st gaschange event after
cyl = get_cylinder_index(dive, ev); // the current gas change. cyl = get_cylinder_index(dive, *ev); // the current gas change.
if (cyl < 0) if (cyl < 0)
cyl = sensor; cyl = sensor;
ev = get_next_event(ev->next, "gaschange"); ev = loop_gas.next(*dc);
} }
while (b_ev && b_ev->time.seconds <= time) { // Keep existing divemode, then divemode_t dmode = loop_mode.next(time);
dmode = static_cast<divemode_t>(b_ev->value); // find 1st divemode change event after the current
b_ev = get_next_event(b_ev->next, "modechange"); // divemode change.
}
if (current != std::string::npos) { // calculate pressure-time, taking into account the dive mode for this specific segment. if (current != std::string::npos) { // calculate pressure-time, taking into account the dive mode for this specific segment.
entry.pressure_time = (int)(calc_pressure_time(dive, pi.entry[i - 1], entry) * gasfactor[dmode] + 0.5); entry.pressure_time = (int)(calc_pressure_time(dive, pi.entry[i - 1], entry) * gasfactor[dmode] + 0.5);

View file

@ -363,7 +363,7 @@ static void handle_event(struct divecomputer *dc, const struct sample &sample, d
time += sample.time.seconds; time += sample.time.seconds;
ev = add_event(dc, time, type, value.event.flags, value.event.value, name); ev = add_event(dc, time, type, value.event.flags, value.event.value, name);
if (event_is_gaschange(ev) && ev->gas.index >= 0) if (event_is_gaschange(*ev) && ev->gas.index >= 0)
current_gas_index = ev->gas.index; current_gas_index = ev->gas.index;
} }

View file

@ -839,7 +839,6 @@ static void parse_dc_event(char *line, struct git_parser_state *state)
{ {
int m, s = 0; int m, s = 0;
struct parse_event p; struct parse_event p;
struct event *ev;
m = strtol(line, &line, 10); m = strtol(line, &line, 10);
if (*line == ':') if (*line == ':')
@ -859,17 +858,17 @@ static void parse_dc_event(char *line, struct git_parser_state *state)
if (p.has_divemode && p.name != "modechange") if (p.has_divemode && p.name != "modechange")
p.name = "modechange"; p.name = "modechange";
ev = add_event(state->active_dc, p.ev.time.seconds, p.ev.type, p.ev.flags, p.ev.value, p.name.c_str()); struct event *ev = add_event(state->active_dc, p.ev.time.seconds, p.ev.type, p.ev.flags, p.ev.value, p.name.c_str());
/* /*
* Older logs might mark the dive to be CCR by having an "SP change" event at time 0:00. * Older logs might mark the dive to be CCR by having an "SP change" event at time 0:00.
* Better to mark them being CCR on import so no need for special treatments elsewhere on * Better to mark them being CCR on import so no need for special treatments elsewhere on
* the code. * the code.
*/ */
if (ev && p.ev.time.seconds == 0 && p.ev.type == SAMPLE_EVENT_PO2 && p.ev.value && state->active_dc->divemode==OC) if (p.ev.time.seconds == 0 && p.ev.type == SAMPLE_EVENT_PO2 && p.ev.value && state->active_dc->divemode==OC)
state->active_dc->divemode = CCR; state->active_dc->divemode = CCR;
if (ev && event_is_gaschange(ev)) { if (event_is_gaschange(*ev)) {
/* /*
* We subtract one here because "0" is "no index", * We subtract one here because "0" is "no index",
* and the parsing will add one for actual cylinder * and the parsing will add one for actual cylinder

View file

@ -75,30 +75,30 @@ void event_end(struct parser_state *state)
pic.offset.seconds = state->cur_event.time.seconds; pic.offset.seconds = state->cur_event.time.seconds;
add_picture(&state->cur_dive->pictures, pic); /* Takes ownership. */ add_picture(&state->cur_dive->pictures, pic); /* Takes ownership. */
} else { } else {
struct event *ev;
/* At some point gas change events did not have any type. Thus we need to add /* At some point gas change events did not have any type. Thus we need to add
* one on import, if we encounter the type one missing. * one on import, if we encounter the type one missing.
*/ */
if (state->cur_event.type == 0 && state->cur_event.name == "gaschange") if (state->cur_event.type == 0 && state->cur_event.name == "gaschange")
state->cur_event.type = state->cur_event.value >> 16 > 0 ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE; state->cur_event.type = state->cur_event.value >> 16 > 0 ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE;
ev = add_event(dc, state->cur_event.time.seconds,
state->cur_event.type, state->cur_event.flags, struct event ev(state->cur_event.time.seconds, state->cur_event.type, state->cur_event.flags,
state->cur_event.value, state->cur_event.name); state->cur_event.value, state->cur_event.name);
/* /*
* Older logs might mark the dive to be CCR by having an "SP change" event at time 0:00. Better * Older logs might mark the dive to be CCR by having an "SP change" event at time 0:00. Better
* to mark them being CCR on import so no need for special treatments elsewhere on the code. * to mark them being CCR on import so no need for special treatments elsewhere on the code.
*/ */
if (ev && state->cur_event.time.seconds == 0 && state->cur_event.type == SAMPLE_EVENT_PO2 && state->cur_event.value && dc->divemode==OC) { if (ev.time.seconds == 0 && ev.type == SAMPLE_EVENT_PO2 && ev.value && dc->divemode==OC)
dc->divemode = CCR; dc->divemode = CCR;
if (event_is_gaschange(ev)) {
/* See try_to_fill_event() on why the filled-in index is one too big */
ev.gas.index = state->cur_event.gas.index-1;
if (state->cur_event.gas.mix.o2.permille || state->cur_event.gas.mix.he.permille)
ev.gas.mix = state->cur_event.gas.mix;
} }
if (ev && event_is_gaschange(ev)) { add_event_to_dc(dc, std::move(ev));
/* See try_to_fill_event() on why the filled-in index is one too big */
ev->gas.index = state->cur_event.gas.index-1;
if (state->cur_event.gas.mix.o2.permille || state->cur_event.gas.mix.he.permille)
ev->gas.mix = state->cur_event.gas.mix;
}
} }
state->event_active = false; /* No longer active */ state->event_active = false; /* No longer active */
} }

View file

@ -83,11 +83,11 @@ int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_
{ {
// we start with the first cylinder unless an event tells us otherwise // we start with the first cylinder unless an event tells us otherwise
int cylinder_idx = 0; int cylinder_idx = 0;
struct event *event = dc->events; for (const auto &event: dc->events) {
while (event && event->time.seconds <= time.seconds) { if (event.time.seconds > time.seconds)
if (event->name == "gaschange") break;
if (event.name == "gaschange")
cylinder_idx = get_cylinder_index(dive, event); cylinder_idx = get_cylinder_index(dive, event);
event = event->next;
} }
return cylinder_idx; return cylinder_idx;
} }
@ -127,16 +127,14 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct
if (dc->samples.empty()) if (dc->samples.empty())
return 0; return 0;
const struct event *evdm = NULL;
enum divemode_t divemode = UNDEF_COMP_TYPE;
const struct sample *psample = nullptr; const struct sample *psample = nullptr;
divemode_loop loop(dive->dc);
for (auto &sample: dc->samples) { for (auto &sample: dc->samples) {
o2pressure_t setpoint = psample ? psample->setpoint o2pressure_t setpoint = psample ? psample->setpoint
: sample.setpoint; : sample.setpoint;
duration_t t1 = sample.time; duration_t t1 = sample.time;
struct gasmix gas = get_gasmix_at_time(dive, dc, t0); struct gasmix gas = get_gasmix_at_time(*dive, *dc, t0);
if (psample) if (psample)
lastdepth = psample->depth; lastdepth = psample->depth;
@ -163,7 +161,7 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct
ds->max_bottom_ceiling_pressure.mbar = ceiling_pressure.mbar; ds->max_bottom_ceiling_pressure.mbar = ceiling_pressure.mbar;
} }
divemode = get_current_divemode(&dive->dc, t0.seconds + 1, &evdm, &divemode); divemode_t divemode = loop.next(t0.seconds + 1);
interpolate_transition(ds, dive, t0, t1, lastdepth, sample.depth, gas, setpoint, divemode); interpolate_transition(ds, dive, t0, t1, lastdepth, sample.depth, gas, setpoint, divemode);
psample = &sample; psample = &sample;
t0 = t1; t0 = t1;
@ -201,7 +199,6 @@ static void update_cylinder_pressure(struct dive *d, int old_depth, int new_dept
static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, struct divecomputer *dc, bool track_gas) static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, struct divecomputer *dc, bool track_gas)
{ {
struct divedatapoint *dp; struct divedatapoint *dp;
struct event *ev;
cylinder_t *cyl; cylinder_t *cyl;
int oldpo2 = 0; int oldpo2 = 0;
int lasttime = 0, last_manual_point = 0; int lasttime = 0, last_manual_point = 0;
@ -223,10 +220,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive,
dc->surface_pressure.mbar = diveplan->surface_pressure; dc->surface_pressure.mbar = diveplan->surface_pressure;
dc->salinity = diveplan->salinity; dc->salinity = diveplan->salinity;
dc->samples.clear(); dc->samples.clear();
while ((ev = dc->events)) { dc->events.clear();
dc->events = dc->events->next;
delete ev;
}
dp = diveplan->dp; dp = diveplan->dp;
/* Create first sample at time = 0, not based on dp because /* Create first sample at time = 0, not based on dp because
* there is no real dp for time = 0, set first cylinder to 0 * there is no real dp for time = 0, set first cylinder to 0
@ -722,9 +716,8 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
current_cylinder = get_cylinderid_at_time(dive, dc, sample.time); current_cylinder = get_cylinderid_at_time(dive, dc, sample.time);
// Find the divemode at the end of the dive // Find the divemode at the end of the dive
const struct event *ev = NULL; divemode_loop loop(*dc);
divemode = UNDEF_COMP_TYPE; divemode = loop.next(bottom_time);
divemode = get_current_divemode(dc, bottom_time, &ev, &divemode);
gas = get_cylinder(dive, current_cylinder)->gasmix; gas = get_cylinder(dive, current_cylinder)->gasmix;
po2 = sample.setpoint.mbar; po2 = sample.setpoint.mbar;

View file

@ -12,6 +12,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "dive.h" #include "dive.h"
#include "deco.h" #include "deco.h"
#include "event.h"
#include "units.h" #include "units.h"
#include "divelist.h" #include "divelist.h"
#include "planner.h" #include "planner.h"
@ -570,18 +571,16 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d
{ {
dp = diveplan->dp; dp = diveplan->dp;
bool o2warning_exist = false; bool o2warning_exist = false;
enum divemode_t current_divemode;
double amb; double amb;
const struct event *evd = NULL;
current_divemode = UNDEF_COMP_TYPE;
divemode_loop loop(dive->dc);
if (dive->dc.divemode != CCR) { if (dive->dc.divemode != CCR) {
while (dp) { while (dp) {
if (dp->time != 0) { if (dp->time != 0) {
std::string temp; std::string temp;
struct gasmix gasmix = get_cylinder(dive, dp->cylinderid)->gasmix; struct gasmix gasmix = get_cylinder(dive, dp->cylinderid)->gasmix;
current_divemode = get_current_divemode(&dive->dc, dp->time, &evd, &current_divemode); divemode_t current_divemode = loop.next(dp->time);
amb = depth_to_atm(dp->depth.mm, dive); amb = depth_to_atm(dp->depth.mm, dive);
gas_pressures pressures = fill_pressures(amb, gasmix, (current_divemode == OC) ? 0.0 : amb * gasmix.o2.permille / 1000.0, current_divemode); gas_pressures pressures = fill_pressures(amb, gasmix, (current_divemode == OC) ? 0.0 : amb * gasmix.o2.permille / 1000.0, current_divemode);

View file

@ -199,13 +199,13 @@ static void analyze_plot_info(struct plot_info &pi)
* Some dive computers give cylinder indices, some * Some dive computers give cylinder indices, some
* give just the gas mix. * give just the gas mix.
*/ */
int get_cylinder_index(const struct dive *dive, const struct event *ev) int get_cylinder_index(const struct dive *dive, const struct event &ev)
{ {
int best; int best;
struct gasmix mix; struct gasmix mix;
if (ev->gas.index >= 0) if (ev.gas.index >= 0)
return ev->gas.index; return ev.gas.index;
/* /*
* This should no longer happen! * This should no longer happen!
@ -220,34 +220,6 @@ int get_cylinder_index(const struct dive *dive, const struct event *ev)
return best < 0 ? 0 : best; return best < 0 ? 0 : best;
} }
struct event *get_next_event(struct event *event, const std::string &name)
{
if (name.empty())
return NULL;
while (event) {
if (event->name == name)
return event;
event = event->next;
}
return event;
}
const struct event *get_next_event(const struct event *event, const std::string &name)
{
return get_next_event((struct event *)event, name);
}
static int count_events(const struct divecomputer *dc)
{
int result = 0;
struct event *ev = dc->events;
while (ev != NULL) {
result++;
ev = ev->next;
}
return result;
}
static size_t set_setpoint(struct plot_info &pi, size_t i, int setpoint, int end) static size_t set_setpoint(struct plot_info &pi, size_t i, int setpoint, int end)
{ {
while (i < pi.entry.size()) { while (i < pi.entry.size()) {
@ -265,17 +237,16 @@ static void check_setpoint_events(const struct dive *, const struct divecomputer
size_t i = 0; size_t i = 0;
pressure_t setpoint; pressure_t setpoint;
setpoint.mbar = 0; setpoint.mbar = 0;
const struct event *ev = get_next_event(dc->events, "SP change");
if (!ev) event_loop loop("SP change");
return; bool found = false;
while (auto ev = loop.next(*dc)) {
do {
i = set_setpoint(pi, i, setpoint.mbar, ev->time.seconds); i = set_setpoint(pi, i, setpoint.mbar, ev->time.seconds);
setpoint.mbar = ev->value; setpoint.mbar = ev->value;
ev = get_next_event(ev->next, "SP change"); found = true;
} while (ev); }
set_setpoint(pi, i, setpoint.mbar, INT_MAX); if (found) // Fill the last setpoint until the end of the dive
set_setpoint(pi, i, setpoint.mbar, INT_MAX);
} }
static void calculate_max_limits_new(const struct dive *dive, const struct divecomputer *given_dc, struct plot_info &pi, bool in_planner) static void calculate_max_limits_new(const struct dive *dive, const struct divecomputer *given_dc, struct plot_info &pi, bool in_planner)
@ -306,15 +277,10 @@ static void calculate_max_limits_new(const struct dive *dive, const struct divec
if (dc == given_dc) if (dc == given_dc)
seen = true; seen = true;
int lastdepth = 0; int lastdepth = 0;
struct event *ev;
/* Make sure we can fit all events */ /* Make sure we can fit all events */
ev = dc->events; if (!dc->events.empty())
while (ev) { maxtime = std::max(maxtime, dc->events.back().time.seconds);
if (ev->time.seconds > maxtime)
maxtime = ev->time.seconds;
ev = ev->next;
}
for (auto &s: dc->samples) { for (auto &s: dc->samples) {
int depth = s.depth.mm; int depth = s.depth.mm;
@ -399,8 +365,6 @@ static void insert_entry(struct plot_info &pi, int time, int depth, int sac)
static void populate_plot_entries(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) static void populate_plot_entries(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi)
{ {
struct event *ev = dc->events;
pi.nr_cylinders = dive->cylinders.nr; pi.nr_cylinders = dive->cylinders.nr;
/* /*
@ -413,7 +377,7 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp
* that has time > maxtime (because there can be surface samples * that has time > maxtime (because there can be surface samples
* past "maxtime" in the original sample data) * past "maxtime" in the original sample data)
*/ */
size_t nr = dc->samples.size() + 6 + pi.maxtime / 10 + count_events(dc); size_t nr = dc->samples.size() + 6 + pi.maxtime / 10 + dc->events.size();
pi.entry.reserve(nr); pi.entry.reserve(nr);
pi.pressures.reserve(nr * pi.nr_cylinders); pi.pressures.reserve(nr * pi.nr_cylinders);
@ -425,8 +389,9 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp
int lasttime = 0; int lasttime = 0;
int lasttemp = 0; int lasttemp = 0;
/* skip events at time = 0 */ /* skip events at time = 0 */
while (ev && ev->time.seconds == 0) auto evit = dc->events.begin();
ev = ev->next; while (evit != dc->events.end() && evit->time.seconds == 0)
++evit;
for (const auto &sample: dc->samples) { for (const auto &sample: dc->samples) {
int time = sample.time.seconds; int time = sample.time.seconds;
int offset, delta; int offset, delta;
@ -444,23 +409,23 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp
break; break;
/* Add events if they are between plot entries */ /* Add events if they are between plot entries */
while (ev && (int)ev->time.seconds < lasttime + offset) { while (evit != dc->events.end() && static_cast<int>(evit->time.seconds) < lasttime + offset) {
insert_entry(pi, ev->time.seconds, interpolate(lastdepth, depth, ev->time.seconds - lasttime, delta), sac); insert_entry(pi, evit->time.seconds, interpolate(lastdepth, depth, evit->time.seconds - lasttime, delta), sac);
ev = ev->next; ++evit;
} }
/* now insert the time interpolated entry */ /* now insert the time interpolated entry */
insert_entry(pi, lasttime + offset, interpolate(lastdepth, depth, offset, delta), sac); insert_entry(pi, lasttime + offset, interpolate(lastdepth, depth, offset, delta), sac);
/* skip events that happened at this time */ /* skip events that happened at this time */
while (ev && (int)ev->time.seconds == lasttime + offset) while (evit != dc->events.end() && static_cast<int>(evit->time.seconds) == lasttime + offset)
ev = ev->next; ++evit;
} }
/* Add events if they are between plot entries */ /* Add events if they are between plot entries */
while (ev && (int)ev->time.seconds < time) { while (evit != dc->events.end() && static_cast<int>(evit->time.seconds) < time) {
insert_entry(pi, ev->time.seconds, interpolate(lastdepth, depth, ev->time.seconds - lasttime, delta), sac); insert_entry(pi, evit->time.seconds, interpolate(lastdepth, depth, evit->time.seconds - lasttime, delta), sac);
ev = ev->next; ++evit;
} }
plot_data &entry = add_entry(pi); plot_data &entry = add_entry(pi);
@ -497,8 +462,8 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp
if (sample.rbt.seconds) if (sample.rbt.seconds)
entry.rbt = sample.rbt.seconds; entry.rbt = sample.rbt.seconds;
/* skip events that happened at this time */ /* skip events that happened at this time */
while (ev && (int)ev->time.seconds == time) while (evit != dc->events.end() && static_cast<int>(evit->time.seconds) == time)
ev = ev->next; ++evit;
lasttime = time; lasttime = time;
lastdepth = depth; lastdepth = depth;
@ -507,14 +472,14 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp
} }
/* Add any remaining events */ /* Add any remaining events */
while (ev) { while (evit != dc->events.end()) {
int time = ev->time.seconds; int time = evit->time.seconds;
if (time > lasttime) { if (time > lasttime) {
insert_entry(pi, ev->time.seconds, 0, 0); insert_entry(pi, evit->time.seconds, 0, 0);
lasttime = time; lasttime = time;
} }
ev = ev->next; ++evit;
} }
/* Add two final surface events */ /* Add two final surface events */
@ -674,18 +639,17 @@ static void matching_gases(const struct dive *dive, struct gasmix gasmix, char g
static void calculate_sac(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) static void calculate_sac(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi)
{ {
struct gasmix gasmix = gasmix_invalid;
const struct event *ev = NULL;
std::vector<char> gases(pi.nr_cylinders, false); std::vector<char> gases(pi.nr_cylinders, false);
/* This might be premature optimization, but let's allocate the gas array for /* This might be premature optimization, but let's allocate the gas array for
* the fill_sac function only once an not once per sample */ * the fill_sac function only once an not once per sample */
std::vector<char> gases_scratch(pi.nr_cylinders); std::vector<char> gases_scratch(pi.nr_cylinders);
struct gasmix gasmix = gasmix_invalid;
gasmix_loop loop(*dive, *dc);
for (int i = 0; i < pi.nr; i++) { for (int i = 0; i < pi.nr; i++) {
const struct plot_data &entry = pi.entry[i]; const struct plot_data &entry = pi.entry[i];
struct gasmix newmix = get_gasmix(dive, dc, entry.sec, &ev, gasmix); struct gasmix newmix = loop.next(entry.sec);
if (!same_gasmix(newmix, gasmix)) { if (!same_gasmix(newmix, gasmix)) {
gasmix = newmix; gasmix = newmix;
matching_gases(dive, newmix, gases.data()); matching_gases(dive, newmix, gases.data());
@ -736,9 +700,6 @@ static void add_plot_pressure(struct plot_info &pi, int time, int cyl, pressure_
static void setup_gas_sensor_pressure(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) static void setup_gas_sensor_pressure(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi)
{ {
int i;
const struct event *ev;
if (pi.nr_cylinders == 0) if (pi.nr_cylinders == 0)
return; return;
@ -753,7 +714,8 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive
prev = prev >= 0 ? prev : 0; prev = prev >= 0 ? prev : 0;
seen[prev] = 1; seen[prev] = 1;
for (ev = get_next_event(dc->events, "gaschange"); ev != NULL; ev = get_next_event(ev->next, "gaschange")) { event_loop loop("gaschange");
while (auto ev = loop.next(*dc)) {
int cyl = ev->gas.index; int cyl = ev->gas.index;
int sec = ev->time.seconds; int sec = ev->time.seconds;
@ -778,7 +740,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive
// Fill in "seen[]" array - mark cylinders we're not interested // Fill in "seen[]" array - mark cylinders we're not interested
// in as negative. // in as negative.
for (i = 0; i < pi.nr_cylinders; i++) { for (int i = 0; i < pi.nr_cylinders; i++) {
const cylinder_t *cyl = get_cylinder(dive, i); const cylinder_t *cyl = get_cylinder(dive, i);
int start = cyl->start.mbar; int start = cyl->start.mbar;
int end = cyl->end.mbar; int end = cyl->end.mbar;
@ -807,7 +769,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive
} }
} }
for (i = 0; i < pi.nr_cylinders; i++) { for (int i = 0; i < pi.nr_cylinders; i++) {
if (seen[i] >= 0) { if (seen[i] >= 0) {
const cylinder_t *cyl = get_cylinder(dive, i); const cylinder_t *cyl = get_cylinder(dive, i);
@ -940,18 +902,17 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_
int last_ndl_tts_calc_time = 0, first_ceiling = 0, current_ceiling, last_ceiling = 0, final_tts = 0 , time_clear_ceiling = 0; int last_ndl_tts_calc_time = 0, first_ceiling = 0, current_ceiling, last_ceiling = 0, final_tts = 0 , time_clear_ceiling = 0;
if (decoMode(in_planner) == VPMB) if (decoMode(in_planner) == VPMB)
ds->first_ceiling_pressure.mbar = depth_to_mbar(first_ceiling, dive); ds->first_ceiling_pressure.mbar = depth_to_mbar(first_ceiling, dive);
struct gasmix gasmix = gasmix_invalid;
const struct event *ev = NULL, *evd = NULL;
enum divemode_t current_divemode = UNDEF_COMP_TYPE;
gasmix_loop loop(*dive, *dc);
divemode_loop loop_d(*dc);
for (i = 1; i < pi.nr; i++) { for (i = 1; i < pi.nr; i++) {
struct plot_data &entry = pi.entry[i]; struct plot_data &entry = pi.entry[i];
struct plot_data &prev = pi.entry[i - 1]; struct plot_data &prev = pi.entry[i - 1];
int j, t0 = prev.sec, t1 = entry.sec; int j, t0 = prev.sec, t1 = entry.sec;
int time_stepsize = 20, max_ceiling = -1; int time_stepsize = 20, max_ceiling = -1;
current_divemode = get_current_divemode(dc, entry.sec, &evd, &current_divemode); divemode_t current_divemode = loop_d.next(entry.sec);
gasmix = get_gasmix(dive, dc, t1, &ev, gasmix); struct gasmix gasmix = loop.next(t1);
entry.ambpressure = depth_to_bar(entry.depth, dive); entry.ambpressure = depth_to_bar(entry.depth, dive);
entry.gfline = get_gf(ds, entry.ambpressure, dive) * (100.0 - AMB_PERCENTAGE) + AMB_PERCENTAGE; entry.gfline = get_gf(ds, entry.ambpressure, dive) * (100.0 - AMB_PERCENTAGE) + AMB_PERCENTAGE;
if (t0 > t1) { if (t0 > t1) {
@ -1181,22 +1142,21 @@ static void calculate_gas_information_new(const struct dive *dive, const struct
{ {
int i; int i;
double amb_pressure; double amb_pressure;
struct gasmix gasmix = gasmix_invalid;
const struct event *evg = NULL, *evd = NULL;
enum divemode_t current_divemode = UNDEF_COMP_TYPE;
gasmix_loop loop(*dive, *dc);
divemode_loop loop_d(*dc);
for (i = 1; i < pi.nr; i++) { for (i = 1; i < pi.nr; i++) {
double fn2, fhe; double fn2, fhe;
struct plot_data &entry = pi.entry[i]; struct plot_data &entry = pi.entry[i];
gasmix = get_gasmix(dive, dc, entry.sec, &evg, gasmix); auto gasmix = loop.next(entry.sec);
amb_pressure = depth_to_bar(entry.depth, dive); amb_pressure = depth_to_bar(entry.depth, dive);
current_divemode = get_current_divemode(dc, entry.sec, &evd, &current_divemode); divemode_t current_divemode = loop_d.next(entry.sec);
entry.pressures = fill_pressures(amb_pressure, gasmix, (current_divemode == OC) ? 0.0 : entry.o2pressure.mbar / 1000.0, current_divemode); entry.pressures = fill_pressures(amb_pressure, gasmix, (current_divemode == OC) ? 0.0 : entry.o2pressure.mbar / 1000.0, current_divemode);
fn2 = 1000.0 * entry.pressures.n2 / amb_pressure; fn2 = 1000.0 * entry.pressures.n2 / amb_pressure;
fhe = 1000.0 * entry.pressures.he / amb_pressure; fhe = 1000.0 * entry.pressures.he / amb_pressure;
if (dc->divemode == PSCR) { // OC pO2 is calulated for PSCR with or without external PO2 monitoring. if (dc->divemode == PSCR) { // OC pO2 is calulated for PSCR with or without external PO2 monitoring.
struct gasmix gasmix2 = get_gasmix(dive, dc, entry.sec, &evg, gasmix); struct gasmix gasmix2 = loop.next(entry.sec);
entry.scr_OC_pO2.mbar = (int) depth_to_mbar(entry.depth, dive) * get_o2(gasmix2) / 1000; entry.scr_OC_pO2.mbar = (int) depth_to_mbar(entry.depth, dive) * get_o2(gasmix2) / 1000;
} }

View file

@ -383,32 +383,30 @@ static void save_samples(struct membuffer *b, struct dive *dive, struct divecomp
save_sample(b, s, dummy, o2sensor); save_sample(b, s, dummy, o2sensor);
} }
static void save_one_event(struct membuffer *b, struct dive *dive, struct event *ev) static void save_one_event(struct membuffer *b, struct dive *dive, const struct event &ev)
{ {
put_format(b, "event %d:%02d", FRACTION_TUPLE(ev->time.seconds, 60)); put_format(b, "event %d:%02d", FRACTION_TUPLE(ev.time.seconds, 60));
show_index(b, ev->type, "type=", ""); show_index(b, ev.type, "type=", "");
show_index(b, ev->flags, "flags=", ""); show_index(b, ev.flags, "flags=", "");
if (ev->name == "modechange") if (ev.name == "modechange")
show_utf8(b, " divemode=", divemode_text[ev->value], ""); show_utf8(b, " divemode=", divemode_text[ev.value], "");
else else
show_index(b, ev->value, "value=", ""); show_index(b, ev.value, "value=", "");
show_utf8(b, " name=", ev->name.c_str(), ""); show_utf8(b, " name=", ev.name.c_str(), "");
if (event_is_gaschange(ev)) { if (event_is_gaschange(ev)) {
struct gasmix mix = get_gasmix_from_event(dive, ev); struct gasmix mix = get_gasmix_from_event(dive, ev);
if (ev->gas.index >= 0) if (ev.gas.index >= 0)
show_integer(b, ev->gas.index, "cylinder=", ""); show_integer(b, ev.gas.index, "cylinder=", "");
put_gasmix(b, mix); put_gasmix(b, mix);
} }
put_string(b, "\n"); put_string(b, "\n");
} }
static void save_events(struct membuffer *b, struct dive *dive, struct event *ev) static void save_events(struct membuffer *b, struct dive *dive, const struct divecomputer *dc)
{ {
while (ev) { for (auto &ev: dc->events)
save_one_event(b, dive, ev); save_one_event(b, dive, ev);
ev = ev->next;
}
} }
static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer *dc) static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer *dc)
@ -436,7 +434,7 @@ static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer
put_duration(b, dc->surfacetime, "surfacetime ", "min\n"); put_duration(b, dc->surfacetime, "surfacetime ", "min\n");
save_extra_data(b, dc); save_extra_data(b, dc);
save_events(b, dive, dc->events); save_events(b, dive, dc);
save_samples(b, dive, dc); save_samples(b, dive, dc);
} }

View file

@ -31,7 +31,7 @@ static void write_attribute(struct membuffer *b, const char *att_name, const cha
put_format(b, "\"%s", separator); put_format(b, "\"%s", separator);
} }
static void save_photos(struct membuffer *b, const char *photos_dir, struct dive *dive) static void save_photos(struct membuffer *b, const char *photos_dir, const struct dive *dive)
{ {
if (dive->pictures.nr <= 0) if (dive->pictures.nr <= 0)
return; return;
@ -49,10 +49,10 @@ static void save_photos(struct membuffer *b, const char *photos_dir, struct dive
put_string(b, "],"); put_string(b, "],");
} }
static void write_divecomputers(struct membuffer *b, struct dive *dive) static void write_divecomputers(struct membuffer *b, const struct dive *dive)
{ {
put_string(b, "\"divecomputers\":["); put_string(b, "\"divecomputers\":[");
struct divecomputer *dc; const struct divecomputer *dc;
const char *separator = ""; const char *separator = "";
for_each_dc (dive, dc) { for_each_dc (dive, dc) {
put_string(b, separator); put_string(b, separator);
@ -72,36 +72,30 @@ static void write_divecomputers(struct membuffer *b, struct dive *dive)
put_string(b, "],"); put_string(b, "],");
} }
static void write_dive_status(struct membuffer *b, struct dive *dive) static void write_dive_status(struct membuffer *b, const struct dive *dive)
{ {
put_format(b, "\"sac\":\"%d\",", dive->sac); put_format(b, "\"sac\":\"%d\",", dive->sac);
put_format(b, "\"otu\":\"%d\",", dive->otu); put_format(b, "\"otu\":\"%d\",", dive->otu);
put_format(b, "\"cns\":\"%d\",", dive->cns); put_format(b, "\"cns\":\"%d\",", dive->cns);
} }
static void put_HTML_bookmarks(struct membuffer *b, struct dive *dive) static void put_HTML_bookmarks(struct membuffer *b, const struct dive *dive)
{ {
struct event *ev = dive->dc.events;
if (!ev)
return;
const char *separator = "\"events\":["; const char *separator = "\"events\":[";
do { for (const auto &ev: dive->dc.events) {
put_string(b, separator); put_string(b, separator);
separator = ", "; separator = ", ";
put_string(b, "{\"name\":\""); put_string(b, "{\"name\":\"");
put_quoted(b, ev->name.c_str(), 1, 0); put_quoted(b, ev.name.c_str(), 1, 0);
put_string(b, "\","); put_string(b, "\",");
put_format(b, "\"value\":\"%d\",", ev->value); put_format(b, "\"value\":\"%d\",", ev.value);
put_format(b, "\"type\":\"%d\",", ev->type); put_format(b, "\"type\":\"%d\",", ev.type);
put_format(b, "\"time\":\"%d\"}", ev->time.seconds); put_format(b, "\"time\":\"%d\"}", ev.time.seconds);
ev = ev->next; }
} while (ev);
put_string(b, "],"); put_string(b, "],");
} }
static void put_weightsystem_HTML(struct membuffer *b, struct dive *dive) static void put_weightsystem_HTML(struct membuffer *b, const struct dive *dive)
{ {
int i, nr; int i, nr;
@ -126,7 +120,7 @@ static void put_weightsystem_HTML(struct membuffer *b, struct dive *dive)
put_string(b, "],"); put_string(b, "],");
} }
static void put_cylinder_HTML(struct membuffer *b, struct dive *dive) static void put_cylinder_HTML(struct membuffer *b, const struct dive *dive)
{ {
int i, nr; int i, nr;
const char *separator = "\"Cylinders\":["; const char *separator = "\"Cylinders\":[";
@ -176,7 +170,7 @@ static void put_cylinder_HTML(struct membuffer *b, struct dive *dive)
} }
static void put_HTML_samples(struct membuffer *b, struct dive *dive) static void put_HTML_samples(struct membuffer *b, const struct dive *dive)
{ {
put_format(b, "\"maxdepth\":%d,", dive->dc.maxdepth.mm); put_format(b, "\"maxdepth\":%d,", dive->dc.maxdepth.mm);
put_format(b, "\"duration\":%d,", dive->dc.duration.seconds); put_format(b, "\"duration\":%d,", dive->dc.duration.seconds);
@ -192,7 +186,7 @@ static void put_HTML_samples(struct membuffer *b, struct dive *dive)
put_string(b, "],"); put_string(b, "],");
} }
static void put_HTML_coordinates(struct membuffer *b, struct dive *dive) static void put_HTML_coordinates(struct membuffer *b, const struct dive *dive)
{ {
struct dive_site *ds = get_dive_site_for_dive(dive); struct dive_site *ds = get_dive_site_for_dive(dive);
if (!ds) if (!ds)
@ -210,7 +204,7 @@ static void put_HTML_coordinates(struct membuffer *b, struct dive *dive)
put_string(b, "},"); put_string(b, "},");
} }
void put_HTML_date(struct membuffer *b, struct dive *dive, const char *pre, const char *post) void put_HTML_date(struct membuffer *b, const struct dive *dive, const char *pre, const char *post)
{ {
struct tm tm; struct tm tm;
utc_mkdate(dive->when, &tm); utc_mkdate(dive->when, &tm);
@ -223,7 +217,7 @@ void put_HTML_quoted(struct membuffer *b, const char *text)
put_quoted(b, text, is_attribute, is_html); put_quoted(b, text, is_attribute, is_html);
} }
void put_HTML_notes(struct membuffer *b, struct dive *dive, const char *pre, const char *post) void put_HTML_notes(struct membuffer *b, const struct dive *dive, const char *pre, const char *post)
{ {
put_string(b, pre); put_string(b, pre);
if (dive->notes) { if (dive->notes) {
@ -268,14 +262,14 @@ void put_HTML_weight_units(struct membuffer *b, unsigned int grams, const char *
put_format(b, "%s%.1f %s%s", pre, value, unit, post); put_format(b, "%s%.1f %s%s", pre, value, unit, post);
} }
void put_HTML_time(struct membuffer *b, struct dive *dive, const char *pre, const char *post) void put_HTML_time(struct membuffer *b, const struct dive *dive, const char *pre, const char *post)
{ {
struct tm tm; struct tm tm;
utc_mkdate(dive->when, &tm); utc_mkdate(dive->when, &tm);
put_format(b, "%s%02u:%02u:%02u%s", pre, tm.tm_hour, tm.tm_min, tm.tm_sec, post); put_format(b, "%s%02u:%02u:%02u%s", pre, tm.tm_hour, tm.tm_min, tm.tm_sec, post);
} }
void put_HTML_depth(struct membuffer *b, struct dive *dive, const char *pre, const char *post) void put_HTML_depth(struct membuffer *b, const struct dive *dive, const char *pre, const char *post)
{ {
const char *unit; const char *unit;
double value; double value;
@ -298,7 +292,7 @@ void put_HTML_depth(struct membuffer *b, struct dive *dive, const char *pre, con
} }
} }
void put_HTML_airtemp(struct membuffer *b, struct dive *dive, const char *pre, const char *post) void put_HTML_airtemp(struct membuffer *b, const struct dive *dive, const char *pre, const char *post)
{ {
const char *unit; const char *unit;
double value; double value;
@ -311,7 +305,7 @@ void put_HTML_airtemp(struct membuffer *b, struct dive *dive, const char *pre, c
put_format(b, "%s%.1f %s%s", pre, value, unit, post); put_format(b, "%s%.1f %s%s", pre, value, unit, post);
} }
void put_HTML_watertemp(struct membuffer *b, struct dive *dive, const char *pre, const char *post) void put_HTML_watertemp(struct membuffer *b, const struct dive *dive, const char *pre, const char *post)
{ {
const char *unit; const char *unit;
double value; double value;
@ -324,7 +318,7 @@ void put_HTML_watertemp(struct membuffer *b, struct dive *dive, const char *pre,
put_format(b, "%s%.1f %s%s", pre, value, unit, post); put_format(b, "%s%.1f %s%s", pre, value, unit, post);
} }
static void put_HTML_tags(struct membuffer *b, struct dive *dive, const char *pre, const char *post) static void put_HTML_tags(struct membuffer *b, const struct dive *dive, const char *pre, const char *post)
{ {
put_string(b, pre); put_string(b, pre);
struct tag_entry *tag = dive->tag_list; struct tag_entry *tag = dive->tag_list;
@ -345,7 +339,7 @@ static void put_HTML_tags(struct membuffer *b, struct dive *dive, const char *pr
} }
/* if exporting list_only mode, we neglect exporting the samples, bookmarks and cylinders */ /* if exporting list_only mode, we neglect exporting the samples, bookmarks and cylinders */
static void write_one_dive(struct membuffer *b, struct dive *dive, const char *photos_dir, int *dive_no, bool list_only) static void write_one_dive(struct membuffer *b, const struct dive *dive, const char *photos_dir, int *dive_no, bool list_only)
{ {
put_string(b, "{"); put_string(b, "{");
put_format(b, "\"number\":%d,", *dive_no); put_format(b, "\"number\":%d,", *dive_no);
@ -388,7 +382,7 @@ static void write_one_dive(struct membuffer *b, struct dive *dive, const char *p
static void write_no_trip(struct membuffer *b, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only, char *sep) static void write_no_trip(struct membuffer *b, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only, char *sep)
{ {
int i; int i;
struct dive *dive; const struct dive *dive;
const char *separator = ""; const char *separator = "";
bool found_sel_dive = 0; bool found_sel_dive = 0;
@ -414,7 +408,7 @@ static void write_no_trip(struct membuffer *b, int *dive_no, bool selected_only,
static void write_trip(struct membuffer *b, dive_trip_t *trip, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only, char *sep) static void write_trip(struct membuffer *b, dive_trip_t *trip, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only, char *sep)
{ {
struct dive *dive; const struct dive *dive;
const char *separator = ""; const char *separator = "";
bool found_sel_dive = 0; bool found_sel_dive = 0;
@ -444,7 +438,7 @@ static void write_trip(struct membuffer *b, dive_trip_t *trip, int *dive_no, boo
static void write_trips(struct membuffer *b, const char *photos_dir, bool selected_only, const bool list_only) static void write_trips(struct membuffer *b, const char *photos_dir, bool selected_only, const bool list_only)
{ {
int i, dive_no = 0; int i, dive_no = 0;
struct dive *dive; const struct dive *dive;
dive_trip_t *trip; dive_trip_t *trip;
char sep_ = ' '; char sep_ = ' ';
char *sep = &sep_; char *sep = &sep_;

View file

@ -6,12 +6,12 @@
struct dive; struct dive;
void put_HTML_date(struct membuffer *b, struct dive *dive, const char *pre, const char *post); void put_HTML_date(struct membuffer *b, const struct dive *dive, const char *pre, const char *post);
void put_HTML_depth(struct membuffer *b, struct dive *dive, const char *pre, const char *post); void put_HTML_depth(struct membuffer *b, const struct dive *dive, const char *pre, const char *post);
void put_HTML_airtemp(struct membuffer *b, struct dive *dive, const char *pre, const char *post); void put_HTML_airtemp(struct membuffer *b, const struct dive *dive, const char *pre, const char *post);
void put_HTML_watertemp(struct membuffer *b, struct dive *dive, const char *pre, const char *post); void put_HTML_watertemp(struct membuffer *b, const struct dive *dive, const char *pre, const char *post);
void put_HTML_time(struct membuffer *b, struct dive *dive, const char *pre, const char *post); void put_HTML_time(struct membuffer *b, const struct dive *dive, const char *pre, const char *post);
void put_HTML_notes(struct membuffer *b, struct dive *dive, const char *pre, const char *post); void put_HTML_notes(struct membuffer *b, const struct dive *dive, const char *pre, const char *post);
void put_HTML_quoted(struct membuffer *b, const char *text); void put_HTML_quoted(struct membuffer *b, const char *text);
void put_HTML_pressure_units(struct membuffer *b, pressure_t pressure, const char *pre, const char *post); void put_HTML_pressure_units(struct membuffer *b, pressure_t pressure, const char *pre, const char *post);
void put_HTML_weight_units(struct membuffer *b, unsigned int grams, const char *pre, const char *post); void put_HTML_weight_units(struct membuffer *b, unsigned int grams, const char *pre, const char *post);

View file

@ -353,32 +353,30 @@ static void save_sample(struct membuffer *b, const struct sample &sample, struct
put_format(b, " />\n"); put_format(b, " />\n");
} }
static void save_one_event(struct membuffer *b, struct dive *dive, struct event *ev) static void save_one_event(struct membuffer *b, struct dive *dive, const struct event &ev)
{ {
put_format(b, " <event time='%d:%02d min'", FRACTION_TUPLE(ev->time.seconds, 60)); put_format(b, " <event time='%d:%02d min'", FRACTION_TUPLE(ev.time.seconds, 60));
show_index(b, ev->type, "type='", "'"); show_index(b, ev.type, "type='", "'");
show_index(b, ev->flags, "flags='", "'"); show_index(b, ev.flags, "flags='", "'");
if (ev->name == "modechange") if (ev.name == "modechange")
show_utf8(b, divemode_text[ev->value], " divemode='", "'",1); show_utf8(b, divemode_text[ev.value], " divemode='", "'",1);
else else
show_index(b, ev->value, "value='", "'"); show_index(b, ev.value, "value='", "'");
show_utf8(b, ev->name.c_str(), " name='", "'", 1); show_utf8(b, ev.name.c_str(), " name='", "'", 1);
if (event_is_gaschange(ev)) { if (event_is_gaschange(ev)) {
struct gasmix mix = get_gasmix_from_event(dive, ev); struct gasmix mix = get_gasmix_from_event(dive, ev);
if (ev->gas.index >= 0) if (ev.gas.index >= 0)
show_integer(b, ev->gas.index, "cylinder='", "'"); show_integer(b, ev.gas.index, "cylinder='", "'");
put_gasmix(b, mix); put_gasmix(b, mix);
} }
put_format(b, " />\n"); put_format(b, " />\n");
} }
static void save_events(struct membuffer *b, struct dive *dive, struct event *ev) static void save_events(struct membuffer *b, struct dive *dive, const struct divecomputer *dc)
{ {
while (ev) { for (auto &ev: dc->events)
save_one_event(b, dive, ev); save_one_event(b, dive, ev);
ev = ev->next;
}
} }
static void save_tags(struct membuffer *b, struct tag_entry *entry) static void save_tags(struct membuffer *b, struct tag_entry *entry)
@ -465,7 +463,7 @@ static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer
save_salinity(b, dc); save_salinity(b, dc);
put_duration(b, dc->surfacetime, " <surfacetime>", " min</surfacetime>\n"); put_duration(b, dc->surfacetime, " <surfacetime>", " min</surfacetime>\n");
save_extra_data(b, dc); save_extra_data(b, dc);
save_events(b, dive, dc->events); save_events(b, dive, dc);
save_samples(b, dive, dc); save_samples(b, dive, dc);
put_format(b, " </divecomputer>\n"); put_format(b, " </divecomputer>\n");

View file

@ -260,14 +260,13 @@ stats_t calculate_stats_selected()
bool has_gaschange_event(const struct dive *dive, const struct divecomputer *dc, int idx) bool has_gaschange_event(const struct dive *dive, const struct divecomputer *dc, int idx)
{ {
bool first_gas_explicit = false; bool first_gas_explicit = false;
const struct event *event = get_next_event(dc->events, "gaschange"); event_loop loop("gaschange");
while (event) { while (auto event = loop.next(*dc)) {
if (!dc->samples.empty() && (event->time.seconds == 0 || if (!dc->samples.empty() && (event->time.seconds == 0 ||
(dc->samples[0].time.seconds == event->time.seconds))) (dc->samples[0].time.seconds == event->time.seconds)))
first_gas_explicit = true; first_gas_explicit = true;
if (get_cylinder_index(dive, event) == idx) if (get_cylinder_index(dive, *event) == idx)
return true; return true;
event = get_next_event(event->next, "gaschange");
} }
return !first_gas_explicit && idx == 0; return !first_gas_explicit && idx == 0;
} }

View file

@ -4,6 +4,8 @@
#include "profile-widget/profilewidget2.h" #include "profile-widget/profilewidget2.h"
#include "commands/command.h" #include "commands/command.h"
#include "core/color.h" #include "core/color.h"
#include "core/event.h"
#include "core/sample.h"
#include "core/selection.h" #include "core/selection.h"
#include "core/settings/qPrefTechnicalDetails.h" #include "core/settings/qPrefTechnicalDetails.h"
#include "core/settings/qPrefPartialPressureGas.h" #include "core/settings/qPrefPartialPressureGas.h"

View file

@ -16,14 +16,15 @@
static int depthAtTime(const plot_info &pi, duration_t time); static int depthAtTime(const plot_info &pi, duration_t time);
DiveEventItem::DiveEventItem(const struct dive *d, struct event *ev, struct gasmix lastgasmix, DiveEventItem::DiveEventItem(const struct dive *d, int idx, const struct event &ev, struct gasmix lastgasmix,
const plot_info &pi, DiveCartesianAxis *hAxis, DiveCartesianAxis *vAxis, const plot_info &pi, DiveCartesianAxis *hAxis, DiveCartesianAxis *vAxis,
int speed, const DivePixmaps &pixmaps, QGraphicsItem *parent) : DivePixmapItem(parent), int speed, const DivePixmaps &pixmaps, QGraphicsItem *parent) : DivePixmapItem(parent),
vAxis(vAxis), vAxis(vAxis),
hAxis(hAxis), hAxis(hAxis),
idx(idx),
ev(ev), ev(ev),
dive(d), dive(d),
depth(depthAtTime(pi, ev->time)) depth(depthAtTime(pi, ev.time))
{ {
setFlag(ItemIgnoresTransformations); setFlag(ItemIgnoresTransformations);
@ -36,27 +37,17 @@ DiveEventItem::~DiveEventItem()
{ {
} }
const struct event *DiveEventItem::getEvent() const
{
return ev;
}
struct event *DiveEventItem::getEventMutable()
{
return ev;
}
void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pixmaps) void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pixmaps)
{ {
event_severity severity = get_event_severity(ev); event_severity severity = get_event_severity(ev);
if (ev->name.empty()) { if (ev.name.empty()) {
setPixmap(pixmaps.warning); setPixmap(pixmaps.warning);
} else if (same_string_caseinsensitive(ev->name.c_str(), "modechange")) { } else if (same_string_caseinsensitive(ev.name.c_str(), "modechange")) {
if (ev->value == 0) if (ev.value == 0)
setPixmap(pixmaps.bailout); setPixmap(pixmaps.bailout);
else else
setPixmap(pixmaps.onCCRLoop); setPixmap(pixmaps.onCCRLoop);
} else if (ev->type == SAMPLE_EVENT_BOOKMARK) { } else if (ev.type == SAMPLE_EVENT_BOOKMARK) {
setPixmap(pixmaps.bookmark); setPixmap(pixmaps.bookmark);
setOffset(QPointF(0.0, -pixmap().height())); setOffset(QPointF(0.0, -pixmap().height()));
} else if (event_is_gaschange(ev)) { } else if (event_is_gaschange(ev)) {
@ -84,10 +75,10 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix
else else
setPixmap(pixmaps.gaschangeEAN); setPixmap(pixmaps.gaschangeEAN);
} }
} else if ((((ev->flags & SAMPLE_FLAGS_SEVERITY_MASK) >> SAMPLE_FLAGS_SEVERITY_SHIFT) == 1) || } else if ((((ev.flags & SAMPLE_FLAGS_SEVERITY_MASK) >> SAMPLE_FLAGS_SEVERITY_SHIFT) == 1) ||
// those are useless internals of the dive computer // those are useless internals of the dive computer
same_string_caseinsensitive(ev->name.c_str(), "heading") || same_string_caseinsensitive(ev.name.c_str(), "heading") ||
(same_string_caseinsensitive(ev->name.c_str(), "SP change") && ev->time.seconds == 0)) { (same_string_caseinsensitive(ev.name.c_str(), "SP change") && ev.time.seconds == 0)) {
// 2 cases: // 2 cases:
// a) some dive computers have heading in every sample // a) some dive computers have heading in every sample
// b) at t=0 we might have an "SP change" to indicate dive type // b) at t=0 we might have an "SP change" to indicate dive type
@ -102,19 +93,19 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix
setPixmap(pixmaps.warning); setPixmap(pixmaps.warning);
} else if (severity == EVENT_SEVERITY_ALARM) { } else if (severity == EVENT_SEVERITY_ALARM) {
setPixmap(pixmaps.violation); setPixmap(pixmaps.violation);
} else if (same_string_caseinsensitive(ev->name.c_str(), "violation") || // generic libdivecomputer } else if (same_string_caseinsensitive(ev.name.c_str(), "violation") || // generic libdivecomputer
same_string_caseinsensitive(ev->name.c_str(), "Safety stop violation") || // the rest are from the Uemis downloader same_string_caseinsensitive(ev.name.c_str(), "Safety stop violation") || // the rest are from the Uemis downloader
same_string_caseinsensitive(ev->name.c_str(), "pO₂ ascend alarm") || same_string_caseinsensitive(ev.name.c_str(), "pO₂ ascend alarm") ||
same_string_caseinsensitive(ev->name.c_str(), "RGT alert") || same_string_caseinsensitive(ev.name.c_str(), "RGT alert") ||
same_string_caseinsensitive(ev->name.c_str(), "Dive time alert") || same_string_caseinsensitive(ev.name.c_str(), "Dive time alert") ||
same_string_caseinsensitive(ev->name.c_str(), "Low battery alert") || same_string_caseinsensitive(ev.name.c_str(), "Low battery alert") ||
same_string_caseinsensitive(ev->name.c_str(), "Speed alarm")) { same_string_caseinsensitive(ev.name.c_str(), "Speed alarm")) {
setPixmap(pixmaps.violation); setPixmap(pixmaps.violation);
} else if (same_string_caseinsensitive(ev->name.c_str(), "non stop time") || // generic libdivecomputer } else if (same_string_caseinsensitive(ev.name.c_str(), "non stop time") || // generic libdivecomputer
same_string_caseinsensitive(ev->name.c_str(), "safety stop") || same_string_caseinsensitive(ev.name.c_str(), "safety stop") ||
same_string_caseinsensitive(ev->name.c_str(), "safety stop (voluntary)") || same_string_caseinsensitive(ev.name.c_str(), "safety stop (voluntary)") ||
same_string_caseinsensitive(ev->name.c_str(), "Tank change suggested") || // Uemis downloader same_string_caseinsensitive(ev.name.c_str(), "Tank change suggested") || // Uemis downloader
same_string_caseinsensitive(ev->name.c_str(), "Marker")) { same_string_caseinsensitive(ev.name.c_str(), "Marker")) {
setPixmap(pixmaps.info); setPixmap(pixmaps.info);
} else { } else {
// we should do some guessing based on the type / name of the event; // we should do some guessing based on the type / name of the event;
@ -126,9 +117,9 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix
void DiveEventItem::setupToolTipString(struct gasmix lastgasmix) void DiveEventItem::setupToolTipString(struct gasmix lastgasmix)
{ {
// we display the event on screen - so translate // we display the event on screen - so translate
QString name = gettextFromC::tr(ev->name.c_str()); QString name = gettextFromC::tr(ev.name.c_str());
int value = ev->value; int value = ev.value;
int type = ev->type; int type = ev.type;
if (event_is_gaschange(ev)) { if (event_is_gaschange(ev)) {
struct icd_data icd_data; struct icd_data icd_data;
@ -137,8 +128,8 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix)
name += gasname(mix); name += gasname(mix);
/* Do we have an explicit cylinder index? Show it. */ /* Do we have an explicit cylinder index? Show it. */
if (ev->gas.index >= 0) if (ev.gas.index >= 0)
name += tr(" (cyl. %1)").arg(ev->gas.index + 1); name += tr(" (cyl. %1)").arg(ev.gas.index + 1);
bool icd = isobaric_counterdiffusion(lastgasmix, mix, &icd_data); bool icd = isobaric_counterdiffusion(lastgasmix, mix, &icd_data);
if (icd_data.dHe < 0) { if (icd_data.dHe < 0) {
name += qasprintf_loc("\n%s %s:%+.3g%% %s:%+.3g%%%s%+.3g%%", name += qasprintf_loc("\n%s %s:%+.3g%% %s:%+.3g%%%s%+.3g%%",
@ -147,25 +138,25 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix)
qPrintable(tr("ΔN₂")), icd_data.dN2 / 10.0, qPrintable(tr("ΔN₂")), icd_data.dN2 / 10.0,
icd ? ">" : "<", lrint(-icd_data.dHe / 5.0) / 10.0); icd ? ">" : "<", lrint(-icd_data.dHe / 5.0) / 10.0);
} }
} else if (ev->name == "modechange") { } else if (ev.name == "modechange") {
name += QString(": %1").arg(gettextFromC::tr(divemode_text_ui[ev->value])); name += QString(": %1").arg(gettextFromC::tr(divemode_text_ui[ev.value]));
} else if (value) { } else if (value) {
if (type == SAMPLE_EVENT_PO2 && ev->name == "SP change") { if (type == SAMPLE_EVENT_PO2 && ev.name == "SP change") {
name += QString(": %1bar").arg((double)value / 1000, 0, 'f', 1); name += QString(": %1bar").arg((double)value / 1000, 0, 'f', 1);
} else if (type == SAMPLE_EVENT_CEILING && ev->name == "planned waypoint above ceiling") { } else if (type == SAMPLE_EVENT_CEILING && ev.name == "planned waypoint above ceiling") {
const char *depth_unit; const char *depth_unit;
double depth_value = get_depth_units(value*1000, NULL, &depth_unit); double depth_value = get_depth_units(value*1000, NULL, &depth_unit);
name += QString(": %1%2").arg((int) round(depth_value)).arg(depth_unit); name += QString(": %1%2").arg((int) round(depth_value)).arg(depth_unit);
} else { } else {
name += QString(": %1").arg(value); name += QString(": %1").arg(value);
} }
} else if (type == SAMPLE_EVENT_PO2 && ev->name == "SP change") { } else if (type == SAMPLE_EVENT_PO2 && ev.name == "SP change") {
// this is a bad idea - we are abusing an existing event type that is supposed to // this is a bad idea - we are abusing an existing event type that is supposed to
// warn of high or low pO₂ and are turning it into a setpoint change event // warn of high or low pO₂ and are turning it into a setpoint change event
name += ":\n" + tr("Manual switch to OC"); name += ":\n" + tr("Manual switch to OC");
} else { } else {
name += ev->flags & SAMPLE_FLAGS_BEGIN ? tr(" begin", "Starts with space!") : name += ev.flags & SAMPLE_FLAGS_BEGIN ? tr(" begin", "Starts with space!") :
ev->flags & SAMPLE_FLAGS_END ? tr(" end", "Starts with space!") : ""; ev.flags & SAMPLE_FLAGS_END ? tr(" end", "Starts with space!") : "";
} }
setToolTip(QString("<img height=\"16\" src=\":status-warning-icon\">&nbsp; ") + name); setToolTip(QString("<img height=\"16\" src=\":status-warning-icon\">&nbsp; ") + name);
} }
@ -188,31 +179,31 @@ static int depthAtTime(const plot_info &pi, duration_t time)
} }
bool DiveEventItem::isInteresting(const struct dive *d, const struct divecomputer *dc, bool DiveEventItem::isInteresting(const struct dive *d, const struct divecomputer *dc,
const struct event *ev, const plot_info &pi, const struct event &ev, const plot_info &pi,
int firstSecond, int lastSecond) int firstSecond, int lastSecond)
{ {
/* /*
* Ignore items outside of plot range * Ignore items outside of plot range
*/ */
if (ev->time.seconds < firstSecond || ev->time.seconds >= lastSecond) if (ev.time.seconds < firstSecond || ev.time.seconds >= lastSecond)
return false; return false;
/* /*
* Some gas change events are special. Some dive computers just tell us the initial gas this way. * Some gas change events are special. Some dive computers just tell us the initial gas this way.
* Don't bother showing those * Don't bother showing those
*/ */
if (ev->name == "gaschange" && if (ev.name == "gaschange" &&
(ev->time.seconds == 0 || (ev.time.seconds == 0 ||
(!dc->samples.empty() && ev->time.seconds == dc->samples[0].time.seconds) || (!dc->samples.empty() && ev.time.seconds == dc->samples[0].time.seconds) ||
depthAtTime(pi, ev->time) < SURFACE_THRESHOLD)) depthAtTime(pi, ev.time) < SURFACE_THRESHOLD))
return false; return false;
/* /*
* Some divecomputers give "surface" events that just aren't interesting. * Some divecomputers give "surface" events that just aren't interesting.
* Like at the beginning or very end of a dive. Well, duh. * Like at the beginning or very end of a dive. Well, duh.
*/ */
if (ev->name == "surface") { if (ev.name == "surface") {
int time = ev->time.seconds; int time = ev.time.seconds;
if (time <= 30 || time + 30 >= (int)dc->duration.seconds) if (time <= 30 || time + 30 >= (int)dc->duration.seconds)
return false; return false;
} }
@ -221,15 +212,12 @@ bool DiveEventItem::isInteresting(const struct dive *d, const struct divecompute
void DiveEventItem::recalculatePos() void DiveEventItem::recalculatePos()
{ {
if (!ev)
return;
if (depth == DEPTH_NOT_FOUND) { if (depth == DEPTH_NOT_FOUND) {
hide(); hide();
return; return;
} }
setVisible(!ev->hidden && !is_event_type_hidden(ev)); setVisible(!ev.hidden && !is_event_type_hidden(&ev));
double x = hAxis->posAtValue(ev->time.seconds); double x = hAxis->posAtValue(ev.time.seconds);
double y = vAxis->posAtValue(depth); double y = vAxis->posAtValue(depth);
setPos(x, y); setPos(x, y);
} }

View file

@ -3,6 +3,7 @@
#define DIVEEVENTITEM_H #define DIVEEVENTITEM_H
#include "divepixmapitem.h" #include "divepixmapitem.h"
#include "core/event.h"
class DiveCartesianAxis; class DiveCartesianAxis;
class DivePixmaps; class DivePixmaps;
@ -12,17 +13,15 @@ struct plot_info;
class DiveEventItem : public DivePixmapItem { class DiveEventItem : public DivePixmapItem {
Q_OBJECT Q_OBJECT
public: public:
DiveEventItem(const struct dive *d, struct event *ev, struct gasmix lastgasmix, DiveEventItem(const struct dive *d, int idx, const struct event &ev, struct gasmix lastgasmix,
const struct plot_info &pi, DiveCartesianAxis *hAxis, DiveCartesianAxis *vAxis, const struct plot_info &pi, DiveCartesianAxis *hAxis, DiveCartesianAxis *vAxis,
int speed, const DivePixmaps &pixmaps, QGraphicsItem *parent = nullptr); int speed, const DivePixmaps &pixmaps, QGraphicsItem *parent = nullptr);
~DiveEventItem(); ~DiveEventItem();
const struct event *getEvent() const;
struct event *getEventMutable();
void eventVisibilityChanged(const QString &eventName, bool visible); void eventVisibilityChanged(const QString &eventName, bool visible);
void setVerticalAxis(DiveCartesianAxis *axis, int speed); void setVerticalAxis(DiveCartesianAxis *axis, int speed);
void setHorizontalAxis(DiveCartesianAxis *axis); void setHorizontalAxis(DiveCartesianAxis *axis);
static bool isInteresting(const struct dive *d, const struct divecomputer *dc, static bool isInteresting(const struct dive *d, const struct divecomputer *dc,
const struct event *ev, const struct plot_info &pi, const struct event &ev, const struct plot_info &pi,
int firstSecond, int lastSecond); int firstSecond, int lastSecond);
private: private:
@ -31,7 +30,9 @@ private:
void recalculatePos(); void recalculatePos();
DiveCartesianAxis *vAxis; DiveCartesianAxis *vAxis;
DiveCartesianAxis *hAxis; DiveCartesianAxis *hAxis;
struct event *ev; public:
int idx;
struct event ev;
const struct dive *dive; const struct dive *dive;
int depth; int depth;
}; };

View file

@ -2,6 +2,7 @@
#include "divepercentageitem.h" #include "divepercentageitem.h"
#include "divecartesianaxis.h" #include "divecartesianaxis.h"
#include "core/dive.h" #include "core/dive.h"
#include "core/event.h"
#include "core/profile.h" #include "core/profile.h"
#include <array> #include <array>
@ -105,7 +106,7 @@ void DivePercentageItem::replot(const dive *d, const struct divecomputer *dc, co
int x = 0; int x = 0;
QRgb *scanline = (QRgb *)img.scanLine(line); QRgb *scanline = (QRgb *)img.scanLine(line);
QRgb color = 0; QRgb color = 0;
const struct event *ev = NULL; gasmix_loop loop(*d, *dc);
for (int i = 0; i < pi.nr; i++) { for (int i = 0; i < pi.nr; i++) {
const plot_data &item = pi.entry[i]; const plot_data &item = pi.entry[i];
int sec = item.sec; int sec = item.sec;
@ -114,7 +115,7 @@ void DivePercentageItem::replot(const dive *d, const struct divecomputer *dc, co
continue; continue;
double value = item.percentages[tissue]; double value = item.percentages[tissue];
struct gasmix gasmix = get_gasmix(d, dc, sec, &ev, gasmix_air); struct gasmix gasmix = loop.next(sec);
int inert = get_n2(gasmix) + get_he(gasmix); int inert = get_n2(gasmix) + get_he(gasmix);
color = colorScale(value, inert); color = colorScale(value, inert);
if (nextX >= width) if (nextX >= width)

View file

@ -14,6 +14,7 @@
#include "core/pref.h" #include "core/pref.h"
#include "core/profile.h" #include "core/profile.h"
#include "core/qthelper.h" // for decoMode() #include "core/qthelper.h" // for decoMode()
#include "core/range.h"
#include "core/subsurface-float.h" #include "core/subsurface-float.h"
#include "core/subsurface-string.h" #include "core/subsurface-string.h"
#include "core/settings/qPrefDisplay.h" #include "core/settings/qPrefDisplay.h"
@ -550,23 +551,20 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM
// while all other items are up there on the constructor. // while all other items are up there on the constructor.
qDeleteAll(eventItems); qDeleteAll(eventItems);
eventItems.clear(); eventItems.clear();
struct event *event = currentdc->events; struct gasmix lastgasmix = get_gasmix_at_time(*d, *currentdc, duration_t{1});
struct gasmix lastgasmix = get_gasmix_at_time(d, currentdc, duration_t{1});
while (event) { for (auto [idx, event]: enumerated_range(currentdc->events)) {
// if print mode is selected only draw headings, SP change, gas events or bookmark event // if print mode is selected only draw headings, SP change, gas events or bookmark event
if (printMode) { if (printMode) {
if (event->name.empty() || if (event.name.empty() ||
!(event->name == "heading" || !(event.name == "heading" ||
(event->name == "SP change" && event->time.seconds == 0) || (event.name == "SP change" && event.time.seconds == 0) ||
event_is_gaschange(event) || event_is_gaschange(event) ||
event->type == SAMPLE_EVENT_BOOKMARK)) { event.type == SAMPLE_EVENT_BOOKMARK))
event = event->next;
continue; continue;
}
} }
if (DiveEventItem::isInteresting(d, currentdc, event, plotInfo, firstSecond, lastSecond)) { if (DiveEventItem::isInteresting(d, currentdc, event, plotInfo, firstSecond, lastSecond)) {
auto item = new DiveEventItem(d, event, lastgasmix, plotInfo, auto item = new DiveEventItem(d, idx, event, lastgasmix, plotInfo,
timeAxis, profileYAxis, animSpeed, *pixmaps); timeAxis, profileYAxis, animSpeed, *pixmaps);
item->setZValue(2); item->setZValue(2);
addItem(item); addItem(item);
@ -574,7 +572,6 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM
} }
if (event_is_gaschange(event)) if (event_is_gaschange(event))
lastgasmix = get_gasmix_from_event(d, event); lastgasmix = get_gasmix_from_event(d, event);
event = event->next;
} }
QString dcText = QString::fromStdString(get_dc_nickname(currentdc)); QString dcText = QString::fromStdString(get_dc_nickname(currentdc));

View file

@ -559,8 +559,8 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
DiveEventItem *item = dynamic_cast<DiveEventItem *>(sceneItem); DiveEventItem *item = dynamic_cast<DiveEventItem *>(sceneItem);
// Add or edit Gas Change // Add or edit Gas Change
if (d && item && event_is_gaschange(item->getEvent())) { if (d && item && event_is_gaschange(item->ev)) {
int eventTime = item->getEvent()->time.seconds; int eventTime = item->ev.time.seconds;
QMenu *gasChange = m.addMenu(tr("Edit Gas Change")); QMenu *gasChange = m.addMenu(tr("Edit Gas Change"));
for (int i = 0; i < d->cylinders.nr; i++) { for (int i = 0; i < d->cylinders.nr; i++) {
const cylinder_t *cylinder = get_cylinder(d, i); const cylinder_t *cylinder = get_cylinder(d, i);
@ -579,10 +579,9 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
m.addAction(tr("Add setpoint change"), [this, seconds]() { ProfileWidget2::addSetpointChange(seconds); }); m.addAction(tr("Add setpoint change"), [this, seconds]() { ProfileWidget2::addSetpointChange(seconds); });
m.addAction(tr("Add bookmark"), [this, seconds]() { addBookmark(seconds); }); m.addAction(tr("Add bookmark"), [this, seconds]() { addBookmark(seconds); });
m.addAction(tr("Split dive into two"), [this, seconds]() { splitDive(seconds); }); m.addAction(tr("Split dive into two"), [this, seconds]() { splitDive(seconds); });
const struct event *ev = NULL;
enum divemode_t divemode = UNDEF_COMP_TYPE;
get_current_divemode(get_dive_dc_const(d, dc), seconds, &ev, &divemode); divemode_loop loop(*get_dive_dc_const(d, dc));
divemode_t divemode = loop.next(seconds);
QMenu *changeMode = m.addMenu(tr("Change divemode")); QMenu *changeMode = m.addMenu(tr("Change divemode"));
if (divemode != OC) if (divemode != OC)
changeMode->addAction(gettextFromC::tr(divemode_text_ui[OC]), changeMode->addAction(gettextFromC::tr(divemode_text_ui[OC]),
@ -595,23 +594,22 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
[this, seconds](){ addDivemodeSwitch(seconds, PSCR); }); [this, seconds](){ addDivemodeSwitch(seconds, PSCR); });
if (DiveEventItem *item = dynamic_cast<DiveEventItem *>(sceneItem)) { if (DiveEventItem *item = dynamic_cast<DiveEventItem *>(sceneItem)) {
const struct event *dcEvent = item->getEvent();
m.addAction(tr("Remove event"), [this,item] { removeEvent(item); }); m.addAction(tr("Remove event"), [this,item] { removeEvent(item); });
m.addAction(tr("Hide event"), [this, item] { hideEvent(item); }); m.addAction(tr("Hide event"), [this, item] { hideEvent(item); });
m.addAction(tr("Hide events of type '%1'").arg(event_type_name(dcEvent)), m.addAction(tr("Hide events of type '%1'").arg(event_type_name(item->ev)),
[this, item] { hideEventType(item); }); [this, item] { hideEventType(item); });
if (dcEvent->type == SAMPLE_EVENT_BOOKMARK) if (item->ev.type == SAMPLE_EVENT_BOOKMARK)
m.addAction(tr("Edit name"), [this, item] { editName(item); }); m.addAction(tr("Edit name"), [this, item] { editName(item); });
#if 0 // TODO::: FINISH OR DISABLE #if 0 // TODO::: FINISH OR DISABLE
QPointF scenePos = mapToScene(event->pos()); QPointF scenePos = mapToScene(event->pos());
int idx = getEntryFromPos(scenePos); int idx = getEntryFromPos(scenePos);
// this shows how to figure out if we should ask the user if they want adjust interpolated pressures // this shows how to figure out if we should ask the user if they want adjust interpolated pressures
// at either side of a gas change // at either side of a gas change
if (dcEvent->type == SAMPLE_EVENT_GASCHANGE || dcEvent->type == SAMPLE_EVENT_GASCHANGE2) { if (item->ev->type == SAMPLE_EVENT_GASCHANGE || item->ev->type == SAMPLE_EVENT_GASCHANGE2) {
int gasChangeIdx = idx; int gasChangeIdx = idx;
while (gasChangeIdx > 0) { while (gasChangeIdx > 0) {
--gasChangeIdx; --gasChangeIdx;
if (plotInfo.entry[gasChangeIdx].sec <= dcEvent->time.seconds) if (plotInfo.entry[gasChangeIdx].sec <= item->ev->time.seconds)
break; break;
} }
const struct plot_data &gasChangeEntry = plotInfo.entry[newGasIdx]; const struct plot_data &gasChangeEntry = plotInfo.entry[newGasIdx];
@ -650,8 +648,9 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
} }
m2->addAction(tr("All event types"), this, &ProfileWidget2::unhideEventTypes); m2->addAction(tr("All event types"), this, &ProfileWidget2::unhideEventTypes);
} }
if (std::any_of(profileScene->eventItems.begin(), profileScene->eventItems.end(), const struct divecomputer *currentdc = get_dive_dc_const(d, dc);
[] (const DiveEventItem *item) { return item->getEvent()->hidden; })) if (currentdc && std::any_of(currentdc->events.begin(), currentdc->events.end(),
[] (auto &ev) { return ev.hidden; }))
m.addAction(tr("Unhide individually hidden events of this dive"), this, &ProfileWidget2::unhideEvents); m.addAction(tr("Unhide individually hidden events of this dive"), this, &ProfileWidget2::unhideEvents);
m.exec(event->globalPos()); m.exec(event->globalPos());
} }
@ -690,16 +689,18 @@ void ProfileWidget2::renameCurrentDC()
void ProfileWidget2::hideEvent(DiveEventItem *item) void ProfileWidget2::hideEvent(DiveEventItem *item)
{ {
item->getEventMutable()->hidden = true; struct divecomputer *currentdc = get_dive_dc(mutable_dive(), dc);
int idx = item->idx;
if (!currentdc || idx < 0 || static_cast<size_t>(idx) >= currentdc->events.size())
return;
currentdc->events[idx].hidden = true;
item->hide(); item->hide();
} }
void ProfileWidget2::hideEventType(DiveEventItem *item) void ProfileWidget2::hideEventType(DiveEventItem *item)
{ {
const struct event *event = item->getEvent(); if (!item->ev.name.empty()) {
hide_event_type(&item->ev);
if (!event->name.empty()) {
hide_event_type(event);
replot(); replot();
} }
@ -707,10 +708,13 @@ void ProfileWidget2::hideEventType(DiveEventItem *item)
void ProfileWidget2::unhideEvents() void ProfileWidget2::unhideEvents()
{ {
for (DiveEventItem *item: profileScene->eventItems) { struct divecomputer *currentdc = get_dive_dc(mutable_dive(), dc);
item->getEventMutable()->hidden = false; if (!currentdc)
return;
for (auto &ev: currentdc->events)
ev.hidden = false;
for (DiveEventItem *item: profileScene->eventItems)
item->show(); item->show();
}
} }
void ProfileWidget2::unhideEventTypes() void ProfileWidget2::unhideEventTypes()
@ -722,15 +726,12 @@ void ProfileWidget2::unhideEventTypes()
void ProfileWidget2::removeEvent(DiveEventItem *item) void ProfileWidget2::removeEvent(DiveEventItem *item)
{ {
struct event *event = item->getEventMutable(); const struct event &ev = item->ev;
if (!event || !d)
return;
if (QMessageBox::question(this, TITLE_OR_TEXT( if (QMessageBox::question(this, TITLE_OR_TEXT(
tr("Remove the selected event?"), tr("Remove the selected event?"),
tr("%1 @ %2:%3").arg(QString::fromStdString(event->name)).arg(event->time.seconds / 60).arg(event->time.seconds % 60, 2, 10, QChar('0'))), tr("%1 @ %2:%3").arg(QString::fromStdString(ev.name)).arg(ev.time.seconds / 60).arg(ev.time.seconds % 60, 2, 10, QChar('0'))),
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok)
Command::removeEvent(mutable_dive(), dc, event); Command::removeEvent(mutable_dive(), dc, item->idx);
} }
void ProfileWidget2::addBookmark(int seconds) void ProfileWidget2::addBookmark(int seconds)
@ -781,13 +782,12 @@ void ProfileWidget2::changeGas(int index, int newCylinderId)
void ProfileWidget2::editName(DiveEventItem *item) void ProfileWidget2::editName(DiveEventItem *item)
{ {
struct event *event = item->getEventMutable(); if (!d)
if (!event || !d)
return; return;
bool ok; bool ok;
QString newName = QInputDialog::getText(this, tr("Edit name of bookmark"), QString newName = QInputDialog::getText(this, tr("Edit name of bookmark"),
tr("Custom name:"), QLineEdit::Normal, tr("Custom name:"), QLineEdit::Normal,
event->name.c_str(), &ok); item->ev.name.c_str(), &ok);
if (ok && !newName.isEmpty()) { if (ok && !newName.isEmpty()) {
if (newName.length() > 22) { //longer names will display as garbage. if (newName.length() > 22) { //longer names will display as garbage.
QMessageBox lengthWarning; QMessageBox lengthWarning;
@ -795,7 +795,7 @@ void ProfileWidget2::editName(DiveEventItem *item)
lengthWarning.exec(); lengthWarning.exec();
return; return;
} }
Command::renameEvent(mutable_dive(), dc, event, qPrintable(newName)); Command::renameEvent(mutable_dive(), dc, item->idx, newName.toStdString());
} }
} }

View file

@ -80,17 +80,19 @@ void TankItem::setData(const struct dive *d, const struct divecomputer *dc, int
return; return;
// start with the first gasmix and at the start of the plotted range // start with the first gasmix and at the start of the plotted range
const struct event *ev = NULL; event_loop loop("gaschange");
struct gasmix gasmix = gasmix_air; struct gasmix gasmix = gasmix_invalid;
gasmix = get_gasmix(d, dc, plotStartTime, &ev, gasmix); const struct event *ev;
while ((ev = loop.next(*dc)) != nullptr && ev->time.seconds <= plotStartTime)
gasmix = get_gasmix_from_event(d, *ev);
// work through all the gas changes and add the rectangle for each gas while it was used // work through all the gas changes and add the rectangle for each gas while it was used
int startTime = plotStartTime; int startTime = plotStartTime;
while (ev && (int)ev->time.seconds < plotEndTime) { while (ev && (int)ev->time.seconds < plotEndTime) {
createBar(startTime, ev->time.seconds, gasmix); createBar(startTime, ev->time.seconds, gasmix);
startTime = ev->time.seconds; startTime = ev->time.seconds;
gasmix = get_gasmix_from_event(d, ev); gasmix = get_gasmix_from_event(d, *ev);
ev = get_next_event(ev->next, "gaschange"); ev = loop.next(*dc);
} }
createBar(startTime, plotEndTime, gasmix); createBar(startTime, plotEndTime, gasmix);
} }

View file

@ -3,6 +3,7 @@
#include "core/dive.h" #include "core/dive.h"
#include "core/divelist.h" #include "core/divelist.h"
#include "core/divelog.h" #include "core/divelog.h"
#include "core/event.h"
#include "core/subsurface-string.h" #include "core/subsurface-string.h"
#include "qt-models/cylindermodel.h" #include "qt-models/cylindermodel.h"
#include "qt-models/models.h" // For defaultModelFont(). #include "qt-models/models.h" // For defaultModelFont().
@ -119,8 +120,6 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn)
int samplecount = 0; int samplecount = 0;
o2pressure_t last_sp; o2pressure_t last_sp;
struct divecomputer *dc = get_dive_dc(d, dcNr); struct divecomputer *dc = get_dive_dc(d, dcNr);
const struct event *evd = NULL;
enum divemode_t current_divemode = UNDEF_COMP_TYPE;
cylinders.updateDive(d, dcNr); cylinders.updateDive(d, dcNr);
duration_t lasttime; duration_t lasttime;
duration_t lastrecordedtime; duration_t lastrecordedtime;
@ -150,6 +149,7 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn)
int cylinderid = 0; int cylinderid = 0;
last_sp.mbar = 0; last_sp.mbar = 0;
divemode_loop loop(*dc);
for (int i = 0; i < plansamples - 1; i++) { for (int i = 0; i < plansamples - 1; i++) {
if (dc->last_manual_time.seconds && dc->last_manual_time.seconds > 120 && lasttime.seconds >= dc->last_manual_time.seconds) if (dc->last_manual_time.seconds && dc->last_manual_time.seconds > 120 && lasttime.seconds >= dc->last_manual_time.seconds)
break; break;
@ -171,7 +171,7 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn)
if (newtime.seconds - lastrecordedtime.seconds > 10 || cylinderid == get_cylinderid_at_time(d, dc, nexttime)) { if (newtime.seconds - lastrecordedtime.seconds > 10 || cylinderid == get_cylinderid_at_time(d, dc, nexttime)) {
if (newtime.seconds == lastrecordedtime.seconds) if (newtime.seconds == lastrecordedtime.seconds)
newtime.seconds += 10; newtime.seconds += 10;
current_divemode = get_current_divemode(dc, newtime.seconds - 1, &evd, &current_divemode); divemode_t current_divemode = loop.next(newtime.seconds - 1);
addStop(depthsum / samplecount, newtime.seconds, cylinderid, last_sp.mbar, true, current_divemode); addStop(depthsum / samplecount, newtime.seconds, cylinderid, last_sp.mbar, true, current_divemode);
lastrecordedtime = newtime; lastrecordedtime = newtime;
} }
@ -181,7 +181,7 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn)
} }
} }
// make sure we get the last point right so the duration is correct // make sure we get the last point right so the duration is correct
current_divemode = get_current_divemode(dc, dc->duration.seconds, &evd, &current_divemode); divemode_t current_divemode = loop.next(dc->duration.seconds);
if (!hasMarkedSamples && !dc->last_manual_time.seconds) if (!hasMarkedSamples && !dc->last_manual_time.seconds)
addStop(0, dc->duration.seconds,cylinderid, last_sp.mbar, true, current_divemode); addStop(0, dc->duration.seconds,cylinderid, last_sp.mbar, true, current_divemode);
preserved_until = d->duration; preserved_until = d->duration;

View file

@ -728,16 +728,12 @@ static void smtk_parse_other(struct dive *dive, const std::vector<std::string> &
* Returns a pointer to a bookmark event in an event list if it exists for * Returns a pointer to a bookmark event in an event list if it exists for
* a given time. Return NULL otherwise. * a given time. Return NULL otherwise.
*/ */
static struct event *find_bookmark(struct event *orig, int t) static struct event *find_bookmark(struct divecomputer &dc, int t)
{ {
struct event *ev = orig; auto it = std::find_if(dc.events.begin(), dc.events.end(),
[t](auto &ev)
while (ev) { { return ev.time.seconds == t && ev.type == SAMPLE_EVENT_BOOKMARK; });
if ((ev->time.seconds == t) && (ev->type == SAMPLE_EVENT_BOOKMARK)) return it != dc.events.end() ? &*it : nullptr;
return ev;
ev = ev->next;
}
return NULL;
} }
/* /*
@ -763,7 +759,7 @@ static void smtk_parse_bookmarks(MdbHandle *mdb, struct dive *d, char *dive_idx)
if (same_string(table.get_data(0), dive_idx)) { if (same_string(table.get_data(0), dive_idx)) {
time = lrint(strtod(table.get_data(4), NULL) * 60); time = lrint(strtod(table.get_data(4), NULL) * 60);
const char *tmp = table.get_data(2); const char *tmp = table.get_data(2);
ev = find_bookmark(d->dc.events, time); ev = find_bookmark(d->dc, time);
if (ev) if (ev)
ev->name = tmp; ev->name = tmp;
else else

View file

@ -496,14 +496,14 @@ void TestPlan::testMetric()
while (!dp->minimum_gas.mbar && dp->next) while (!dp->minimum_gas.mbar && dp->next)
dp = dp->next; dp = dp->next;
QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 148l); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 148l);
QVERIFY(dive.dc.events.size() >= 2);
// check first gas change to EAN36 at 33m // check first gas change to EAN36 at 33m
struct event *ev = dive.dc.events; struct event *ev = &dive.dc.events[0];
QVERIFY(ev != NULL);
QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->gas.index, 1);
QCOMPARE(ev->value, 36); QCOMPARE(ev->value, 36);
QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 33000); QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 33000);
// check second gas change to Oxygen at 6m // check second gas change to Oxygen at 6m
ev = ev->next; ev = &dive.dc.events[1];
QVERIFY(ev != NULL); QVERIFY(ev != NULL);
QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->gas.index, 2);
QCOMPARE(ev->value, 100); QCOMPARE(ev->value, 100);
@ -537,14 +537,15 @@ void TestPlan::testImperial()
while (!dp->minimum_gas.mbar && dp->next) while (!dp->minimum_gas.mbar && dp->next)
dp = dp->next; dp = dp->next;
QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l);
QVERIFY(dive.dc.events.size() >= 2);
// check first gas change to EAN36 at 33m // check first gas change to EAN36 at 33m
struct event *ev = dive.dc.events; struct event *ev = &dive.dc.events[0];
QVERIFY(ev != NULL); QVERIFY(ev != NULL);
QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->gas.index, 1);
QCOMPARE(ev->value, 36); QCOMPARE(ev->value, 36);
QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 33528); QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 33528);
// check second gas change to Oxygen at 6m // check second gas change to Oxygen at 6m
ev = ev->next; ev = &dive.dc.events[1];
QVERIFY(ev != NULL); QVERIFY(ev != NULL);
QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->gas.index, 2);
QCOMPARE(ev->value, 100); QCOMPARE(ev->value, 100);
@ -669,8 +670,9 @@ void TestPlan::testVpmbMetric60m30minEan50()
QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l);
// print first ceiling // print first ceiling
printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001));
QVERIFY(dive.dc.events.size() >= 1);
// check first gas change to EAN50 at 21m // check first gas change to EAN50 at 21m
struct event *ev = dive.dc.events; struct event *ev = &dive.dc.events[0];
QVERIFY(ev != NULL); QVERIFY(ev != NULL);
QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->gas.index, 1);
QCOMPARE(ev->value, 50); QCOMPARE(ev->value, 50);
@ -706,7 +708,8 @@ void TestPlan::testVpmbMetric60m30minTx()
// print first ceiling // print first ceiling
printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001));
// check first gas change to EAN50 at 21m // check first gas change to EAN50 at 21m
struct event *ev = dive.dc.events; QVERIFY(dive.dc.events.size() >= 1);
struct event *ev = &dive.dc.events[0];
QVERIFY(ev != NULL); QVERIFY(ev != NULL);
QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->gas.index, 1);
QCOMPARE(ev->value, 50); QCOMPARE(ev->value, 50);
@ -741,14 +744,15 @@ void TestPlan::testVpmbMetric100m60min()
QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 157l); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 157l);
// print first ceiling // print first ceiling
printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001));
QVERIFY(dive.dc.events.size() >= 2);
// check first gas change to EAN50 at 21m // check first gas change to EAN50 at 21m
struct event *ev = dive.dc.events; struct event *ev = &dive.dc.events[0];
QVERIFY(ev != NULL); QVERIFY(ev != NULL);
QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->gas.index, 1);
QCOMPARE(ev->value, 50); QCOMPARE(ev->value, 50);
QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000); QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000);
// check second gas change to Oxygen at 6m // check second gas change to Oxygen at 6m
ev = ev->next; ev = &dive.dc.events[1];
QVERIFY(ev != NULL); QVERIFY(ev != NULL);
QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->gas.index, 2);
QCOMPARE(ev->value, 100); QCOMPARE(ev->value, 100);
@ -778,7 +782,7 @@ void TestPlan::testMultipleGases()
#endif #endif
gasmix gas; gasmix gas;
gas = get_gasmix_at_time(&dive, &dive.dc, {20 * 60 + 1}); gas = get_gasmix_at_time(dive, dive.dc, {20 * 60 + 1});
QCOMPARE(get_o2(gas), 110); QCOMPARE(get_o2(gas), 110);
QVERIFY(compareDecoTime(dive.dc.duration.seconds, 2480u, 2480u)); QVERIFY(compareDecoTime(dive.dc.duration.seconds, 2480u, 2480u));
} }
@ -839,14 +843,15 @@ void TestPlan::testVpmbMetric100m10min()
QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 175l); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 175l);
// print first ceiling // print first ceiling
printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001));
QVERIFY(dive.dc.events.size() >= 2);
// check first gas change to EAN50 at 21m // check first gas change to EAN50 at 21m
struct event *ev = dive.dc.events; struct event *ev = &dive.dc.events[0];
QVERIFY(ev != NULL); QVERIFY(ev != NULL);
QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->gas.index, 1);
QCOMPARE(ev->value, 50); QCOMPARE(ev->value, 50);
QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000); QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000);
// check second gas change to Oxygen at 6m // check second gas change to Oxygen at 6m
ev = ev->next; ev = &dive.dc.events[1];
QVERIFY(ev != NULL); QVERIFY(ev != NULL);
QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->gas.index, 2);
QCOMPARE(ev->value, 100); QCOMPARE(ev->value, 100);
@ -906,20 +911,21 @@ void TestPlan::testVpmbMetricRepeat()
QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 80l); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 80l);
// print first ceiling // print first ceiling
printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001));
QVERIFY(dive.dc.events.size() >= 3);
// check first gas change to 21/35 at 66m // check first gas change to 21/35 at 66m
struct event *ev = dive.dc.events; struct event *ev = &dive.dc.events[0];
QVERIFY(ev != NULL); QVERIFY(ev != NULL);
QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->gas.index, 1);
QCOMPARE(ev->gas.mix.o2.permille, 210); QCOMPARE(ev->gas.mix.o2.permille, 210);
QCOMPARE(ev->gas.mix.he.permille, 350); QCOMPARE(ev->gas.mix.he.permille, 350);
QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 66000); QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 66000);
// check second gas change to EAN50 at 21m // check second gas change to EAN50 at 21m
ev = ev->next; ev = &dive.dc.events[1];
QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->gas.index, 2);
QCOMPARE(ev->value, 50); QCOMPARE(ev->value, 50);
QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000); QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000);
// check third gas change to Oxygen at 6m // check third gas change to Oxygen at 6m
ev = ev->next; ev = &dive.dc.events[2];
QVERIFY(ev != NULL); QVERIFY(ev != NULL);
QCOMPARE(ev->gas.index, 3); QCOMPARE(ev->gas.index, 3);
QCOMPARE(ev->value, 100); QCOMPARE(ev->value, 100);