]> git.tdb.fi Git - ext/subsurface.git/blob - libdivecomputer.c
Fix profile and average depth for freedives
[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 /* Christ. Libdivecomputer has the worst configuration system ever. */
14 #ifdef HW_FROG_H
15   #define NOT_FROG , 0
16   #define LIBDIVECOMPUTER_SUPPORTS_FROG
17 #else
18   #define NOT_FROG
19 #endif
20
21 static GError *error(const char *fmt, ...)
22 {
23         va_list args;
24         GError *error;
25
26         va_start(args, fmt);
27         error = g_error_new_valist(
28                 g_quark_from_string("subsurface"),
29                 DIVE_ERROR_PARSE, fmt, args);
30         va_end(args);
31         return error;
32 }
33
34 static dc_status_t create_parser(device_data_t *devdata, dc_parser_t **parser)
35 {
36         return dc_parser_new(parser, devdata->device);
37 }
38
39 static int parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_parser_t *parser, int ngases)
40 {
41         int i;
42
43         for (i = 0; i < ngases; i++) {
44                 int rc;
45                 dc_gasmix_t gasmix = {0};
46                 int o2, he;
47
48                 rc = dc_parser_get_field(parser, DC_FIELD_GASMIX, i, &gasmix);
49                 if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED)
50                         return rc;
51
52                 if (i >= MAX_CYLINDERS)
53                         continue;
54
55                 o2 = gasmix.oxygen * 1000 + 0.5;
56                 he = gasmix.helium * 1000 + 0.5;
57
58                 /* Ignore bogus data - libdivecomputer does some crazy stuff */
59                 if (o2 <= AIR_PERMILLE || o2 >= 1000)
60                         o2 = 0;
61                 if (he < 0 || he >= 800 || o2+he >= 1000)
62                         he = 0;
63
64                 dive->cylinder[i].gasmix.o2.permille = o2;
65                 dive->cylinder[i].gasmix.he.permille = he;
66         }
67         return DC_STATUS_SUCCESS;
68 }
69
70 static void handle_event(struct dive *dive, struct sample *sample, dc_sample_value_t value)
71 {
72         int type, time;
73         static const char *events[] = {
74                 "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
75                 "violation", "bookmark", "surface", "safety stop", "gaschange",
76                 "safety stop (voluntary)", "safety stop (mandatory)", "deepstop",
77                 "ceiling (safety stop)", "unknown", "divetime", "maxdepth",
78                 "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning"
79         };
80         const int nr_events = sizeof(events) / sizeof(const char *);
81         const char *name;
82
83         /*
84          * Just ignore surface events.  They are pointless.  What "surface"
85          * means depends on the dive computer (and possibly even settings
86          * in the dive computer). It does *not* necessarily mean "depth 0",
87          * so don't even turn it into that.
88          */
89         if (value.event.type == SAMPLE_EVENT_SURFACE)
90                 return;
91
92         /*
93          * Other evens might be more interesting, but for now we just print them out.
94          */
95         type = value.event.type;
96         name = "invalid event number";
97         if (type < nr_events)
98                 name = events[type];
99
100         time = value.event.time;
101         if (sample)
102                 time += sample->time.seconds;
103
104         add_event(dive, time, type, value.event.flags, value.event.value, name);
105 }
106
107 void
108 sample_cb(dc_sample_type_t type, dc_sample_value_t value, void *userdata)
109 {
110         int i;
111         struct dive **divep = userdata;
112         struct dive *dive = *divep;
113         struct sample *sample;
114
115         /*
116          * We fill in the "previous" sample - except for DC_SAMPLE_TIME,
117          * which creates a new one.
118          */
119         sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
120
121         switch (type) {
122         case DC_SAMPLE_TIME:
123                 sample = prepare_sample(divep);
124                 sample->time.seconds = value.time;
125                 finish_sample(*divep);
126                 break;
127         case DC_SAMPLE_DEPTH:
128                 sample->depth.mm = value.depth * 1000 + 0.5;
129                 break;
130         case DC_SAMPLE_PRESSURE:
131                 sample->cylinderindex = value.pressure.tank;
132                 sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
133                 break;
134         case DC_SAMPLE_TEMPERATURE:
135                 sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
136                 break;
137         case DC_SAMPLE_EVENT:
138                 handle_event(dive, sample, value);
139                 break;
140         case DC_SAMPLE_RBT:
141                 printf("   <rbt>%u</rbt>\n", value.rbt);
142                 break;
143         case DC_SAMPLE_HEARTBEAT:
144                 printf("   <heartbeat>%u</heartbeat>\n", value.heartbeat);
145                 break;
146         case DC_SAMPLE_BEARING:
147                 printf("   <bearing>%u</bearing>\n", value.bearing);
148                 break;
149         case DC_SAMPLE_VENDOR:
150                 printf("   <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
151                 for (i = 0; i < value.vendor.size; ++i)
152                         printf("%02X", ((unsigned char *) value.vendor.data)[i]);
153                 printf("</vendor>\n");
154                 break;
155         default:
156                 break;
157         }
158 }
159
160 static void dev_info(device_data_t *devdata, const char *fmt, ...)
161 {
162         char buffer[32];
163         va_list ap;
164
165         va_start(ap, fmt);
166         vsnprintf(buffer, sizeof(buffer), fmt, ap);
167         va_end(ap);
168         update_progressbar_text(&devdata->progress, buffer);
169 }
170
171 static int import_dive_number = 0;
172
173 static int parse_samples(device_data_t *devdata, struct dive **divep, dc_parser_t *parser)
174 {
175         // Parse the sample data.
176         return dc_parser_samples_foreach(parser, sample_cb, divep);
177 }
178
179 /*
180  * Check if this dive already existed before the import
181  */
182 static int find_dive(struct dive *dive, device_data_t *devdata)
183 {
184         int i;
185
186         for (i = 0; i < dive_table.preexisting; i++) {
187                 struct dive *old = dive_table.dives[i];
188
189                 if (dive->when != old->when)
190                         continue;
191                 return 1;
192         }
193         return 0;
194 }
195
196 static inline int year(int year)
197 {
198         if (year < 70)
199                 return year + 2000;
200         if (year < 100)
201                 return year + 1900;
202         return year;
203 }
204
205 static int dive_cb(const unsigned char *data, unsigned int size,
206         const unsigned char *fingerprint, unsigned int fsize,
207         void *userdata)
208 {
209         int rc;
210         dc_parser_t *parser = NULL;
211         device_data_t *devdata = userdata;
212         dc_datetime_t dt = {0};
213         struct tm tm;
214         struct dive *dive;
215
216         rc = create_parser(devdata, &parser);
217         if (rc != DC_STATUS_SUCCESS) {
218                 dev_info(devdata, "Unable to create parser for %s %s", devdata->vendor, devdata->product);
219                 return rc;
220         }
221
222         rc = dc_parser_set_data(parser, data, size);
223         if (rc != DC_STATUS_SUCCESS) {
224                 dev_info(devdata, "Error registering the data");
225                 dc_parser_destroy(parser);
226                 return rc;
227         }
228
229         import_dive_number++;
230         dive = alloc_dive();
231         rc = dc_parser_get_datetime(parser, &dt);
232         if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
233                 dev_info(devdata, "Error parsing the datetime");
234                 dc_parser_destroy(parser);
235                 return rc;
236         }
237
238         tm.tm_year = dt.year;
239         tm.tm_mon = dt.month-1;
240         tm.tm_mday = dt.day;
241         tm.tm_hour = dt.hour;
242         tm.tm_min = dt.minute;
243         tm.tm_sec = dt.second;
244         dive->when = utc_mktime(&tm);
245
246         // Parse the divetime.
247         dev_info(devdata, "Dive %d: %s %d %04d", import_dive_number,
248                 monthname(tm.tm_mon), tm.tm_mday, year(tm.tm_year));
249         unsigned int divetime = 0;
250         rc = dc_parser_get_field (parser, DC_FIELD_DIVETIME, 0, &divetime);
251         if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
252                 dev_info(devdata, "Error parsing the divetime");
253                 dc_parser_destroy(parser);
254                 return rc;
255         }
256         dive->duration.seconds = divetime;
257
258         // Parse the maxdepth.
259         double maxdepth = 0.0;
260         rc = dc_parser_get_field(parser, DC_FIELD_MAXDEPTH, 0, &maxdepth);
261         if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
262                 dev_info(devdata, "Error parsing the maxdepth");
263                 dc_parser_destroy(parser);
264                 return rc;
265         }
266         dive->maxdepth.mm = maxdepth * 1000 + 0.5;
267
268         // Parse the gas mixes.
269         unsigned int ngases = 0;
270         rc = dc_parser_get_field(parser, DC_FIELD_GASMIX_COUNT, 0, &ngases);
271         if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
272                 dev_info(devdata, "Error parsing the gas mix count");
273                 dc_parser_destroy(parser);
274                 return rc;
275         }
276
277         rc = parse_gasmixes(devdata, dive, parser, ngases);
278         if (rc != DC_STATUS_SUCCESS) {
279                 dev_info(devdata, "Error parsing the gas mix");
280                 dc_parser_destroy(parser);
281                 return rc;
282         }
283
284         // Initialize the sample data.
285         rc = parse_samples(devdata, &dive, parser);
286         if (rc != DC_STATUS_SUCCESS) {
287                 dev_info(devdata, "Error parsing the samples");
288                 dc_parser_destroy(parser);
289                 return rc;
290         }
291
292         dc_parser_destroy(parser);
293
294         /* If we already saw this dive, abort. */
295         if (find_dive(dive, devdata))
296                 return 0;
297
298         record_dive(dive);
299         return 1;
300 }
301
302
303 static dc_status_t import_device_data(dc_device_t *device, device_data_t *devicedata)
304 {
305         return dc_device_foreach(device, dive_cb, devicedata);
306 }
307
308
309 static void event_cb(dc_device_t *device, dc_event_type_t event, const void *data, void *userdata)
310 {
311         const dc_event_progress_t *progress = data;
312         const dc_event_devinfo_t *devinfo = data;
313         const dc_event_clock_t *clock = data;
314         device_data_t *devdata = userdata;
315
316         switch (event) {
317         case DC_EVENT_WAITING:
318                 dev_info(devdata, "Event: waiting for user action");
319                 break;
320         case DC_EVENT_PROGRESS:
321                 update_progressbar(&devdata->progress,
322                         (double) progress->current / (double) progress->maximum);
323                 break;
324         case DC_EVENT_DEVINFO:
325                 dev_info(devdata, "model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)",
326                         devinfo->model, devinfo->model,
327                         devinfo->firmware, devinfo->firmware,
328                         devinfo->serial, devinfo->serial);
329                 break;
330         case DC_EVENT_CLOCK:
331                 dev_info(devdata, "Event: systime=%"PRId64", devtime=%u\n",
332                         (uint64_t)clock->systime, clock->devtime);
333                 break;
334         default:
335                 break;
336         }
337 }
338
339 static int import_thread_done = 0, import_thread_cancelled;
340
341 static int
342 cancel_cb(void *userdata)
343 {
344         return import_thread_cancelled;
345 }
346
347 static const char *do_device_import(device_data_t *data)
348 {
349         dc_status_t rc;
350         dc_device_t *device = data->device;
351
352         // Register the event handler.
353         int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK;
354         rc = dc_device_set_events(device, events, event_cb, data);
355         if (rc != DC_STATUS_SUCCESS)
356                 return "Error registering the event handler.";
357
358         // Register the cancellation handler.
359         rc = dc_device_set_cancel(device, cancel_cb, data);
360         if (rc != DC_STATUS_SUCCESS)
361                 return "Error registering the cancellation handler.";
362
363         rc = import_device_data(device, data);
364         if (rc != DC_STATUS_SUCCESS)
365                 return "Dive data import error";
366
367         /* All good */
368         return NULL;
369 }
370
371 static const char *do_libdivecomputer_import(device_data_t *data)
372 {
373         dc_status_t rc;
374         const char *err;
375
376         import_dive_number = 0;
377         data->device = NULL;
378         data->context = NULL;
379
380         rc = dc_context_new(&data->context);
381         if (rc != DC_STATUS_SUCCESS)
382                 return "Unable to create libdivecomputer context";
383
384         err = "Unable to open %s %s (%s)";
385         rc = dc_device_open(&data->device, data->context, data->descriptor, data->devname);
386         if (rc == DC_STATUS_SUCCESS) {
387                 err = do_device_import(data);
388                 dc_device_close(data->device);
389         }
390         dc_context_free(data->context);
391         return err;
392 }
393
394 static void *pthread_wrapper(void *_data)
395 {
396         device_data_t *data = _data;
397         const char *err_string = do_libdivecomputer_import(data);
398         import_thread_done = 1;
399         return (void *)err_string;
400 }
401
402 GError *do_import(device_data_t *data)
403 {
404         pthread_t pthread;
405         void *retval;
406
407         /* I'm sure there is some better interface for waiting on a thread in a UI main loop */
408         import_thread_done = 0;
409         pthread_create(&pthread, NULL, pthread_wrapper, data);
410         while (!import_thread_done) {
411                 import_thread_cancelled = process_ui_events();
412                 usleep(100000);
413         }
414         if (pthread_join(pthread, &retval) < 0)
415                 retval = "Odd pthread error return";
416         if (retval)
417                 return error(retval, data->vendor, data->product, data->devname);
418         return NULL;
419 }