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