]> git.tdb.fi Git - ext/subsurface.git/blob - libdivecomputer.c
Quick fix to hardcode device name only once
[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         device_t *device = NULL;
438         device_status_t rc;
439
440         rc = device_open(data->devname, data->type, &device);
441         if (rc != DEVICE_STATUS_SUCCESS) {
442                 error("Unable to open %s (%s)", data->name, data->devname);
443                 return;
444         }
445
446         // Register the event handler.
447         int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
448         rc = device_set_events(device, events, event_cb, data);
449         if (rc != DEVICE_STATUS_SUCCESS) {
450                 error("Error registering the event handler.");
451                 device_close(device);
452                 return;
453         }
454
455         // Register the cancellation handler.
456         rc = device_set_cancel(device, cancel_cb, data);
457         if (rc != DEVICE_STATUS_SUCCESS) {
458                 error("Error registering the cancellation handler.");
459                 device_close(device);
460                 return;
461         }
462
463         rc = import_device_data(device, data);
464         if (rc != DEVICE_STATUS_SUCCESS) {
465                 error("Dive data import error");
466                 device_close(device);
467                 return;
468         }
469
470         device_close(device);
471 }
472
473 /*
474  * Taken from 'example.c' in libdivecomputer.
475  *
476  * I really wish there was some way to just have
477  * libdivecomputer tell us what devices it supports,
478  * rather than have the application have to know..
479  */
480 struct device_list {
481         const char *name;
482         device_type_t type;
483 } device_list[] = {
484         { "Suunto Solution",    DEVICE_TYPE_SUUNTO_SOLUTION },
485         { "Suunto Eon",         DEVICE_TYPE_SUUNTO_EON },
486         { "Suunto Vyper",       DEVICE_TYPE_SUUNTO_VYPER },
487         { "Suunto Vyper Air",   DEVICE_TYPE_SUUNTO_VYPER2 },
488         { "Suunto D9",          DEVICE_TYPE_SUUNTO_D9 },
489         { "Uwatec Aladin",      DEVICE_TYPE_UWATEC_ALADIN },
490         { "Uwatec Memo Mouse",  DEVICE_TYPE_UWATEC_MEMOMOUSE },
491         { "Uwatec Smart",       DEVICE_TYPE_UWATEC_SMART },
492         { "ReefNet Sensus",     DEVICE_TYPE_REEFNET_SENSUS },
493         { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
494         { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
495         { "Oceanic VT Pro",     DEVICE_TYPE_OCEANIC_VTPRO },
496         { "Oceanic Veo250",     DEVICE_TYPE_OCEANIC_VEO250 },
497         { "Oceanic Atom 2",     DEVICE_TYPE_OCEANIC_ATOM2 },
498         { "Mares Nemo",         DEVICE_TYPE_MARES_NEMO },
499         { "Mares Puck",         DEVICE_TYPE_MARES_PUCK },
500         { "Mares Icon HD",      DEVICE_TYPE_MARES_ICONHD },
501         { "OSTC",               DEVICE_TYPE_HW_OSTC },
502         { "Cressi Edy",         DEVICE_TYPE_CRESSI_EDY },
503         { "Zeagle N2iTiON 3",   DEVICE_TYPE_ZEAGLE_N2ITION3 },
504         { "Atomics Cobalt",     DEVICE_TYPE_ATOMICS_COBALT },
505         { NULL }
506 };
507
508 static void fill_computer_list(GtkListStore *store)
509 {
510         GtkTreeIter iter;
511         struct device_list *list = device_list;
512
513         for (list = device_list ; list->name ; list++) {
514                 gtk_list_store_append(store, &iter);
515                 gtk_list_store_set(store, &iter,
516                         0, list->name,
517                         1, list->type,
518                         -1);
519         }
520 }
521
522 static GtkComboBox *dive_computer_selector(GtkWidget *dialog)
523 {
524         GtkWidget *hbox, *combo_box;
525         GtkListStore *model;
526         GtkCellRenderer *renderer;
527
528         hbox = gtk_hbox_new(FALSE, 6);
529         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 3);
530
531         model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
532         fill_computer_list(model);
533
534         combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
535         gtk_box_pack_start(GTK_BOX(hbox), combo_box, FALSE, TRUE, 3);
536
537         renderer = gtk_cell_renderer_text_new();
538         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE);
539         gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), renderer, "text", 0, NULL);
540
541         return GTK_COMBO_BOX(combo_box);
542 }
543
544 void import_dialog(GtkWidget *w, gpointer data)
545 {
546         int result;
547         GtkWidget *dialog, *hbox;
548         GtkComboBox *computer;
549         device_data_t devicedata = {
550                 .devname = "/dev/ttyUSB0",
551         };
552
553         dialog = gtk_dialog_new_with_buttons("Import from dive computer",
554                 GTK_WINDOW(main_window),
555                 GTK_DIALOG_DESTROY_WITH_PARENT,
556                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
557                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
558                 NULL);
559
560         computer = dive_computer_selector(dialog);
561
562         hbox = gtk_hbox_new(FALSE, 6);
563         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, TRUE, 3);
564         devicedata.progressbar = gtk_progress_bar_new();
565         gtk_container_add(GTK_CONTAINER(hbox), devicedata.progressbar);
566
567         gtk_widget_show_all(dialog);
568         result = gtk_dialog_run(GTK_DIALOG(dialog));
569         switch (result) {
570                 int type;
571                 GtkTreeIter iter;
572                 GtkTreeModel *model;
573                 const char *comp;
574         case GTK_RESPONSE_ACCEPT:
575                 if (!gtk_combo_box_get_active_iter(computer, &iter))
576                         break;
577                 model = gtk_combo_box_get_model(computer);
578                 gtk_tree_model_get(model, &iter,
579                         0, &comp,
580                         1, &type,
581                         -1);
582                 devicedata.type = type;
583                 devicedata.name = comp;
584                 do_import(&devicedata);
585                 break;
586         default:
587                 break;
588         }
589         gtk_widget_destroy(dialog);
590
591         report_dives();
592         dive_list_update_dives(dive_list);
593 }