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));
}
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)

View file

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

View file

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

View file

@ -2,7 +2,6 @@
#include "command_event.h"
#include "core/dive.h"
#include "core/event.h"
#include "core/selection.h"
#include "core/subsurface-qt/divelistnotifier.h"
#include "core/libdivecomputer.h"
@ -35,8 +34,8 @@ void EventBase::updateDive()
setSelection({ d }, d, dcNr);
}
AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr),
eventToAdd(ev)
AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event ev) : EventBase(d, dcNr),
ev(std::move(ev))
{
}
@ -48,32 +47,29 @@ bool AddEventBase::workToBeDone()
void AddEventBase::redoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
eventToRemove = eventToAdd.get();
add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend
idx = add_event_to_dc(dc, ev); // return ownership to backend
}
void AddEventBase::undoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
remove_event_from_dc(dc, eventToRemove);
eventToAdd.reset(eventToRemove); // take ownership of event
eventToRemove = nullptr;
ev = remove_event_from_dc(dc, idx);
}
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"));
}
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])));
}
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)
{
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);
}
RenameEvent::RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name) : EventBase(d, dcNr),
eventToAdd(clone_event_rename(ev, name)),
eventToRemove(ev)
RenameEvent::RenameEvent(struct dive *d, int dcNr, int idx, const std::string name) : EventBase(d, dcNr),
idx(idx),
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()
@ -106,23 +102,24 @@ bool RenameEvent::workToBeDone()
void RenameEvent::redoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
swap_event(dc, eventToRemove, eventToAdd.get());
event *tmp = eventToRemove;
eventToRemove = eventToAdd.release();
eventToAdd.reset(tmp);
event *ev = get_event(dc, idx);
if (ev)
std::swap(ev->name, name);
}
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();
}
RemoveEvent::RemoveEvent(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr),
eventToRemove(ev),
cylinder(ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE ?
ev->gas.index : -1)
RemoveEvent::RemoveEvent(struct dive *d, int dcNr, int idx) : EventBase(d, dcNr),
idx(idx), cylinder(-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()));
}
@ -134,16 +131,13 @@ bool RemoveEvent::workToBeDone()
void RemoveEvent::redoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
remove_event_from_dc(dc, eventToRemove);
eventToAdd.reset(eventToRemove); // take ownership of event
eventToRemove = nullptr;
ev = remove_event_from_dc(dc, idx);
}
void RemoveEvent::undoit()
{
struct divecomputer *dc = get_dive_dc(d, dcNr);
eventToRemove = eventToAdd.get();
add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend
idx = add_event_to_dc(dc, std::move(ev));
}
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
// support that anyway.
struct divecomputer *dc = get_dive_dc(d, dcNr);
struct event *gasChangeEvent = dc->events;
while ((gasChangeEvent = get_next_event(gasChangeEvent, "gaschange")) != NULL) {
if (gasChangeEvent->time.seconds == seconds) {
eventsToRemove.push_back(gasChangeEvent);
int idx = gasChangeEvent->gas.index;
if (std::find(cylinders.begin(), cylinders.end(), idx) == cylinders.end())
cylinders.push_back(idx); // cylinders might have changed their status
}
gasChangeEvent = gasChangeEvent->next;
// Note that we remove events in reverse order so that the indexes don't change
// meaning while removing. This should be an extremely rare case anyway.
for (int idx = static_cast<int>(dc->events.size()) - 1; idx > 0; --idx) {
const event &ev = dc->events[idx];
if (ev.time.seconds == seconds && ev.name == "gaschange")
eventsToRemove.push_back(idx);
if (std::find(cylinders.begin(), cylinders.end(), ev.gas.index) == cylinders.end())
cylinders.push_back(ev.gas.index); // cylinders might have changed their status
}
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()
@ -186,20 +180,21 @@ bool AddGasSwitch::workToBeDone()
void AddGasSwitch::redoit()
{
std::vector<std::unique_ptr<event>> newEventsToAdd;
std::vector<event *> newEventsToRemove;
std::vector<event> newEventsToAdd;
std::vector<int> newEventsToRemove;
newEventsToAdd.reserve(eventsToRemove.size());
newEventsToRemove.reserve(eventsToAdd.size());
struct divecomputer *dc = get_dive_dc(d, dcNr);
for (event *ev: eventsToRemove) {
remove_event_from_dc(dc, ev);
newEventsToAdd.emplace_back(ev); // take ownership of event
}
for (auto &ev: eventsToAdd) {
newEventsToRemove.push_back(ev.get());
add_event_to_dc(dc, ev.release()); // return ownership to backend
}
for (int idx: eventsToRemove)
newEventsToAdd.push_back(remove_event_from_dc(dc, idx));
for (auto &ev: eventsToAdd)
newEventsToRemove.push_back(add_event_to_dc(dc, std::move(ev)));
// Make sure that events are removed in reverse order
std::sort(newEventsToRemove.begin(), newEventsToRemove.end(), std::greater<int>());
eventsToAdd = std::move(newEventsToAdd);
eventsToRemove = std::move(newEventsToRemove);

View file

@ -6,15 +6,12 @@
#include "command_base.h"
#include "core/divemode.h"
#include "core/event.h"
// We put everything in a namespace, so that we can shorten names without polluting the global namespace
namespace Command {
// Events are a strange thing: they contain there own description which means
// that on changing the description a new object must be allocated. Moreover,
// it means that these objects can't be collected in a table.
// Therefore, the undo commands work on events as they do with dives: using
// owning pointers. See comments in command_base.h
// Pointers to events are not stable, so we always store indexes.
class EventBase : public Base {
protected:
@ -25,8 +22,7 @@ protected:
virtual void undoit() = 0;
// 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
// are probably not stable.
// Pointers to divecomputers are not stable.
struct dive *d;
int dcNr;
private:
@ -35,15 +31,15 @@ private:
class AddEventBase : public EventBase {
public:
AddEventBase(struct dive *d, int dcNr, struct event *ev); // Takes ownership of event!
AddEventBase(struct dive *d, int dcNr, struct event ev); // Takes ownership of event!
protected:
void undoit() override;
void redoit() override;
private:
bool workToBeDone() override;
std::unique_ptr<event> eventToAdd; // for redo
event *eventToRemove; // for undo
struct event ev; // for redo
int idx; // for undo
};
class AddEventBookmark : public AddEventBase {
@ -67,28 +63,28 @@ private:
class RenameEvent : public EventBase {
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:
bool workToBeDone() override;
void undoit() override;
void redoit() override;
std::unique_ptr<event> eventToAdd; // for undo and redo
event *eventToRemove; // for undo and redo
int idx; // for undo and redo
std::string name; // for undo and redo
};
class RemoveEvent : public EventBase {
public:
RemoveEvent(struct dive *d, int dcNr, struct event *ev);
RemoveEvent(struct dive *d, int dcNr, int idx);
private:
bool workToBeDone() override;
void undoit() override;
void redoit() override;
void post() const; // Called to fix up dives should a gas-change have happened.
std::unique_ptr<event> eventToAdd; // for undo
event *eventToRemove; // for redo
int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch.
event ev; // for undo
int idx; // for redo
int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch.
};
class AddGasSwitch : public EventBase {
@ -100,8 +96,8 @@ private:
void redoit() override;
std::vector<int> cylinders; // cylinders that are modified
std::vector<std::unique_ptr<event>> eventsToAdd;
std::vector<event *> eventsToRemove;
std::vector<event> eventsToAdd;
std::vector<int> eventsToRemove;
};
} // 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 */
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 */
struct gasmix mix = get_cylinder(dive, idx)->gasmix;
int o2 = get_o2(mix);
int he = get_he(mix);
struct event *ev;
int value;
o2 = (o2 + 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");
ev->gas.index = idx;
ev->gas.mix = mix;
struct event ev(seconds, he ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE, 0, value, "gaschange");
ev.gas.index = idx;
ev.gas.mix = mix;
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);
return;
}
struct event *ev = create_gas_switch_event(dive, dc, seconds, idx);
add_event_to_dc(dc, ev);
struct event ev = create_gas_switch_event(dive, dc, seconds, idx);
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)) {
int index = ev->gas.index;
if (event_is_gaschange(ev)) {
int index = ev.gas.index;
// FIXME: The planner uses one past cylinder-count to signify "surface air". Remove in due course.
if (index == dive->cylinders.nr)
return gasmix_air;
if (index >= 0 && index < dive->cylinders.nr)
return get_cylinder(dive, index)->gasmix;
return ev->gas.mix;
return ev.gas.mix;
}
return gasmix_air;
}
@ -158,7 +156,6 @@ int dive_getUniqID()
static void copy_dc(const struct divecomputer *sdc, struct divecomputer *ddc)
{
*ddc = *sdc;
copy_events(sdc, ddc);
}
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[])
{
for (;;) {
copy_dc(sdc, ddc);
*ddc = *sdc;
dc_cylinder_renumber(d, ddc, cylinders_map);
if (!sdc->next)
break;
@ -243,14 +240,14 @@ void copy_dive(const struct dive *s, struct dive *d)
copy_dive_nodc(s, d);
// 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);
}
static void copy_dive_onedc(const struct dive *s, const struct divecomputer *sdc, struct dive *d)
{
copy_dive_nodc(s, d);
copy_dc(sdc, &d->dc);
d->dc = *sdc;
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)
return;
const struct event *ev;
ev = s->events;
while (ev != NULL) {
for (const auto &ev: s->events) {
// Don't add events the planner knows about
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);
ev = ev->next;
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);
}
}
@ -413,7 +407,6 @@ static bool has_unknown_used_cylinders(const struct dive *dive, const struct div
const bool used_cylinders[], int num)
{
int idx;
const struct event *ev;
auto used_and_unknown = std::make_unique<bool[]>(dive->cylinders.nr);
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 */
ev = get_next_event(dc->events, "gaschange");
while (ev && num > 0) {
idx = get_cylinder_index(dive, ev);
event_loop loop("gaschange");
const struct event *ev;
while ((ev = loop.next(*dc)) != nullptr && num > 0) {
idx = get_cylinder_index(dive, *ev);
if (idx >= 0 && used_and_unknown[idx]) {
used_and_unknown[idx] = false;
num--;
}
ev = get_next_event(ev->next, "gaschange");
}
return num > 0;
@ -494,7 +487,8 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i
}
if (dc->samples.empty())
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);
for (auto it = dc->samples.begin(); it != dc->samples.end(); ++it) {
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' */
while (ev && lasttime >= ev->time.seconds) {
idx = get_cylinder_index(dive, ev);
ev = get_next_event(ev->next, "gaschange");
idx = get_cylinder_index(dive, *ev);
ev = loop.next(*dc);
}
/* 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)
return -1;
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))
res = get_cylinder_index(dive, ev);
res = get_cylinder_index(dive, *ev);
else if (dc->divemode == CCR)
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)
{
struct event *ev;
int new_setpoint = 0;
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_
// So we make sure, this comes from a Predator or Petrel and we only remove
// pO2 values we would have computed anyway.
const struct event *ev = get_next_event(dc->events, "gaschange");
struct gasmix gasmix = get_gasmix_from_event(dive, ev);
const struct event *next = get_next_event(ev, "gaschange");
event_loop loop("gaschange");
const struct event *ev = loop.next(*dc);
struct gasmix gasmix = get_gasmix_from_event(dive, *ev);
const struct event *next = loop.next(*dc);
for (auto &sample: dc->samples) {
if (next && sample.time.seconds >= next->time.seconds) {
ev = next;
gasmix = get_gasmix_from_event(dive, ev);
next = get_next_event(ev, "gaschange");
gasmix = get_gasmix_from_event(dive, *ev);
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);
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
// 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
ev = get_next_event(dc->events, "SP change");
struct event *ev = get_first_event(*dc, "SP change");
if (ev && ev->time.seconds == 0) {
ev->value = new_setpoint;
} else {
@ -705,33 +699,17 @@ static void sanitize_cylinder_info(struct dive *dive)
}
/* 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;
if (event->name == "bookmark")
if (event.name == "bookmark")
return false;
if (event->name == "heading")
if (event.name == "heading")
return false;
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)
{
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
* seconds... that would be pretty pointless to plot the
* 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)
{
struct event *event;
std::vector<int> to_delete;
event = dc->events;
while (event) {
struct event *prev;
if (is_potentially_redundant(event)) {
prev = find_previous_event(dc, event);
if (prev && prev->value == event->value &&
prev->flags == event->flags &&
event->time.seconds - prev->time.seconds < 61)
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;
for (auto [idx, event]: enumerated_range(dc->events)) {
if (!is_potentially_redundant(event))
continue;
for (int idx2 = idx - 1; idx2 > 0; --idx2) {
const auto &prev = dc->events[idx2];
if (prev.name == event.name && prev.flags == event.flags &&
event.time.seconds - prev.time.seconds < 61)
to_delete.push_back(idx);
}
}
// 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)
@ -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
*/
static bool validate_gaschange(struct dive *dive, struct event *event)
static bool validate_gaschange(struct dive *dive, struct event &event)
{
int index;
int o2, he, value;
/* We'll get rid of the per-event gasmix, but for now sanitize it */
if (gasmix_is_air(event->gas.mix))
event->gas.mix.o2.permille = 0;
if (gasmix_is_air(event.gas.mix))
event.gas.mix.o2.permille = 0;
/* Do we already have a cylinder index for this gasmix? */
if (event->gas.index >= 0)
if (event.gas.index >= 0)
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)
return false;
/* Fix up the event to have the right information */
event->gas.index = index;
event->gas.mix = get_cylinder(dive, index)->gasmix;
event.gas.index = index;
event.gas.mix = get_cylinder(dive, index)->gasmix;
/* Convert to odd libdivecomputer format */
o2 = get_o2(event->gas.mix);
he = get_he(event->gas.mix);
o2 = get_o2(event.gas.mix);
he = get_he(event.gas.mix);
o2 = (o2 + 5) / 10;
he = (he + 5) / 10;
value = o2 + (he << 16);
event->value = value;
event.value = value;
if (he)
event->type = SAMPLE_EVENT_GASCHANGE2;
event.type = SAMPLE_EVENT_GASCHANGE2;
return true;
}
/* 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))
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)
{
struct event **evp, *event;
evp = &dc->events;
while ((event = *evp) != NULL) {
if (validate_event(dive, event)) {
evp = &event->next;
continue;
}
/* Delete this event and try the next one */
*evp = event->next;
}
// erase-remove idiom
auto &events = dc->events;
events.erase(std::remove_if(events.begin(), events.end(),
[dive](auto &ev) { return !validate_event(dive, ev); }),
events.end());
}
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) \
if (a != b) \
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_FIELD(a, b, type);
SORT_FIELD(a, b, flags);
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)
@ -1454,7 +1413,7 @@ static int same_gas(const struct event *a, const struct event *b)
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 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,
int offset)
{
const struct event *a, *b;
struct event **p = &res->events;
const struct event *last_gas = NULL;
/* Always use positive offsets */
@ -1481,40 +1438,40 @@ static void merge_events(struct dive *d, struct divecomputer *res,
cylinders_map2 = cylinders_map_tmp;
}
a = src1->events;
b = src2->events;
auto a = src1->events.begin();
auto b = src2->events.begin();
while (a || b) {
int s;
while (a != src1->events.end() || b != src2->events.end()) {
int s = 0;
const struct event *pick;
const int *cylinders_map;
int event_offset;
if (!b)
if (b == src2->events.end())
goto pick_a;
if (!a)
if (a == src1->events.end())
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) */
if (!s) {
a = a->next;
++a;
continue;
}
/* Otherwise, pick the one that sorts first */
if (s < 0) {
pick_a:
pick = a;
a = a->next;
pick = &*a;
++a;
event_offset = 0;
cylinders_map = cylinders_map1;
} else {
pick_b:
pick = b;
b = b->next;
pick = &*b;
++b;
event_offset = offset;
cylinders_map = cylinders_map2;
}
@ -1523,17 +1480,16 @@ pick_b:
* If that's a gas-change that matches the previous
* 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))
continue;
last_gas = pick;
}
/* Add it to the target list */
*p = clone_event(pick);
(*p)->time.seconds += event_offset;
event_renumber(*p, cylinders_map);
p = &(*p)->next;
res->events.push_back(*pick);
res->events.back().time.seconds += event_offset;
event_renumber(res->events.back(), cylinders_map);
}
/* 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,
* refrain from adding the initial event */
const struct event *ev = dc->events;
while(ev && (ev = get_next_event(ev, "gaschange")) != NULL) {
event_loop loop("gaschange");
while(auto ev = loop.next(*dc)) {
if (ev->time.seconds > offset + 30)
break;
else if (ev->time.seconds > offset)
return;
ev = ev->next;
}
/* 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);
}
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))
return;
if (ev->gas.index < 0)
if (ev.gas.index < 0)
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[])
{
struct event *ev;
/* Remap or delete the sensor indices */
for (auto [i, sample]: enumerated_range(dc->samples))
sample_renumber(sample, i > 0 ? &dc->samples[i-1] : nullptr, mapping);
/* Remap the gas change indices */
for (ev = dc->events; ev; ev = ev->next)
for (auto &ev: dc->events)
event_renumber(ev, mapping);
/* 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)
{
int i;
const struct event *eva, *evb;
i = match_one_dc(a, b);
if (i)
return i > 0;
@ -2269,15 +2220,7 @@ static int same_dc(struct divecomputer *a, struct divecomputer *b)
return 0;
if (a->samples != b->samples)
return 0;
eva = a->events;
evb = b->events;
while (eva && evb) {
if (!same_event(eva, evb))
return 0;
eva = eva->next;
evb = evb->next;
}
return eva == evb;
return a->events == b->events;
}
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->samples.clear();
res->events = NULL;
res->events.clear();
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;
struct dive *d1, *d2;
struct divecomputer *dc1, *dc2;
struct event *event, **evp;
/* if we can't find the dive in the dive list, don't bother */
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;
/* Remove the events past 't' from d1 */
evp = &dc1->events;
while ((event = *evp) != NULL && event->time.seconds < t)
evp = &event->next;
*evp = NULL;
while (event) {
struct event *next = event->next;
delete event;
event = next;
}
auto it = std::lower_bound(dc1->events.begin(), dc1->events.end(), t,
[] (struct event &ev, int t)
{ return ev.time.seconds < t; });
dc1->events.erase(it, dc1->events.end());
/* Remove the events before 't' from d2, and shift the rest */
evp = &dc2->events;
while ((event = *evp) != NULL) {
if (event->time.seconds < t) {
*evp = event->next;
delete event;
} else {
event->time.seconds -= t;
}
}
it = std::lower_bound(dc2->events.begin(), dc2->events.end(), t,
[] (struct event &ev, int t)
{ return ev.time.seconds < t; });
dc2->events.erase(dc2->events.begin(), it);
for (auto &ev: dc2->events)
ev.time.seconds -= t;
dc1 = dc1->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.
* Yes, this is "inefficient", but I don't care. Will be removed anyways. */
struct divecomputer *fdc = d->dc.next;
free_dc_contents(&d->dc);
copy_dc(fdc, &d->dc);
d->dc = *fdc;
delete fdc;
} else {
struct divecomputer *pdc = &d->dc;
@ -3331,40 +3265,37 @@ location_t dive_get_gps_location(const struct dive *d)
return res;
}
/* When evaluated at the time of a gasswitch, this returns the new gas */
struct gasmix get_gasmix(const struct dive *dive, const struct divecomputer *dc, int time, const struct event **evp, struct gasmix gasmix)
gasmix_loop::gasmix_loop(const struct dive &d, const struct divecomputer &dc) :
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 (dive->cylinders.nr <= 0)
return gasmix_air;
if (dive.cylinders.nr <= 0)
return;
if (!ev) {
/* on first invocation, get initial gas mix and first event (if any) */
int cyl = explicit_first_cylinder(dive, dc);
res = get_cylinder(dive, cyl)->gasmix;
ev = dc ? get_next_event(dc->events, "gaschange") : NULL;
} else {
res = gasmix;
}
/* on first invocation, get initial gas mix and first event (if any) */
int cyl = explicit_first_cylinder(&dive, &dc);
last = get_cylinder(&dive, cyl)->gasmix;
ev = loop.next(dc);
}
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) {
res = get_gasmix_from_event(dive, ev);
ev = get_next_event(ev->next, "gaschange");
last = get_gasmix_from_event(&dive, *ev);
ev = loop.next(dc);
}
*evp = ev;
return res;
return last;
}
/* get the gas at a certain time during the dive */
/* 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;
struct gasmix gasmix = gasmix_air;
return get_gasmix(d, dc, time.seconds, &ev, gasmix);
return gasmix_loop(d, dc).next(time.seconds);
}
/* Does that cylinder have any pressure readings? */

