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_DARWIN:
71 return mares_darwin_parser_create(parser, devdata->devinfo.model);
73 case DEVICE_TYPE_MARES_NEMO:
74 case DEVICE_TYPE_MARES_PUCK:
75 return mares_nemo_parser_create(parser, devdata->devinfo.model);
77 case DEVICE_TYPE_MARES_ICONHD:
78 return mares_iconhd_parser_create(parser, devdata->devinfo.model);
80 case DEVICE_TYPE_HW_OSTC:
81 return hw_ostc_parser_create(parser);
83 case DEVICE_TYPE_CRESSI_EDY:
84 case DEVICE_TYPE_ZEAGLE_N2ITION3:
85 return cressi_edy_parser_create(parser, devdata->devinfo.model);
87 case DEVICE_TYPE_ATOMICS_COBALT:
88 return atomics_cobalt_parser_create(parser);
91 return PARSER_STATUS_ERROR;
95 static int parse_gasmixes(struct dive *dive, parser_t *parser, int ngases)
99 for (i = 0; i < ngases; i++) {
101 gasmix_t gasmix = {0};
104 rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
105 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
108 if (i >= MAX_CYLINDERS)
111 o2 = gasmix.oxygen * 1000 + 0.5;
112 he = gasmix.helium * 1000 + 0.5;
114 /* Ignore bogus data - libdivecomputer does some crazy stuff */
115 if (o2 <= AIR_PERMILLE || o2 >= 1000)
117 if (he < 0 || he >= 800 || o2+he >= 1000)
120 dive->cylinder[i].gasmix.o2.permille = o2;
121 dive->cylinder[i].gasmix.he.permille = he;
123 return PARSER_STATUS_SUCCESS;
126 static void handle_event(struct dive *dive, struct sample *sample, parser_sample_value_t value)
129 static const char *events[] = {
130 "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
131 "violation", "bookmark", "surface", "safety stop", "gaschange",
132 "safety stop (voluntary)", "safety stop (mandatory)", "deepstop",
133 "ceiling (safety stop)", "unknown", "divetime", "maxdepth",
134 "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning"
136 const int nr_events = sizeof(events) / sizeof(const char *);
140 * Just ignore surface events. They are pointless. What "surface"
141 * means depends on the dive computer (and possibly even settings
142 * in the dive computer). It does *not* necessarily mean "depth 0",
143 * so don't even turn it into that.
145 if (value.event.type == SAMPLE_EVENT_SURFACE)
149 * Other evens might be more interesting, but for now we just print them out.
151 type = value.event.type;
152 name = "invalid event number";
153 if (type < nr_events)
156 time = value.event.time;
158 time += sample->time.seconds;
160 add_event(dive, time, type, value.event.flags, value.event.value, name);
164 sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
167 struct dive **divep = userdata;
168 struct dive *dive = *divep;
169 struct sample *sample;
172 * We fill in the "previous" sample - except for SAMPLE_TYPE_TIME,
173 * which creates a new one.
175 sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
178 case SAMPLE_TYPE_TIME:
179 sample = prepare_sample(divep);
180 sample->time.seconds = value.time;
181 finish_sample(*divep);
183 case SAMPLE_TYPE_DEPTH:
184 sample->depth.mm = value.depth * 1000 + 0.5;
186 case SAMPLE_TYPE_PRESSURE:
187 sample->cylinderindex = value.pressure.tank;
188 sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
190 case SAMPLE_TYPE_TEMPERATURE:
191 sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
193 case SAMPLE_TYPE_EVENT:
194 handle_event(dive, sample, value);
196 case SAMPLE_TYPE_RBT:
197 printf(" <rbt>%u</rbt>\n", value.rbt);
199 case SAMPLE_TYPE_HEARTBEAT:
200 printf(" <heartbeat>%u</heartbeat>\n", value.heartbeat);
202 case SAMPLE_TYPE_BEARING:
203 printf(" <bearing>%u</bearing>\n", value.bearing);
205 case SAMPLE_TYPE_VENDOR:
206 printf(" <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
207 for (i = 0; i < value.vendor.size; ++i)
208 printf("%02X", ((unsigned char *) value.vendor.data)[i]);
209 printf("</vendor>\n");
217 static int parse_samples(struct dive **divep, parser_t *parser)
219 // Parse the sample data.
220 printf("Parsing the sample data.\n");
221 return parser_samples_foreach(parser, sample_cb, divep);
225 * Check if this dive already existed before the import
227 static int find_dive(struct dive *dive, device_data_t *devdata)
231 for (i = 0; i < dive_table.preexisting; i++) {
232 struct dive *old = dive_table.dives[i];
234 if (dive->when != old->when)
241 static int dive_cb(const unsigned char *data, unsigned int size,
242 const unsigned char *fingerprint, unsigned int fsize,
246 parser_t *parser = NULL;
247 device_data_t *devdata = userdata;
248 dc_datetime_t dt = {0};
252 rc = create_parser(devdata, &parser);
253 if (rc != PARSER_STATUS_SUCCESS) {
254 fprintf(stderr, "Unable to create parser for %s", devdata->name);
258 rc = parser_set_data(parser, data, size);
259 if (rc != PARSER_STATUS_SUCCESS) {
260 fprintf(stderr, "Error registering the data.");
261 parser_destroy(parser);
266 rc = parser_get_datetime(parser, &dt);
267 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
268 fprintf(stderr, "Error parsing the datetime.");
269 parser_destroy (parser);
273 tm.tm_year = dt.year;
274 tm.tm_mon = dt.month-1;
276 tm.tm_hour = dt.hour;
277 tm.tm_min = dt.minute;
278 tm.tm_sec = dt.second;
279 dive->when = utc_mktime(&tm);
281 // Parse the divetime.
282 printf("Parsing the divetime.\n");
283 unsigned int divetime = 0;
284 rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
285 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
286 fprintf(stderr, "Error parsing the divetime.");
287 parser_destroy(parser);
290 dive->duration.seconds = divetime;
292 // Parse the maxdepth.
293 printf("Parsing the maxdepth.\n");
294 double maxdepth = 0.0;
295 rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
296 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
297 fprintf(stderr, "Error parsing the maxdepth.");
298 parser_destroy(parser);
301 dive->maxdepth.mm = maxdepth * 1000 + 0.5;
303 // Parse the gas mixes.
304 printf("Parsing the gas mixes.\n");
305 unsigned int ngases = 0;
306 rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
307 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
308 fprintf(stderr, "Error parsing the gas mix count.");
309 parser_destroy(parser);
313 rc = parse_gasmixes(dive, parser, ngases);
314 if (rc != PARSER_STATUS_SUCCESS) {
315 fprintf(stderr, "Error parsing the gas mix.");
316 parser_destroy(parser);
320 // Initialize the sample data.
321 rc = parse_samples(&dive, parser);
322 if (rc != PARSER_STATUS_SUCCESS) {
323 fprintf(stderr, "Error parsing the samples.");
324 parser_destroy(parser);
328 parser_destroy(parser);
330 /* If we already saw this dive, abort. */
331 if (find_dive(dive, devdata))
339 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
341 return device_foreach(device, dive_cb, devicedata);
344 static device_status_t device_open(const char *devname,
349 case DEVICE_TYPE_SUUNTO_SOLUTION:
350 return suunto_solution_device_open(device, devname);
352 case DEVICE_TYPE_SUUNTO_EON:
353 return suunto_eon_device_open(device, devname);
355 case DEVICE_TYPE_SUUNTO_VYPER:
356 return suunto_vyper_device_open(device, devname);
358 case DEVICE_TYPE_SUUNTO_VYPER2:
359 return suunto_vyper2_device_open(device, devname);
361 case DEVICE_TYPE_SUUNTO_D9:
362 return suunto_d9_device_open(device, devname);
364 case DEVICE_TYPE_UWATEC_ALADIN:
365 return uwatec_aladin_device_open(device, devname);
367 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
368 return uwatec_memomouse_device_open(device, devname);
370 case DEVICE_TYPE_UWATEC_SMART:
371 return uwatec_smart_device_open(device);
373 case DEVICE_TYPE_REEFNET_SENSUS:
374 return reefnet_sensus_device_open(device, devname);
376 case DEVICE_TYPE_REEFNET_SENSUSPRO:
377 return reefnet_sensuspro_device_open(device, devname);
379 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
380 return reefnet_sensusultra_device_open(device, devname);
382 case DEVICE_TYPE_OCEANIC_VTPRO:
383 return oceanic_vtpro_device_open(device, devname);
385 case DEVICE_TYPE_OCEANIC_VEO250:
386 return oceanic_veo250_device_open(device, devname);
388 case DEVICE_TYPE_OCEANIC_ATOM2:
389 return oceanic_atom2_device_open(device, devname);
391 case DEVICE_TYPE_MARES_DARWIN:
392 return mares_darwin_device_open(device, devname, 0); /// last parameter is model type (taken from example), 0 seems to be standard, 1 is DARWIN_AIR => Darwin Air wont work if this is fixed here?
394 case DEVICE_TYPE_MARES_NEMO:
395 return mares_nemo_device_open(device, devname);
397 case DEVICE_TYPE_MARES_PUCK:
398 return mares_puck_device_open(device, devname);
400 case DEVICE_TYPE_MARES_ICONHD:
401 return mares_iconhd_device_open(device, devname);
403 case DEVICE_TYPE_HW_OSTC:
404 return hw_ostc_device_open(device, devname);
406 case DEVICE_TYPE_CRESSI_EDY:
407 return cressi_edy_device_open(device, devname);
409 case DEVICE_TYPE_ZEAGLE_N2ITION3:
410 return zeagle_n2ition3_device_open(device, devname);
412 case DEVICE_TYPE_ATOMICS_COBALT:
413 return atomics_cobalt_device_open(device);
416 return DEVICE_STATUS_ERROR;
420 static void event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
422 const device_progress_t *progress = data;
423 const device_devinfo_t *devinfo = data;
424 const device_clock_t *clock = data;
425 device_data_t *devdata = userdata;
428 case DEVICE_EVENT_WAITING:
429 printf("Event: waiting for user action\n");
431 case DEVICE_EVENT_PROGRESS:
432 update_progressbar(&devdata->progress,
433 (double) progress->current / (double) progress->maximum);
435 case DEVICE_EVENT_DEVINFO:
436 devdata->devinfo = *devinfo;
437 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
438 devinfo->model, devinfo->model,
439 devinfo->firmware, devinfo->firmware,
440 devinfo->serial, devinfo->serial);
442 case DEVICE_EVENT_CLOCK:
443 devdata->clock = *clock;
444 printf("Event: systime=%"PRId64", devtime=%u\n",
445 (uint64_t)clock->systime, clock->devtime);
452 static int import_thread_done = 0, import_thread_cancelled;
455 cancel_cb(void *userdata)
457 return import_thread_cancelled;
460 static const char *do_libdivecomputer_import(device_data_t *data)
462 device_t *device = NULL;
465 rc = device_open(data->devname, data->type, &device);
466 if (rc != DEVICE_STATUS_SUCCESS)
467 return "Unable to open %s (%s)";
469 // Register the event handler.
470 int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
471 rc = device_set_events(device, events, event_cb, data);
472 if (rc != DEVICE_STATUS_SUCCESS) {
473 device_close(device);
474 return "Error registering the event handler.";
477 // Register the cancellation handler.
478 rc = device_set_cancel(device, cancel_cb, data);
479 if (rc != DEVICE_STATUS_SUCCESS) {
480 device_close(device);
481 return "Error registering the cancellation handler.";
484 rc = import_device_data(device, data);
485 if (rc != DEVICE_STATUS_SUCCESS) {
486 device_close(device);
487 return "Dive data import error";
490 device_close(device);
494 static void *pthread_wrapper(void *_data)
496 device_data_t *data = _data;
497 const char *err_string = do_libdivecomputer_import(data);
498 import_thread_done = 1;
499 return (void *)err_string;
502 void do_import(device_data_t *data)
507 /* I'm sure there is some better interface for waiting on a thread in a UI main loop */
508 import_thread_done = 0;
509 pthread_create(&pthread, NULL, pthread_wrapper, data);
510 while (!import_thread_done) {
511 import_thread_cancelled = process_ui_events();
514 if (pthread_join(pthread, &retval) < 0)
515 retval = "Odd pthread error return";
517 error(retval, data->name, data->devname);
521 * Taken from 'example.c' in libdivecomputer.
523 * I really wish there was some way to just have
524 * libdivecomputer tell us what devices it supports,
525 * rather than have the application have to know..
527 struct device_list device_list[] = {
528 { "Suunto Solution", DEVICE_TYPE_SUUNTO_SOLUTION },
529 { "Suunto Eon", DEVICE_TYPE_SUUNTO_EON },
530 { "Suunto Vyper", DEVICE_TYPE_SUUNTO_VYPER },
531 { "Suunto Vyper Air", DEVICE_TYPE_SUUNTO_VYPER2 },
532 { "Suunto D9", DEVICE_TYPE_SUUNTO_D9 },
533 { "Uwatec Aladin", DEVICE_TYPE_UWATEC_ALADIN },
534 { "Uwatec Memo Mouse", DEVICE_TYPE_UWATEC_MEMOMOUSE },
535 { "Uwatec Smart", DEVICE_TYPE_UWATEC_SMART },
536 { "ReefNet Sensus", DEVICE_TYPE_REEFNET_SENSUS },
537 { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
538 { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
539 { "Oceanic VT Pro", DEVICE_TYPE_OCEANIC_VTPRO },
540 { "Oceanic Veo250", DEVICE_TYPE_OCEANIC_VEO250 },
541 { "Oceanic Atom 2", DEVICE_TYPE_OCEANIC_ATOM2 },
542 { "Mares Darwin, M1, M2, Airlab", DEVICE_TYPE_MARES_DARWIN },
543 { "Mares Nemo, Excel, Apneist", DEVICE_TYPE_MARES_NEMO },
544 { "Mares Puck, Nemo Air, Nemo Wide", DEVICE_TYPE_MARES_PUCK },
545 { "Mares Icon HD", DEVICE_TYPE_MARES_ICONHD },
546 { "OSTC", DEVICE_TYPE_HW_OSTC },
547 { "Cressi Edy", DEVICE_TYPE_CRESSI_EDY },
548 { "Zeagle N2iTiON 3", DEVICE_TYPE_ZEAGLE_N2ITION3 },
549 { "Atomics Cobalt", DEVICE_TYPE_ATOMICS_COBALT },