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