]> git.tdb.fi Git - ext/subsurface.git/blob - libdivecomputer.c
Clean up event handling from libdivecomputer
[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 **divep, struct sample *sample, parser_sample_value_t value)
122 {
123         static const char *events[] = {
124                 "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
125                 "violation", "bookmark", "surface", "safety stop", "gaschange",
126                 "safety stop (voluntary)", "safety stop (mandatory)", "deepstop",
127                 "ceiling (safety stop)", "unknown", "divetime", "maxdepth",
128                 "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning"};
129
130         printf("   <event type=\"%u\" time=\"%u\" flags=\"%u\" value=\"%u\">%s</event>\n",
131                 value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]);
132 }
133
134
135 void
136 sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
137 {
138         int i;
139         struct dive **divep = userdata;
140         struct dive *dive = *divep;
141         struct sample *sample;
142
143         /*
144          * We fill in the "previous" sample - except for SAMPLE_TYPE_TIME,
145          * which creates a new one.
146          */
147         sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
148
149         switch (type) {
150         case SAMPLE_TYPE_TIME:
151                 sample = prepare_sample(divep);
152                 sample->time.seconds = value.time;
153                 finish_sample(*divep, sample);
154                 break;
155         case SAMPLE_TYPE_DEPTH:
156                 sample->depth.mm = value.depth * 1000 + 0.5;
157                 break;
158         case SAMPLE_TYPE_PRESSURE:
159                 sample->cylinderindex = value.pressure.tank;
160                 sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
161                 break;
162         case SAMPLE_TYPE_TEMPERATURE:
163                 sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
164                 break;
165         case SAMPLE_TYPE_EVENT:
166                 handle_event(divep, sample, value);
167                 break;
168         case SAMPLE_TYPE_RBT:
169                 printf("   <rbt>%u</rbt>\n", value.rbt);
170                 break;
171         case SAMPLE_TYPE_HEARTBEAT:
172                 printf("   <heartbeat>%u</heartbeat>\n", value.heartbeat);
173                 break;
174         case SAMPLE_TYPE_BEARING:
175                 printf("   <bearing>%u</bearing>\n", value.bearing);
176                 break;
177         case SAMPLE_TYPE_VENDOR:
178                 printf("   <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
179                 for (i = 0; i < value.vendor.size; ++i)
180                         printf("%02X", ((unsigned char *) value.vendor.data)[i]);
181                 printf("</vendor>\n");
182                 break;
183         default:
184                 break;
185         }
186 }
187
188
189 static int parse_samples(struct dive **divep, parser_t *parser)
190 {
191         // Parse the sample data.
192         printf("Parsing the sample data.\n");
193         return parser_samples_foreach(parser, sample_cb, divep);
194 }
195
196 static int dive_cb(const unsigned char *data, unsigned int size,
197         const unsigned char *fingerprint, unsigned int fsize,
198         void *userdata)
199 {
200         int rc;
201         parser_t *parser = NULL;
202         device_data_t *devdata = userdata;
203         dc_datetime_t dt = {0};
204         struct tm tm;
205         struct dive *dive;
206
207         rc = create_parser(devdata, &parser);
208         if (rc != PARSER_STATUS_SUCCESS) {
209                 fprintf(stderr, "Unable to create parser for %s", devdata->name);
210                 return rc;
211         }
212
213         rc = parser_set_data(parser, data, size);
214         if (rc != PARSER_STATUS_SUCCESS) {
215                 fprintf(stderr, "Error registering the data.");
216                 parser_destroy(parser);
217                 return rc;
218         }
219
220         dive = alloc_dive();
221         rc = parser_get_datetime(parser, &dt);
222         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
223                 fprintf(stderr, "Error parsing the datetime.");
224                 parser_destroy (parser);
225                 return rc;
226         }
227
228         tm.tm_year = dt.year;
229         tm.tm_mon = dt.month-1;
230         tm.tm_mday = dt.day;
231         tm.tm_hour = dt.hour;
232         tm.tm_min = dt.minute;
233         tm.tm_sec = dt.second;
234         dive->when = utc_mktime(&tm);
235
236         // Parse the divetime.
237         printf("Parsing the divetime.\n");
238         unsigned int divetime = 0;
239         rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
240         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
241                 fprintf(stderr, "Error parsing the divetime.");
242                 parser_destroy(parser);
243                 return rc;
244         }
245         dive->duration.seconds = divetime;
246
247         // Parse the maxdepth.
248         printf("Parsing the maxdepth.\n");
249         double maxdepth = 0.0;
250         rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
251         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
252                 fprintf(stderr, "Error parsing the maxdepth.");
253                 parser_destroy(parser);
254                 return rc;
255         }
256         dive->maxdepth.mm = maxdepth * 1000 + 0.5;
257
258         // Parse the gas mixes.
259         printf("Parsing the gas mixes.\n");
260         unsigned int ngases = 0;
261         rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
262         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
263                 fprintf(stderr, "Error parsing the gas mix count.");
264                 parser_destroy(parser);
265                 return rc;
266         }
267
268         rc = parse_gasmixes(dive, parser, ngases);
269         if (rc != PARSER_STATUS_SUCCESS) {
270                 fprintf(stderr, "Error parsing the gas mix.");
271                 parser_destroy(parser);
272                 return rc;
273         }
274
275         // Initialize the sample data.
276         rc = parse_samples(&dive, parser);
277         if (rc != PARSER_STATUS_SUCCESS) {
278                 fprintf(stderr, "Error parsing the samples.");
279                 parser_destroy(parser);
280                 return rc;
281         }
282         record_dive(dive);
283
284         parser_destroy(parser);
285         return 1;
286 }
287
288
289 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
290 {
291         return device_foreach(device, dive_cb, devicedata);
292 }
293
294 static device_status_t device_open(const char *devname,
295         device_type_t type,
296         device_t **device)
297 {
298         switch (type) {
299         case DEVICE_TYPE_SUUNTO_SOLUTION:
300                 return suunto_solution_device_open(device, devname);
301
302         case DEVICE_TYPE_SUUNTO_EON:
303                 return suunto_eon_device_open(device, devname);
304
305         case DEVICE_TYPE_SUUNTO_VYPER:
306                 return suunto_vyper_device_open(device, devname);
307
308         case DEVICE_TYPE_SUUNTO_VYPER2:
309                 return suunto_vyper2_device_open(device, devname);
310
311         case DEVICE_TYPE_SUUNTO_D9:
312                 return suunto_d9_device_open(device, devname);
313
314         case DEVICE_TYPE_UWATEC_ALADIN:
315                 return uwatec_aladin_device_open(device, devname);
316
317         case DEVICE_TYPE_UWATEC_MEMOMOUSE:
318                 return uwatec_memomouse_device_open(device, devname);
319
320         case DEVICE_TYPE_UWATEC_SMART:
321                 return uwatec_smart_device_open(device);
322
323         case DEVICE_TYPE_REEFNET_SENSUS:
324                 return reefnet_sensus_device_open(device, devname);
325
326         case DEVICE_TYPE_REEFNET_SENSUSPRO:
327                 return reefnet_sensuspro_device_open(device, devname);
328
329         case DEVICE_TYPE_REEFNET_SENSUSULTRA:
330                 return reefnet_sensusultra_device_open(device, devname);
331
332         case DEVICE_TYPE_OCEANIC_VTPRO:
333                 return oceanic_vtpro_device_open(device, devname);
334
335         case DEVICE_TYPE_OCEANIC_VEO250:
336                 return oceanic_veo250_device_open(device, devname);
337
338         case DEVICE_TYPE_OCEANIC_ATOM2:
339                 return oceanic_atom2_device_open(device, devname);
340
341         case DEVICE_TYPE_MARES_NEMO:
342                 return mares_nemo_device_open(device, devname);
343
344         case DEVICE_TYPE_MARES_PUCK:
345                 return mares_puck_device_open(device, devname);
346
347         case DEVICE_TYPE_MARES_ICONHD:
348                 return mares_iconhd_device_open(device, devname);
349
350         case DEVICE_TYPE_HW_OSTC:
351                 return hw_ostc_device_open(device, devname);
352
353         case DEVICE_TYPE_CRESSI_EDY:
354                 return cressi_edy_device_open(device, devname);
355
356         case DEVICE_TYPE_ZEAGLE_N2ITION3:
357                 return zeagle_n2ition3_device_open(device, devname);
358
359         case DEVICE_TYPE_ATOMICS_COBALT:
360                 return atomics_cobalt_device_open(device);
361
362         default:
363                 return DEVICE_STATUS_ERROR;
364         }
365 }
366
367 static void event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
368 {
369         const device_progress_t *progress = (device_progress_t *) data;
370         const device_devinfo_t *devinfo = (device_devinfo_t *) data;
371         const device_clock_t *clock = (device_clock_t *) data;
372         device_data_t *devdata = (device_data_t *) userdata;
373
374         switch (event) {
375         case DEVICE_EVENT_WAITING:
376                 printf("Event: waiting for user action\n");
377                 break;
378         case DEVICE_EVENT_PROGRESS:
379                 update_progressbar(&devdata->progress,
380                         (double) progress->current / (double) progress->maximum);
381                 break;
382         case DEVICE_EVENT_DEVINFO:
383                 devdata->devinfo = *devinfo;
384                 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
385                         devinfo->model, devinfo->model,
386                         devinfo->firmware, devinfo->firmware,
387                         devinfo->serial, devinfo->serial);
388                 break;
389         case DEVICE_EVENT_CLOCK:
390                 devdata->clock = *clock;
391                 printf("Event: systime=%lld, devtime=%u\n",
392                         clock->systime, clock->devtime);
393                 break;
394         default:
395                 break;
396         }
397 }
398
399 static int import_thread_done = 0, import_thread_cancelled;
400
401 static int
402 cancel_cb(void *userdata)
403 {
404         return import_thread_cancelled;
405 }
406
407 static const char *do_libdivecomputer_import(device_data_t *data)
408 {
409         device_t *device = NULL;
410         device_status_t rc;
411
412         rc = device_open(data->devname, data->type, &device);
413         if (rc != DEVICE_STATUS_SUCCESS)
414                 return "Unable to open %s (%s)";
415
416         // Register the event handler.
417         int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
418         rc = device_set_events(device, events, event_cb, data);
419         if (rc != DEVICE_STATUS_SUCCESS) {
420                 device_close(device);
421                 return "Error registering the event handler.";
422         }
423
424         // Register the cancellation handler.
425         rc = device_set_cancel(device, cancel_cb, data);
426         if (rc != DEVICE_STATUS_SUCCESS) {
427                 device_close(device);
428                 return "Error registering the cancellation handler.";
429         }
430
431         rc = import_device_data(device, data);
432         if (rc != DEVICE_STATUS_SUCCESS) {
433                 device_close(device);
434                 return "Dive data import error";
435         }
436
437         device_close(device);
438         return NULL;
439 }
440
441 static void *pthread_wrapper(void *_data)
442 {
443         device_data_t *data = _data;
444         const char *err_string = do_libdivecomputer_import(data);
445         import_thread_done = 1;
446         return (void *)err_string;
447 }
448
449 void do_import(device_data_t *data)
450 {
451         pthread_t pthread;
452         void *retval;
453
454         if (data->type == DEVICE_TYPE_UEMIS)
455                 return uemis_import();
456
457         /* I'm sure there is some better interface for waiting on a thread in a UI main loop */
458         import_thread_done = 0;
459         pthread_create(&pthread, NULL, pthread_wrapper, data);
460         while (!import_thread_done) {
461                 import_thread_cancelled = process_ui_events();
462                 usleep(100000);
463         }
464         if (pthread_join(pthread, &retval) < 0)
465                 retval = "Odd pthread error return";
466         if (retval)
467                 error(retval, data->name, data->devname);
468 }
469
470 /*
471  * Taken from 'example.c' in libdivecomputer.
472  *
473  * I really wish there was some way to just have
474  * libdivecomputer tell us what devices it supports,
475  * rather than have the application have to know..
476  */
477 struct device_list device_list[] = {
478         { "Suunto Solution",    DEVICE_TYPE_SUUNTO_SOLUTION },
479         { "Suunto Eon",         DEVICE_TYPE_SUUNTO_EON },
480         { "Suunto Vyper",       DEVICE_TYPE_SUUNTO_VYPER },
481         { "Suunto Vyper Air",   DEVICE_TYPE_SUUNTO_VYPER2 },
482         { "Suunto D9",          DEVICE_TYPE_SUUNTO_D9 },
483         { "Uwatec Aladin",      DEVICE_TYPE_UWATEC_ALADIN },
484         { "Uwatec Memo Mouse",  DEVICE_TYPE_UWATEC_MEMOMOUSE },
485         { "Uwatec Smart",       DEVICE_TYPE_UWATEC_SMART },
486         { "ReefNet Sensus",     DEVICE_TYPE_REEFNET_SENSUS },
487         { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
488         { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
489         { "Oceanic VT Pro",     DEVICE_TYPE_OCEANIC_VTPRO },
490         { "Oceanic Veo250",     DEVICE_TYPE_OCEANIC_VEO250 },
491         { "Oceanic Atom 2",     DEVICE_TYPE_OCEANIC_ATOM2 },
492         { "Mares Nemo",         DEVICE_TYPE_MARES_NEMO },
493         { "Mares Puck",         DEVICE_TYPE_MARES_PUCK },
494         { "Mares Icon HD",      DEVICE_TYPE_MARES_ICONHD },
495         { "OSTC",               DEVICE_TYPE_HW_OSTC },
496         { "Cressi Edy",         DEVICE_TYPE_CRESSI_EDY },
497         { "Zeagle N2iTiON 3",   DEVICE_TYPE_ZEAGLE_N2ITION3 },
498         { "Atomics Cobalt",     DEVICE_TYPE_ATOMICS_COBALT },
499         { "Uemis Zurich SDA",   DEVICE_TYPE_UEMIS },
500         { NULL }
501 };