22 * I'd love to do a while-loop here for pending events, but
23 * that seems to screw up with the dive computer reading timing.
25 * I may need to spawn a new thread to do the computer
28 static int run_gtk_mainloop(void)
30 return gtk_main_iteration_do(0);
33 static void error(const char *fmt, ...)
39 error = g_error_new_valist(
40 g_quark_from_string("divelog"),
41 DIVE_ERROR_PARSE, fmt, args);
47 typedef struct device_data_t {
49 const char *name, *devname;
50 GtkWidget *progressbar;
51 device_devinfo_t devinfo;
55 static parser_status_t create_parser(device_data_t *devdata, parser_t **parser)
57 switch (devdata->type) {
58 case DEVICE_TYPE_SUUNTO_SOLUTION:
59 return suunto_solution_parser_create(parser);
61 case DEVICE_TYPE_SUUNTO_EON:
62 return suunto_eon_parser_create(parser, 0);
64 case DEVICE_TYPE_SUUNTO_VYPER:
65 if (devdata->devinfo.model == 0x01)
66 return suunto_eon_parser_create(parser, 1);
67 return suunto_vyper_parser_create(parser);
69 case DEVICE_TYPE_SUUNTO_VYPER2:
70 case DEVICE_TYPE_SUUNTO_D9:
71 return suunto_d9_parser_create(parser, devdata->devinfo.model);
73 case DEVICE_TYPE_UWATEC_ALADIN:
74 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
75 return uwatec_memomouse_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
77 case DEVICE_TYPE_UWATEC_SMART:
78 return uwatec_smart_parser_create(parser, devdata->devinfo.model, devdata->clock.devtime, devdata->clock.systime);
80 case DEVICE_TYPE_REEFNET_SENSUS:
81 return reefnet_sensus_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
83 case DEVICE_TYPE_REEFNET_SENSUSPRO:
84 return reefnet_sensuspro_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
86 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
87 return reefnet_sensusultra_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
89 case DEVICE_TYPE_OCEANIC_VTPRO:
90 return oceanic_vtpro_parser_create(parser);
92 case DEVICE_TYPE_OCEANIC_VEO250:
93 return oceanic_veo250_parser_create(parser, devdata->devinfo.model);
95 case DEVICE_TYPE_OCEANIC_ATOM2:
96 return oceanic_atom2_parser_create(parser, devdata->devinfo.model);
98 case DEVICE_TYPE_MARES_NEMO:
99 case DEVICE_TYPE_MARES_PUCK:
100 return mares_nemo_parser_create(parser, devdata->devinfo.model);
102 case DEVICE_TYPE_MARES_ICONHD:
103 return mares_iconhd_parser_create(parser);
105 case DEVICE_TYPE_HW_OSTC:
106 return hw_ostc_parser_create(parser);
108 case DEVICE_TYPE_CRESSI_EDY:
109 case DEVICE_TYPE_ZEAGLE_N2ITION3:
110 return cressi_edy_parser_create(parser, devdata->devinfo.model);
112 case DEVICE_TYPE_ATOMICS_COBALT:
113 return atomics_cobalt_parser_create(parser);
116 return PARSER_STATUS_ERROR;
120 static int parse_gasmixes(struct dive *dive, parser_t *parser, int ngases)
124 for (i = 0; i < ngases; i++) {
126 gasmix_t gasmix = {0};
128 rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
129 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
132 if (i >= MAX_CYLINDERS)
135 dive->cylinder[i].gasmix.o2.permille = gasmix.oxygen * 1000 + 0.5;
136 dive->cylinder[i].gasmix.he.permille = gasmix.helium * 1000 + 0.5;
138 return PARSER_STATUS_SUCCESS;
142 sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
145 static const char *events[] = {
146 "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
147 "violation", "bookmark", "surface", "safety stop", "gaschange",
148 "safety stop (voluntary)", "safety stop (mandatory)", "deepstop",
149 "ceiling (safety stop)", "unknown", "divetime", "maxdepth",
150 "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning"};
151 struct dive **divep = userdata;
152 struct dive *dive = *divep;
153 struct sample *sample;
156 * We fill in the "previous" sample - except for SAMPLE_TYPE_TIME,
157 * which creates a new one.
159 sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
162 case SAMPLE_TYPE_TIME:
163 sample = prepare_sample(divep);
164 sample->time.seconds = value.time;
165 finish_sample(*divep, sample);
167 case SAMPLE_TYPE_DEPTH:
168 sample->depth.mm = value.depth * 1000 + 0.5;
170 case SAMPLE_TYPE_PRESSURE:
171 sample->cylinderindex = value.pressure.tank;
172 sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
174 case SAMPLE_TYPE_TEMPERATURE:
175 sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
177 case SAMPLE_TYPE_EVENT:
178 printf(" <event type=\"%u\" time=\"%u\" flags=\"%u\" value=\"%u\">%s</event>\n",
179 value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]);
181 case SAMPLE_TYPE_RBT:
182 printf(" <rbt>%u</rbt>\n", value.rbt);
184 case SAMPLE_TYPE_HEARTBEAT:
185 printf(" <heartbeat>%u</heartbeat>\n", value.heartbeat);
187 case SAMPLE_TYPE_BEARING:
188 printf(" <bearing>%u</bearing>\n", value.bearing);
190 case SAMPLE_TYPE_VENDOR:
191 printf(" <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
192 for (i = 0; i < value.vendor.size; ++i)
193 printf("%02X", ((unsigned char *) value.vendor.data)[i]);
194 printf("</vendor>\n");
202 static int parse_samples(struct dive **divep, parser_t *parser)
204 // Parse the sample data.
205 printf("Parsing the sample data.\n");
206 return parser_samples_foreach(parser, sample_cb, divep);
209 static int dive_cb(const unsigned char *data, unsigned int size,
210 const unsigned char *fingerprint, unsigned int fsize,
214 parser_t *parser = NULL;
215 device_data_t *devdata = userdata;
216 dc_datetime_t dt = {0};
220 /* Christ, this is hacky */
223 rc = create_parser(devdata, &parser);
224 if (rc != PARSER_STATUS_SUCCESS) {
225 error("Unable to create parser for %s", devdata->name);
229 rc = parser_set_data(parser, data, size);
230 if (rc != PARSER_STATUS_SUCCESS) {
231 error("Error registering the data.");
232 parser_destroy(parser);
237 rc = parser_get_datetime(parser, &dt);
238 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
239 error("Error parsing the datetime.");
240 parser_destroy (parser);
244 tm.tm_year = dt.year;
245 tm.tm_mon = dt.month-1;
247 tm.tm_hour = dt.hour;
248 tm.tm_min = dt.minute;
249 tm.tm_sec = dt.second;
250 dive->when = utc_mktime(&tm);
252 // Parse the divetime.
253 printf("Parsing the divetime.\n");
254 unsigned int divetime = 0;
255 rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
256 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
257 error("Error parsing the divetime.");
258 parser_destroy(parser);
261 dive->duration.seconds = divetime;
263 // Parse the maxdepth.
264 printf("Parsing the maxdepth.\n");
265 double maxdepth = 0.0;
266 rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
267 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
268 error("Error parsing the maxdepth.");
269 parser_destroy(parser);
272 dive->maxdepth.mm = maxdepth * 1000 + 0.5;
274 // Parse the gas mixes.
275 printf("Parsing the gas mixes.\n");
276 unsigned int ngases = 0;
277 rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
278 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
279 error("Error parsing the gas mix count.");
280 parser_destroy(parser);
284 rc = parse_gasmixes(dive, parser, ngases);
285 if (rc != PARSER_STATUS_SUCCESS) {
286 error("Error parsing the gas mix.");
287 parser_destroy(parser);
291 // Initialize the sample data.
292 rc = parse_samples(&dive, parser);
293 if (rc != PARSER_STATUS_SUCCESS) {
294 error("Error parsing the samples.");
295 parser_destroy(parser);
300 parser_destroy(parser);
305 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
307 return device_foreach(device, dive_cb, devicedata);
310 static device_status_t device_open(const char *devname,
315 case DEVICE_TYPE_SUUNTO_SOLUTION:
316 return suunto_solution_device_open(device, devname);
318 case DEVICE_TYPE_SUUNTO_EON:
319 return suunto_eon_device_open(device, devname);
321 case DEVICE_TYPE_SUUNTO_VYPER:
322 return suunto_vyper_device_open(device, devname);
324 case DEVICE_TYPE_SUUNTO_VYPER2:
325 return suunto_vyper2_device_open(device, devname);
327 case DEVICE_TYPE_SUUNTO_D9:
328 return suunto_d9_device_open(device, devname);
330 case DEVICE_TYPE_UWATEC_ALADIN:
331 return uwatec_aladin_device_open(device, devname);
333 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
334 return uwatec_memomouse_device_open(device, devname);
336 case DEVICE_TYPE_UWATEC_SMART:
337 return uwatec_smart_device_open(device);
339 case DEVICE_TYPE_REEFNET_SENSUS:
340 return reefnet_sensus_device_open(device, devname);
342 case DEVICE_TYPE_REEFNET_SENSUSPRO:
343 return reefnet_sensuspro_device_open(device, devname);
345 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
346 return reefnet_sensusultra_device_open(device, devname);
348 case DEVICE_TYPE_OCEANIC_VTPRO:
349 return oceanic_vtpro_device_open(device, devname);
351 case DEVICE_TYPE_OCEANIC_VEO250:
352 return oceanic_veo250_device_open(device, devname);
354 case DEVICE_TYPE_OCEANIC_ATOM2:
355 return oceanic_atom2_device_open(device, devname);
357 case DEVICE_TYPE_MARES_NEMO:
358 return mares_nemo_device_open(device, devname);
360 case DEVICE_TYPE_MARES_PUCK:
361 return mares_puck_device_open(device, devname);
363 case DEVICE_TYPE_MARES_ICONHD:
364 return mares_iconhd_device_open(device, devname);
366 case DEVICE_TYPE_HW_OSTC:
367 return hw_ostc_device_open(device, devname);
369 case DEVICE_TYPE_CRESSI_EDY:
370 return cressi_edy_device_open(device, devname);
372 case DEVICE_TYPE_ZEAGLE_N2ITION3:
373 return zeagle_n2ition3_device_open(device, devname);
375 case DEVICE_TYPE_ATOMICS_COBALT:
376 return atomics_cobalt_device_open(device);
379 return DEVICE_STATUS_ERROR;
384 event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
386 const device_progress_t *progress = (device_progress_t *) data;
387 const device_devinfo_t *devinfo = (device_devinfo_t *) data;
388 const device_clock_t *clock = (device_clock_t *) data;
389 device_data_t *devdata = (device_data_t *) userdata;
391 /* Christ, this is hacky */
395 case DEVICE_EVENT_WAITING:
396 printf("Event: waiting for user action\n");
398 case DEVICE_EVENT_PROGRESS:
399 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(devdata->progressbar),
400 (double) progress->current / (double) progress->maximum);
402 case DEVICE_EVENT_DEVINFO:
403 devdata->devinfo = *devinfo;
404 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
405 devinfo->model, devinfo->model,
406 devinfo->firmware, devinfo->firmware,
407 devinfo->serial, devinfo->serial);
409 case DEVICE_EVENT_CLOCK:
410 devdata->clock = *clock;
411 printf("Event: systime=%lld, devtime=%u\n",
412 clock->systime, clock->devtime);
420 cancel_cb(void *userdata)
422 return run_gtk_mainloop();
425 static void do_import(device_data_t *data)
427 /* FIXME! Needs user input! */
428 const char *devname = "/dev/ttyUSB0";
429 device_t *device = NULL;
432 rc = device_open(devname, data->type, &device);
433 if (rc != DEVICE_STATUS_SUCCESS) {
434 error("Unable to open %s (%s)", data->name, data->devname);
438 // Register the event handler.
439 int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
440 rc = device_set_events(device, events, event_cb, data);
441 if (rc != DEVICE_STATUS_SUCCESS) {
442 error("Error registering the event handler.");
443 device_close(device);
447 // Register the cancellation handler.
448 rc = device_set_cancel(device, cancel_cb, data);
449 if (rc != DEVICE_STATUS_SUCCESS) {
450 error("Error registering the cancellation handler.");
451 device_close(device);
455 rc = import_device_data(device, data);
456 if (rc != DEVICE_STATUS_SUCCESS) {
457 error("Dive data import error");
458 device_close(device);
462 device_close(device);
466 * Taken from 'example.c' in libdivecomputer.
468 * I really wish there was some way to just have
469 * libdivecomputer tell us what devices it supports,
470 * rather than have the application have to know..
476 { "Suunto Solution", DEVICE_TYPE_SUUNTO_SOLUTION },
477 { "Suunto Eon", DEVICE_TYPE_SUUNTO_EON },
478 { "Suunto Vyper", DEVICE_TYPE_SUUNTO_VYPER },
479 { "Suunto Vyper Air", DEVICE_TYPE_SUUNTO_VYPER2 },
480 { "Suunto D9", DEVICE_TYPE_SUUNTO_D9 },
481 { "Uwatec Aladin", DEVICE_TYPE_UWATEC_ALADIN },
482 { "Uwatec Memo Mouse", DEVICE_TYPE_UWATEC_MEMOMOUSE },
483 { "Uwatec Smart", DEVICE_TYPE_UWATEC_SMART },
484 { "ReefNet Sensus", DEVICE_TYPE_REEFNET_SENSUS },
485 { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
486 { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
487 { "Oceanic VT Pro", DEVICE_TYPE_OCEANIC_VTPRO },
488 { "Oceanic Veo250", DEVICE_TYPE_OCEANIC_VEO250 },
489 { "Oceanic Atom 2", DEVICE_TYPE_OCEANIC_ATOM2 },
490 { "Mares Nemo", DEVICE_TYPE_MARES_NEMO },
491 { "Mares Puck", DEVICE_TYPE_MARES_PUCK },
492 { "Mares Icon HD", DEVICE_TYPE_MARES_ICONHD },
493 { "OSTC", DEVICE_TYPE_HW_OSTC },
494 { "Cressi Edy", DEVICE_TYPE_CRESSI_EDY },
495 { "Zeagle N2iTiON 3", DEVICE_TYPE_ZEAGLE_N2ITION3 },
496 { "Atomics Cobalt", DEVICE_TYPE_ATOMICS_COBALT },
500 static void fill_computer_list(GtkListStore *store)
503 struct device_list *list = device_list;
505 for (list = device_list ; list->name ; list++) {
506 gtk_list_store_append(store, &iter);
507 gtk_list_store_set(store, &iter,
514 static GtkComboBox *dive_computer_selector(GtkWidget *dialog)
516 GtkWidget *hbox, *combo_box;
518 GtkCellRenderer *renderer;
520 hbox = gtk_hbox_new(FALSE, 6);
521 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 3);
523 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
524 fill_computer_list(model);
526 combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
527 gtk_box_pack_start(GTK_BOX(hbox), combo_box, FALSE, TRUE, 3);
529 renderer = gtk_cell_renderer_text_new();
530 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE);
531 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), renderer, "text", 0, NULL);
533 return GTK_COMBO_BOX(combo_box);
536 void import_dialog(GtkWidget *w, gpointer data)
539 GtkWidget *dialog, *hbox;
540 GtkComboBox *computer;
541 device_data_t devicedata = {
542 .devname = "/dev/ttyUSB0",
545 dialog = gtk_dialog_new_with_buttons("Import from dive computer",
546 GTK_WINDOW(main_window),
547 GTK_DIALOG_DESTROY_WITH_PARENT,
548 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
549 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
552 computer = dive_computer_selector(dialog);
554 hbox = gtk_hbox_new(FALSE, 6);
555 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, TRUE, 3);
556 devicedata.progressbar = gtk_progress_bar_new();
557 gtk_container_add(GTK_CONTAINER(hbox), devicedata.progressbar);
559 gtk_widget_show_all(dialog);
560 result = gtk_dialog_run(GTK_DIALOG(dialog));
566 case GTK_RESPONSE_ACCEPT:
567 if (!gtk_combo_box_get_active_iter(computer, &iter))
569 model = gtk_combo_box_get_model(computer);
570 gtk_tree_model_get(model, &iter,
574 devicedata.type = type;
575 devicedata.name = comp;
576 do_import(&devicedata);
581 gtk_widget_destroy(dialog);
584 dive_list_update_dives(dive_list);