View file

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

View file

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

View file

@ -8,6 +8,7 @@
#include <vector>
struct extra_data;
struct event;
struct sample;
/* Is this header the correct place? */
@ -37,8 +38,10 @@ struct divecomputer {
int salinity = 0; // kg per 10000 l
std::string model, serial, fw_version;
uint32_t deviceid = 0, diveid = 0;
// Note: ve store samples, events and extra_data in std::vector<>s.
// This means that pointers to these items are *not* stable.
std::vector<struct sample> samples;
struct event *events = nullptr;
std::vector<struct event> events;
std::vector<struct extra_data> extra_data;
struct divecomputer *next = nullptr;
@ -50,7 +53,6 @@ struct divecomputer {
extern void fake_dc(struct divecomputer *dc);
extern void free_dc_contents(struct divecomputer *dc);
extern enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode);
extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time);
extern void free_dive_dcs(struct divecomputer *dc);
extern 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 unsigned int dc_airtemp(const struct divecomputer *dc);
extern unsigned int dc_watertemp(const struct divecomputer *dc);
extern void copy_events(const struct divecomputer *s, struct divecomputer *d);
extern void swap_event(struct divecomputer *dc, struct event *from, struct event *to);
extern void add_event_to_dc(struct divecomputer *dc, struct event *ev);
extern int add_event_to_dc(struct divecomputer *dc, struct event ev); // event structure is consumed, returns index of inserted event
extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name);
extern 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 uint32_t calculate_string_hash(const char *str);
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)
{
struct gasmix gas = get_gasmix_at_time(dive, dc, time);
struct gasmix gas = get_gasmix_at_time(*dive, *dc, time);
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 pamb_pressure = depth_to_bar(psample.depth.mm , dive);
if (dc->divemode == PSCR) {
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));
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));
} else {
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
@ -141,8 +141,8 @@ static int calculate_otu(const struct dive *dive)
double amb_presure = depth_to_bar(sample.depth.mm, dive);
double pamb_pressure = depth_to_bar(psample.depth.mm , dive);
if (dc->divemode == PSCR) {
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));
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));
} else {
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
@ -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)
{
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)) {
int t0 = psample.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++) {
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,
get_current_divemode(&dive->dc, j, &evd, &current_divemode), dive->sac,
loop_d.next(j), dive->sac,
in_planner);
}
}
@ -417,11 +413,12 @@ int get_divenr(const struct dive *dive)
int i;
const struct dive *d;
// tempting as it may be, don't die when called with dive=NULL
if (dive)
if (dive) {
for_each_dive(i, d) {
if (d->id == dive->id) // don't compare pointers, we could be passing in a copy of the dive
return i;
}
}
return -1;
}

