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};
129 rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
130 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
133 if (i >= MAX_CYLINDERS)
136 o2 = gasmix.oxygen * 1000 + 0.5;
137 he = gasmix.helium * 1000 + 0.5;
139 /* Ignore bogus data - libdivecomputer does some crazy stuff */
140 if (o2 < 210 || o2 >= 1000)
142 if (he < 0 || he >= 800 || o2+he >= 1000)
145 dive->cylinder[i].gasmix.o2.permille = o2;
146 dive->cylinder[i].gasmix.he.permille = he;
148 return PARSER_STATUS_SUCCESS;
152 sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
155 static const char *events[] = {
156 "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
157 "violation", "bookmark", "surface", "safety stop", "gaschange",
158 "safety stop (voluntary)", "safety stop (mandatory)", "deepstop",
159 "ceiling (safety stop)", "unknown", "divetime", "maxdepth",
160 "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning"};
161 struct dive **divep = userdata;
162 struct dive *dive = *divep;
163 struct sample *sample;
166 * We fill in the "previous" sample - except for SAMPLE_TYPE_TIME,
167 * which creates a new one.
169 sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
172 case SAMPLE_TYPE_TIME:
173 sample = prepare_sample(divep);
174 sample->time.seconds = value.time;
175 finish_sample(*divep, sample);
177 case SAMPLE_TYPE_DEPTH:
178 sample->depth.mm = value.depth * 1000 + 0.5;
180 case SAMPLE_TYPE_PRESSURE:
181 sample->cylinderindex = value.pressure.tank;
182 sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
184 case SAMPLE_TYPE_TEMPERATURE:
185 sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
187 case SAMPLE_TYPE_EVENT:
188 printf(" <event type=\"%u\" time=\"%u\" flags=\"%u\" value=\"%u\">%s</event>\n",
189 value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]);
191 case SAMPLE_TYPE_RBT:
192 printf(" <rbt>%u</rbt>\n", value.rbt);
194 case SAMPLE_TYPE_HEARTBEAT:
195 printf(" <heartbeat>%u</heartbeat>\n", value.heartbeat);
197 case SAMPLE_TYPE_BEARING:
198 printf(" <bearing>%u</bearing>\n", value.bearing);
200 case SAMPLE_TYPE_VENDOR:
201 printf(" <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
202 for (i = 0; i < value.vendor.size; ++i)
203 printf("%02X", ((unsigned char *) value.vendor.data)[i]);
204 printf("</vendor>\n");
212 static int parse_samples(struct dive **divep, parser_t *parser)
214 // Parse the sample data.
215 printf("Parsing the sample data.\n");
216 return parser_samples_foreach(parser, sample_cb, divep);
219 static int dive_cb(const unsigned char *data, unsigned int size,
220 const unsigned char *fingerprint, unsigned int fsize,
224 parser_t *parser = NULL;
225 device_data_t *devdata = userdata;
226 dc_datetime_t dt = {0};
230 /* Christ, this is hacky */
233 rc = create_parser(devdata, &parser);
234 if (rc != PARSER_STATUS_SUCCESS) {
235 error("Unable to create parser for %s", devdata->name);
239 rc = parser_set_data(parser, data, size);
240 if (rc != PARSER_STATUS_SUCCESS) {
241 error("Error registering the data.");
242 parser_destroy(parser);
247 rc = parser_get_datetime(parser, &dt);
248 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
249 error("Error parsing the datetime.");
250 parser_destroy (parser);
254 tm.tm_year = dt.year;
255 tm.tm_mon = dt.month-1;
257 tm.tm_hour = dt.hour;
258 tm.tm_min = dt.minute;
259 tm.tm_sec = dt.second;
260 dive->when = utc_mktime(&tm);
262 // Parse the divetime.
263 printf("Parsing the divetime.\n");
264 unsigned int divetime = 0;
265 rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
266 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
267 error("Error parsing the divetime.");
268 parser_destroy(parser);
271 dive->duration.seconds = divetime;
273 // Parse the maxdepth.
274 printf("Parsing the maxdepth.\n");
275 double maxdepth = 0.0;
276 rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
277 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
278 error("Error parsing the maxdepth.");
279 parser_destroy(parser);
282 dive->maxdepth.mm = maxdepth * 1000 + 0.5;
284 // Parse the gas mixes.
285 printf("Parsing the gas mixes.\n");
286 unsigned int ngases = 0;
287 rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
288 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
289 error("Error parsing the gas mix count.");
290 parser_destroy(parser);
294 rc = parse_gasmixes(dive, parser, ngases);
295 if (rc != PARSER_STATUS_SUCCESS) {
296 error("Error parsing the gas mix.");
297 parser_destroy(parser);
301 // Initialize the sample data.
302 rc = parse_samples(&dive, parser);
303 if (rc != PARSER_STATUS_SUCCESS) {
304 error("Error parsing the samples.");
305 parser_destroy(parser);
310 parser_destroy(parser);
315 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
317 return device_foreach(device, dive_cb, devicedata);
320 static device_status_t device_open(const char *devname,
325 case DEVICE_TYPE_SUUNTO_SOLUTION:
326 return suunto_solution_device_open(device, devname);
328 case DEVICE_TYPE_SUUNTO_EON:
329 return suunto_eon_device_open(device, devname);
331 case DEVICE_TYPE_SUUNTO_VYPER:
332 return suunto_vyper_device_open(device, devname);
334 case DEVICE_TYPE_SUUNTO_VYPER2:
335 return suunto_vyper2_device_open(device, devname);
337 case DEVICE_TYPE_SUUNTO_D9:
338 return suunto_d9_device_open(device, devname);
340 case DEVICE_TYPE_UWATEC_ALADIN:
341 return uwatec_aladin_device_open(device, devname);
343 case DEVICE_TYPE_UWATEC_MEMOMOUSE:
344 return uwatec_memomouse_device_open(device, devname);
346 case DEVICE_TYPE_UWATEC_SMART:
347 return uwatec_smart_device_open(device);
349 case DEVICE_TYPE_REEFNET_SENSUS:
350 return reefnet_sensus_device_open(device, devname);
352 case DEVICE_TYPE_REEFNET_SENSUSPRO:
353 return reefnet_sensuspro_device_open(device, devname);
355 case DEVICE_TYPE_REEFNET_SENSUSULTRA:
356 return reefnet_sensusultra_device_open(device, devname);
358 case DEVICE_TYPE_OCEANIC_VTPRO:
359 return oceanic_vtpro_device_open(device, devname);
361 case DEVICE_TYPE_OCEANIC_VEO250:
362 return oceanic_veo250_device_open(device, devname);
364 case DEVICE_TYPE_OCEANIC_ATOM2:
365 return oceanic_atom2_device_open(device, devname);
367 case DEVICE_TYPE_MARES_NEMO:
368 return mares_nemo_device_open(device, devname);
370 case DEVICE_TYPE_MARES_PUCK:
371 return mares_puck_device_open(device, devname);
373 case DEVICE_TYPE_MARES_ICONHD:
374 return mares_iconhd_device_open(device, devname);
376 case DEVICE_TYPE_HW_OSTC:
377 return hw_ostc_device_open(device, devname);
379 case DEVICE_TYPE_CRESSI_EDY:
380 return cressi_edy_device_open(device, devname);
382 case DEVICE_TYPE_ZEAGLE_N2ITION3:
383 return zeagle_n2ition3_device_open(device, devname);
385 case DEVICE_TYPE_ATOMICS_COBALT:
386 return atomics_cobalt_device_open(device);
389 return DEVICE_STATUS_ERROR;
394 event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
396 const device_progress_t *progress = (device_progress_t *) data;
397 const device_devinfo_t *devinfo = (device_devinfo_t *) data;
398 const device_clock_t *clock = (device_clock_t *) data;
399 device_data_t *devdata = (device_data_t *) userdata;
401 /* Christ, this is hacky */
405 case DEVICE_EVENT_WAITING:
406 printf("Event: waiting for user action\n");
408 case DEVICE_EVENT_PROGRESS:
409 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(devdata->progressbar),
410 (double) progress->current / (double) progress->maximum);
412 case DEVICE_EVENT_DEVINFO:
413 devdata->devinfo = *devinfo;
414 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
415 devinfo->model, devinfo->model,
416 devinfo->firmware, devinfo->firmware,
417 devinfo->serial, devinfo->serial);
419 case DEVICE_EVENT_CLOCK:
420 devdata->clock = *clock;
421 printf("Event: systime=%lld, devtime=%u\n",
422 clock->systime, clock->devtime);
430 cancel_cb(void *userdata)
432 return run_gtk_mainloop();
435 static void do_import(device_data_t *data)
437 /* FIXME! Needs user input! */
438 const char *devname = "/dev/ttyUSB0";
439 device_t *device = NULL;
442 rc = device_open(devname, data->type, &device);
443 if (rc != DEVICE_STATUS_SUCCESS) {
444 error("Unable to open %s (%s)", data->name, data->devname);
448 // Register the event handler.
449 int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
450 rc = device_set_events(device, events, event_cb, data);
451 if (rc != DEVICE_STATUS_SUCCESS) {
452 error("Error registering the event handler.");
453 device_close(device);
457 // Register the cancellation handler.
458 rc = device_set_cancel(device, cancel_cb, data);
459 if (rc != DEVICE_STATUS_SUCCESS) {
460 error("Error registering the cancellation handler.");
461 device_close(device);
465 rc = import_device_data(device, data);
466 if (rc != DEVICE_STATUS_SUCCESS) {
467 error("Dive data import error");
468 device_close(device);
472 device_close(device);
476 * Taken from 'example.c' in libdivecomputer.
478 * I really wish there was some way to just have
479 * libdivecomputer tell us what devices it supports,
480 * rather than have the application have to know..
486 { "Suunto Solution", DEVICE_TYPE_SUUNTO_SOLUTION },
487 { "Suunto Eon", DEVICE_TYPE_SUUNTO_EON },
488 { "Suunto Vyper", DEVICE_TYPE_SUUNTO_VYPER },
489 { "Suunto Vyper Air", DEVICE_TYPE_SUUNTO_VYPER2 },
490 { "Suunto D9", DEVICE_TYPE_SUUNTO_D9 },
491 { "Uwatec Aladin", DEVICE_TYPE_UWATEC_ALADIN },
492 { "Uwatec Memo Mouse", DEVICE_TYPE_UWATEC_MEMOMOUSE },
493 { "Uwatec Smart", DEVICE_TYPE_UWATEC_SMART },
494 { "ReefNet Sensus", DEVICE_TYPE_REEFNET_SENSUS },
495 { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
496 { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
497 { "Oceanic VT Pro", DEVICE_TYPE_OCEANIC_VTPRO },
498 { "Oceanic Veo250", DEVICE_TYPE_OCEANIC_VEO250 },
499 { "Oceanic Atom 2", DEVICE_TYPE_OCEANIC_ATOM2 },
500 { "Mares Nemo", DEVICE_TYPE_MARES_NEMO },
501 { "Mares Puck", DEVICE_TYPE_MARES_PUCK },
502 { "Mares Icon HD", DEVICE_TYPE_MARES_ICONHD },
503 { "OSTC", DEVICE_TYPE_HW_OSTC },
504 { "Cressi Edy", DEVICE_TYPE_CRESSI_EDY },
505 { "Zeagle N2iTiON 3", DEVICE_TYPE_ZEAGLE_N2ITION3 },
506 { "Atomics Cobalt", DEVICE_TYPE_ATOMICS_COBALT },
510 static void fill_computer_list(GtkListStore *store)
513 struct device_list *list = device_list;
515 for (list = device_list ; list->name ; list++) {
516 gtk_list_store_append(store, &iter);
517 gtk_list_store_set(store, &iter,
524 static GtkComboBox *dive_computer_selector(GtkWidget *dialog)
526 GtkWidget *hbox, *combo_box;
528 GtkCellRenderer *renderer;
530 hbox = gtk_hbox_new(FALSE, 6);
531 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 3);
533 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
534 fill_computer_list(model);
536 combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
537 gtk_box_pack_start(GTK_BOX(hbox), combo_box, FALSE, TRUE, 3);
539 renderer = gtk_cell_renderer_text_new();
540 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE);
541 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), renderer, "text", 0, NULL);
543 return GTK_COMBO_BOX(combo_box);
546 void import_dialog(GtkWidget *w, gpointer data)
549 GtkWidget *dialog, *hbox;
550 GtkComboBox *computer;
551 device_data_t devicedata = {
552 .devname = "/dev/ttyUSB0",
555 dialog = gtk_dialog_new_with_buttons("Import from dive computer",
556 GTK_WINDOW(main_window),
557 GTK_DIALOG_DESTROY_WITH_PARENT,
558 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
559 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
562 computer = dive_computer_selector(dialog);
564 hbox = gtk_hbox_new(FALSE, 6);
565 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, TRUE, 3);
566 devicedata.progressbar = gtk_progress_bar_new();
567 gtk_container_add(GTK_CONTAINER(hbox), devicedata.progressbar);
569 gtk_widget_show_all(dialog);
570 result = gtk_dialog_run(GTK_DIALOG(dialog));
576 case GTK_RESPONSE_ACCEPT:
577 if (!gtk_combo_box_get_active_iter(computer, &iter))
579 model = gtk_combo_box_get_model(computer);
580 gtk_tree_model_get(model, &iter,
584 devicedata.type = type;
585 devicedata.name = comp;
586 do_import(&devicedata);
591 gtk_widget_destroy(dialog);
594 dive_list_update_dives(dive_list);