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