9 #include "display-gtk.h"
11 #include "libdivecomputer.h"
13 static void error(const char *fmt, ...)
19 error = g_error_new_valist(
20 g_quark_from_string("subsurface"),
21 DIVE_ERROR_PARSE, fmt, args);
27 static parser_status_t create_parser(device_data_t *devdata, parser_t **parser)
29 switch (devdata->type) {
30 case DEVICE_TYPE_SUUNTO_SOLUTION:
31 return suunto_solution_parser_create(parser);
33 case DEVICE_TYPE_SUUNTO_EON:
34 return suunto_eon_parser_create(parser, 0);
36 case DEVICE_TYPE_SUUNTO_VYPER:
37 if (devdata->devinfo.model == 0x01)
38 return suunto_eon_parser_create(parser, 1);
39 return suunto_vyper_parser_create(parser);
41 case DEVICE_TYPE_SUUNTO_VYPER2:
42 case DEVICE_TYPE_SUUNTO_D9:
43 return suunto_d9_parser_create(parser, devdata->devinfo.model);
45 case DEVICE_TYPE_UWATEC_ALADIN:
46 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
47 return uwatec_memomouse_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
49 case DEVICE_TYPE_UWATEC_SMART:
50 return uwatec_smart_parser_create(parser, devdata->devinfo.model, devdata->clock.devtime, devdata->clock.systime);
52 case DEVICE_TYPE_REEFNET_SENSUS:
53 return reefnet_sensus_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
55 case DEVICE_TYPE_REEFNET_SENSUSPRO:
56 return reefnet_sensuspro_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
58 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
59 return reefnet_sensusultra_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
61 case DEVICE_TYPE_OCEANIC_VTPRO:
62 return oceanic_vtpro_parser_create(parser);
64 case DEVICE_TYPE_OCEANIC_VEO250:
65 return oceanic_veo250_parser_create(parser, devdata->devinfo.model);
67 case DEVICE_TYPE_OCEANIC_ATOM2:
68 return oceanic_atom2_parser_create(parser, devdata->devinfo.model);
70 case DEVICE_TYPE_MARES_NEMO:
71 case DEVICE_TYPE_MARES_PUCK:
72 return mares_nemo_parser_create(parser, devdata->devinfo.model);
74 case DEVICE_TYPE_MARES_ICONHD:
75 return mares_iconhd_parser_create(parser, devdata->devinfo.model);
77 case DEVICE_TYPE_HW_OSTC:
78 return hw_ostc_parser_create(parser);
80 case DEVICE_TYPE_CRESSI_EDY:
81 case DEVICE_TYPE_ZEAGLE_N2ITION3:
82 return cressi_edy_parser_create(parser, devdata->devinfo.model);
84 case DEVICE_TYPE_ATOMICS_COBALT:
85 return atomics_cobalt_parser_create(parser);
88 return PARSER_STATUS_ERROR;
92 static int parse_gasmixes(struct dive *dive, parser_t *parser, int ngases)
96 for (i = 0; i < ngases; i++) {
98 gasmix_t gasmix = {0};
101 rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
102 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
105 if (i >= MAX_CYLINDERS)
108 o2 = gasmix.oxygen * 1000 + 0.5;
109 he = gasmix.helium * 1000 + 0.5;
111 /* Ignore bogus data - libdivecomputer does some crazy stuff */
112 if (o2 < 210 || o2 >= 1000)
114 if (he < 0 || he >= 800 || o2+he >= 1000)
117 dive->cylinder[i].gasmix.o2.permille = o2;
118 dive->cylinder[i].gasmix.he.permille = he;
120 return PARSER_STATUS_SUCCESS;
123 static void handle_event(struct dive *dive, struct sample *sample, parser_sample_value_t value)
126 static const char *events[] = {
127 "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
128 "violation", "bookmark", "surface", "safety stop", "gaschange",
129 "safety stop (voluntary)", "safety stop (mandatory)", "deepstop",
130 "ceiling (safety stop)", "unknown", "divetime", "maxdepth",
131 "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning"
133 const int nr_events = sizeof(events) / sizeof(const char *);
137 * Just ignore surface events. They are pointless. What "surface"
138 * means depends on the dive computer (and possibly even settings
139 * in the dive computer). It does *not* necessarily mean "depth 0",
140 * so don't even turn it into that.
142 if (value.event.type == SAMPLE_EVENT_SURFACE)
146 * Other evens might be more interesting, but for now we just print them out.
148 type = value.event.type;
149 name = "invalid event number";
150 if (type < nr_events)
153 time = value.event.time;
155 time += sample->time.seconds;
157 add_event(dive, time, type, value.event.flags, value.event.value, name);
161 sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
164 struct dive **divep = userdata;
165 struct dive *dive = *divep;
166 struct sample *sample;
169 * We fill in the "previous" sample - except for SAMPLE_TYPE_TIME,
170 * which creates a new one.
172 sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
175 case SAMPLE_TYPE_TIME:
176 sample = prepare_sample(divep);
177 sample->time.seconds = value.time;
178 finish_sample(*divep, sample);
180 case SAMPLE_TYPE_DEPTH:
181 sample->depth.mm = value.depth * 1000 + 0.5;
183 case SAMPLE_TYPE_PRESSURE:
184 sample->cylinderindex = value.pressure.tank;
185 sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
187 case SAMPLE_TYPE_TEMPERATURE:
188 sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
190 case SAMPLE_TYPE_EVENT:
191 handle_event(dive, sample, value);
193 case SAMPLE_TYPE_RBT:
194 printf(" <rbt>%u</rbt>\n", value.rbt);
196 case SAMPLE_TYPE_HEARTBEAT:
197 printf(" <heartbeat>%u</heartbeat>\n", value.heartbeat);
199 case SAMPLE_TYPE_BEARING:
200 printf(" <bearing>%u</bearing>\n", value.bearing);
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");
214 static int parse_samples(struct dive **divep, parser_t *parser)
216 // Parse the sample data.
217 printf("Parsing the sample data.\n");
218 return parser_samples_foreach(parser, sample_cb, divep);
222 * Check if this dive already existed before the import
224 static int find_dive(struct dive *dive, device_data_t *devdata)
228 for (i = 0; i < dive_table.preexisting; i++) {
229 struct dive *old = dive_table.dives[i];
231 if (dive->when != old->when)
238 static int dive_cb(const unsigned char *data, unsigned int size,
239 const unsigned char *fingerprint, unsigned int fsize,
243 parser_t *parser = NULL;
244 device_data_t *devdata = userdata;
245 dc_datetime_t dt = {0};
249 rc = create_parser(devdata, &parser);
250 if (rc != PARSER_STATUS_SUCCESS) {
251 fprintf(stderr, "Unable to create parser for %s", devdata->name);
255 rc = parser_set_data(parser, data, size);
256 if (rc != PARSER_STATUS_SUCCESS) {
257 fprintf(stderr, "Error registering the data.");
258 parser_destroy(parser);
263 rc = parser_get_datetime(parser, &dt);
264 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
265 fprintf(stderr, "Error parsing the datetime.");
266 parser_destroy (parser);
270 tm.tm_year = dt.year;
271 tm.tm_mon = dt.month-1;
273 tm.tm_hour = dt.hour;
274 tm.tm_min = dt.minute;
275 tm.tm_sec = dt.second;
276 dive->when = utc_mktime(&tm);
278 // Parse the divetime.
279 printf("Parsing the divetime.\n");
280 unsigned int divetime = 0;
281 rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
282 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
283 fprintf(stderr, "Error parsing the divetime.");
284 parser_destroy(parser);
287 dive->duration.seconds = divetime;
289 // Parse the maxdepth.
290 printf("Parsing the maxdepth.\n");
291 double maxdepth = 0.0;
292 rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
293 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
294 fprintf(stderr, "Error parsing the maxdepth.");
295 parser_destroy(parser);
298 dive->maxdepth.mm = maxdepth * 1000 + 0.5;
300 // Parse the gas mixes.
301 printf("Parsing the gas mixes.\n");
302 unsigned int ngases = 0;
303 rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
304 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
305 fprintf(stderr, "Error parsing the gas mix count.");
306 parser_destroy(parser);
310 rc = parse_gasmixes(dive, parser, ngases);
311 if (rc != PARSER_STATUS_SUCCESS) {
312 fprintf(stderr, "Error parsing the gas mix.");
313 parser_destroy(parser);
317 // Initialize the sample data.
318 rc = parse_samples(&dive, parser);
319 if (rc != PARSER_STATUS_SUCCESS) {
320 fprintf(stderr, "Error parsing the samples.");
321 parser_destroy(parser);
325 parser_destroy(parser);
327 /* If we already saw this dive, abort. */
328 if (find_dive(dive, devdata))
336 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
338 return device_foreach(device, dive_cb, devicedata);
341 static device_status_t device_open(const char *devname,
346 case DEVICE_TYPE_SUUNTO_SOLUTION:
347 return suunto_solution_device_open(device, devname);
349 case DEVICE_TYPE_SUUNTO_EON:
350 return suunto_eon_device_open(device, devname);
352 case DEVICE_TYPE_SUUNTO_VYPER:
353 return suunto_vyper_device_open(device, devname);
355 case DEVICE_TYPE_SUUNTO_VYPER2:
356 return suunto_vyper2_device_open(device, devname);
358 case DEVICE_TYPE_SUUNTO_D9:
359 return suunto_d9_device_open(device, devname);
361 case DEVICE_TYPE_UWATEC_ALADIN:
362 return uwatec_aladin_device_open(device, devname);
364 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
365 return uwatec_memomouse_device_open(device, devname);
367 case DEVICE_TYPE_UWATEC_SMART:
368 return uwatec_smart_device_open(device);
370 case DEVICE_TYPE_REEFNET_SENSUS:
371 return reefnet_sensus_device_open(device, devname);
373 case DEVICE_TYPE_REEFNET_SENSUSPRO:
374 return reefnet_sensuspro_device_open(device, devname);
376 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
377 return reefnet_sensusultra_device_open(device, devname);
379 case DEVICE_TYPE_OCEANIC_VTPRO:
380 return oceanic_vtpro_device_open(device, devname);
382 case DEVICE_TYPE_OCEANIC_VEO250:
383 return oceanic_veo250_device_open(device, devname);
385 case DEVICE_TYPE_OCEANIC_ATOM2:
386 return oceanic_atom2_device_open(device, devname);
388 case DEVICE_TYPE_MARES_NEMO:
389 return mares_nemo_device_open(device, devname);
391 case DEVICE_TYPE_MARES_PUCK:
392 return mares_puck_device_open(device, devname);
394 case DEVICE_TYPE_MARES_ICONHD:
395 return mares_iconhd_device_open(device, devname);
397 case DEVICE_TYPE_HW_OSTC:
398 return hw_ostc_device_open(device, devname);
400 case DEVICE_TYPE_CRESSI_EDY:
401 return cressi_edy_device_open(device, devname);
403 case DEVICE_TYPE_ZEAGLE_N2ITION3:
404 return zeagle_n2ition3_device_open(device, devname);
406 case DEVICE_TYPE_ATOMICS_COBALT:
407 return atomics_cobalt_device_open(device);
410 return DEVICE_STATUS_ERROR;
414 static void event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
416 const device_progress_t *progress = data;
417 const device_devinfo_t *devinfo = data;
418 const device_clock_t *clock = data;
419 device_data_t *devdata = userdata;
422 case DEVICE_EVENT_WAITING:
423 printf("Event: waiting for user action\n");
425 case DEVICE_EVENT_PROGRESS:
426 update_progressbar(&devdata->progress,
427 (double) progress->current / (double) progress->maximum);
429 case DEVICE_EVENT_DEVINFO:
430 devdata->devinfo = *devinfo;
431 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
432 devinfo->model, devinfo->model,
433 devinfo->firmware, devinfo->firmware,
434 devinfo->serial, devinfo->serial);
436 case DEVICE_EVENT_CLOCK:
437 devdata->clock = *clock;
438 printf("Event: systime=%"PRId64", devtime=%u\n",
439 (uint64_t)clock->systime, clock->devtime);
446 static int import_thread_done = 0, import_thread_cancelled;
449 cancel_cb(void *userdata)
451 return import_thread_cancelled;
454 static const char *do_libdivecomputer_import(device_data_t *data)
456 device_t *device = NULL;
459 rc = device_open(data->devname, data->type, &device);
460 if (rc != DEVICE_STATUS_SUCCESS)
461 return "Unable to open %s (%s)";
463 // Register the event handler.
464 int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
465 rc = device_set_events(device, events, event_cb, data);
466 if (rc != DEVICE_STATUS_SUCCESS) {
467 device_close(device);
468 return "Error registering the event handler.";
471 // Register the cancellation handler.
472 rc = device_set_cancel(device, cancel_cb, data);
473 if (rc != DEVICE_STATUS_SUCCESS) {
474 device_close(device);
475 return "Error registering the cancellation handler.";
478 rc = import_device_data(device, data);
479 if (rc != DEVICE_STATUS_SUCCESS) {
480 device_close(device);
481 return "Dive data import error";
484 device_close(device);
488 static void *pthread_wrapper(void *_data)
490 device_data_t *data = _data;
491 const char *err_string = do_libdivecomputer_import(data);
492 import_thread_done = 1;
493 return (void *)err_string;
496 void do_import(device_data_t *data)
501 /* I'm sure there is some better interface for waiting on a thread in a UI main loop */
502 import_thread_done = 0;
503 pthread_create(&pthread, NULL, pthread_wrapper, data);
504 while (!import_thread_done) {
505 import_thread_cancelled = process_ui_events();
508 if (pthread_join(pthread, &retval) < 0)
509 retval = "Odd pthread error return";
511 error(retval, data->name, data->devname);
515 * Taken from 'example.c' in libdivecomputer.
517 * I really wish there was some way to just have
518 * libdivecomputer tell us what devices it supports,
519 * rather than have the application have to know..
521 struct device_list device_list[] = {
522 { "Suunto Solution", DEVICE_TYPE_SUUNTO_SOLUTION },
523 { "Suunto Eon", DEVICE_TYPE_SUUNTO_EON },
524 { "Suunto Vyper", DEVICE_TYPE_SUUNTO_VYPER },
525 { "Suunto Vyper Air", DEVICE_TYPE_SUUNTO_VYPER2 },
526 { "Suunto D9", DEVICE_TYPE_SUUNTO_D9 },
527 { "Uwatec Aladin", DEVICE_TYPE_UWATEC_ALADIN },
528 { "Uwatec Memo Mouse", DEVICE_TYPE_UWATEC_MEMOMOUSE },
529 { "Uwatec Smart", DEVICE_TYPE_UWATEC_SMART },
530 { "ReefNet Sensus", DEVICE_TYPE_REEFNET_SENSUS },
531 { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
532 { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
533 { "Oceanic VT Pro", DEVICE_TYPE_OCEANIC_VTPRO },
534 { "Oceanic Veo250", DEVICE_TYPE_OCEANIC_VEO250 },
535 { "Oceanic Atom 2", DEVICE_TYPE_OCEANIC_ATOM2 },
536 { "Mares Nemo", DEVICE_TYPE_MARES_NEMO },
537 { "Mares Puck", DEVICE_TYPE_MARES_PUCK },
538 { "Mares Icon HD", DEVICE_TYPE_MARES_ICONHD },
539 { "OSTC", DEVICE_TYPE_HW_OSTC },
540 { "Cressi Edy", DEVICE_TYPE_CRESSI_EDY },
541 { "Zeagle N2iTiON 3", DEVICE_TYPE_ZEAGLE_N2ITION3 },
542 { "Atomics Cobalt", DEVICE_TYPE_ATOMICS_COBALT },