summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Dirk Hohndel <dirk@hohndel.org>2013-01-06 22:09:12 -0800
committerGravatar Dirk Hohndel <dirk@hohndel.org>2013-01-06 22:15:03 -0800
commitbe9be189f7350cfb92899680907735719566631e (patch)
treed7d5a8a33d14a0f6782b08328ede15b96c8c002e
parentfb99c9a16383ffd00a7b71f85bdc5556e9c86cae (diff)
downloadsubsurface-be9be189f7350cfb92899680907735719566631e.tar.gz
Display dive profile of the dive we plan, as we plan it
As the user enters data into the entry fields, that data is validated and as soon as there is enough data we start constructing a dive profile, including the final ascent to the surface, including required deco stops, etc. This commit still has some serious issues. - when data is input that doesn't validate, we just print a warning to stdout - instead we need to change the backgroundcolor of the input field or something. - when we switch to the last dive in order to show the profile we don't actually search for the last dive - we just show the first one in the tree. This works for the default sort order but is of course wrong otherwise I'm sure there are many other bugs, but I want to push it out where it is right now for others to be able to take a look. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r--dive.h6
-rw-r--r--divelist.c24
-rw-r--r--divelist.h1
-rw-r--r--gtk-gui.c110
-rw-r--r--planner.c80
5 files changed, 203 insertions, 18 deletions
diff --git a/dive.h b/dive.h
index aae57e8b7..697ab8512 100644
--- a/dive.h
+++ b/dive.h
@@ -595,8 +595,12 @@ struct diveplan {
};
extern void test_planner(void);
-void plan(struct diveplan *diveplan);
+void plan(struct diveplan *diveplan, char **cache_datap, struct dive **divep);
void plan_add_segment(struct diveplan *diveplan, int duration, int depth, int o2, int he);
+void add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration);
+void add_depth_to_nth_dp(struct diveplan *diveplan, int idx, int depth);
+void add_gas_to_nth_dp(struct diveplan *diveplan, int idx, int o2, int he);
+void free_dps(struct divedatapoint *dp);
#ifdef DEBUGFILE
extern char *debugfilename;
diff --git a/divelist.c b/divelist.c
index fd141ba28..6397f62fd 100644
--- a/divelist.c
+++ b/divelist.c
@@ -1304,6 +1304,30 @@ static void clear_trip_indexes(void)
trip->index = 0;
}
+void select_last_dive(void)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ struct dive *dive;
+ int i;
+
+ /* select the last dive (and make sure it's an actual dive that is selected) */
+ /* WARNING - this only works when sorted by date!!!
+ *
+ *
+ *
+ */
+ gtk_tree_model_get_iter_first(MODEL(dive_list), &iter);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view));
+ gtk_tree_selection_unselect_all(selection);
+ for_each_dive(i, dive)
+ dive->selected = FALSE;
+ amount_selected = 0;
+ gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &selected_dive, -1);
+ first_leaf(MODEL(dive_list), &iter, &selected_dive);
+ gtk_tree_selection_select_iter(selection, &iter);
+}
+
static void fill_dive_list(void)
{
int i, trip_index = 0;
diff --git a/divelist.h b/divelist.h
index 1690ec657..89762187b 100644
--- a/divelist.h
+++ b/divelist.h
@@ -15,5 +15,6 @@ extern void remember_tree_state(void);
extern void restore_tree_state(void);
extern void select_next_dive(void);
extern void select_prev_dive(void);
+extern void select_last_dive(void);
extern double init_decompression(struct dive * dive);
#endif
diff --git a/gtk-gui.c b/gtk-gui.c
index ee6008c1a..70be30120 100644
--- a/gtk-gui.c
+++ b/gtk-gui.c
@@ -1322,19 +1322,106 @@ static GtkWidget *add_entry_to_box(GtkWidget *box, const char *label)
GtkWidget *entry_depth[MAX_WAYPOINTS], *entry_duration[MAX_WAYPOINTS], *entry_gas[MAX_WAYPOINTS];
int nr_waypoints = 0;
static GtkListStore *gas_model = NULL;
+struct diveplan diveplan = {};
+char *cache_data = NULL;
+struct dive *planned_dive = NULL;
+
+/* make a copy of the diveplan so far and display the corresponding dive */
+void show_planned_dive(void)
+{
+ struct diveplan tempplan;
+ struct divedatapoint *dp, **dpp;
+
+ memcpy(&tempplan, &diveplan, sizeof(struct diveplan));
+ dpp = &tempplan.dp;
+ dp = diveplan.dp;
+ while(*dpp) {
+ *dpp = malloc(sizeof(struct divedatapoint));
+ memcpy(*dpp, dp, sizeof(struct divedatapoint));
+ dp = dp->next;
+ if (dp && !dp->time) {
+ /* we have an incomplete entry - stop before it */
+ (*dpp)->next = NULL;
+ break;
+ }
+ dpp = &(*dpp)->next;
+ }
+ plan(&tempplan, &cache_data, &planned_dive);
+ free_dps(tempplan.dp);
+}
-static gboolean gas_focus_out_cb(GtkWidget *entry, gpointer data)
+static gboolean gas_focus_out_cb(GtkWidget *entry, GdkEvent *event, gpointer data)
{
char *gastext;
int o2, he;
+ int idx = data - NULL;
gastext = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
- if (validate_gas(gastext, &o2, &he))
+ if (validate_gas(gastext, &o2, &he)) {
add_string_list_entry(gastext, gas_model);
+ add_gas_to_nth_dp(&diveplan, idx, o2, he);
+ show_planned_dive();
+ } else {
+ /* we need to instead change the color of the input field or something */
+ printf("invalid gas for row %d\n",idx);
+ }
free(gastext);
return FALSE;
}
+static gboolean depth_focus_out_cb(GtkWidget *entry, GdkEvent *event, gpointer data)
+{
+ char *depthtext;
+ int depth;
+ int idx = data - NULL;
+
+ depthtext = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+ if (validate_depth(depthtext, &depth)) {
+ add_depth_to_nth_dp(&diveplan, idx, depth);
+ show_planned_dive();
+ } else {
+ /* we need to instead change the color of the input field or something */
+ printf("invalid depth for row %d\n", idx);
+ }
+ free(depthtext);
+ return FALSE;
+}
+
+static gboolean duration_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data)
+{
+ char *durationtext;
+ int duration, is_rel;
+ int idx = data - NULL;
+
+ durationtext = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+ if (validate_time(durationtext, &duration, &is_rel)) {
+ add_duration_to_nth_dp(&diveplan, idx, duration);
+ show_planned_dive();
+ } else {
+ /* we need to instead change the color of the input field or something */
+ printf("invalid duration for row %d\n", idx);
+ }
+ free(durationtext);
+ return FALSE;
+}
+
+static gboolean starttime_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data)
+{
+ char *starttimetext;
+ int starttime, is_rel;
+
+ starttimetext = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+ if (validate_time(starttimetext, &starttime, &is_rel)) {
+ /* we alway make this relative for now */
+ diveplan.when = time(NULL) + starttime;
+ } else {
+ /* we need to instead change the color of the input field or something */
+ printf("invalid starttime\n");
+ }
+ free(starttimetext);
+ return FALSE;
+}
+
static GtkWidget *add_gas_combobox_to_box(GtkWidget *box, const char *label)
{
GtkWidget *frame, *combo;
@@ -1386,6 +1473,10 @@ static void add_waypoint_widgets(GtkWidget *box, int idx)
entry_duration[idx] = add_entry_to_box(hbox, NULL);
entry_gas[idx] = add_gas_combobox_to_box(hbox, NULL);
}
+ gtk_widget_add_events(entry_depth[idx], GDK_FOCUS_CHANGE_MASK);
+ g_signal_connect(entry_depth[idx], "focus-out-event", G_CALLBACK(depth_focus_out_cb), NULL + idx);
+ gtk_widget_add_events(entry_duration[idx], GDK_FOCUS_CHANGE_MASK);
+ g_signal_connect(entry_duration[idx], "focus-out-event", G_CALLBACK(duration_focus_out_cb), NULL + idx);
}
static void add_waypoint_cb(GtkButton *button, gpointer _data)
@@ -1407,8 +1498,12 @@ void input_plan()
{
GtkWidget *planner, *content, *vbox, *outervbox, *add_row, *deltat;
int lasttime = 0;
- struct diveplan diveplan = {};
+ char starttimebuf[64] = "+60:00";
+ if (diveplan.dp)
+ free_dps(diveplan.dp);
+ memset(&diveplan, 0, sizeof(diveplan));
+ planned_dive = NULL;
planner = gtk_dialog_new_with_buttons(_("Dive Plan - THIS IS JUST A SIMULATION; DO NOT USE FOR DIVING"),
GTK_WINDOW(main_window),
GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -1422,6 +1517,11 @@ void input_plan()
vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(outervbox), vbox, TRUE, TRUE, 0);
deltat = add_entry_to_box(vbox, _("Dive starts in how many minutes?"));
+ gtk_entry_set_max_length(GTK_ENTRY(deltat), 12);
+ gtk_entry_set_text(GTK_ENTRY(deltat), starttimebuf);
+ gtk_widget_add_events(deltat, GDK_FOCUS_CHANGE_MASK);
+ g_signal_connect(deltat, "focus-out-event", G_CALLBACK(starttime_focus_out_cb), NULL);
+ diveplan.when = time(NULL) + 3600;
nr_waypoints = 4;
add_waypoint_widgets(vbox, 0);
add_waypoint_widgets(vbox, 1);
@@ -1437,6 +1537,8 @@ void input_plan()
deltattext = gtk_entry_get_text(GTK_ENTRY(deltat));
diveplan.when = time(NULL) + 60 * atoi(deltattext);
+ free_dps(diveplan.dp);
+ diveplan.dp = 0;
for (i = 0; i < nr_waypoints; i++) {
char *depthtext, *durationtext, *gastext;
int depth, duration, o2, he, is_rel;
@@ -1477,7 +1579,7 @@ void input_plan()
free(gastext);
}
}
- plan(&diveplan);
+ show_planned_dive();
gtk_widget_destroy(planner);
}
diff --git a/planner.c b/planner.c
index 9d20d376f..13a36e62b 100644
--- a/planner.c
+++ b/planner.c
@@ -8,7 +8,7 @@
#include "dive.h"
#include "divelist.h"
-int stoplevels[] = { 3000, 6000, 9000, 12000, 15000, 21000, 30000, 42000, 60000, 90000 };
+int stoplevels[] = { 0, 3000, 6000, 9000, 12000, 15000, 21000, 30000, 42000, 60000, 90000 };
/* returns the tissue tolerance at the end of this (partial) dive */
double tissue_at_end(struct dive *dive, char **cached_datap)
@@ -108,7 +108,7 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan)
dc = &dive->dc;
dc->model = "Simulated Dive";
dp = diveplan->dp;
- while (dp) {
+ while (dp && dp->time) {
int i, depth;
if (dp->o2 != dive->cylinder[gasused].gasmix.o2.permille ||
@@ -132,9 +132,23 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan)
dp = dp->next;
dc->samples++;
}
+ if (dc->samples == 0) {
+ /* not enough there yet to create a dive - most likely the first time is missing */
+ free(dive);
+ dive = NULL;
+ }
return dive;
}
+void free_dps(struct divedatapoint *dp)
+{
+ while (dp) {
+ struct divedatapoint *ndp = dp->next;
+ free(dp);
+ dp = ndp;
+ }
+}
+
struct divedatapoint *create_dp(int time_incr, int depth, int o2, int he)
{
struct divedatapoint *dp;
@@ -148,6 +162,41 @@ struct divedatapoint *create_dp(int time_incr, int depth, int o2, int he)
return dp;
}
+struct divedatapoint *get_nth_dp(struct diveplan *diveplan, int idx)
+{
+ struct divedatapoint **ldpp, *dp = diveplan->dp;
+ int i = 0;
+ ldpp = &diveplan->dp;
+
+ while (dp && i++ < idx) {
+ ldpp = &dp->next;
+ dp = dp->next;
+ }
+ while (i++ <= idx) {
+ *ldpp = dp = create_dp(0, 0, 0, 0);
+ ldpp = &((*ldpp)->next);
+ }
+ return dp;
+}
+
+void add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration)
+{
+ struct divedatapoint *dp = get_nth_dp(diveplan, idx);
+ dp->time = duration;
+}
+
+void add_depth_to_nth_dp(struct diveplan *diveplan, int idx, int depth)
+{
+ struct divedatapoint *dp = get_nth_dp(diveplan, idx);
+ dp->depth = depth;
+}
+
+void add_gas_to_nth_dp(struct diveplan *diveplan, int idx, int o2, int he)
+{
+ struct divedatapoint *dp = get_nth_dp(diveplan, idx);
+ dp->o2 = o2;
+ dp->he = he;
+}
void add_to_end_of_diveplan(struct diveplan *diveplan, struct divedatapoint *dp)
{
struct divedatapoint **lastdp = &diveplan->dp;
@@ -167,7 +216,7 @@ void plan_add_segment(struct diveplan *diveplan, int duration, int depth, int o2
add_to_end_of_diveplan(diveplan, dp);
}
-void plan(struct diveplan *diveplan)
+void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep)
{
struct dive *dive;
struct sample *sample;
@@ -175,11 +224,12 @@ void plan(struct diveplan *diveplan)
int ceiling, depth, transitiontime;
int stopidx;
double tissue_tolerance;
- char *cached_data = NULL;
if (!diveplan->surface_pressure)
diveplan->surface_pressure = 1013;
- dive = create_dive_from_plan(diveplan);
+ if (*divep)
+ delete_single_dive(dive_table.nr - 1);
+ *divep = dive = create_dive_from_plan(diveplan);
if (!dive)
return;
record_dive(dive);
@@ -188,42 +238,44 @@ void plan(struct diveplan *diveplan)
o2 = dive->cylinder[sample->sensor].gasmix.o2.permille;
he = dive->cylinder[sample->sensor].gasmix.he.permille;
- tissue_tolerance = tissue_at_end(dive, &cached_data);
+ tissue_tolerance = tissue_at_end(dive, cached_datap);
ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1);
- for (stopidx = 0; stopidx < sizeof(stoplevels) / sizeof(int); stopidx++)
+ for (stopidx = 1; stopidx < sizeof(stoplevels) / sizeof(int); stopidx++)
if (stoplevels[stopidx] >= ceiling)
break;
- while (stopidx >= 0) {
+ while (stopidx > 0) {
depth = dive->dc.sample[dive->dc.samples - 1].depth.mm;
if (depth > stoplevels[stopidx]) {
transitiontime = (depth - stoplevels[stopidx]) / 150;
plan_add_segment(diveplan, transitiontime, stoplevels[stopidx], o2, he);
/* re-create the dive */
delete_single_dive(dive_table.nr - 1);
- dive = create_dive_from_plan(diveplan);
+ *divep = dive = create_dive_from_plan(diveplan);
record_dive(dive);
}
- wait_time = time_at_last_depth(dive, stoplevels[stopidx - 1], &cached_data);
+ wait_time = time_at_last_depth(dive, stoplevels[stopidx - 1], cached_datap);
if (wait_time)
plan_add_segment(diveplan, wait_time, stoplevels[stopidx], o2, he);
transitiontime = (stoplevels[stopidx] - stoplevels[stopidx - 1]) / 150;
plan_add_segment(diveplan, transitiontime, stoplevels[stopidx - 1], o2, he);
/* re-create the dive */
delete_single_dive(dive_table.nr - 1);
- dive = create_dive_from_plan(diveplan);
+ *divep = dive = create_dive_from_plan(diveplan);
record_dive(dive);
stopidx--;
}
/* now make the dive visible as last dive of the dive list */
- free(cached_data);
report_dives(FALSE, FALSE);
+ select_last_dive();
}
void test_planner()
{
+ struct dive *dive = NULL;
struct diveplan diveplan = {};
+ char *cache_data;
int end_of_last_dive = dive_table.dives[dive_table.nr - 1]->when + dive_table.dives[dive_table.nr -1]->duration.seconds;
diveplan.when = end_of_last_dive + 50 * 3600; /* don't take previous dives into account for deco calculation */
diveplan.surface_pressure = 1013;
@@ -232,5 +284,7 @@ void test_planner()
plan_add_segment(&diveplan, 59, 27000, 209, 0);
plan_add_segment(&diveplan, 1, 27000, 400, 0);
- plan(&diveplan);
+ plan(&diveplan, &cache_data, &dive);
+ /* we are not rerunning the plan, so free the cache right away */
+ free(cache_data);
}