21 /* handling uemis Zurich SDA files */
25 * I'd love to do a while-loop here for pending events, but
26 * that seems to screw up with the dive computer reading timing.
28 * I may need to spawn a new thread to do the computer
31 static int run_gtk_mainloop(void)
33 return gtk_main_iteration_do(0);
36 static void error(const char *fmt, ...)
42 error = g_error_new_valist(
43 g_quark_from_string("subsurface"),
44 DIVE_ERROR_PARSE, fmt, args);
50 typedef struct device_data_t {
52 const char *name, *devname;
53 GtkWidget *progressbar;
54 device_devinfo_t devinfo;
58 static parser_status_t create_parser(device_data_t *devdata, parser_t **parser)
60 switch (devdata->type) {
61 case DEVICE_TYPE_SUUNTO_SOLUTION:
62 return suunto_solution_parser_create(parser);
64 case DEVICE_TYPE_SUUNTO_EON:
65 return suunto_eon_parser_create(parser, 0);
67 case DEVICE_TYPE_SUUNTO_VYPER:
68 if (devdata->devinfo.model == 0x01)
69 return suunto_eon_parser_create(parser, 1);
70 return suunto_vyper_parser_create(parser);
72 case DEVICE_TYPE_SUUNTO_VYPER2:
73 case DEVICE_TYPE_SUUNTO_D9:
74 return suunto_d9_parser_create(parser, devdata->devinfo.model);
76 case DEVICE_TYPE_UWATEC_ALADIN:
77 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
78 return uwatec_memomouse_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
80 case DEVICE_TYPE_UWATEC_SMART:
81 return uwatec_smart_parser_create(parser, devdata->devinfo.model, devdata->clock.devtime, devdata->clock.systime);
83 case DEVICE_TYPE_REEFNET_SENSUS:
84 return reefnet_sensus_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
86 case DEVICE_TYPE_REEFNET_SENSUSPRO:
87 return reefnet_sensuspro_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
89 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
90 return reefnet_sensusultra_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
92 case DEVICE_TYPE_OCEANIC_VTPRO:
93 return oceanic_vtpro_parser_create(parser);
95 case DEVICE_TYPE_OCEANIC_VEO250:
96 return oceanic_veo250_parser_create(parser, devdata->devinfo.model);
98 case DEVICE_TYPE_OCEANIC_ATOM2:
99 return oceanic_atom2_parser_create(parser, devdata->devinfo.model);
101 case DEVICE_TYPE_MARES_NEMO:
102 case DEVICE_TYPE_MARES_PUCK:
103 return mares_nemo_parser_create(parser, devdata->devinfo.model);
105 case DEVICE_TYPE_MARES_ICONHD:
106 return mares_iconhd_parser_create(parser);
108 case DEVICE_TYPE_HW_OSTC:
109 return hw_ostc_parser_create(parser);
111 case DEVICE_TYPE_CRESSI_EDY:
112 case DEVICE_TYPE_ZEAGLE_N2ITION3:
113 return cressi_edy_parser_create(parser, devdata->devinfo.model);
115 case DEVICE_TYPE_ATOMICS_COBALT:
116 return atomics_cobalt_parser_create(parser);
119 return PARSER_STATUS_ERROR;
123 static int parse_gasmixes(struct dive *dive, parser_t *parser, int ngases)
127 for (i = 0; i < ngases; i++) {
129 gasmix_t gasmix = {0};
132 rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
133 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
136 if (i >= MAX_CYLINDERS)
139 o2 = gasmix.oxygen * 1000 + 0.5;
140 he = gasmix.helium * 1000 + 0.5;
142 /* Ignore bogus data - libdivecomputer does some crazy stuff */
143 if (o2 < 210 || o2 >= 1000)
145 if (he < 0 || he >= 800 || o2+he >= 1000)
148 dive->cylinder[i].gasmix.o2.permille = o2;
149 dive->cylinder[i].gasmix.he.permille = he;
151 return PARSER_STATUS_SUCCESS;
155 sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
158 static const char *events[] = {
159 "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
160 "violation", "bookmark", "surface", "safety stop", "gaschange",
161 "safety stop (voluntary)", "safety stop (mandatory)", "deepstop",
162 "ceiling (safety stop)", "unknown", "divetime", "maxdepth",
163 "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning"};
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 printf(" <event type=\"%u\" time=\"%u\" flags=\"%u\" value=\"%u\">%s</event>\n",
192 value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]);
194 case SAMPLE_TYPE_RBT:
195 printf(" <rbt>%u</rbt>\n", value.rbt);
197 case SAMPLE_TYPE_HEARTBEAT:
198 printf(" <heartbeat>%u</heartbeat>\n", value.heartbeat);
200 case SAMPLE_TYPE_BEARING:
201 printf(" <bearing>%u</bearing>\n", value.bearing);
203 case SAMPLE_TYPE_VENDOR:
204 printf(" <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
205 for (i = 0; i < value.vendor.size; ++i)
206 printf("%02X", ((unsigned char *) value.vendor.data)[i]);
207 printf("</vendor>\n");
215 static int parse_samples(struct dive **divep, parser_t *parser)
217 // Parse the sample data.
218 printf("Parsing the sample data.\n");
219 return parser_samples_foreach(parser, sample_cb, divep);
222 static int dive_cb(const unsigned char *data, unsigned int size,
223 const unsigned char *fingerprint, unsigned int fsize,
227 parser_t *parser = NULL;
228 device_data_t *devdata = userdata;
229 dc_datetime_t dt = {0};
233 /* Christ, this is hacky */
236 rc = create_parser(devdata, &parser);
237 if (rc != PARSER_STATUS_SUCCESS) {
238 error("Unable to create parser for %s", devdata->name);
242 rc = parser_set_data(parser, data, size);
243 if (rc != PARSER_STATUS_SUCCESS) {
244 error("Error registering the data.");
245 parser_destroy(parser);
250 rc = parser_get_datetime(parser, &dt);
251 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
252 error("Error parsing the datetime.");
253 parser_destroy (parser);
257 tm.tm_year = dt.year;
258 tm.tm_mon = dt.month-1;
260 tm.tm_hour = dt.hour;
261 tm.tm_min = dt.minute;
262 tm.tm_sec = dt.second;
263 dive->when = utc_mktime(&tm);
265 // Parse the divetime.
266 printf("Parsing the divetime.\n");
267 unsigned int divetime = 0;
268 rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
269 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
270 error("Error parsing the divetime.");
271 parser_destroy(parser);
274 dive->duration.seconds = divetime;
276 // Parse the maxdepth.
277 printf("Parsing the maxdepth.\n");
278 double maxdepth = 0.0;
279 rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
280 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
281 error("Error parsing the maxdepth.");
282 parser_destroy(parser);
285 dive->maxdepth.mm = maxdepth * 1000 + 0.5;
287 // Parse the gas mixes.
288 printf("Parsing the gas mixes.\n");
289 unsigned int ngases = 0;
290 rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
291 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
292 error("Error parsing the gas mix count.");
293 parser_destroy(parser);
297 rc = parse_gasmixes(dive, parser, ngases);
298 if (rc != PARSER_STATUS_SUCCESS) {
299 error("Error parsing the gas mix.");
300 parser_destroy(parser);
304 // Initialize the sample data.
305 rc = parse_samples(&dive, parser);
306 if (rc != PARSER_STATUS_SUCCESS) {
307 error("Error parsing the samples.");
308 parser_destroy(parser);
313 parser_destroy(parser);
318 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
320 return device_foreach(device, dive_cb, devicedata);
323 static device_status_t device_open(const char *devname,
328 case DEVICE_TYPE_SUUNTO_SOLUTION:
329 return suunto_solution_device_open(device, devname);
331 case DEVICE_TYPE_SUUNTO_EON:
332 return suunto_eon_device_open(device, devname);
334 case DEVICE_TYPE_SUUNTO_VYPER:
335 return suunto_vyper_device_open(device, devname);
337 case DEVICE_TYPE_SUUNTO_VYPER2:
338 return suunto_vyper2_device_open(device, devname);
340 case DEVICE_TYPE_SUUNTO_D9:
341 return suunto_d9_device_open(device, devname);
343 case DEVICE_TYPE_UWATEC_ALADIN:
344 return uwatec_aladin_device_open(device, devname);
346 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
347 return uwatec_memomouse_device_open(device, devname);
349 case DEVICE_TYPE_UWATEC_SMART:
350 return uwatec_smart_device_open(device);
352 case DEVICE_TYPE_REEFNET_SENSUS:
353 return reefnet_sensus_device_open(device, devname);
355 case DEVICE_TYPE_REEFNET_SENSUSPRO:
356 return reefnet_sensuspro_device_open(device, devname);
358 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
359 return reefnet_sensusultra_device_open(device, devname);
361 case DEVICE_TYPE_OCEANIC_VTPRO:
362 return oceanic_vtpro_device_open(device, devname);
364 case DEVICE_TYPE_OCEANIC_VEO250:
365 return oceanic_veo250_device_open(device, devname);
367 case DEVICE_TYPE_OCEANIC_ATOM2:
368 return oceanic_atom2_device_open(device, devname);
370 case DEVICE_TYPE_MARES_NEMO:
371 return mares_nemo_device_open(device, devname);
373 case DEVICE_TYPE_MARES_PUCK:
374 return mares_puck_device_open(device, devname);
376 case DEVICE_TYPE_MARES_ICONHD:
377 return mares_iconhd_device_open(device, devname);
379 case DEVICE_TYPE_HW_OSTC:
380 return hw_ostc_device_open(device, devname);
382 case DEVICE_TYPE_CRESSI_EDY:
383 return cressi_edy_device_open(device, devname);
385 case DEVICE_TYPE_ZEAGLE_N2ITION3:
386 return zeagle_n2ition3_device_open(device, devname);
388 case DEVICE_TYPE_ATOMICS_COBALT:
389 return atomics_cobalt_device_open(device);
392 return DEVICE_STATUS_ERROR;
397 event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
399 const device_progress_t *progress = (device_progress_t *) data;
400 const device_devinfo_t *devinfo = (device_devinfo_t *) data;
401 const device_clock_t *clock = (device_clock_t *) data;
402 device_data_t *devdata = (device_data_t *) userdata;
404 /* Christ, this is hacky */
408 case DEVICE_EVENT_WAITING:
409 printf("Event: waiting for user action\n");
411 case DEVICE_EVENT_PROGRESS:
412 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(devdata->progressbar),
413 (double) progress->current / (double) progress->maximum);
415 case DEVICE_EVENT_DEVINFO:
416 devdata->devinfo = *devinfo;
417 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
418 devinfo->model, devinfo->model,
419 devinfo->firmware, devinfo->firmware,
420 devinfo->serial, devinfo->serial);
422 case DEVICE_EVENT_CLOCK:
423 devdata->clock = *clock;
424 printf("Event: systime=%lld, devtime=%u\n",
425 clock->systime, clock->devtime);
433 cancel_cb(void *userdata)
435 return run_gtk_mainloop();
438 static void do_import(device_data_t *data)
440 device_t *device = NULL;
443 if (data->type == DEVICE_TYPE_UEMIS) {
444 return uemis_import();
447 rc = device_open(data->devname, data->type, &device);
448 if (rc != DEVICE_STATUS_SUCCESS) {
449 error("Unable to open %s (%s)", data->name, data->devname);
453 // Register the event handler.
454 int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
455 rc = device_set_events(device, events, event_cb, data);
456 if (rc != DEVICE_STATUS_SUCCESS) {
457 error("Error registering the event handler.");
458 device_close(device);
462 // Register the cancellation handler.
463 rc = device_set_cancel(device, cancel_cb, data);
464 if (rc != DEVICE_STATUS_SUCCESS) {
465 error("Error registering the cancellation handler.");
466 device_close(device);
470 rc = import_device_data(device, data);
471 if (rc != DEVICE_STATUS_SUCCESS) {
472 error("Dive data import error");
473 device_close(device);
477 device_close(device);
481 * Taken from 'example.c' in libdivecomputer.
483 * I really wish there was some way to just have
484 * libdivecomputer tell us what devices it supports,
485 * rather than have the application have to know..
491 { "Suunto Solution", DEVICE_TYPE_SUUNTO_SOLUTION },
492 { "Suunto Eon", DEVICE_TYPE_SUUNTO_EON },
493 { "Suunto Vyper", DEVICE_TYPE_SUUNTO_VYPER },
494 { "Suunto Vyper Air", DEVICE_TYPE_SUUNTO_VYPER2 },
495 { "Suunto D9", DEVICE_TYPE_SUUNTO_D9 },
496 { "Uwatec Aladin", DEVICE_TYPE_UWATEC_ALADIN },
497 { "Uwatec Memo Mouse", DEVICE_TYPE_UWATEC_MEMOMOUSE },
498 { "Uwatec Smart", DEVICE_TYPE_UWATEC_SMART },
499 { "ReefNet Sensus", DEVICE_TYPE_REEFNET_SENSUS },
500 { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
501 { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
502 { "Oceanic VT Pro", DEVICE_TYPE_OCEANIC_VTPRO },
503 { "Oceanic Veo250", DEVICE_TYPE_OCEANIC_VEO250 },
504 { "Oceanic Atom 2", DEVICE_TYPE_OCEANIC_ATOM2 },
505 { "Mares Nemo", DEVICE_TYPE_MARES_NEMO },
506 { "Mares Puck", DEVICE_TYPE_MARES_PUCK },
507 { "Mares Icon HD", DEVICE_TYPE_MARES_ICONHD },
508 { "OSTC", DEVICE_TYPE_HW_OSTC },
509 { "Cressi Edy", DEVICE_TYPE_CRESSI_EDY },
510 { "Zeagle N2iTiON 3", DEVICE_TYPE_ZEAGLE_N2ITION3 },
511 { "Atomics Cobalt", DEVICE_TYPE_ATOMICS_COBALT },
512 { "Uemis Zurich SDA", DEVICE_TYPE_UEMIS },
516 static void fill_computer_list(GtkListStore *store)
519 struct device_list *list = device_list;
521 for (list = device_list ; list->name ; list++) {
522 gtk_list_store_append(store, &iter);
523 gtk_list_store_set(store, &iter,
530 static GtkComboBox *dive_computer_selector(GtkWidget *dialog)
532 GtkWidget *hbox, *combo_box;
534 GtkCellRenderer *renderer;
536 hbox = gtk_hbox_new(FALSE, 6);
537 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 3);
539 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
540 fill_computer_list(model);
542 combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
543 gtk_box_pack_start(GTK_BOX(hbox), combo_box, FALSE, TRUE, 3);
545 renderer = gtk_cell_renderer_text_new();
546 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE);
547 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), renderer, "text", 0, NULL);
549 return GTK_COMBO_BOX(combo_box);
552 void import_dialog(GtkWidget *w, gpointer data)
555 GtkWidget *dialog, *hbox;
556 GtkComboBox *computer;
557 device_data_t devicedata = {
558 .devname = "/dev/ttyUSB0",
561 dialog = gtk_dialog_new_with_buttons("Import from dive computer",
562 GTK_WINDOW(main_window),
563 GTK_DIALOG_DESTROY_WITH_PARENT,
564 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
565 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
568 computer = dive_computer_selector(dialog);
570 hbox = gtk_hbox_new(FALSE, 6);
571 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, TRUE, 3);
572 devicedata.progressbar = gtk_progress_bar_new();
573 gtk_container_add(GTK_CONTAINER(hbox), devicedata.progressbar);
575 gtk_widget_show_all(dialog);
576 result = gtk_dialog_run(GTK_DIALOG(dialog));
582 case GTK_RESPONSE_ACCEPT:
583 if (!gtk_combo_box_get_active_iter(computer, &iter))
585 model = gtk_combo_box_get_model(computer);
586 gtk_tree_model_get(model, &iter,
590 devicedata.type = type;
591 devicedata.name = comp;
592 do_import(&devicedata);
597 gtk_widget_destroy(dialog);
600 dive_list_update_dives(dive_list);