]> git.tdb.fi Git - ext/subsurface.git/blob - libdivecomputer.c
Label the temperature graph
[ext/subsurface.git] / libdivecomputer.c
1 #include <stdio.h>
2 #include <gtk/gtk.h>
3
4 #include "dive.h"
5 #include "divelist.h"
6 #include "display.h"
7
8 /* libdivecomputer */
9 #include <device.h>
10 #include <suunto.h>
11 #include <reefnet.h>
12 #include <uwatec.h>
13 #include <oceanic.h>
14 #include <mares.h>
15 #include <hw.h>
16 #include <cressi.h>
17 #include <zeagle.h>
18 #include <atomics.h>
19 #include <utils.h>
20
21 /*
22  * I'd love to do a while-loop here for pending events, but
23  * that seems to screw up with the dive computer reading timing.
24  *
25  * I may need to spawn a new thread to do the computer
26  * reading stuff..
27  */
28 static int run_gtk_mainloop(void)
29 {
30         return gtk_main_iteration_do(0);
31 }
32
33 static void error(const char *fmt, ...)
34 {
35         va_list args;
36         GError *error;
37
38         va_start(args, fmt);
39         error = g_error_new_valist(
40                 g_quark_from_string("divelog"),
41                 DIVE_ERROR_PARSE, fmt, args);
42         va_end(args);
43         report_error(error);
44         g_error_free(error);
45 }
46
47 typedef struct device_data_t {
48         device_type_t type;
49         const char *name, *devname;
50         GtkWidget *progressbar;
51         device_devinfo_t devinfo;
52         device_clock_t clock;
53 } device_data_t;
54
55 static parser_status_t create_parser(device_data_t *devdata, parser_t **parser)
56 {
57         switch (devdata->type) {
58         case DEVICE_TYPE_SUUNTO_SOLUTION:
59                 return suunto_solution_parser_create(parser);
60
61         case DEVICE_TYPE_SUUNTO_EON:
62                 return suunto_eon_parser_create(parser, 0);
63
64         case DEVICE_TYPE_SUUNTO_VYPER:
65                 if (devdata->devinfo.model == 0x01)
66                         return suunto_eon_parser_create(parser, 1);
67                 return suunto_vyper_parser_create(parser);
68
69         case DEVICE_TYPE_SUUNTO_VYPER2:
70         case DEVICE_TYPE_SUUNTO_D9:
71                 return suunto_d9_parser_create(parser, devdata->devinfo.model);
72
73         case DEVICE_TYPE_UWATEC_ALADIN:
74         case DEVICE_TYPE_UWATEC_MEMOMOUSE:
75                 return uwatec_memomouse_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
76
77         case DEVICE_TYPE_UWATEC_SMART:
78                 return uwatec_smart_parser_create(parser, devdata->devinfo.model, devdata->clock.devtime, devdata->clock.systime);
79
80         case DEVICE_TYPE_REEFNET_SENSUS:
81                 return reefnet_sensus_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
82
83         case DEVICE_TYPE_REEFNET_SENSUSPRO:
84                 return reefnet_sensuspro_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
85
86         case DEVICE_TYPE_REEFNET_SENSUSULTRA:
87                 return reefnet_sensusultra_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
88
89         case DEVICE_TYPE_OCEANIC_VTPRO:
90                 return oceanic_vtpro_parser_create(parser);
91
92         case DEVICE_TYPE_OCEANIC_VEO250:
93                 return oceanic_veo250_parser_create(parser, devdata->devinfo.model);
94
95         case DEVICE_TYPE_OCEANIC_ATOM2:
96                 return oceanic_atom2_parser_create(parser, devdata->devinfo.model);
97
98         case DEVICE_TYPE_MARES_NEMO:
99         case DEVICE_TYPE_MARES_PUCK:
100                 return mares_nemo_parser_create(parser, devdata->devinfo.model);
101
102         case DEVICE_TYPE_MARES_ICONHD:
103                 return mares_iconhd_parser_create(parser);
104
105         case DEVICE_TYPE_HW_OSTC:
106                 return hw_ostc_parser_create(parser);
107
108         case DEVICE_TYPE_CRESSI_EDY:
109         case DEVICE_TYPE_ZEAGLE_N2ITION3:
110                 return cressi_edy_parser_create(parser, devdata->devinfo.model);
111
112         case DEVICE_TYPE_ATOMICS_COBALT:
113                 return atomics_cobalt_parser_create(parser);
114
115         default:
116                 return PARSER_STATUS_ERROR;
117         }
118 }
119
120 static int parse_gasmixes(struct dive *dive, parser_t *parser, int ngases)
121 {
122         int i;
123
124         for (i = 0; i < ngases; i++) {
125                 int rc;
126                 gasmix_t gasmix = {0};
127                 int o2, he;
128
129                 rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
130                 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
131                         return rc;
132
133                 if (i >= MAX_CYLINDERS)
134                         continue;
135
136                 o2 = gasmix.oxygen * 1000 + 0.5;
137                 he = gasmix.helium * 1000 + 0.5;
138
139                 /* Ignore bogus data - libdivecomputer does some crazy stuff */
140                 if (o2 < 210 || o2 >= 1000)
141                         o2 = 0;
142                 if (he < 0 || he >= 800 || o2+he >= 1000)
143                         he = 0;
144
145                 dive->cylinder[i].gasmix.o2.permille = o2;
146                 dive->cylinder[i].gasmix.he.permille = he;
147         }
148         return PARSER_STATUS_SUCCESS;
149 }
150
151 void
152 sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
153 {
154         int i;
155         static const char *events[] = {
156                 "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
157                 "violation", "bookmark", "surface", "safety stop", "gaschange",
158                 "safety stop (voluntary)", "safety stop (mandatory)", "deepstop",
159                 "ceiling (safety stop)", "unknown", "divetime", "maxdepth",
160                 "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning"};
161         struct dive **divep = userdata;
162         struct dive *dive = *divep;
163         struct sample *sample;
164
165         /*
166          * We fill in the "previous" sample - except for SAMPLE_TYPE_TIME,
167          * which creates a new one.
168          */
169         sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
170
171         switch (type) {
172         case SAMPLE_TYPE_TIME:
173                 sample = prepare_sample(divep);
174                 sample->time.seconds = value.time;
175                 finish_sample(*divep, sample);
176                 break;
177         case SAMPLE_TYPE_DEPTH:
178                 sample->depth.mm = value.depth * 1000 + 0.5;
179                 break;
180         case SAMPLE_TYPE_PRESSURE:
181                 sample->cylinderindex = value.pressure.tank;
182                 sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
183                 break;
184         case SAMPLE_TYPE_TEMPERATURE:
185                 sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
186                 break;
187         case SAMPLE_TYPE_EVENT:
188                 printf("   <event type=\"%u\" time=\"%u\" flags=\"%u\" value=\"%u\">%s</event>\n",
189                         value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]);
190                 break;
191         case SAMPLE_TYPE_RBT:
192                 printf("   <rbt>%u</rbt>\n", value.rbt);
193                 break;
194         case SAMPLE_TYPE_HEARTBEAT:
195                 printf("   <heartbeat>%u</heartbeat>\n", value.heartbeat);
196                 break;
197         case SAMPLE_TYPE_BEARING:
198                 printf("   <bearing>%u</bearing>\n", value.bearing);
199                 break;
200         case SAMPLE_TYPE_VENDOR:
201                 printf("   <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
202                 for (i = 0; i < value.vendor.size; ++i)
203                         printf("%02X", ((unsigned char *) value.vendor.data)[i]);
204                 printf("</vendor>\n");
205                 break;
206         default:
207                 break;
208         }
209 }
210
211
212 static int parse_samples(struct dive **divep, parser_t *parser)
213 {
214         // Parse the sample data.
215         printf("Parsing the sample data.\n");
216         return parser_samples_foreach(parser, sample_cb, divep);
217 }
218
219 static int dive_cb(const unsigned char *data, unsigned int size,
220         const unsigned char *fingerprint, unsigned int fsize,
221         void *userdata)
222 {
223         int rc;
224         parser_t *parser = NULL;
225         device_data_t *devdata = userdata;
226         dc_datetime_t dt = {0};
227         struct tm tm;
228         struct dive *dive;
229
230         /* Christ, this is hacky */
231         run_gtk_mainloop();
232
233         rc = create_parser(devdata, &parser);
234         if (rc != PARSER_STATUS_SUCCESS) {
235                 error("Unable to create parser for %s", devdata->name);
236                 return rc;
237         }
238
239         rc = parser_set_data(parser, data, size);
240         if (rc != PARSER_STATUS_SUCCESS) {
241                 error("Error registering the data.");
242                 parser_destroy(parser);
243                 return rc;
244         }
245
246         dive = alloc_dive();
247         rc = parser_get_datetime(parser, &dt);
248         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
249                 error("Error parsing the datetime.");
250                 parser_destroy (parser);
251                 return rc;
252         }
253
254         tm.tm_year = dt.year;
255         tm.tm_mon = dt.month-1;
256         tm.tm_mday = dt.day;
257         tm.tm_hour = dt.hour;
258         tm.tm_min = dt.minute;
259         tm.tm_sec = dt.second;
260         dive->when = utc_mktime(&tm);
261
262         // Parse the divetime.
263         printf("Parsing the divetime.\n");
264         unsigned int divetime = 0;
265         rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
266         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
267                 error("Error parsing the divetime.");
268                 parser_destroy(parser);
269                 return rc;
270         }
271         dive->duration.seconds = divetime;
272
273         // Parse the maxdepth.
274         printf("Parsing the maxdepth.\n");
275         double maxdepth = 0.0;
276         rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
277         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
278                 error("Error parsing the maxdepth.");
279                 parser_destroy(parser);
280                 return rc;
281         }
282         dive->maxdepth.mm = maxdepth * 1000 + 0.5;
283
284         // Parse the gas mixes.
285         printf("Parsing the gas mixes.\n");
286         unsigned int ngases = 0;
287         rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
288         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
289                 error("Error parsing the gas mix count.");
290                 parser_destroy(parser);
291                 return rc;
292         }
293
294         rc = parse_gasmixes(dive, parser, ngases);
295         if (rc != PARSER_STATUS_SUCCESS) {
296                 error("Error parsing the gas mix.");
297                 parser_destroy(parser);
298                 return rc;
299         }
300
301         // Initialize the sample data.
302         rc = parse_samples(&dive, parser);
303         if (rc != PARSER_STATUS_SUCCESS) {
304                 error("Error parsing the samples.");
305                 parser_destroy(parser);
306                 return rc;
307         }
308         record_dive(dive);
309
310         parser_destroy(parser);
311         return 1;
312 }
313
314
315 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
316 {
317         return device_foreach(device, dive_cb, devicedata);
318 }
319
320 static device_status_t device_open(const char *devname,
321         device_type_t type,
322         device_t **device)
323 {
324         switch (type) {
325         case DEVICE_TYPE_SUUNTO_SOLUTION:
326                 return suunto_solution_device_open(device, devname);
327
328         case DEVICE_TYPE_SUUNTO_EON:
329                 return suunto_eon_device_open(device, devname);
330
331         case DEVICE_TYPE_SUUNTO_VYPER:
332                 return suunto_vyper_device_open(device, devname);
333
334         case DEVICE_TYPE_SUUNTO_VYPER2:
335                 return suunto_vyper2_device_open(device, devname);
336
337         case DEVICE_TYPE_SUUNTO_D9:
338                 return suunto_d9_device_open(device, devname);
339
340         case DEVICE_TYPE_UWATEC_ALADIN:
341                 return uwatec_aladin_device_open(device, devname);
342
343         case DEVICE_TYPE_UWATEC_MEMOMOUSE:
344                 return uwatec_memomouse_device_open(device, devname);
345
346         case DEVICE_TYPE_UWATEC_SMART:
347                 return uwatec_smart_device_open(device);
348
349         case DEVICE_TYPE_REEFNET_SENSUS:
350                 return reefnet_sensus_device_open(device, devname);
351
352         case DEVICE_TYPE_REEFNET_SENSUSPRO:
353                 return reefnet_sensuspro_device_open(device, devname);
354
355         case DEVICE_TYPE_REEFNET_SENSUSULTRA:
356                 return reefnet_sensusultra_device_open(device, devname);
357
358         case DEVICE_TYPE_OCEANIC_VTPRO:
359                 return oceanic_vtpro_device_open(device, devname);
360
361         case DEVICE_TYPE_OCEANIC_VEO250:
362                 return oceanic_veo250_device_open(device, devname);
363
364         case DEVICE_TYPE_OCEANIC_ATOM2:
365                 return oceanic_atom2_device_open(device, devname);
366
367         case DEVICE_TYPE_MARES_NEMO:
368                 return mares_nemo_device_open(device, devname);
369
370         case DEVICE_TYPE_MARES_PUCK:
371                 return mares_puck_device_open(device, devname);
372
373         case DEVICE_TYPE_MARES_ICONHD:
374                 return mares_iconhd_device_open(device, devname);
375
376         case DEVICE_TYPE_HW_OSTC:
377                 return hw_ostc_device_open(device, devname);
378
379         case DEVICE_TYPE_CRESSI_EDY:
380                 return cressi_edy_device_open(device, devname);
381
382         case DEVICE_TYPE_ZEAGLE_N2ITION3:
383                 return zeagle_n2ition3_device_open(device, devname);
384
385         case DEVICE_TYPE_ATOMICS_COBALT:
386                 return atomics_cobalt_device_open(device);
387
388         default:
389                 return DEVICE_STATUS_ERROR;
390         }
391 }
392
393 static void
394 event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
395 {
396         const device_progress_t *progress = (device_progress_t *) data;
397         const device_devinfo_t *devinfo = (device_devinfo_t *) data;
398         const device_clock_t *clock = (device_clock_t *) data;
399         device_data_t *devdata = (device_data_t *) userdata;
400
401         /* Christ, this is hacky */
402         run_gtk_mainloop();
403
404         switch (event) {
405         case DEVICE_EVENT_WAITING:
406                 printf("Event: waiting for user action\n");
407                 break;
408         case DEVICE_EVENT_PROGRESS:
409                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(devdata->progressbar),
410                         (double) progress->current / (double) progress->maximum);
411                 break;
412         case DEVICE_EVENT_DEVINFO:
413                 devdata->devinfo = *devinfo;
414                 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
415                         devinfo->model, devinfo->model,
416                         devinfo->firmware, devinfo->firmware,
417                         devinfo->serial, devinfo->serial);
418                 break;
419         case DEVICE_EVENT_CLOCK:
420                 devdata->clock = *clock;
421                 printf("Event: systime=%lld, devtime=%u\n",
422                         clock->systime, clock->devtime);
423                 break;
424         default:
425                 break;
426         }
427 }
428
429 static int
430 cancel_cb(void *userdata)
431 {
432         return run_gtk_mainloop();
433 }
434
435 static void do_import(device_data_t *data)
436 {
437         /* FIXME! Needs user input! */
438         const char *devname = "/dev/ttyUSB0";
439         device_t *device = NULL;
440         device_status_t rc;
441
442         rc = device_open(devname, data->type, &device);
443         if (rc != DEVICE_STATUS_SUCCESS) {
444                 error("Unable to open %s (%s)", data->name, data->devname);
445                 return;
446         }
447
448         // Register the event handler.
449         int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
450         rc = device_set_events(device, events, event_cb, data);
451         if (rc != DEVICE_STATUS_SUCCESS) {
452                 error("Error registering the event handler.");
453                 device_close(device);
454                 return;
455         }
456
457         // Register the cancellation handler.
458         rc = device_set_cancel(device, cancel_cb, data);
459         if (rc != DEVICE_STATUS_SUCCESS) {
460                 error("Error registering the cancellation handler.");
461                 device_close(device);
462                 return;
463         }
464
465         rc = import_device_data(device, data);
466         if (rc != DEVICE_STATUS_SUCCESS) {
467                 error("Dive data import error");
468                 device_close(device);
469                 return;
470         }
471
472         device_close(device);
473 }
474
475 /*
476  * Taken from 'example.c' in libdivecomputer.
477  *
478  * I really wish there was some way to just have
479  * libdivecomputer tell us what devices it supports,
480  * rather than have the application have to know..
481  */
482 struct device_list {
483         const char *name;
484         device_type_t type;
485 } device_list[] = {
486         { "Suunto Solution",    DEVICE_TYPE_SUUNTO_SOLUTION },
487         { "Suunto Eon",         DEVICE_TYPE_SUUNTO_EON },
488         { "Suunto Vyper",       DEVICE_TYPE_SUUNTO_VYPER },
489         { "Suunto Vyper Air",   DEVICE_TYPE_SUUNTO_VYPER2 },
490         { "Suunto D9",          DEVICE_TYPE_SUUNTO_D9 },
491         { "Uwatec Aladin",      DEVICE_TYPE_UWATEC_ALADIN },
492         { "Uwatec Memo Mouse",  DEVICE_TYPE_UWATEC_MEMOMOUSE },
493         { "Uwatec Smart",       DEVICE_TYPE_UWATEC_SMART },
494         { "ReefNet Sensus",     DEVICE_TYPE_REEFNET_SENSUS },
495         { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
496         { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
497         { "Oceanic VT Pro",     DEVICE_TYPE_OCEANIC_VTPRO },
498         { "Oceanic Veo250",     DEVICE_TYPE_OCEANIC_VEO250 },
499         { "Oceanic Atom 2",     DEVICE_TYPE_OCEANIC_ATOM2 },
500         { "Mares Nemo",         DEVICE_TYPE_MARES_NEMO },
501         { "Mares Puck",         DEVICE_TYPE_MARES_PUCK },
502         { "Mares Icon HD",      DEVICE_TYPE_MARES_ICONHD },
503         { "OSTC",               DEVICE_TYPE_HW_OSTC },
504         { "Cressi Edy",         DEVICE_TYPE_CRESSI_EDY },
505         { "Zeagle N2iTiON 3",   DEVICE_TYPE_ZEAGLE_N2ITION3 },
506         { "Atomics Cobalt",     DEVICE_TYPE_ATOMICS_COBALT },
507         { NULL }
508 };
509
510 static void fill_computer_list(GtkListStore *store)
511 {
512         GtkTreeIter iter;
513         struct device_list *list = device_list;
514
515         for (list = device_list ; list->name ; list++) {
516                 gtk_list_store_append(store, &iter);
517                 gtk_list_store_set(store, &iter,
518                         0, list->name,
519                         1, list->type,
520                         -1);
521         }
522 }
523
524 static GtkComboBox *dive_computer_selector(GtkWidget *dialog)
525 {
526         GtkWidget *hbox, *combo_box;
527         GtkListStore *model;
528         GtkCellRenderer *renderer;
529
530         hbox = gtk_hbox_new(FALSE, 6);
531         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 3);
532
533         model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
534         fill_computer_list(model);
535
536         combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
537         gtk_box_pack_start(GTK_BOX(hbox), combo_box, FALSE, TRUE, 3);
538
539         renderer = gtk_cell_renderer_text_new();
540         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE);
541         gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), renderer, "text", 0, NULL);
542
543         return GTK_COMBO_BOX(combo_box);
544 }
545
546 void import_dialog(GtkWidget *w, gpointer data)
547 {
548         int result;
549         GtkWidget *dialog, *hbox;
550         GtkComboBox *computer;
551         device_data_t devicedata = {
552                 .devname = "/dev/ttyUSB0",
553         };
554
555         dialog = gtk_dialog_new_with_buttons("Import from dive computer",
556                 GTK_WINDOW(main_window),
557                 GTK_DIALOG_DESTROY_WITH_PARENT,
558                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
559                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
560                 NULL);
561
562         computer = dive_computer_selector(dialog);
563
564         hbox = gtk_hbox_new(FALSE, 6);
565         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, TRUE, 3);
566         devicedata.progressbar = gtk_progress_bar_new();
567         gtk_container_add(GTK_CONTAINER(hbox), devicedata.progressbar);
568
569         gtk_widget_show_all(dialog);
570         result = gtk_dialog_run(GTK_DIALOG(dialog));
571         switch (result) {
572                 int type;
573                 GtkTreeIter iter;
574                 GtkTreeModel *model;
575                 const char *comp;
576         case GTK_RESPONSE_ACCEPT:
577                 if (!gtk_combo_box_get_active_iter(computer, &iter))
578                         break;
579                 model = gtk_combo_box_get_model(computer);
580                 gtk_tree_model_get(model, &iter,
581                         0, &comp,
582                         1, &type,
583                         -1);
584                 devicedata.type = type;
585                 devicedata.name = comp;
586                 do_import(&devicedata);
587                 break;
588         default:
589                 break;
590         }
591         gtk_widget_destroy(dialog);
592
593         report_dives();
594         dive_list_update_dives(dive_list);
595 }