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