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