View file

@ -1,67 +1,24 @@
// SPDX-License-Identifier: GPL-2.0
#include "event.h"
#include "divecomputer.h"
#include "eventtype.h"
#include "subsurface-string.h"
#include <string.h>
#include <stdlib.h>
event::event() : next(nullptr), type(SAMPLE_EVENT_NONE), flags(0), value(0),
divemode(OC), deleted(false), hidden(false)
event::event() : type(SAMPLE_EVENT_NONE), flags(0), value(0),
divemode(OC), hidden(false)
{
/* That overwrites divemode. Is this a smart thing to do? */
gas.index = -1;
gas.mix = gasmix_air;
}
event::~event()
{
}
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)
event::event(unsigned int time, int type, int flags, int value, const std::string &name) :
type(type), flags(flags), value(value), divemode(OC),
hidden(false), name(name)
{
int gas_index = -1;
struct event *ev;
ev = new event;
ev->name = name;
ev->time.seconds = time;
ev->type = type;
ev->flags = flags;
ev->value = value;
fraction_t he;
this->time.seconds = time;
/*
* 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) {
case SAMPLE_EVENT_GASCHANGE2:
/* 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' */
/* 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 */
case SAMPLE_EVENT_GASCHANGE:
/* Low 16 bits are O2 percentage */
ev->gas.mix.o2.permille = (value & 0xffff) * 10;
ev->gas.index = gas_index;
gas.mix.he = he;
gas.mix.o2.permille = (value & 0xffff) * 10;
gas.index = gas_index;
break;
}
remember_event_type(ev);
return ev;
remember_event_type(this);
}
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 0;
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;
return ev.type == SAMPLE_EVENT_GASCHANGE ||
ev.type == SAMPLE_EVENT_GASCHANGE2;
}
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:
return EVENT_SEVERITY_INFO;
case SAMPLE_FLAGS_SEVERITY_WARN:
@ -120,3 +79,35 @@ extern enum event_severity get_event_severity(const struct event *ev)
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 <libdivecomputer/parser.h>
struct divecomputer;
enum event_severity {
EVENT_SEVERITY_NONE = 0,
EVENT_SEVERITY_INFO,
@ -22,7 +24,6 @@ enum event_severity {
*/
struct event {
struct event *next;
duration_t time;
int type;
/* This is the annoying libdivecomputer format. */
@ -40,23 +41,53 @@ struct event {
struct gasmix mix;
} gas;
};
bool deleted; // used internally in the parser and in fixup_dive().
bool hidden;
std::string name;
event();
event(unsigned int time, int type, int flags, int value, const std::string &name);
~event();
bool operator==(const event &b2) const;
};
extern int event_is_gaschange(const struct event *ev);
extern bool event_is_divemodechange(const struct event *ev);
extern struct event *clone_event(const struct event *src_ev);
extern void free_events(struct event *ev);
extern struct event *create_event(unsigned int time, int type, int flags, int value, const std::string &name);
extern struct event *clone_event_rename(const struct event *ev, const std::string &name);
extern bool same_event(const struct event *a, const struct event *b);
extern enum event_severity get_event_severity(const struct event *ev);
class event_loop
{
std::string name;
size_t idx;
public:
event_loop(const char *name);
struct event *next(struct divecomputer &dc); // nullptr -> end
const struct event *next(const struct divecomputer &dc); // nullptr -> end
};
extern const struct event *get_next_event(const struct event *event, const std::string &name);
extern struct event *get_next_event(struct event *event, const std::string &name);
/* Get gasmixes at increasing timestamps. */
class gasmix_loop {
const struct dive &dive;
const struct divecomputer &dc;
struct gasmix last;
event_loop loop;
const struct event *ev;
public:
gasmix_loop(const struct dive &dive, const struct divecomputer &dc);
gasmix next(int time);
};
/* 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

View file

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

View file

@ -14,7 +14,7 @@ extern void show_all_event_types();
extern void show_event_type(int idx);
extern bool any_event_types_hidden();
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);
#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);
std::vector<pr_track_t> track;
size_t current = std::string::npos;
const struct event *ev, *b_ev;
int missing_pr = 0, dense = 1;
enum divemode_t dmode = dc->divemode;
const double gasfactor[5] = {1.0, 0.0, prefs.pscr_ratio/1000.0, 1.0, 1.0 };
if (sensor < 0 || 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.
*/
cyl = sensor;
ev = NULL;
if (has_gaschange_event(dive, dc, sensor))
ev = get_next_event(dc->events, "gaschange");
b_ev = get_next_event(dc->events, "modechange");
event_loop loop_gas("gaschange");
const struct event *ev = has_gaschange_event(dive, dc, sensor) ?
loop_gas.next(*dc) : nullptr;
divemode_loop loop_mode(*dc);
for (int i = first; i <= last; 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;
while (ev && ev->time.seconds <= time) { // Find 1st gaschange event after
cyl = get_cylinder_index(dive, ev); // the current gas change.
cyl = get_cylinder_index(dive, *ev); // the current gas change.
if (cyl < 0)
cyl = sensor;
ev = get_next_event(ev->next, "gaschange");
ev = loop_gas.next(*dc);
}
while (b_ev && b_ev->time.seconds <= time) { // Keep existing divemode, then
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.
}
divemode_t dmode = loop_mode.next(time);
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);

View file

@ -363,7 +363,7 @@ static void handle_event(struct divecomputer *dc, const struct sample &sample, d
time += sample.time.seconds;
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;
}

View file

@ -839,7 +839,6 @@ static void parse_dc_event(char *line, struct git_parser_state *state)
{
int m, s = 0;
struct parse_event p;
struct event *ev;
m = strtol(line, &line, 10);
if (*line == ':')
@ -859,17 +858,17 @@ static void parse_dc_event(char *line, struct git_parser_state *state)
if (p.has_divemode && 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.
* Better to mark them being CCR on import so no need for special treatments elsewhere on
* 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;
if (ev && event_is_gaschange(ev)) {
if (event_is_gaschange(*ev)) {
/*
* We subtract one here because "0" is "no index",
* 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;
add_picture(&state->cur_dive->pictures, pic); /* Takes ownership. */
} else {
struct event *ev;
/* 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.
*/
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;
ev = add_event(dc, state->cur_event.time.seconds,
state->cur_event.type, state->cur_event.flags,
state->cur_event.value, state->cur_event.name);
struct event ev(state->cur_event.time.seconds, state->cur_event.type, state->cur_event.flags,
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
* 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;
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)) {
/* 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;
}
add_event_to_dc(dc, std::move(ev));
}
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
int cylinder_idx = 0;
struct event *event = dc->events;
while (event && event->time.seconds <= time.seconds) {
if (event->name == "gaschange")
for (const auto &event: dc->events) {
if (event.time.seconds > time.seconds)
break;
if (event.name == "gaschange")
cylinder_idx = get_cylinder_index(dive, event);
event = event->next;
}
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())
return 0;
const struct event *evdm = NULL;
enum divemode_t divemode = UNDEF_COMP_TYPE;
const struct sample *psample = nullptr;
divemode_loop loop(dive->dc);
for (auto &sample: dc->samples) {
o2pressure_t setpoint = psample ? psample->setpoint
: sample.setpoint;
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)
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;
}
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);
psample = &sample;
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)
{
struct divedatapoint *dp;
struct event *ev;
cylinder_t *cyl;
int oldpo2 = 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->salinity = diveplan->salinity;
dc->samples.clear();
while ((ev = dc->events)) {
dc->events = dc->events->next;
delete ev;
}
dc->events.clear();
dp = diveplan->dp;
/* Create first sample at time = 0, not based on dp because
* 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);
// Find the divemode at the end of the dive
const struct event *ev = NULL;
divemode = UNDEF_COMP_TYPE;
divemode = get_current_divemode(dc, bottom_time, &ev, &divemode);
divemode_loop loop(*dc);
divemode = loop.next(bottom_time);
gas = get_cylinder(dive, current_cylinder)->gasmix;
po2 = sample.setpoint.mbar;

View file

@ -12,6 +12,7 @@
#include <stdlib.h>
#include "dive.h"
#include "deco.h"
#include "event.h"
#include "units.h"
#include "divelist.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;
bool o2warning_exist = false;
enum divemode_t current_divemode;
double amb;
const struct event *evd = NULL;
current_divemode = UNDEF_COMP_TYPE;
divemode_loop loop(dive->dc);
if (dive->dc.divemode != CCR) {
while (dp) {
if (dp->time != 0) {
std::string temp;
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);
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
* 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;
struct gasmix mix;
if (ev->gas.index >= 0)
return ev->gas.index;
if (ev.gas.index >= 0)
return ev.gas.index;
/*
* 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;
}
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)
{
while (i < pi.entry.size()) {
@ -265,17 +237,16 @@ static void check_setpoint_events(const struct dive *, const struct divecomputer
size_t i = 0;
pressure_t setpoint;
setpoint.mbar = 0;
const struct event *ev = get_next_event(dc->events, "SP change");
if (!ev)
return;
do {
event_loop loop("SP change");
bool found = false;
while (auto ev = loop.next(*dc)) {
i = set_setpoint(pi, i, setpoint.mbar, ev->time.seconds);
setpoint.mbar = ev->value;
ev = get_next_event(ev->next, "SP change");
} while (ev);
set_setpoint(pi, i, setpoint.mbar, INT_MAX);
found = true;
}
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)
@ -306,15 +277,10 @@ static void calculate_max_limits_new(const struct dive *dive, const struct divec
if (dc == given_dc)
seen = true;
int lastdepth = 0;
struct event *ev;
/* Make sure we can fit all events */
ev = dc->events;
while (ev) {
if (ev->time.seconds > maxtime)
maxtime = ev->time.seconds;
ev = ev->next;
}
if (!dc->events.empty())
maxtime = std::max(maxtime, dc->events.back().time.seconds);
for (auto &s: dc->samples) {
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)
{
struct event *ev = dc->events;
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
* 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.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 lasttemp = 0;
/* skip events at time = 0 */
while (ev && ev->time.seconds == 0)
ev = ev->next;
auto evit = dc->events.begin();
while (evit != dc->events.end() && evit->time.seconds == 0)
++evit;
for (const auto &sample: dc->samples) {
int time = sample.time.seconds;
int offset, delta;
@ -444,23 +409,23 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp
break;
/* Add events if they are between plot entries */
while (ev && (int)ev->time.seconds < lasttime + offset) {
insert_entry(pi, ev->time.seconds, interpolate(lastdepth, depth, ev->time.seconds - lasttime, delta), sac);
ev = ev->next;
while (evit != dc->events.end() && static_cast<int>(evit->time.seconds) < lasttime + offset) {
insert_entry(pi, evit->time.seconds, interpolate(lastdepth, depth, evit->time.seconds - lasttime, delta), sac);
++evit;
}
/* now insert the time interpolated entry */
insert_entry(pi, lasttime + offset, interpolate(lastdepth, depth, offset, delta), sac);
/* skip events that happened at this time */
while (ev && (int)ev->time.seconds == lasttime + offset)
ev = ev->next;
while (evit != dc->events.end() && static_cast<int>(evit->time.seconds) == lasttime + offset)
++evit;
}
/* Add events if they are between plot entries */
while (ev && (int)ev->time.seconds < time) {
insert_entry(pi, ev->time.seconds, interpolate(lastdepth, depth, ev->time.seconds - lasttime, delta), sac);
ev = ev->next;
while (evit != dc->events.end() && static_cast<int>(evit->time.seconds) < time) {
insert_entry(pi, evit->time.seconds, interpolate(lastdepth, depth, evit->time.seconds - lasttime, delta), sac);
++evit;
}
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)
entry.rbt = sample.rbt.seconds;
/* skip events that happened at this time */
while (ev && (int)ev->time.seconds == time)
ev = ev->next;
while (evit != dc->events.end() && static_cast<int>(evit->time.seconds) == time)
++evit;
lasttime = time;
lastdepth = depth;
@ -507,14 +472,14 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp
}
/* Add any remaining events */
while (ev) {
int time = ev->time.seconds;
while (evit != dc->events.end()) {
int time = evit->time.seconds;
if (time > lasttime) {
insert_entry(pi, ev->time.seconds, 0, 0);
insert_entry(pi, evit->time.seconds, 0, 0);
lasttime = time;
}
ev = ev->next;
++evit;
}
/* 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)
{
struct gasmix gasmix = gasmix_invalid;
const struct event *ev = NULL;
std::vector<char> gases(pi.nr_cylinders, false);
/* This might be premature optimization, but let's allocate the gas array for
* the fill_sac function only once an not once per sample */
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++) {
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)) {
gasmix = newmix;
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)
{
int i;
const struct event *ev;
if (pi.nr_cylinders == 0)
return;
@ -753,7 +714,8 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive
prev = prev >= 0 ? prev : 0;
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 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
// 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);
int start = cyl->start.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) {
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;
if (decoMode(in_planner) == VPMB)
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++) {
struct plot_data &entry = pi.entry[i];
struct plot_data &prev = pi.entry[i - 1];
int j, t0 = prev.sec, t1 = entry.sec;
int time_stepsize = 20, max_ceiling = -1;
current_divemode = get_current_divemode(dc, entry.sec, &evd, &current_divemode);
gasmix = get_gasmix(dive, dc, t1, &ev, gasmix);
divemode_t current_divemode = loop_d.next(entry.sec);
struct gasmix gasmix = loop.next(t1);
entry.ambpressure = depth_to_bar(entry.depth, dive);
entry.gfline = get_gf(ds, entry.ambpressure, dive) * (100.0 - AMB_PERCENTAGE) + AMB_PERCENTAGE;
if (t0 > t1) {
@ -1181,22 +1142,21 @@ static void calculate_gas_information_new(const struct dive *dive, const struct
{
int i;
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++) {
double fn2, fhe;
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);
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);
fn2 = 1000.0 * entry.pressures.n2 / 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.
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;
}

View file

@ -383,32 +383,30 @@ static void save_samples(struct membuffer *b, struct dive *dive, struct divecomp
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));
show_index(b, ev->type, "type=", "");
show_index(b, ev->flags, "flags=", "");
put_format(b, "event %d:%02d", FRACTION_TUPLE(ev.time.seconds, 60));
show_index(b, ev.type, "type=", "");
show_index(b, ev.flags, "flags=", "");
if (ev->name == "modechange")
show_utf8(b, " divemode=", divemode_text[ev->value], "");
if (ev.name == "modechange")
show_utf8(b, " divemode=", divemode_text[ev.value], "");
else
show_index(b, ev->value, "value=", "");
show_utf8(b, " name=", ev->name.c_str(), "");
show_index(b, ev.value, "value=", "");
show_utf8(b, " name=", ev.name.c_str(), "");
if (event_is_gaschange(ev)) {
struct gasmix mix = get_gasmix_from_event(dive, ev);
if (ev->gas.index >= 0)
show_integer(b, ev->gas.index, "cylinder=", "");
if (ev.gas.index >= 0)
show_integer(b, ev.gas.index, "cylinder=", "");
put_gasmix(b, mix);
}
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);
ev = ev->next;
}
}
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");
save_extra_data(b, dc);
save_events(b, dive, dc->events);
save_events(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);
}
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)
return;
@ -49,10 +49,10 @@ static void save_photos(struct membuffer *b, const char *photos_dir, struct dive
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\":[");
struct divecomputer *dc;
const struct divecomputer *dc;
const char *separator = "";
for_each_dc (dive, dc) {
put_string(b, separator);
@ -72,36 +72,30 @@ static void write_divecomputers(struct membuffer *b, struct dive *dive)
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, "\"otu\":\"%d\",", dive->otu);
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\":[";
do {
for (const auto &ev: dive->dc.events) {
put_string(b, separator);
separator = ", ";
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_format(b, "\"value\":\"%d\",", ev->value);
put_format(b, "\"type\":\"%d\",", ev->type);
put_format(b, "\"time\":\"%d\"}", ev->time.seconds);
ev = ev->next;
} while (ev);
put_format(b, "\"value\":\"%d\",", ev.value);
put_format(b, "\"type\":\"%d\",", ev.type);
put_format(b, "\"time\":\"%d\"}", ev.time.seconds);
}
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;
@ -126,7 +120,7 @@ static void put_weightsystem_HTML(struct membuffer *b, struct dive *dive)
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;
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, "\"duration\":%d,", dive->dc.duration.seconds);
@ -192,7 +186,7 @@ static void put_HTML_samples(struct membuffer *b, struct dive *dive)
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);
if (!ds)
@ -210,7 +204,7 @@ static void put_HTML_coordinates(struct membuffer *b, struct dive *dive)
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;
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);
}
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);
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);
}
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;
utc_mkdate(dive->when, &tm);
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;
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;
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);
}
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;
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);
}
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);
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 */
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_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)
{
int i;
struct dive *dive;
const struct dive *dive;
const char *separator = "";
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)
{
struct dive *dive;
const struct dive *dive;
const char *separator = "";
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)
{
int i, dive_no = 0;
struct dive *dive;
const struct dive *dive;
dive_trip_t *trip;
char sep_ = ' ';
char *sep = &sep_;

View file

@ -6,12 +6,12 @@
struct dive;
void put_HTML_date(struct membuffer *b, 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_airtemp(struct membuffer *b, 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_time(struct membuffer *b, 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_date(struct membuffer *b, const 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, const 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, const 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_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);

View file

@ -353,32 +353,30 @@ static void save_sample(struct membuffer *b, const struct sample &sample, struct
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));
show_index(b, ev->type, "type='", "'");
show_index(b, ev->flags, "flags='", "'");
if (ev->name == "modechange")
show_utf8(b, divemode_text[ev->value], " divemode='", "'",1);
put_format(b, " <event time='%d:%02d min'", FRACTION_TUPLE(ev.time.seconds, 60));
show_index(b, ev.type, "type='", "'");
show_index(b, ev.flags, "flags='", "'");
if (ev.name == "modechange")
show_utf8(b, divemode_text[ev.value], " divemode='", "'",1);
else
show_index(b, ev->value, "value='", "'");
show_utf8(b, ev->name.c_str(), " name='", "'", 1);
show_index(b, ev.value, "value='", "'");
show_utf8(b, ev.name.c_str(), " name='", "'", 1);
if (event_is_gaschange(ev)) {
struct gasmix mix = get_gasmix_from_event(dive, ev);
if (ev->gas.index >= 0)
show_integer(b, ev->gas.index, "cylinder='", "'");
if (ev.gas.index >= 0)
show_integer(b, ev.gas.index, "cylinder='", "'");
put_gasmix(b, mix);
}
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);
ev = ev->next;
}
}
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);
put_duration(b, dc->surfacetime, " <surfacetime>", " min</surfacetime>\n");
save_extra_data(b, dc);
save_events(b, dive, dc->events);
save_events(b, dive, dc);
save_samples(b, dive, dc);
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 first_gas_explicit = false;
const struct event *event = get_next_event(dc->events, "gaschange");
while (event) {
event_loop loop("gaschange");
while (auto event = loop.next(*dc)) {
if (!dc->samples.empty() && (event->time.seconds == 0 ||
(dc->samples[0].time.seconds == event->time.seconds)))
first_gas_explicit = true;
if (get_cylinder_index(dive, event) == idx)
if (get_cylinder_index(dive, *event) == idx)
return true;
event = get_next_event(event->next, "gaschange");
}
return !first_gas_explicit && idx == 0;
}

