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