22 /* handling uemis Zurich SDA files */
25 static void error(const char *fmt, ...)
31 error = g_error_new_valist(
32 g_quark_from_string("subsurface"),
33 DIVE_ERROR_PARSE, fmt, args);
39 typedef struct device_data_t {
41 const char *name, *devname;
42 GtkWidget *progressbar;
43 device_devinfo_t devinfo;
47 static parser_status_t create_parser(device_data_t *devdata, parser_t **parser)
49 switch (devdata->type) {
50 case DEVICE_TYPE_SUUNTO_SOLUTION:
51 return suunto_solution_parser_create(parser);
53 case DEVICE_TYPE_SUUNTO_EON:
54 return suunto_eon_parser_create(parser, 0);
56 case DEVICE_TYPE_SUUNTO_VYPER:
57 if (devdata->devinfo.model == 0x01)
58 return suunto_eon_parser_create(parser, 1);
59 return suunto_vyper_parser_create(parser);
61 case DEVICE_TYPE_SUUNTO_VYPER2:
62 case DEVICE_TYPE_SUUNTO_D9:
63 return suunto_d9_parser_create(parser, devdata->devinfo.model);
65 case DEVICE_TYPE_UWATEC_ALADIN:
66 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
67 return uwatec_memomouse_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
69 case DEVICE_TYPE_UWATEC_SMART:
70 return uwatec_smart_parser_create(parser, devdata->devinfo.model, devdata->clock.devtime, devdata->clock.systime);
72 case DEVICE_TYPE_REEFNET_SENSUS:
73 return reefnet_sensus_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
75 case DEVICE_TYPE_REEFNET_SENSUSPRO:
76 return reefnet_sensuspro_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
78 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
79 return reefnet_sensusultra_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
81 case DEVICE_TYPE_OCEANIC_VTPRO:
82 return oceanic_vtpro_parser_create(parser);
84 case DEVICE_TYPE_OCEANIC_VEO250:
85 return oceanic_veo250_parser_create(parser, devdata->devinfo.model);
87 case DEVICE_TYPE_OCEANIC_ATOM2:
88 return oceanic_atom2_parser_create(parser, devdata->devinfo.model);
90 case DEVICE_TYPE_MARES_NEMO:
91 case DEVICE_TYPE_MARES_PUCK:
92 return mares_nemo_parser_create(parser, devdata->devinfo.model);
94 case DEVICE_TYPE_MARES_ICONHD:
95 return mares_iconhd_parser_create(parser);
97 case DEVICE_TYPE_HW_OSTC:
98 return hw_ostc_parser_create(parser);
100 case DEVICE_TYPE_CRESSI_EDY:
101 case DEVICE_TYPE_ZEAGLE_N2ITION3:
102 return cressi_edy_parser_create(parser, devdata->devinfo.model);
104 case DEVICE_TYPE_ATOMICS_COBALT:
105 return atomics_cobalt_parser_create(parser);
108 return PARSER_STATUS_ERROR;
112 static int parse_gasmixes(struct dive *dive, parser_t *parser, int ngases)
116 for (i = 0; i < ngases; i++) {
118 gasmix_t gasmix = {0};
121 rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
122 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
125 if (i >= MAX_CYLINDERS)
128 o2 = gasmix.oxygen * 1000 + 0.5;
129 he = gasmix.helium * 1000 + 0.5;
131 /* Ignore bogus data - libdivecomputer does some crazy stuff */
132 if (o2 < 210 || o2 >= 1000)
134 if (he < 0 || he >= 800 || o2+he >= 1000)
137 dive->cylinder[i].gasmix.o2.permille = o2;
138 dive->cylinder[i].gasmix.he.permille = he;
140 return PARSER_STATUS_SUCCESS;
144 sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
147 static const char *events[] = {
148 "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
149 "violation", "bookmark", "surface", "safety stop", "gaschange",
150 "safety stop (voluntary)", "safety stop (mandatory)", "deepstop",
151 "ceiling (safety stop)", "unknown", "divetime", "maxdepth",
152 "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning"};
153 struct dive **divep = userdata;
154 struct dive *dive = *divep;
155 struct sample *sample;
158 * We fill in the "previous" sample - except for SAMPLE_TYPE_TIME,
159 * which creates a new one.
161 sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
164 case SAMPLE_TYPE_TIME:
165 sample = prepare_sample(divep);
166 sample->time.seconds = value.time;
167 finish_sample(*divep, sample);
169 case SAMPLE_TYPE_DEPTH:
170 sample->depth.mm = value.depth * 1000 + 0.5;
172 case SAMPLE_TYPE_PRESSURE:
173 sample->cylinderindex = value.pressure.tank;
174 sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
176 case SAMPLE_TYPE_TEMPERATURE:
177 sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
179 case SAMPLE_TYPE_EVENT:
180 printf(" <event type=\"%u\" time=\"%u\" flags=\"%u\" value=\"%u\">%s</event>\n",
181 value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]);
183 case SAMPLE_TYPE_RBT:
184 printf(" <rbt>%u</rbt>\n", value.rbt);
186 case SAMPLE_TYPE_HEARTBEAT:
187 printf(" <heartbeat>%u</heartbeat>\n", value.heartbeat);
189 case SAMPLE_TYPE_BEARING:
190 printf(" <bearing>%u</bearing>\n", value.bearing);
192 case SAMPLE_TYPE_VENDOR:
193 printf(" <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
194 for (i = 0; i < value.vendor.size; ++i)
195 printf("%02X", ((unsigned char *) value.vendor.data)[i]);
196 printf("</vendor>\n");
204 static int parse_samples(struct dive **divep, parser_t *parser)
206 // Parse the sample data.
207 printf("Parsing the sample data.\n");
208 return parser_samples_foreach(parser, sample_cb, divep);
211 static int dive_cb(const unsigned char *data, unsigned int size,
212 const unsigned char *fingerprint, unsigned int fsize,
216 parser_t *parser = NULL;
217 device_data_t *devdata = userdata;
218 dc_datetime_t dt = {0};
222 rc = create_parser(devdata, &parser);
223 if (rc != PARSER_STATUS_SUCCESS) {
224 fprintf(stderr, "Unable to create parser for %s", devdata->name);
228 rc = parser_set_data(parser, data, size);
229 if (rc != PARSER_STATUS_SUCCESS) {
230 fprintf(stderr, "Error registering the data.");
231 parser_destroy(parser);
236 rc = parser_get_datetime(parser, &dt);
237 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
238 fprintf(stderr, "Error parsing the datetime.");
239 parser_destroy (parser);
243 tm.tm_year = dt.year;
244 tm.tm_mon = dt.month-1;
246 tm.tm_hour = dt.hour;
247 tm.tm_min = dt.minute;
248 tm.tm_sec = dt.second;
249 dive->when = utc_mktime(&tm);
251 // Parse the divetime.
252 printf("Parsing the divetime.\n");
253 unsigned int divetime = 0;
254 rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
255 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
256 fprintf(stderr, "Error parsing the divetime.");
257 parser_destroy(parser);
260 dive->duration.seconds = divetime;
262 // Parse the maxdepth.
263 printf("Parsing the maxdepth.\n");
264 double maxdepth = 0.0;
265 rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
266 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
267 fprintf(stderr, "Error parsing the maxdepth.");
268 parser_destroy(parser);
271 dive->maxdepth.mm = maxdepth * 1000 + 0.5;
273 // Parse the gas mixes.
274 printf("Parsing the gas mixes.\n");
275 unsigned int ngases = 0;
276 rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
277 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
278 fprintf(stderr, "Error parsing the gas mix count.");
279 parser_destroy(parser);
283 rc = parse_gasmixes(dive, parser, ngases);
284 if (rc != PARSER_STATUS_SUCCESS) {
285 fprintf(stderr, "Error parsing the gas mix.");
286 parser_destroy(parser);
290 // Initialize the sample data.
291 rc = parse_samples(&dive, parser);
292 if (rc != PARSER_STATUS_SUCCESS) {
293 fprintf(stderr, "Error parsing the samples.");
294 parser_destroy(parser);
299 parser_destroy(parser);
304 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
306 return device_foreach(device, dive_cb, devicedata);
309 static device_status_t device_open(const char *devname,
314 case DEVICE_TYPE_SUUNTO_SOLUTION:
315 return suunto_solution_device_open(device, devname);
317 case DEVICE_TYPE_SUUNTO_EON:
318 return suunto_eon_device_open(device, devname);
320 case DEVICE_TYPE_SUUNTO_VYPER:
321 return suunto_vyper_device_open(device, devname);
323 case DEVICE_TYPE_SUUNTO_VYPER2:
324 return suunto_vyper2_device_open(device, devname);
326 case DEVICE_TYPE_SUUNTO_D9:
327 return suunto_d9_device_open(device, devname);
329 case DEVICE_TYPE_UWATEC_ALADIN:
330 return uwatec_aladin_device_open(device, devname);
332 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
333 return uwatec_memomouse_device_open(device, devname);
335 case DEVICE_TYPE_UWATEC_SMART:
336 return uwatec_smart_device_open(device);
338 case DEVICE_TYPE_REEFNET_SENSUS:
339 return reefnet_sensus_device_open(device, devname);
341 case DEVICE_TYPE_REEFNET_SENSUSPRO:
342 return reefnet_sensuspro_device_open(device, devname);
344 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
345 return reefnet_sensusultra_device_open(device, devname);
347 case DEVICE_TYPE_OCEANIC_VTPRO:
348 return oceanic_vtpro_device_open(device, devname);
350 case DEVICE_TYPE_OCEANIC_VEO250:
351 return oceanic_veo250_device_open(device, devname);
353 case DEVICE_TYPE_OCEANIC_ATOM2:
354 return oceanic_atom2_device_open(device, devname);
356 case DEVICE_TYPE_MARES_NEMO:
357 return mares_nemo_device_open(device, devname);
359 case DEVICE_TYPE_MARES_PUCK:
360 return mares_puck_device_open(device, devname);
362 case DEVICE_TYPE_MARES_ICONHD:
363 return mares_iconhd_device_open(device, devname);
365 case DEVICE_TYPE_HW_OSTC:
366 return hw_ostc_device_open(device, devname);
368 case DEVICE_TYPE_CRESSI_EDY:
369 return cressi_edy_device_open(device, devname);
371 case DEVICE_TYPE_ZEAGLE_N2ITION3:
372 return zeagle_n2ition3_device_open(device, devname);
374 case DEVICE_TYPE_ATOMICS_COBALT:
375 return atomics_cobalt_device_open(device);
378 return DEVICE_STATUS_ERROR;
383 event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
385 const device_progress_t *progress = (device_progress_t *) data;
386 const device_devinfo_t *devinfo = (device_devinfo_t *) data;
387 const device_clock_t *clock = (device_clock_t *) data;
388 device_data_t *devdata = (device_data_t *) userdata;
391 case DEVICE_EVENT_WAITING:
392 printf("Event: waiting for user action\n");
394 case DEVICE_EVENT_PROGRESS:
395 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(devdata->progressbar),
396 (double) progress->current / (double) progress->maximum);
398 case DEVICE_EVENT_DEVINFO:
399 devdata->devinfo = *devinfo;
400 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
401 devinfo->model, devinfo->model,
402 devinfo->firmware, devinfo->firmware,
403 devinfo->serial, devinfo->serial);
405 case DEVICE_EVENT_CLOCK:
406 devdata->clock = *clock;
407 printf("Event: systime=%lld, devtime=%u\n",
408 clock->systime, clock->devtime);
415 static int import_thread_done = 0, import_thread_cancelled;
418 cancel_cb(void *userdata)
420 return import_thread_cancelled;
423 static const char *do_libdivecomputer_import(device_data_t *data)
425 device_t *device = NULL;
428 rc = device_open(data->devname, data->type, &device);
429 if (rc != DEVICE_STATUS_SUCCESS)
430 return "Unable to open %s (%s)";
432 // Register the event handler.
433 int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
434 rc = device_set_events(device, events, event_cb, data);
435 if (rc != DEVICE_STATUS_SUCCESS) {
436 device_close(device);
437 return "Error registering the event handler.";
440 // Register the cancellation handler.
441 rc = device_set_cancel(device, cancel_cb, data);
442 if (rc != DEVICE_STATUS_SUCCESS) {
443 device_close(device);
444 return "Error registering the cancellation handler.";
447 rc = import_device_data(device, data);
448 if (rc != DEVICE_STATUS_SUCCESS) {
449 device_close(device);
450 return "Dive data import error";
453 device_close(device);
457 static void *pthread_wrapper(void *_data)
459 device_data_t *data = _data;
460 const char *err_string = do_libdivecomputer_import(data);
461 import_thread_done = 1;
462 return (void *)err_string;
465 static void do_import(device_data_t *data)
470 if (data->type == DEVICE_TYPE_UEMIS)
471 return uemis_import();
473 /* I'm sure there is some better interface for waiting on a thread in a gtk main loop */
474 import_thread_done = 0;
475 pthread_create(&pthread, NULL, pthread_wrapper, data);
476 while (!import_thread_done) {
477 while (gtk_events_pending()) {
478 if (gtk_main_iteration_do(0)) {
479 import_thread_cancelled = 1;
485 if (pthread_join(pthread, &retval) < 0)
486 retval = "Odd pthread error return";
488 error(retval, data->name, data->devname);
492 * Taken from 'example.c' in libdivecomputer.
494 * I really wish there was some way to just have
495 * libdivecomputer tell us what devices it supports,
496 * rather than have the application have to know..
502 { "Suunto Solution", DEVICE_TYPE_SUUNTO_SOLUTION },
503 { "Suunto Eon", DEVICE_TYPE_SUUNTO_EON },
504 { "Suunto Vyper", DEVICE_TYPE_SUUNTO_VYPER },
505 { "Suunto Vyper Air", DEVICE_TYPE_SUUNTO_VYPER2 },
506 { "Suunto D9", DEVICE_TYPE_SUUNTO_D9 },
507 { "Uwatec Aladin", DEVICE_TYPE_UWATEC_ALADIN },
508 { "Uwatec Memo Mouse", DEVICE_TYPE_UWATEC_MEMOMOUSE },
509 { "Uwatec Smart", DEVICE_TYPE_UWATEC_SMART },
510 { "ReefNet Sensus", DEVICE_TYPE_REEFNET_SENSUS },
511 { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
512 { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
513 { "Oceanic VT Pro", DEVICE_TYPE_OCEANIC_VTPRO },
514 { "Oceanic Veo250", DEVICE_TYPE_OCEANIC_VEO250 },
515 { "Oceanic Atom 2", DEVICE_TYPE_OCEANIC_ATOM2 },
516 { "Mares Nemo", DEVICE_TYPE_MARES_NEMO },
517 { "Mares Puck", DEVICE_TYPE_MARES_PUCK },
518 { "Mares Icon HD", DEVICE_TYPE_MARES_ICONHD },
519 { "OSTC", DEVICE_TYPE_HW_OSTC },
520 { "Cressi Edy", DEVICE_TYPE_CRESSI_EDY },
521 { "Zeagle N2iTiON 3", DEVICE_TYPE_ZEAGLE_N2ITION3 },
522 { "Atomics Cobalt", DEVICE_TYPE_ATOMICS_COBALT },
523 { "Uemis Zurich SDA", DEVICE_TYPE_UEMIS },
527 static void fill_computer_list(GtkListStore *store)
530 struct device_list *list = device_list;
532 for (list = device_list ; list->name ; list++) {
533 gtk_list_store_append(store, &iter);
534 gtk_list_store_set(store, &iter,
541 static GtkComboBox *dive_computer_selector(GtkWidget *dialog)
543 GtkWidget *hbox, *combo_box;
545 GtkCellRenderer *renderer;
547 hbox = gtk_hbox_new(FALSE, 6);
548 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 3);
550 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
551 fill_computer_list(model);
553 combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
554 gtk_box_pack_start(GTK_BOX(hbox), combo_box, FALSE, TRUE, 3);
556 renderer = gtk_cell_renderer_text_new();
557 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE);
558 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), renderer, "text", 0, NULL);
560 return GTK_COMBO_BOX(combo_box);
563 void import_dialog(GtkWidget *w, gpointer data)
566 GtkWidget *dialog, *hbox;
567 GtkComboBox *computer;
568 device_data_t devicedata = {
569 .devname = "/dev/ttyUSB0",
572 dialog = gtk_dialog_new_with_buttons("Import from dive computer",
573 GTK_WINDOW(main_window),
574 GTK_DIALOG_DESTROY_WITH_PARENT,
575 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
576 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
579 computer = dive_computer_selector(dialog);
581 hbox = gtk_hbox_new(FALSE, 6);
582 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, TRUE, 3);
583 devicedata.progressbar = gtk_progress_bar_new();
584 gtk_container_add(GTK_CONTAINER(hbox), devicedata.progressbar);
586 gtk_widget_show_all(dialog);
587 result = gtk_dialog_run(GTK_DIALOG(dialog));
593 case GTK_RESPONSE_ACCEPT:
594 if (!gtk_combo_box_get_active_iter(computer, &iter))
596 model = gtk_combo_box_get_model(computer);
597 gtk_tree_model_get(model, &iter,
601 devicedata.type = type;
602 devicedata.name = comp;
603 do_import(&devicedata);
608 gtk_widget_destroy(dialog);
611 dive_list_update_dives(dive_list);