21 * I'd love to do a while-loop here for pending events, but
22 * that seems to screw up with the dive computer reading timing.
24 * I may need to spawn a new thread to do the computer
27 static int run_gtk_mainloop(void)
29 return gtk_main_iteration_do(0);
32 static void error(const char *fmt, ...)
38 error = g_error_new_valist(
39 g_quark_from_string("divelog"),
40 DIVE_ERROR_PARSE, fmt, args);
46 typedef struct device_data_t {
48 const char *name, *devname;
49 GtkWidget *progressbar;
50 device_devinfo_t devinfo;
54 static parser_status_t create_parser(device_data_t *devdata, parser_t **parser)
56 switch (devdata->type) {
57 case DEVICE_TYPE_SUUNTO_SOLUTION:
58 return suunto_solution_parser_create(parser);
60 case DEVICE_TYPE_SUUNTO_EON:
61 return suunto_eon_parser_create(parser, 0);
63 case DEVICE_TYPE_SUUNTO_VYPER:
64 if (devdata->devinfo.model == 0x01)
65 return suunto_eon_parser_create(parser, 1);
66 return suunto_vyper_parser_create(parser);
68 case DEVICE_TYPE_SUUNTO_VYPER2:
69 case DEVICE_TYPE_SUUNTO_D9:
70 return suunto_d9_parser_create(parser, devdata->devinfo.model);
72 case DEVICE_TYPE_UWATEC_ALADIN:
73 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
74 return uwatec_memomouse_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
76 case DEVICE_TYPE_UWATEC_SMART:
77 return uwatec_smart_parser_create(parser, devdata->devinfo.model, devdata->clock.devtime, devdata->clock.systime);
79 case DEVICE_TYPE_REEFNET_SENSUS:
80 return reefnet_sensus_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
82 case DEVICE_TYPE_REEFNET_SENSUSPRO:
83 return reefnet_sensuspro_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
85 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
86 return reefnet_sensusultra_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
88 case DEVICE_TYPE_OCEANIC_VTPRO:
89 return oceanic_vtpro_parser_create(parser);
91 case DEVICE_TYPE_OCEANIC_VEO250:
92 return oceanic_veo250_parser_create(parser, devdata->devinfo.model);
94 case DEVICE_TYPE_OCEANIC_ATOM2:
95 return oceanic_atom2_parser_create(parser, devdata->devinfo.model);
97 case DEVICE_TYPE_MARES_NEMO:
98 case DEVICE_TYPE_MARES_PUCK:
99 return mares_nemo_parser_create(parser, devdata->devinfo.model);
101 case DEVICE_TYPE_MARES_ICONHD:
102 return mares_iconhd_parser_create(parser);
104 case DEVICE_TYPE_HW_OSTC:
105 return hw_ostc_parser_create(parser);
107 case DEVICE_TYPE_CRESSI_EDY:
108 case DEVICE_TYPE_ZEAGLE_N2ITION3:
109 return cressi_edy_parser_create(parser, devdata->devinfo.model);
111 case DEVICE_TYPE_ATOMICS_COBALT:
112 return atomics_cobalt_parser_create(parser);
115 return PARSER_STATUS_ERROR;
119 static int parse_gasmixes(parser_t *parser, int ngases)
123 for (i = 0; i < ngases; i++) {
125 gasmix_t gasmix = {0};
127 rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
128 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
136 gasmix.helium * 100.0,
137 gasmix.oxygen * 100.0,
138 gasmix.nitrogen * 100.0);
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"};
155 case SAMPLE_TYPE_TIME:
156 printf("<sample>\n");
157 printf(" <time>%02u:%02u</time>\n", value.time / 60, value.time % 60);
159 case SAMPLE_TYPE_DEPTH:
160 printf(" <depth>%.2f</depth>\n", value.depth);
162 case SAMPLE_TYPE_PRESSURE:
163 printf(" <pressure tank=\"%u\">%.2f</pressure>\n", value.pressure.tank, value.pressure.value);
165 case SAMPLE_TYPE_TEMPERATURE:
166 printf(" <temperature>%.2f</temperature>\n", value.temperature);
168 case SAMPLE_TYPE_EVENT:
169 printf(" <event type=\"%u\" time=\"%u\" flags=\"%u\" value=\"%u\">%s</event>\n",
170 value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]);
172 case SAMPLE_TYPE_RBT:
173 printf(" <rbt>%u</rbt>\n", value.rbt);
175 case SAMPLE_TYPE_HEARTBEAT:
176 printf(" <heartbeat>%u</heartbeat>\n", value.heartbeat);
178 case SAMPLE_TYPE_BEARING:
179 printf(" <bearing>%u</bearing>\n", value.bearing);
181 case SAMPLE_TYPE_VENDOR:
182 printf(" <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
183 for (i = 0; i < value.vendor.size; ++i)
184 printf("%02X", ((unsigned char *) value.vendor.data)[i]);
185 printf("</vendor>\n");
193 static int parse_samples(parser_t *parser)
195 // Parse the sample data.
196 printf("Parsing the sample data.\n");
197 return parser_samples_foreach(parser, sample_cb, NULL);
200 static int dive_cb(const unsigned char *data, unsigned int size,
201 const unsigned char *fingerprint, unsigned int fsize,
205 parser_t *parser = NULL;
206 device_data_t *devdata = userdata;
207 dc_datetime_t dt = {0};
209 /* Christ, this is hacky */
212 rc = create_parser(devdata, &parser);
213 if (rc != PARSER_STATUS_SUCCESS) {
214 error("Unable to create parser for %s", devdata->name);
218 rc = parser_set_data(parser, data, size);
219 if (rc != PARSER_STATUS_SUCCESS) {
220 error("Error registering the data.");
221 parser_destroy(parser);
225 rc = parser_get_datetime(parser, &dt);
226 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
227 error("Error parsing the datetime.");
228 parser_destroy (parser);
232 printf("<datetime>%04i-%02i-%02i %02i:%02i:%02i</datetime>\n",
233 dt.year, dt.month, dt.day,
234 dt.hour, dt.minute, dt.second);
236 // Parse the divetime.
237 printf("Parsing the divetime.\n");
238 unsigned int divetime = 0;
239 rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
240 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
241 error("Error parsing the divetime.");
242 parser_destroy(parser);
246 printf("<divetime>%02u:%02u</divetime>\n",
247 divetime / 60, divetime % 60);
249 // Parse the maxdepth.
250 printf("Parsing the maxdepth.\n");
251 double maxdepth = 0.0;
252 rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
253 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
254 error("Error parsing the maxdepth.");
255 parser_destroy(parser);
259 printf("<maxdepth>%.2f</maxdepth>\n", maxdepth);
261 // Parse the gas mixes.
262 printf("Parsing the gas mixes.\n");
263 unsigned int ngases = 0;
264 rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
265 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
266 error("Error parsing the gas mix count.");
267 parser_destroy(parser);
271 rc = parse_gasmixes(parser, ngases);
272 if (rc != PARSER_STATUS_SUCCESS) {
273 error("Error parsing the gas mix.");
274 parser_destroy(parser);
278 // Initialize the sample data.
279 rc = parse_samples(parser);
280 if (rc != PARSER_STATUS_SUCCESS) {
281 error("Error parsing the samples.");
282 parser_destroy(parser);
286 parser_destroy(parser);
291 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
293 return device_foreach(device, dive_cb, devicedata);
296 static device_status_t device_open(const char *devname,
301 case DEVICE_TYPE_SUUNTO_SOLUTION:
302 return suunto_solution_device_open(device, devname);
304 case DEVICE_TYPE_SUUNTO_EON:
305 return suunto_eon_device_open(device, devname);
307 case DEVICE_TYPE_SUUNTO_VYPER:
308 return suunto_vyper_device_open(device, devname);
310 case DEVICE_TYPE_SUUNTO_VYPER2:
311 return suunto_vyper2_device_open(device, devname);
313 case DEVICE_TYPE_SUUNTO_D9:
314 return suunto_d9_device_open(device, devname);
316 case DEVICE_TYPE_UWATEC_ALADIN:
317 return uwatec_aladin_device_open(device, devname);
319 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
320 return uwatec_memomouse_device_open(device, devname);
322 case DEVICE_TYPE_UWATEC_SMART:
323 return uwatec_smart_device_open(device);
325 case DEVICE_TYPE_REEFNET_SENSUS:
326 return reefnet_sensus_device_open(device, devname);
328 case DEVICE_TYPE_REEFNET_SENSUSPRO:
329 return reefnet_sensuspro_device_open(device, devname);
331 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
332 return reefnet_sensusultra_device_open(device, devname);
334 case DEVICE_TYPE_OCEANIC_VTPRO:
335 return oceanic_vtpro_device_open(device, devname);
337 case DEVICE_TYPE_OCEANIC_VEO250:
338 return oceanic_veo250_device_open(device, devname);
340 case DEVICE_TYPE_OCEANIC_ATOM2:
341 return oceanic_atom2_device_open(device, devname);
343 case DEVICE_TYPE_MARES_NEMO:
344 return mares_nemo_device_open(device, devname);
346 case DEVICE_TYPE_MARES_PUCK:
347 return mares_puck_device_open(device, devname);
349 case DEVICE_TYPE_MARES_ICONHD:
350 return mares_iconhd_device_open(device, devname);
352 case DEVICE_TYPE_HW_OSTC:
353 return hw_ostc_device_open(device, devname);
355 case DEVICE_TYPE_CRESSI_EDY:
356 return cressi_edy_device_open(device, devname);
358 case DEVICE_TYPE_ZEAGLE_N2ITION3:
359 return zeagle_n2ition3_device_open(device, devname);
361 case DEVICE_TYPE_ATOMICS_COBALT:
362 return atomics_cobalt_device_open(device);
365 return DEVICE_STATUS_ERROR;
370 event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
372 const device_progress_t *progress = (device_progress_t *) data;
373 const device_devinfo_t *devinfo = (device_devinfo_t *) data;
374 const device_clock_t *clock = (device_clock_t *) data;
375 device_data_t *devdata = (device_data_t *) userdata;
377 /* Christ, this is hacky */
381 case DEVICE_EVENT_WAITING:
382 printf("Event: waiting for user action\n");
384 case DEVICE_EVENT_PROGRESS:
385 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(devdata->progressbar),
386 (double) progress->current / (double) progress->maximum);
388 case DEVICE_EVENT_DEVINFO:
389 devdata->devinfo = *devinfo;
390 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
391 devinfo->model, devinfo->model,
392 devinfo->firmware, devinfo->firmware,
393 devinfo->serial, devinfo->serial);
395 case DEVICE_EVENT_CLOCK:
396 devdata->clock = *clock;
397 printf("Event: systime=%lld, devtime=%u\n",
398 clock->systime, clock->devtime);
406 cancel_cb(void *userdata)
408 return run_gtk_mainloop();
411 static void do_import(device_data_t *data)
413 /* FIXME! Needs user input! */
414 const char *devname = "/dev/ttyUSB0";
415 device_t *device = NULL;
418 rc = device_open(devname, data->type, &device);
419 if (rc != DEVICE_STATUS_SUCCESS) {
420 error("Unable to open %s (%s)", data->name, data->devname);
424 // Register the event handler.
425 int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
426 rc = device_set_events(device, events, event_cb, data);
427 if (rc != DEVICE_STATUS_SUCCESS) {
428 error("Error registering the event handler.");
429 device_close(device);
433 // Register the cancellation handler.
434 rc = device_set_cancel(device, cancel_cb, data);
435 if (rc != DEVICE_STATUS_SUCCESS) {
436 error("Error registering the cancellation handler.");
437 device_close(device);
441 rc = import_device_data(device, data);
442 if (rc != DEVICE_STATUS_SUCCESS) {
443 error("Dive data import error");
444 device_close(device);
448 device_close(device);
452 * Taken from 'example.c' in libdivecomputer.
454 * I really wish there was some way to just have
455 * libdivecomputer tell us what devices it supports,
456 * rather than have the application have to know..
462 { "Suunto Solution", DEVICE_TYPE_SUUNTO_SOLUTION },
463 { "Suunto Eon", DEVICE_TYPE_SUUNTO_EON },
464 { "Suunto Vyper", DEVICE_TYPE_SUUNTO_VYPER },
465 { "Suunto Vyper Air", DEVICE_TYPE_SUUNTO_VYPER2 },
466 { "Suunto D9", DEVICE_TYPE_SUUNTO_D9 },
467 { "Uwatec Aladin", DEVICE_TYPE_UWATEC_ALADIN },
468 { "Uwatec Memo Mouse", DEVICE_TYPE_UWATEC_MEMOMOUSE },
469 { "Uwatec Smart", DEVICE_TYPE_UWATEC_SMART },
470 { "ReefNet Sensus", DEVICE_TYPE_REEFNET_SENSUS },
471 { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
472 { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
473 { "Oceanic VT Pro", DEVICE_TYPE_OCEANIC_VTPRO },
474 { "Oceanic Veo250", DEVICE_TYPE_OCEANIC_VEO250 },
475 { "Oceanic Atom 2", DEVICE_TYPE_OCEANIC_ATOM2 },
476 { "Mares Nemo", DEVICE_TYPE_MARES_NEMO },
477 { "Mares Puck", DEVICE_TYPE_MARES_PUCK },
478 { "Mares Icon HD", DEVICE_TYPE_MARES_ICONHD },
479 { "OSTC", DEVICE_TYPE_HW_OSTC },
480 { "Cressi Edy", DEVICE_TYPE_CRESSI_EDY },
481 { "Zeagle N2iTiON 3", DEVICE_TYPE_ZEAGLE_N2ITION3 },
482 { "Atomics Cobalt", DEVICE_TYPE_ATOMICS_COBALT },
486 static void fill_computer_list(GtkListStore *store)
489 struct device_list *list = device_list;
491 for (list = device_list ; list->name ; list++) {
492 gtk_list_store_append(store, &iter);
493 gtk_list_store_set(store, &iter,
500 static GtkComboBox *dive_computer_selector(GtkWidget *dialog)
502 GtkWidget *hbox, *combo_box;
504 GtkCellRenderer *renderer;
506 hbox = gtk_hbox_new(FALSE, 6);
507 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 3);
509 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
510 fill_computer_list(model);
512 combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
513 gtk_box_pack_start(GTK_BOX(hbox), combo_box, FALSE, TRUE, 3);
515 renderer = gtk_cell_renderer_text_new();
516 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE);
517 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), renderer, "text", 0, NULL);
519 return GTK_COMBO_BOX(combo_box);
522 void import_dialog(GtkWidget *w, gpointer data)
525 GtkWidget *dialog, *hbox;
526 GtkComboBox *computer;
527 device_data_t devicedata = {
528 .devname = "/dev/ttyUSB0",
531 dialog = gtk_dialog_new_with_buttons("Import from dive computer",
532 GTK_WINDOW(main_window),
533 GTK_DIALOG_DESTROY_WITH_PARENT,
534 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
535 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
538 computer = dive_computer_selector(dialog);
540 hbox = gtk_hbox_new(FALSE, 6);
541 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, TRUE, 3);
542 devicedata.progressbar = gtk_progress_bar_new();
543 gtk_container_add(GTK_CONTAINER(hbox), devicedata.progressbar);
545 gtk_widget_show_all(dialog);
546 result = gtk_dialog_run(GTK_DIALOG(dialog));
552 case GTK_RESPONSE_ACCEPT:
553 if (!gtk_combo_box_get_active_iter(computer, &iter))
555 model = gtk_combo_box_get_model(computer);
556 gtk_tree_model_get(model, &iter,
560 devicedata.type = type;
561 devicedata.name = comp;
562 do_import(&devicedata);
567 gtk_widget_destroy(dialog);