]> git.tdb.fi Git - ext/subsurface.git/blob - libdivecomputer.c
Merge branch 'sacplot' of git://git.hohndel.org/subsurface
[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 < dive_table.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         return device_foreach(device, dive_cb, devicedata);
337 }
338
339 static device_status_t device_open(const char *devname,
340         device_type_t type,
341         device_t **device)
342 {
343         switch (type) {
344         case DEVICE_TYPE_SUUNTO_SOLUTION:
345                 return suunto_solution_device_open(device, devname);
346
347         case DEVICE_TYPE_SUUNTO_EON:
348                 return suunto_eon_device_open(device, devname);
349
350         case DEVICE_TYPE_SUUNTO_VYPER:
351                 return suunto_vyper_device_open(device, devname);
352
353         case DEVICE_TYPE_SUUNTO_VYPER2:
354                 return suunto_vyper2_device_open(device, devname);
355
356         case DEVICE_TYPE_SUUNTO_D9:
357                 return suunto_d9_device_open(device, devname);
358
359         case DEVICE_TYPE_UWATEC_ALADIN:
360                 return uwatec_aladin_device_open(device, devname);
361
362         case DEVICE_TYPE_UWATEC_MEMOMOUSE:
363                 return uwatec_memomouse_device_open(device, devname);
364
365         case DEVICE_TYPE_UWATEC_SMART:
366                 return uwatec_smart_device_open(device);
367
368         case DEVICE_TYPE_REEFNET_SENSUS:
369                 return reefnet_sensus_device_open(device, devname);
370
371         case DEVICE_TYPE_REEFNET_SENSUSPRO:
372                 return reefnet_sensuspro_device_open(device, devname);
373
374         case DEVICE_TYPE_REEFNET_SENSUSULTRA:
375                 return reefnet_sensusultra_device_open(device, devname);
376
377         case DEVICE_TYPE_OCEANIC_VTPRO:
378                 return oceanic_vtpro_device_open(device, devname);
379
380         case DEVICE_TYPE_OCEANIC_VEO250:
381                 return oceanic_veo250_device_open(device, devname);
382
383         case DEVICE_TYPE_OCEANIC_ATOM2:
384                 return oceanic_atom2_device_open(device, devname);
385
386         case DEVICE_TYPE_MARES_NEMO:
387                 return mares_nemo_device_open(device, devname);
388
389         case DEVICE_TYPE_MARES_PUCK:
390                 return mares_puck_device_open(device, devname);
391
392         case DEVICE_TYPE_MARES_ICONHD:
393                 return mares_iconhd_device_open(device, devname);
394
395         case DEVICE_TYPE_HW_OSTC:
396                 return hw_ostc_device_open(device, devname);
397
398         case DEVICE_TYPE_CRESSI_EDY:
399                 return cressi_edy_device_open(device, devname);
400
401         case DEVICE_TYPE_ZEAGLE_N2ITION3:
402                 return zeagle_n2ition3_device_open(device, devname);
403
404         case DEVICE_TYPE_ATOMICS_COBALT:
405                 return atomics_cobalt_device_open(device);
406
407         default:
408                 return DEVICE_STATUS_ERROR;
409         }
410 }
411
412 static void event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
413 {
414         const device_progress_t *progress = data;
415         const device_devinfo_t *devinfo = data;
416         const device_clock_t *clock = data;
417         device_data_t *devdata = userdata;
418
419         switch (event) {
420         case DEVICE_EVENT_WAITING:
421                 printf("Event: waiting for user action\n");
422                 break;
423         case DEVICE_EVENT_PROGRESS:
424                 update_progressbar(&devdata->progress,
425                         (double) progress->current / (double) progress->maximum);
426                 break;
427         case DEVICE_EVENT_DEVINFO:
428                 devdata->devinfo = *devinfo;
429                 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
430                         devinfo->model, devinfo->model,
431                         devinfo->firmware, devinfo->firmware,
432                         devinfo->serial, devinfo->serial);
433                 break;
434         case DEVICE_EVENT_CLOCK:
435                 devdata->clock = *clock;
436                 printf("Event: systime=%lld, devtime=%u\n",
437                         clock->systime, clock->devtime);
438                 break;
439         default:
440                 break;
441         }
442 }
443
444 static int import_thread_done = 0, import_thread_cancelled;
445
446 static int
447 cancel_cb(void *userdata)
448 {
449         return import_thread_cancelled;
450 }
451
452 static const char *do_libdivecomputer_import(device_data_t *data)
453 {
454         device_t *device = NULL;
455         device_status_t rc;
456
457         rc = device_open(data->devname, data->type, &device);
458         if (rc != DEVICE_STATUS_SUCCESS)
459                 return "Unable to open %s (%s)";
460
461         // Register the event handler.
462         int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
463         rc = device_set_events(device, events, event_cb, data);
464         if (rc != DEVICE_STATUS_SUCCESS) {
465                 device_close(device);
466                 return "Error registering the event handler.";
467         }
468
469         // Register the cancellation handler.
470         rc = device_set_cancel(device, cancel_cb, data);
471         if (rc != DEVICE_STATUS_SUCCESS) {
472                 device_close(device);
473                 return "Error registering the cancellation handler.";
474         }
475
476         rc = import_device_data(device, data);
477         if (rc != DEVICE_STATUS_SUCCESS) {
478                 device_close(device);
479                 return "Dive data import error";
480         }
481
482         device_close(device);
483         return NULL;
484 }
485
486 static void *pthread_wrapper(void *_data)
487 {
488         device_data_t *data = _data;
489         const char *err_string = do_libdivecomputer_import(data);
490         import_thread_done = 1;
491         return (void *)err_string;
492 }
493
494 void do_import(device_data_t *data)
495 {
496         pthread_t pthread;
497         void *retval;
498
499         /* I'm sure there is some better interface for waiting on a thread in a UI main loop */
500         import_thread_done = 0;
501         pthread_create(&pthread, NULL, pthread_wrapper, data);
502         while (!import_thread_done) {
503                 import_thread_cancelled = process_ui_events();
504                 usleep(100000);
505         }
506         if (pthread_join(pthread, &retval) < 0)
507                 retval = "Odd pthread error return";
508         if (retval)
509                 error(retval, data->name, data->devname);
510 }
511
512 /*
513  * Taken from 'example.c' in libdivecomputer.
514  *
515  * I really wish there was some way to just have
516  * libdivecomputer tell us what devices it supports,
517  * rather than have the application have to know..
518  */
519 struct device_list device_list[] = {
520         { "Suunto Solution",    DEVICE_TYPE_SUUNTO_SOLUTION },
521         { "Suunto Eon",         DEVICE_TYPE_SUUNTO_EON },
522         { "Suunto Vyper",       DEVICE_TYPE_SUUNTO_VYPER },
523         { "Suunto Vyper Air",   DEVICE_TYPE_SUUNTO_VYPER2 },
524         { "Suunto D9",          DEVICE_TYPE_SUUNTO_D9 },
525         { "Uwatec Aladin",      DEVICE_TYPE_UWATEC_ALADIN },
526         { "Uwatec Memo Mouse",  DEVICE_TYPE_UWATEC_MEMOMOUSE },
527         { "Uwatec Smart",       DEVICE_TYPE_UWATEC_SMART },
528         { "ReefNet Sensus",     DEVICE_TYPE_REEFNET_SENSUS },
529         { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
530         { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
531         { "Oceanic VT Pro",     DEVICE_TYPE_OCEANIC_VTPRO },
532         { "Oceanic Veo250",     DEVICE_TYPE_OCEANIC_VEO250 },
533         { "Oceanic Atom 2",     DEVICE_TYPE_OCEANIC_ATOM2 },
534         { "Mares Nemo",         DEVICE_TYPE_MARES_NEMO },
535         { "Mares Puck",         DEVICE_TYPE_MARES_PUCK },
536         { "Mares Icon HD",      DEVICE_TYPE_MARES_ICONHD },
537         { "OSTC",               DEVICE_TYPE_HW_OSTC },
538         { "Cressi Edy",         DEVICE_TYPE_CRESSI_EDY },
539         { "Zeagle N2iTiON 3",   DEVICE_TYPE_ZEAGLE_N2ITION3 },
540         { "Atomics Cobalt",     DEVICE_TYPE_ATOMICS_COBALT },
541         { NULL }
542 };