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