View file

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

View file

@ -16,14 +16,15 @@
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,
int speed, const DivePixmaps &pixmaps, QGraphicsItem *parent) : DivePixmapItem(parent),
vAxis(vAxis),
hAxis(hAxis),
idx(idx),
ev(ev),
dive(d),
depth(depthAtTime(pi, ev->time))
depth(depthAtTime(pi, ev.time))
{
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)
{
event_severity severity = get_event_severity(ev);
if (ev->name.empty()) {
if (ev.name.empty()) {
setPixmap(pixmaps.warning);
} else if (same_string_caseinsensitive(ev->name.c_str(), "modechange")) {
if (ev->value == 0)
} else if (same_string_caseinsensitive(ev.name.c_str(), "modechange")) {
if (ev.value == 0)
setPixmap(pixmaps.bailout);
else
setPixmap(pixmaps.onCCRLoop);
} else if (ev->type == SAMPLE_EVENT_BOOKMARK) {
} else if (ev.type == SAMPLE_EVENT_BOOKMARK) {
setPixmap(pixmaps.bookmark);
setOffset(QPointF(0.0, -pixmap().height()));
} else if (event_is_gaschange(ev)) {
@ -84,10 +75,10 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix
else
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
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(), "heading") ||
(same_string_caseinsensitive(ev.name.c_str(), "SP change") && ev.time.seconds == 0)) {
// 2 cases:
// a) some dive computers have heading in every sample
// 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);
} else if (severity == EVENT_SEVERITY_ALARM) {
setPixmap(pixmaps.violation);
} 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(), "pO₂ ascend alarm") ||
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(), "Low battery alert") ||
same_string_caseinsensitive(ev->name.c_str(), "Speed alarm")) {
} 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(), "pO₂ ascend alarm") ||
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(), "Low battery alert") ||
same_string_caseinsensitive(ev.name.c_str(), "Speed alarm")) {
setPixmap(pixmaps.violation);
} 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 (voluntary)") ||
same_string_caseinsensitive(ev->name.c_str(), "Tank change suggested") || // Uemis downloader
same_string_caseinsensitive(ev->name.c_str(), "Marker")) {
} 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 (voluntary)") ||
same_string_caseinsensitive(ev.name.c_str(), "Tank change suggested") || // Uemis downloader
same_string_caseinsensitive(ev.name.c_str(), "Marker")) {
setPixmap(pixmaps.info);
} else {
// 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)
{
// we display the event on screen - so translate
QString name = gettextFromC::tr(ev->name.c_str());
int value = ev->value;
int type = ev->type;
QString name = gettextFromC::tr(ev.name.c_str());
int value = ev.value;
int type = ev.type;
if (event_is_gaschange(ev)) {
struct icd_data icd_data;
@ -137,8 +128,8 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix)
name += gasname(mix);
/* Do we have an explicit cylinder index? Show it. */
if (ev->gas.index >= 0)
name += tr(" (cyl. %1)").arg(ev->gas.index + 1);
if (ev.gas.index >= 0)
name += tr(" (cyl. %1)").arg(ev.gas.index + 1);
bool icd = isobaric_counterdiffusion(lastgasmix, mix, &icd_data);
if (icd_data.dHe < 0) {
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,
icd ? ">" : "<", lrint(-icd_data.dHe / 5.0) / 10.0);
}
} else if (ev->name == "modechange") {
name += QString(": %1").arg(gettextFromC::tr(divemode_text_ui[ev->value]));
} else if (ev.name == "modechange") {
name += QString(": %1").arg(gettextFromC::tr(divemode_text_ui[ev.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);
} 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;
double depth_value = get_depth_units(value*1000, NULL, &depth_unit);
name += QString(": %1%2").arg((int) round(depth_value)).arg(depth_unit);
} else {
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
// warn of high or low pO₂ and are turning it into a setpoint change event
name += ":\n" + tr("Manual switch to OC");
} else {
name += ev->flags & SAMPLE_FLAGS_BEGIN ? tr(" begin", "Starts with space!") :
ev->flags & SAMPLE_FLAGS_END ? tr(" end", "Starts with space!") : "";
name += ev.flags & SAMPLE_FLAGS_BEGIN ? tr(" begin", "Starts with space!") :
ev.flags & SAMPLE_FLAGS_END ? tr(" end", "Starts with space!") : "";
}
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,
const struct event *ev, const plot_info &pi,
const struct event &ev, const plot_info &pi,
int firstSecond, int lastSecond)
{
/*
* 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;
/*
* Some gas change events are special. Some dive computers just tell us the initial gas this way.
* Don't bother showing those
*/
if (ev->name == "gaschange" &&
(ev->time.seconds == 0 ||
(!dc->samples.empty() && ev->time.seconds == dc->samples[0].time.seconds) ||
depthAtTime(pi, ev->time) < SURFACE_THRESHOLD))
if (ev.name == "gaschange" &&
(ev.time.seconds == 0 ||
(!dc->samples.empty() && ev.time.seconds == dc->samples[0].time.seconds) ||
depthAtTime(pi, ev.time) < SURFACE_THRESHOLD))
return false;
/*
* Some divecomputers give "surface" events that just aren't interesting.
* Like at the beginning or very end of a dive. Well, duh.
*/
if (ev->name == "surface") {
int time = ev->time.seconds;
if (ev.name == "surface") {
int time = ev.time.seconds;
if (time <= 30 || time + 30 >= (int)dc->duration.seconds)
return false;
}
@ -221,15 +212,12 @@ bool DiveEventItem::isInteresting(const struct dive *d, const struct divecompute
void DiveEventItem::recalculatePos()
{
if (!ev)
return;
if (depth == DEPTH_NOT_FOUND) {
hide();
return;
}
setVisible(!ev->hidden && !is_event_type_hidden(ev));
double x = hAxis->posAtValue(ev->time.seconds);
setVisible(!ev.hidden && !is_event_type_hidden(&ev));
double x = hAxis->posAtValue(ev.time.seconds);
double y = vAxis->posAtValue(depth);
setPos(x, y);
}

View file

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

View file

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

View file

@ -14,6 +14,7 @@
#include "core/pref.h"
#include "core/profile.h"
#include "core/qthelper.h" // for decoMode()
#include "core/range.h"
#include "core/subsurface-float.h"
#include "core/subsurface-string.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.
qDeleteAll(eventItems);
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 (printMode) {
if (event->name.empty() ||
!(event->name == "heading" ||
(event->name == "SP change" && event->time.seconds == 0) ||
if (event.name.empty() ||
!(event.name == "heading" ||
(event.name == "SP change" && event.time.seconds == 0) ||
event_is_gaschange(event) ||
event->type == SAMPLE_EVENT_BOOKMARK)) {
event = event->next;
event.type == SAMPLE_EVENT_BOOKMARK))
continue;
}
}
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);
item->setZValue(2);
addItem(item);
@ -574,7 +572,6 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM
}
if (event_is_gaschange(event))
lastgasmix = get_gasmix_from_event(d, event);
event = event->next;
}
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);
// Add or edit Gas Change
if (d && item && event_is_gaschange(item->getEvent())) {
int eventTime = item->getEvent()->time.seconds;
if (d && item && event_is_gaschange(item->ev)) {
int eventTime = item->ev.time.seconds;
QMenu *gasChange = m.addMenu(tr("Edit Gas Change"));
for (int i = 0; i < d->cylinders.nr; 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 bookmark"), [this, seconds]() { addBookmark(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"));
if (divemode != OC)
changeMode->addAction(gettextFromC::tr(divemode_text_ui[OC]),
@ -595,23 +594,22 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
[this, seconds](){ addDivemodeSwitch(seconds, PSCR); });
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("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); });
if (dcEvent->type == SAMPLE_EVENT_BOOKMARK)
if (item->ev.type == SAMPLE_EVENT_BOOKMARK)
m.addAction(tr("Edit name"), [this, item] { editName(item); });
#if 0 // TODO::: FINISH OR DISABLE
QPointF scenePos = mapToScene(event->pos());
int idx = getEntryFromPos(scenePos);
// 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
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;
while (gasChangeIdx > 0) {
--gasChangeIdx;
if (plotInfo.entry[gasChangeIdx].sec <= dcEvent->time.seconds)
if (plotInfo.entry[gasChangeIdx].sec <= item->ev->time.seconds)
break;
}
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);
}
if (std::any_of(profileScene->eventItems.begin(), profileScene->eventItems.end(),
[] (const DiveEventItem *item) { return item->getEvent()->hidden; }))
const struct divecomputer *currentdc = get_dive_dc_const(d, dc);
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.exec(event->globalPos());
}
@ -690,16 +689,18 @@ void ProfileWidget2::renameCurrentDC()
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();
}
void ProfileWidget2::hideEventType(DiveEventItem *item)
{
const struct event *event = item->getEvent();
if (!event->name.empty()) {
hide_event_type(event);
if (!item->ev.name.empty()) {
hide_event_type(&item->ev);
replot();
}
@ -707,10 +708,13 @@ void ProfileWidget2::hideEventType(DiveEventItem *item)
void ProfileWidget2::unhideEvents()
{
for (DiveEventItem *item: profileScene->eventItems) {
item->getEventMutable()->hidden = false;
struct divecomputer *currentdc = get_dive_dc(mutable_dive(), dc);
if (!currentdc)
return;
for (auto &ev: currentdc->events)
ev.hidden = false;
for (DiveEventItem *item: profileScene->eventItems)
item->show();
}
}
void ProfileWidget2::unhideEventTypes()
@ -722,15 +726,12 @@ void ProfileWidget2::unhideEventTypes()
void ProfileWidget2::removeEvent(DiveEventItem *item)
{
struct event *event = item->getEventMutable();
if (!event || !d)
return;
const struct event &ev = item->ev;
if (QMessageBox::question(this, TITLE_OR_TEXT(
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)
Command::removeEvent(mutable_dive(), dc, event);
Command::removeEvent(mutable_dive(), dc, item->idx);
}
void ProfileWidget2::addBookmark(int seconds)
@ -781,13 +782,12 @@ void ProfileWidget2::changeGas(int index, int newCylinderId)
void ProfileWidget2::editName(DiveEventItem *item)
{
struct event *event = item->getEventMutable();
if (!event || !d)
if (!d)
return;
bool ok;
QString newName = QInputDialog::getText(this, tr("Edit name of bookmark"),
tr("Custom name:"), QLineEdit::Normal,
event->name.c_str(), &ok);
item->ev.name.c_str(), &ok);
if (ok && !newName.isEmpty()) {
if (newName.length() > 22) { //longer names will display as garbage.
QMessageBox lengthWarning;
@ -795,7 +795,7 @@ void ProfileWidget2::editName(DiveEventItem *item)
lengthWarning.exec();
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;
// start with the first gasmix and at the start of the plotted range
const struct event *ev = NULL;
struct gasmix gasmix = gasmix_air;
gasmix = get_gasmix(d, dc, plotStartTime, &ev, gasmix);
event_loop loop("gaschange");
struct gasmix gasmix = gasmix_invalid;
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
int startTime = plotStartTime;
while (ev && (int)ev->time.seconds < plotEndTime) {
createBar(startTime, ev->time.seconds, gasmix);
startTime = ev->time.seconds;
gasmix = get_gasmix_from_event(d, ev);
ev = get_next_event(ev->next, "gaschange");
gasmix = get_gasmix_from_event(d, *ev);
ev = loop.next(*dc);
}
createBar(startTime, plotEndTime, gasmix);
}

View file

@ -3,6 +3,7 @@
#include "core/dive.h"
#include "core/divelist.h"
#include "core/divelog.h"
#include "core/event.h"
#include "core/subsurface-string.h"
#include "qt-models/cylindermodel.h"
#include "qt-models/models.h" // For defaultModelFont().
@ -119,8 +120,6 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn)
int samplecount = 0;
o2pressure_t last_sp;
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);
duration_t lasttime;
duration_t lastrecordedtime;
@ -150,6 +149,7 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn)
int cylinderid = 0;
last_sp.mbar = 0;
divemode_loop loop(*dc);
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)
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)
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);
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
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)
addStop(0, dc->duration.seconds,cylinderid, last_sp.mbar, true, current_divemode);
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
* 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;
while (ev) {
if ((ev->time.seconds == t) && (ev->type == SAMPLE_EVENT_BOOKMARK))
return ev;
ev = ev->next;
}
return NULL;
auto it = std::find_if(dc.events.begin(), dc.events.end(),
[t](auto &ev)
{ return ev.time.seconds == t && ev.type == SAMPLE_EVENT_BOOKMARK; });
return it != dc.events.end() ? &*it : nullptr;
}
/*
@ -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)) {
time = lrint(strtod(table.get_data(4), NULL) * 60);
const char *tmp = table.get_data(2);
ev = find_bookmark(d->dc.events, time);
ev = find_bookmark(d->dc, time);
if (ev)
ev->name = tmp;
else

View file

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