]> git.tdb.fi Git - ext/subsurface.git/blob - libdivecomputer.c
divecomputer importing: show the date of the currently importing dive
[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(device_data_t *devdata, 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 static void dev_info(device_data_t *devdata, const char *fmt, ...)
229 {
230         char buffer[32];
231         va_list ap;
232
233         va_start(ap, fmt);
234         vsnprintf(buffer, sizeof(buffer), fmt, ap);
235         va_end(ap);
236         update_progressbar_text(&devdata->progress, buffer);
237 }
238
239 static int import_dive_number = 0;
240
241 static int parse_samples(device_data_t *devdata, struct dive **divep, parser_t *parser)
242 {
243         // Parse the sample data.
244         return parser_samples_foreach(parser, sample_cb, divep);
245 }
246
247 /*
248  * Check if this dive already existed before the import
249  */
250 static int find_dive(struct dive *dive, device_data_t *devdata)
251 {
252         int i;
253
254         for (i = 0; i < dive_table.preexisting; i++) {
255                 struct dive *old = dive_table.dives[i];
256
257                 if (dive->when != old->when)
258                         continue;
259                 return 1;
260         }
261         return 0;
262 }
263
264 static inline int year(int year)
265 {
266         if (year < 70)
267                 return year + 2000;
268         if (year < 100)
269                 return year + 1900;
270         return year;
271 }
272
273 static int dive_cb(const unsigned char *data, unsigned int size,
274         const unsigned char *fingerprint, unsigned int fsize,
275         void *userdata)
276 {
277         int rc;
278         parser_t *parser = NULL;
279         device_data_t *devdata = userdata;
280         dc_datetime_t dt = {0};
281         struct tm tm;
282         struct dive *dive;
283
284         rc = create_parser(devdata, &parser);
285         if (rc != PARSER_STATUS_SUCCESS) {
286                 dev_info(devdata, "Unable to create parser for %s", devdata->name);
287                 return rc;
288         }
289
290         rc = parser_set_data(parser, data, size);
291         if (rc != PARSER_STATUS_SUCCESS) {
292                 dev_info(devdata, "Error registering the data");
293                 parser_destroy(parser);
294                 return rc;
295         }
296
297         import_dive_number++;
298         dive = alloc_dive();
299         rc = parser_get_datetime(parser, &dt);
300         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
301                 dev_info(devdata, "Error parsing the datetime");
302                 parser_destroy (parser);
303                 return rc;
304         }
305
306         tm.tm_year = dt.year;
307         tm.tm_mon = dt.month-1;
308         tm.tm_mday = dt.day;
309         tm.tm_hour = dt.hour;
310         tm.tm_min = dt.minute;
311         tm.tm_sec = dt.second;
312         dive->when = utc_mktime(&tm);
313
314         // Parse the divetime.
315         dev_info(devdata, "Dive %d: %s %d %04d", import_dive_number,
316                 monthname(tm.tm_mon), tm.tm_mday, year(tm.tm_year));
317         unsigned int divetime = 0;
318         rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
319         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
320                 dev_info(devdata, "Error parsing the divetime");
321                 parser_destroy(parser);
322                 return rc;
323         }
324         dive->duration.seconds = divetime;
325
326         // Parse the maxdepth.
327         double maxdepth = 0.0;
328         rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
329         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
330                 dev_info(devdata, "Error parsing the maxdepth");
331                 parser_destroy(parser);
332                 return rc;
333         }
334         dive->maxdepth.mm = maxdepth * 1000 + 0.5;
335
336         // Parse the gas mixes.
337         unsigned int ngases = 0;
338         rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
339         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
340                 dev_info(devdata, "Error parsing the gas mix count");
341                 parser_destroy(parser);
342                 return rc;
343         }
344
345         rc = parse_gasmixes(devdata, dive, parser, ngases);
346         if (rc != PARSER_STATUS_SUCCESS) {
347                 dev_info(devdata, "Error parsing the gas mix");
348                 parser_destroy(parser);
349                 return rc;
350         }
351
352         // Initialize the sample data.
353         rc = parse_samples(devdata, &dive, parser);
354         if (rc != PARSER_STATUS_SUCCESS) {
355                 dev_info(devdata, "Error parsing the samples");
356                 parser_destroy(parser);
357                 return rc;
358         }
359
360         parser_destroy(parser);
361
362         /* If we already saw this dive, abort. */
363         if (find_dive(dive, devdata))
364                 return 0;
365
366         record_dive(dive);
367         return 1;
368 }
369
370
371 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
372 {
373         return device_foreach(device, dive_cb, devicedata);
374 }
375
376 static device_status_t device_open(const char *devname,
377         device_type_t type,
378         device_t **device)
379 {
380         switch (type) {
381         case DEVICE_TYPE_SUUNTO_SOLUTION:
382                 return suunto_solution_device_open(device, devname);
383
384         case DEVICE_TYPE_SUUNTO_EON:
385                 return suunto_eon_device_open(device, devname);
386
387         case DEVICE_TYPE_SUUNTO_VYPER:
388                 return suunto_vyper_device_open(device, devname);
389
390         case DEVICE_TYPE_SUUNTO_VYPER2:
391                 return suunto_vyper2_device_open(device, devname);
392
393         case DEVICE_TYPE_SUUNTO_D9:
394                 return suunto_d9_device_open(device, devname);
395
396         case DEVICE_TYPE_UWATEC_ALADIN:
397                 return uwatec_aladin_device_open(device, devname);
398
399         case DEVICE_TYPE_UWATEC_MEMOMOUSE:
400                 return uwatec_memomouse_device_open(device, devname);
401
402         case DEVICE_TYPE_UWATEC_SMART:
403                 return uwatec_smart_device_open(device);
404
405         case DEVICE_TYPE_REEFNET_SENSUS:
406                 return reefnet_sensus_device_open(device, devname);
407
408         case DEVICE_TYPE_REEFNET_SENSUSPRO:
409                 return reefnet_sensuspro_device_open(device, devname);
410
411         case DEVICE_TYPE_REEFNET_SENSUSULTRA:
412                 return reefnet_sensusultra_device_open(device, devname);
413
414         case DEVICE_TYPE_OCEANIC_VTPRO:
415                 return oceanic_vtpro_device_open(device, devname);
416
417         case DEVICE_TYPE_OCEANIC_VEO250:
418                 return oceanic_veo250_device_open(device, devname);
419
420         case DEVICE_TYPE_OCEANIC_ATOM2:
421                 return oceanic_atom2_device_open(device, devname);
422
423         case DEVICE_TYPE_MARES_DARWIN:
424                 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?
425
426         case DEVICE_TYPE_MARES_NEMO:
427                 return mares_nemo_device_open(device, devname);
428
429         case DEVICE_TYPE_MARES_PUCK:
430                 return mares_puck_device_open(device, devname);
431
432         case DEVICE_TYPE_MARES_ICONHD:
433                 return mares_iconhd_device_open(device, devname);
434
435         case DEVICE_TYPE_HW_OSTC:
436                 return hw_ostc_device_open(device, devname);
437
438         case DEVICE_TYPE_CRESSI_EDY:
439                 return cressi_edy_device_open(device, devname);
440
441         case DEVICE_TYPE_ZEAGLE_N2ITION3:
442                 return zeagle_n2ition3_device_open(device, devname);
443
444         case DEVICE_TYPE_ATOMICS_COBALT:
445                 return atomics_cobalt_device_open(device);
446
447         default:
448                 return DEVICE_STATUS_ERROR;
449         }
450 }
451
452
453 static void event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
454 {
455         const device_progress_t *progress = data;
456         const device_devinfo_t *devinfo = data;
457         const device_clock_t *clock = data;
458         device_data_t *devdata = userdata;
459
460         switch (event) {
461         case DEVICE_EVENT_WAITING:
462                 dev_info(devdata, "Event: waiting for user action");
463                 break;
464         case DEVICE_EVENT_PROGRESS:
465                 update_progressbar(&devdata->progress,
466                         (double) progress->current / (double) progress->maximum);
467                 break;
468         case DEVICE_EVENT_DEVINFO:
469                 devdata->devinfo = *devinfo;
470                 dev_info(devdata, "model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)",
471                         devinfo->model, devinfo->model,
472                         devinfo->firmware, devinfo->firmware,
473                         devinfo->serial, devinfo->serial);
474                 break;
475         case DEVICE_EVENT_CLOCK:
476                 devdata->clock = *clock;
477                 dev_info(devdata, "Event: systime=%"PRId64", devtime=%u\n",
478                         (uint64_t)clock->systime, clock->devtime);
479                 break;
480         default:
481                 break;
482         }
483 }
484
485 static int import_thread_done = 0, import_thread_cancelled;
486
487 static int
488 cancel_cb(void *userdata)
489 {
490         return import_thread_cancelled;
491 }
492
493 static const char *do_libdivecomputer_import(device_data_t *data)
494 {
495         device_t *device = NULL;
496         device_status_t rc;
497
498         import_dive_number = 0;
499         rc = device_open(data->devname, data->type, &device);
500         if (rc != DEVICE_STATUS_SUCCESS)
501                 return "Unable to open %s (%s)";
502
503         // Register the event handler.
504         int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
505         rc = device_set_events(device, events, event_cb, data);
506         if (rc != DEVICE_STATUS_SUCCESS) {
507                 device_close(device);
508                 return "Error registering the event handler.";
509         }
510
511         // Register the cancellation handler.
512         rc = device_set_cancel(device, cancel_cb, data);
513         if (rc != DEVICE_STATUS_SUCCESS) {
514                 device_close(device);
515                 return "Error registering the cancellation handler.";
516         }
517
518         rc = import_device_data(device, data);
519         if (rc != DEVICE_STATUS_SUCCESS) {
520                 device_close(device);
521                 return "Dive data import error";
522         }
523
524         device_close(device);
525         return NULL;
526 }
527
528 static void *pthread_wrapper(void *_data)
529 {
530         device_data_t *data = _data;
531         const char *err_string = do_libdivecomputer_import(data);
532         import_thread_done = 1;
533         return (void *)err_string;
534 }
535
536 GError *do_import(device_data_t *data)
537 {
538         pthread_t pthread;
539         void *retval;
540
541         /* I'm sure there is some better interface for waiting on a thread in a UI main loop */
542         import_thread_done = 0;
543         pthread_create(&pthread, NULL, pthread_wrapper, data);
544         while (!import_thread_done) {
545                 import_thread_cancelled = process_ui_events();
546                 usleep(100000);
547         }
548         if (pthread_join(pthread, &retval) < 0)
549                 retval = "Odd pthread error return";
550         if (retval)
551                 return error(retval, data->name, data->devname);
552         return NULL;
553 }
554
555 /*
556  * Taken from 'example.c' in libdivecomputer.
557  *
558  * I really wish there was some way to just have
559  * libdivecomputer tell us what devices it supports,
560  * rather than have the application have to know..
561  */
562 struct device_list device_list[] = {
563         { "Suunto Solution",    DEVICE_TYPE_SUUNTO_SOLUTION },
564         { "Suunto Eon",         DEVICE_TYPE_SUUNTO_EON },
565         { "Suunto Vyper",       DEVICE_TYPE_SUUNTO_VYPER },
566         { "Suunto Vyper Air",   DEVICE_TYPE_SUUNTO_VYPER2 },
567         { "Suunto D9",          DEVICE_TYPE_SUUNTO_D9 },
568         { "Uwatec Aladin",      DEVICE_TYPE_UWATEC_ALADIN },
569         { "Uwatec Memo Mouse",  DEVICE_TYPE_UWATEC_MEMOMOUSE },
570         { "Uwatec Smart",       DEVICE_TYPE_UWATEC_SMART },
571         { "ReefNet Sensus",     DEVICE_TYPE_REEFNET_SENSUS },
572         { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
573         { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
574         { "Oceanic VT Pro",     DEVICE_TYPE_OCEANIC_VTPRO },
575         { "Oceanic Veo250",     DEVICE_TYPE_OCEANIC_VEO250 },
576         { "Oceanic Atom 2",     DEVICE_TYPE_OCEANIC_ATOM2 },
577         { "Mares Darwin, M1, M2, Airlab",       DEVICE_TYPE_MARES_DARWIN },
578         { "Mares Nemo, Excel, Apneist", DEVICE_TYPE_MARES_NEMO },
579         { "Mares Puck, Nemo Air, Nemo Wide",    DEVICE_TYPE_MARES_PUCK },
580         { "Mares Icon HD",      DEVICE_TYPE_MARES_ICONHD },
581         { "OSTC",               DEVICE_TYPE_HW_OSTC },
582 #ifdef LIBDIVECOMPUTER_SUPPORTS_FROG
583         { "OSTC Frog",          DEVICE_TYPE_HW_FROG },
584 #endif
585         { "Cressi Edy",         DEVICE_TYPE_CRESSI_EDY },
586         { "Zeagle N2iTiON 3",   DEVICE_TYPE_ZEAGLE_N2ITION3 },
587         { "Atomics Cobalt",     DEVICE_TYPE_ATOMICS_COBALT },
588         { NULL }
589 };