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