7 #include "display-gtk.h"
9 #include "libdivecomputer.h"
11 static void error(const char *fmt, ...)
17 error = g_error_new_valist(
18 g_quark_from_string("subsurface"),
19 DIVE_ERROR_PARSE, fmt, args);
25 static parser_status_t create_parser(device_data_t *devdata, parser_t **parser)
27 switch (devdata->type) {
28 case DEVICE_TYPE_SUUNTO_SOLUTION:
29 return suunto_solution_parser_create(parser);
31 case DEVICE_TYPE_SUUNTO_EON:
32 return suunto_eon_parser_create(parser, 0);
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);
39 case DEVICE_TYPE_SUUNTO_VYPER2:
40 case DEVICE_TYPE_SUUNTO_D9:
41 return suunto_d9_parser_create(parser, devdata->devinfo.model);
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);
47 case DEVICE_TYPE_UWATEC_SMART:
48 return uwatec_smart_parser_create(parser, devdata->devinfo.model, devdata->clock.devtime, devdata->clock.systime);
50 case DEVICE_TYPE_REEFNET_SENSUS:
51 return reefnet_sensus_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
53 case DEVICE_TYPE_REEFNET_SENSUSPRO:
54 return reefnet_sensuspro_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
56 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
57 return reefnet_sensusultra_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
59 case DEVICE_TYPE_OCEANIC_VTPRO:
60 return oceanic_vtpro_parser_create(parser);
62 case DEVICE_TYPE_OCEANIC_VEO250:
63 return oceanic_veo250_parser_create(parser, devdata->devinfo.model);
65 case DEVICE_TYPE_OCEANIC_ATOM2:
66 return oceanic_atom2_parser_create(parser, devdata->devinfo.model);
68 case DEVICE_TYPE_MARES_NEMO:
69 case DEVICE_TYPE_MARES_PUCK:
70 return mares_nemo_parser_create(parser, devdata->devinfo.model);
72 case DEVICE_TYPE_MARES_ICONHD:
73 return mares_iconhd_parser_create(parser, devdata->devinfo.model);
75 case DEVICE_TYPE_HW_OSTC:
76 return hw_ostc_parser_create(parser);
78 case DEVICE_TYPE_CRESSI_EDY:
79 case DEVICE_TYPE_ZEAGLE_N2ITION3:
80 return cressi_edy_parser_create(parser, devdata->devinfo.model);
82 case DEVICE_TYPE_ATOMICS_COBALT:
83 return atomics_cobalt_parser_create(parser);
86 return PARSER_STATUS_ERROR;
90 static int parse_gasmixes(struct dive *dive, parser_t *parser, int ngases)
94 for (i = 0; i < ngases; i++) {
96 gasmix_t gasmix = {0};
99 rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
100 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
103 if (i >= MAX_CYLINDERS)
106 o2 = gasmix.oxygen * 1000 + 0.5;
107 he = gasmix.helium * 1000 + 0.5;
109 /* Ignore bogus data - libdivecomputer does some crazy stuff */
110 if (o2 < 210 || o2 >= 1000)
112 if (he < 0 || he >= 800 || o2+he >= 1000)
115 dive->cylinder[i].gasmix.o2.permille = o2;
116 dive->cylinder[i].gasmix.he.permille = he;
118 return PARSER_STATUS_SUCCESS;
121 static void handle_event(struct dive *dive, struct sample *sample, parser_sample_value_t value)
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"
131 const int nr_events = sizeof(events) / sizeof(const char *);
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.
140 if (value.event.type == SAMPLE_EVENT_SURFACE)
144 * Other evens might be more interesting, but for now we just print them out.
146 type = value.event.type;
147 name = "invalid event number";
148 if (type < nr_events)
151 time = value.event.time;
153 time += sample->time.seconds;
155 add_event(dive, time, type, value.event.flags, value.event.value, name);
159 sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
162 struct dive **divep = userdata;
163 struct dive *dive = *divep;
164 struct sample *sample;
167 * We fill in the "previous" sample - except for SAMPLE_TYPE_TIME,
168 * which creates a new one.
170 sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
173 case SAMPLE_TYPE_TIME:
174 sample = prepare_sample(divep);
175 sample->time.seconds = value.time;
176 finish_sample(*divep, sample);
178 case SAMPLE_TYPE_DEPTH:
179 sample->depth.mm = value.depth * 1000 + 0.5;
181 case SAMPLE_TYPE_PRESSURE:
182 sample->cylinderindex = value.pressure.tank;
183 sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
185 case SAMPLE_TYPE_TEMPERATURE:
186 sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
188 case SAMPLE_TYPE_EVENT:
189 handle_event(dive, sample, value);
191 case SAMPLE_TYPE_RBT:
192 printf(" <rbt>%u</rbt>\n", value.rbt);
194 case SAMPLE_TYPE_HEARTBEAT:
195 printf(" <heartbeat>%u</heartbeat>\n", value.heartbeat);
197 case SAMPLE_TYPE_BEARING:
198 printf(" <bearing>%u</bearing>\n", value.bearing);
200 case SAMPLE_TYPE_VENDOR:
201 printf(" <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
202 for (i = 0; i < value.vendor.size; ++i)
203 printf("%02X", ((unsigned char *) value.vendor.data)[i]);
204 printf("</vendor>\n");
212 static int parse_samples(struct dive **divep, parser_t *parser)
214 // Parse the sample data.
215 printf("Parsing the sample data.\n");
216 return parser_samples_foreach(parser, sample_cb, divep);
220 * Check if this dive already existed before the import
222 static int find_dive(struct dive *dive, device_data_t *devdata)
226 for (i = 0; i < devdata->preexisting; i++) {
227 struct dive *old = dive_table.dives[i];
229 if (dive->when != old->when)
236 static int dive_cb(const unsigned char *data, unsigned int size,
237 const unsigned char *fingerprint, unsigned int fsize,
241 parser_t *parser = NULL;
242 device_data_t *devdata = userdata;
243 dc_datetime_t dt = {0};
247 rc = create_parser(devdata, &parser);
248 if (rc != PARSER_STATUS_SUCCESS) {
249 fprintf(stderr, "Unable to create parser for %s", devdata->name);
253 rc = parser_set_data(parser, data, size);
254 if (rc != PARSER_STATUS_SUCCESS) {
255 fprintf(stderr, "Error registering the data.");
256 parser_destroy(parser);
261 rc = parser_get_datetime(parser, &dt);
262 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
263 fprintf(stderr, "Error parsing the datetime.");
264 parser_destroy (parser);
268 tm.tm_year = dt.year;
269 tm.tm_mon = dt.month-1;
271 tm.tm_hour = dt.hour;
272 tm.tm_min = dt.minute;
273 tm.tm_sec = dt.second;
274 dive->when = utc_mktime(&tm);
276 // Parse the divetime.
277 printf("Parsing the divetime.\n");
278 unsigned int divetime = 0;
279 rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
280 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
281 fprintf(stderr, "Error parsing the divetime.");
282 parser_destroy(parser);
285 dive->duration.seconds = divetime;
287 // Parse the maxdepth.
288 printf("Parsing the maxdepth.\n");
289 double maxdepth = 0.0;
290 rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
291 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
292 fprintf(stderr, "Error parsing the maxdepth.");
293 parser_destroy(parser);
296 dive->maxdepth.mm = maxdepth * 1000 + 0.5;
298 // Parse the gas mixes.
299 printf("Parsing the gas mixes.\n");
300 unsigned int ngases = 0;
301 rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
302 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
303 fprintf(stderr, "Error parsing the gas mix count.");
304 parser_destroy(parser);
308 rc = parse_gasmixes(dive, parser, ngases);
309 if (rc != PARSER_STATUS_SUCCESS) {
310 fprintf(stderr, "Error parsing the gas mix.");
311 parser_destroy(parser);
315 // Initialize the sample data.
316 rc = parse_samples(&dive, parser);
317 if (rc != PARSER_STATUS_SUCCESS) {
318 fprintf(stderr, "Error parsing the samples.");
319 parser_destroy(parser);
323 parser_destroy(parser);
325 /* If we already saw this dive, abort. */
326 if (find_dive(dive, devdata))
334 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
336 devicedata->preexisting = dive_table.nr;
337 return device_foreach(device, dive_cb, devicedata);
340 static device_status_t device_open(const char *devname,
345 case DEVICE_TYPE_SUUNTO_SOLUTION:
346 return suunto_solution_device_open(device, devname);
348 case DEVICE_TYPE_SUUNTO_EON:
349 return suunto_eon_device_open(device, devname);
351 case DEVICE_TYPE_SUUNTO_VYPER:
352 return suunto_vyper_device_open(device, devname);
354 case DEVICE_TYPE_SUUNTO_VYPER2:
355 return suunto_vyper2_device_open(device, devname);
357 case DEVICE_TYPE_SUUNTO_D9:
358 return suunto_d9_device_open(device, devname);
360 case DEVICE_TYPE_UWATEC_ALADIN:
361 return uwatec_aladin_device_open(device, devname);
363 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
364 return uwatec_memomouse_device_open(device, devname);
366 case DEVICE_TYPE_UWATEC_SMART:
367 return uwatec_smart_device_open(device);
369 case DEVICE_TYPE_REEFNET_SENSUS:
370 return reefnet_sensus_device_open(device, devname);
372 case DEVICE_TYPE_REEFNET_SENSUSPRO:
373 return reefnet_sensuspro_device_open(device, devname);
375 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
376 return reefnet_sensusultra_device_open(device, devname);
378 case DEVICE_TYPE_OCEANIC_VTPRO:
379 return oceanic_vtpro_device_open(device, devname);
381 case DEVICE_TYPE_OCEANIC_VEO250:
382 return oceanic_veo250_device_open(device, devname);
384 case DEVICE_TYPE_OCEANIC_ATOM2:
385 return oceanic_atom2_device_open(device, devname);
387 case DEVICE_TYPE_MARES_NEMO:
388 return mares_nemo_device_open(device, devname);
390 case DEVICE_TYPE_MARES_PUCK:
391 return mares_puck_device_open(device, devname);
393 case DEVICE_TYPE_MARES_ICONHD:
394 return mares_iconhd_device_open(device, devname);
396 case DEVICE_TYPE_HW_OSTC:
397 return hw_ostc_device_open(device, devname);
399 case DEVICE_TYPE_CRESSI_EDY:
400 return cressi_edy_device_open(device, devname);
402 case DEVICE_TYPE_ZEAGLE_N2ITION3:
403 return zeagle_n2ition3_device_open(device, devname);
405 case DEVICE_TYPE_ATOMICS_COBALT:
406 return atomics_cobalt_device_open(device);
409 return DEVICE_STATUS_ERROR;
413 static void event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
415 const device_progress_t *progress = (device_progress_t *) data;
416 const device_devinfo_t *devinfo = (device_devinfo_t *) data;
417 const device_clock_t *clock = (device_clock_t *) data;
418 device_data_t *devdata = (device_data_t *) userdata;
421 case DEVICE_EVENT_WAITING:
422 printf("Event: waiting for user action\n");
424 case DEVICE_EVENT_PROGRESS:
425 update_progressbar(&devdata->progress,
426 (double) progress->current / (double) progress->maximum);
428 case DEVICE_EVENT_DEVINFO:
429 devdata->devinfo = *devinfo;
430 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
431 devinfo->model, devinfo->model,
432 devinfo->firmware, devinfo->firmware,
433 devinfo->serial, devinfo->serial);
435 case DEVICE_EVENT_CLOCK:
436 devdata->clock = *clock;
437 printf("Event: systime=%lld, devtime=%u\n",
438 clock->systime, clock->devtime);
445 static int import_thread_done = 0, import_thread_cancelled;
448 cancel_cb(void *userdata)
450 return import_thread_cancelled;
453 static const char *do_libdivecomputer_import(device_data_t *data)
455 device_t *device = NULL;
458 rc = device_open(data->devname, data->type, &device);
459 if (rc != DEVICE_STATUS_SUCCESS)
460 return "Unable to open %s (%s)";
462 // Register the event handler.
463 int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
464 rc = device_set_events(device, events, event_cb, data);
465 if (rc != DEVICE_STATUS_SUCCESS) {
466 device_close(device);
467 return "Error registering the event handler.";
470 // Register the cancellation handler.
471 rc = device_set_cancel(device, cancel_cb, data);
472 if (rc != DEVICE_STATUS_SUCCESS) {
473 device_close(device);
474 return "Error registering the cancellation handler.";
477 rc = import_device_data(device, data);
478 if (rc != DEVICE_STATUS_SUCCESS) {
479 device_close(device);
480 return "Dive data import error";
483 device_close(device);
487 static void *pthread_wrapper(void *_data)
489 device_data_t *data = _data;
490 const char *err_string = do_libdivecomputer_import(data);
491 import_thread_done = 1;
492 return (void *)err_string;
495 void do_import(device_data_t *data)
500 if (data->type == DEVICE_TYPE_UEMIS)
501 return uemis_import();
503 /* I'm sure there is some better interface for waiting on a thread in a UI main loop */
504 import_thread_done = 0;
505 pthread_create(&pthread, NULL, pthread_wrapper, data);
506 while (!import_thread_done) {
507 import_thread_cancelled = process_ui_events();
510 if (pthread_join(pthread, &retval) < 0)
511 retval = "Odd pthread error return";
513 error(retval, data->name, data->devname);
517 * Taken from 'example.c' in libdivecomputer.
519 * I really wish there was some way to just have
520 * libdivecomputer tell us what devices it supports,
521 * rather than have the application have to know..
523 struct device_list device_list[] = {
524 { "Suunto Solution", DEVICE_TYPE_SUUNTO_SOLUTION },
525 { "Suunto Eon", DEVICE_TYPE_SUUNTO_EON },
526 { "Suunto Vyper", DEVICE_TYPE_SUUNTO_VYPER },
527 { "Suunto Vyper Air", DEVICE_TYPE_SUUNTO_VYPER2 },
528 { "Suunto D9", DEVICE_TYPE_SUUNTO_D9 },
529 { "Uwatec Aladin", DEVICE_TYPE_UWATEC_ALADIN },
530 { "Uwatec Memo Mouse", DEVICE_TYPE_UWATEC_MEMOMOUSE },
531 { "Uwatec Smart", DEVICE_TYPE_UWATEC_SMART },
532 { "ReefNet Sensus", DEVICE_TYPE_REEFNET_SENSUS },
533 { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
534 { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
535 { "Oceanic VT Pro", DEVICE_TYPE_OCEANIC_VTPRO },
536 { "Oceanic Veo250", DEVICE_TYPE_OCEANIC_VEO250 },
537 { "Oceanic Atom 2", DEVICE_TYPE_OCEANIC_ATOM2 },
538 { "Mares Nemo", DEVICE_TYPE_MARES_NEMO },
539 { "Mares Puck", DEVICE_TYPE_MARES_PUCK },
540 { "Mares Icon HD", DEVICE_TYPE_MARES_ICONHD },
541 { "OSTC", DEVICE_TYPE_HW_OSTC },
542 { "Cressi Edy", DEVICE_TYPE_CRESSI_EDY },
543 { "Zeagle N2iTiON 3", DEVICE_TYPE_ZEAGLE_N2ITION3 },
544 { "Atomics Cobalt", DEVICE_TYPE_ATOMICS_COBALT },
545 { "Uemis Zurich SDA", DEVICE_TYPE_UEMIS },