]> git.tdb.fi Git - ext/subsurface.git/blob - libdivecomputer.c
Libdivecomputer integration, part n+1
[ext/subsurface.git] / libdivecomputer.c
1 #include <stdio.h>
2 #include <gtk/gtk.h>
3
4 #include "dive.h"
5 #include "display.h"
6
7 /* libdivecomputer */
8 #include <device.h>
9 #include <suunto.h>
10 #include <reefnet.h>
11 #include <uwatec.h>
12 #include <oceanic.h>
13 #include <mares.h>
14 #include <hw.h>
15 #include <cressi.h>
16 #include <zeagle.h>
17 #include <atomics.h>
18 #include <utils.h>
19
20 static void error(const char *fmt, ...)
21 {
22         va_list args;
23         GError *error;
24
25         va_start(args, fmt);
26         error = g_error_new_valist(
27                 g_quark_from_string("divelog"),
28                 DIVE_ERROR_PARSE, fmt, args);
29         va_end(args);
30         report_error(error);
31         g_error_free(error);
32 }
33
34 typedef struct device_data_t {
35         device_type_t type;
36         const char *name;
37         device_devinfo_t devinfo;
38         device_clock_t clock;
39 } device_data_t;
40
41 static parser_status_t create_parser(device_data_t *devdata, parser_t **parser)
42 {
43         switch (devdata->type) {
44         case DEVICE_TYPE_SUUNTO_SOLUTION:
45                 return suunto_solution_parser_create(parser);
46
47         case DEVICE_TYPE_SUUNTO_EON:
48                 return suunto_eon_parser_create(parser, 0);
49
50         case DEVICE_TYPE_SUUNTO_VYPER:
51                 if (devdata->devinfo.model == 0x01)
52                         return suunto_eon_parser_create(parser, 1);
53                 return suunto_vyper_parser_create(parser);
54
55         case DEVICE_TYPE_SUUNTO_VYPER2:
56         case DEVICE_TYPE_SUUNTO_D9:
57                 return suunto_d9_parser_create(parser, devdata->devinfo.model);
58
59         case DEVICE_TYPE_UWATEC_ALADIN:
60         case DEVICE_TYPE_UWATEC_MEMOMOUSE:
61                 return uwatec_memomouse_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
62
63         case DEVICE_TYPE_UWATEC_SMART:
64                 return uwatec_smart_parser_create(parser, devdata->devinfo.model, devdata->clock.devtime, devdata->clock.systime);
65
66         case DEVICE_TYPE_REEFNET_SENSUS:
67                 return reefnet_sensus_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
68
69         case DEVICE_TYPE_REEFNET_SENSUSPRO:
70                 return reefnet_sensuspro_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
71
72         case DEVICE_TYPE_REEFNET_SENSUSULTRA:
73                 return reefnet_sensusultra_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
74
75         case DEVICE_TYPE_OCEANIC_VTPRO:
76                 return oceanic_vtpro_parser_create(parser);
77
78         case DEVICE_TYPE_OCEANIC_VEO250:
79                 return oceanic_veo250_parser_create(parser, devdata->devinfo.model);
80
81         case DEVICE_TYPE_OCEANIC_ATOM2:
82                 return oceanic_atom2_parser_create(parser, devdata->devinfo.model);
83
84         case DEVICE_TYPE_MARES_NEMO:
85         case DEVICE_TYPE_MARES_PUCK:
86                 return mares_nemo_parser_create(parser, devdata->devinfo.model);
87
88         case DEVICE_TYPE_MARES_ICONHD:
89                 return mares_iconhd_parser_create(parser);
90
91         case DEVICE_TYPE_HW_OSTC:
92                 return hw_ostc_parser_create(parser);
93
94         case DEVICE_TYPE_CRESSI_EDY:
95         case DEVICE_TYPE_ZEAGLE_N2ITION3:
96                 return cressi_edy_parser_create(parser, devdata->devinfo.model);
97
98         case DEVICE_TYPE_ATOMICS_COBALT:
99                 return atomics_cobalt_parser_create(parser);
100
101         default:
102                 return PARSER_STATUS_ERROR;
103         }
104 }
105
106 static int parse_gasmixes(parser_t *parser, int ngases)
107 {
108         int i;
109
110         for (i = 0; i < ngases; i++) {
111                 int rc;
112                 gasmix_t gasmix = {0};
113
114                 rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
115                 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
116                         return rc;
117
118                 printf("<gasmix>\n"
119                         "   <he>%.1f</he>\n"
120                         "   <o2>%.1f</o2>\n"
121                         "   <n2>%.1f</n2>\n"
122                         "</gasmix>\n",
123                         gasmix.helium * 100.0,
124                         gasmix.oxygen * 100.0,
125                         gasmix.nitrogen * 100.0);
126         }
127         return PARSER_STATUS_SUCCESS;
128 }
129
130 void
131 sample_cb (parser_sample_type_t type, parser_sample_value_t value, void *userdata)
132 {
133         int i;
134         static const char *events[] = {
135                 "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
136                 "violation", "bookmark", "surface", "safety stop", "gaschange",
137                 "safety stop (voluntary)", "safety stop (mandatory)", "deepstop",
138                 "ceiling (safety stop)", "unknown", "divetime", "maxdepth",
139                 "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning"};
140
141         switch (type) {
142         case SAMPLE_TYPE_TIME:
143                 printf("<sample>\n");
144                 printf("   <time>%02u:%02u</time>\n", value.time / 60, value.time % 60);
145                 break;
146         case SAMPLE_TYPE_DEPTH:
147                 printf("   <depth>%.2f</depth>\n", value.depth);
148                 break;
149         case SAMPLE_TYPE_PRESSURE:
150                 printf("   <pressure tank=\"%u\">%.2f</pressure>\n", value.pressure.tank, value.pressure.value);
151                 break;
152         case SAMPLE_TYPE_TEMPERATURE:
153                 printf("   <temperature>%.2f</temperature>\n", value.temperature);
154                 break;
155         case SAMPLE_TYPE_EVENT:
156                 printf("   <event type=\"%u\" time=\"%u\" flags=\"%u\" value=\"%u\">%s</event>\n",
157                         value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]);
158                 break;
159         case SAMPLE_TYPE_RBT:
160                 printf("   <rbt>%u</rbt>\n", value.rbt);
161                 break;
162         case SAMPLE_TYPE_HEARTBEAT:
163                 printf("   <heartbeat>%u</heartbeat>\n", value.heartbeat);
164                 break;
165         case SAMPLE_TYPE_BEARING:
166                 printf("   <bearing>%u</bearing>\n", value.bearing);
167                 break;
168         case SAMPLE_TYPE_VENDOR:
169                 printf("   <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
170                 for (i = 0; i < value.vendor.size; ++i)
171                         printf("%02X", ((unsigned char *) value.vendor.data)[i]);
172                 printf("</vendor>\n");
173                 break;
174         default:
175                 break;
176         }
177 }
178
179
180 static int parse_samples(parser_t *parser)
181 {
182         // Parse the sample data.
183         printf("Parsing the sample data.\n");
184         return parser_samples_foreach(parser, sample_cb, NULL);
185 }
186
187 static int dive_cb(const unsigned char *data, unsigned int size,
188         const unsigned char *fingerprint, unsigned int fsize,
189         void *userdata)
190 {
191         int rc;
192         parser_t *parser = NULL;
193         device_data_t *devdata = userdata;
194         dc_datetime_t dt = {0};
195
196         rc = create_parser(devdata, &parser);
197         if (rc != PARSER_STATUS_SUCCESS) {
198                 error("Unable to create parser for %s", devdata->name);
199                 return rc;
200         }
201
202         rc = parser_set_data(parser, data, size);
203         if (rc != PARSER_STATUS_SUCCESS) {
204                 error("Error registering the data.");
205                 parser_destroy(parser);
206                 return rc;
207         }
208
209         rc = parser_get_datetime(parser, &dt);
210         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
211                 error("Error parsing the datetime.");
212                 parser_destroy (parser);
213                 return rc;
214         }
215
216         printf("<datetime>%04i-%02i-%02i %02i:%02i:%02i</datetime>\n",
217                 dt.year, dt.month, dt.day,
218                 dt.hour, dt.minute, dt.second);
219
220         // Parse the divetime.
221         printf("Parsing the divetime.\n");
222         unsigned int divetime = 0;
223         rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
224         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
225                 error("Error parsing the divetime.");
226                 parser_destroy(parser);
227                 return rc;
228         }
229
230         printf("<divetime>%02u:%02u</divetime>\n",
231                 divetime / 60, divetime % 60);
232
233         // Parse the maxdepth.
234         printf("Parsing the maxdepth.\n");
235         double maxdepth = 0.0;
236         rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
237         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
238                 error("Error parsing the maxdepth.");
239                 parser_destroy(parser);
240                 return rc;
241         }
242
243         printf("<maxdepth>%.2f</maxdepth>\n", maxdepth);
244
245         // Parse the gas mixes.
246         printf("Parsing the gas mixes.\n");
247         unsigned int ngases = 0;
248         rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
249         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
250                 error("Error parsing the gas mix count.");
251                 parser_destroy(parser);
252                 return rc;
253         }
254
255         rc = parse_gasmixes(parser, ngases);
256         if (rc != PARSER_STATUS_SUCCESS) {
257                 error("Error parsing the gas mix.");
258                 parser_destroy(parser);
259                 return rc;
260         }
261
262         // Initialize the sample data.
263         rc = parse_samples(parser);
264         if (rc != PARSER_STATUS_SUCCESS) {
265                 error("Error parsing the samples.");
266                 parser_destroy(parser);
267                 return rc;
268         }
269
270         parser_destroy(parser);
271         return 1;
272 }
273
274
275 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
276 {
277         return device_foreach(device, dive_cb, devicedata);
278 }
279
280 static device_status_t device_open(const char *devname,
281         device_type_t type,
282         device_t **device)
283 {
284         switch (type) {
285         case DEVICE_TYPE_SUUNTO_SOLUTION:
286                 return suunto_solution_device_open(device, devname);
287
288         case DEVICE_TYPE_SUUNTO_EON:
289                 return suunto_eon_device_open(device, devname);
290
291         case DEVICE_TYPE_SUUNTO_VYPER:
292                 return suunto_vyper_device_open(device, devname);
293
294         case DEVICE_TYPE_SUUNTO_VYPER2:
295                 return suunto_vyper2_device_open(device, devname);
296
297         case DEVICE_TYPE_SUUNTO_D9:
298                 return suunto_d9_device_open(device, devname);
299
300         case DEVICE_TYPE_UWATEC_ALADIN:
301                 return uwatec_aladin_device_open(device, devname);
302
303         case DEVICE_TYPE_UWATEC_MEMOMOUSE:
304                 return uwatec_memomouse_device_open(device, devname);
305
306         case DEVICE_TYPE_UWATEC_SMART:
307                 return uwatec_smart_device_open(device);
308
309         case DEVICE_TYPE_REEFNET_SENSUS:
310                 return reefnet_sensus_device_open(device, devname);
311
312         case DEVICE_TYPE_REEFNET_SENSUSPRO:
313                 return reefnet_sensuspro_device_open(device, devname);
314
315         case DEVICE_TYPE_REEFNET_SENSUSULTRA:
316                 return reefnet_sensusultra_device_open(device, devname);
317
318         case DEVICE_TYPE_OCEANIC_VTPRO:
319                 return oceanic_vtpro_device_open(device, devname);
320
321         case DEVICE_TYPE_OCEANIC_VEO250:
322                 return oceanic_veo250_device_open(device, devname);
323
324         case DEVICE_TYPE_OCEANIC_ATOM2:
325                 return oceanic_atom2_device_open(device, devname);
326
327         case DEVICE_TYPE_MARES_NEMO:
328                 return mares_nemo_device_open(device, devname);
329
330         case DEVICE_TYPE_MARES_PUCK:
331                 return mares_puck_device_open(device, devname);
332
333         case DEVICE_TYPE_MARES_ICONHD:
334                 return mares_iconhd_device_open(device, devname);
335
336         case DEVICE_TYPE_HW_OSTC:
337                 return hw_ostc_device_open(device, devname);
338
339         case DEVICE_TYPE_CRESSI_EDY:
340                 return cressi_edy_device_open(device, devname);
341
342         case DEVICE_TYPE_ZEAGLE_N2ITION3:
343                 return zeagle_n2ition3_device_open(device, devname);
344
345         case DEVICE_TYPE_ATOMICS_COBALT:
346                 return atomics_cobalt_device_open(device);
347
348         default:
349                 return DEVICE_STATUS_ERROR;
350         }
351 }
352
353 static void
354 event_cb (device_t *device, device_event_t event, const void *data, void *userdata)
355 {
356         const device_progress_t *progress = (device_progress_t *) data;
357         const device_devinfo_t *devinfo = (device_devinfo_t *) data;
358         const device_clock_t *clock = (device_clock_t *) data;
359         device_data_t *devdata = (device_data_t *) userdata;
360
361         switch (event) {
362         case DEVICE_EVENT_WAITING:
363                 printf("Event: waiting for user action\n");
364                 break;
365         case DEVICE_EVENT_PROGRESS:
366                 printf("Event: progress %3.2f%% (%u/%u)\n",
367                         100.0 * (double) progress->current / (double) progress->maximum,
368                         progress->current, progress->maximum);
369                 break;
370         case DEVICE_EVENT_DEVINFO:
371                 devdata->devinfo = *devinfo;
372                 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
373                         devinfo->model, devinfo->model,
374                         devinfo->firmware, devinfo->firmware,
375                         devinfo->serial, devinfo->serial);
376                 break;
377         case DEVICE_EVENT_CLOCK:
378                 devdata->clock = *clock;
379                 printf("Event: systime=%lld, devtime=%u\n",
380                         clock->systime, clock->devtime);
381                 break;
382         default:
383                 break;
384         }
385 }
386
387 static int
388 cancel_cb (void *userdata)
389 {
390         return 0;
391 }
392
393 static void do_import(const char *computer, device_type_t type)
394 {
395         /* FIXME! Needs user input! */
396         const char *devname = "/dev/ttyUSB0";
397         device_t *device = NULL;
398         device_status_t rc;
399         device_data_t devicedata = {
400                 .type = type,
401                 .name = computer,
402         };
403
404         rc = device_open(devname, type, &device);
405         if (rc != DEVICE_STATUS_SUCCESS) {
406                 error("Unable to open %s (%s)", computer, devname);
407                 return;
408         }
409
410         // Register the event handler.
411         int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
412         rc = device_set_events(device, events, event_cb, &devicedata);
413         if (rc != DEVICE_STATUS_SUCCESS) {
414                 error("Error registering the event handler.");
415                 device_close(device);
416                 return;
417         }
418
419         // Register the cancellation handler.
420         rc = device_set_cancel(device, cancel_cb, &devicedata);
421         if (rc != DEVICE_STATUS_SUCCESS) {
422                 error("Error registering the cancellation handler.");
423                 device_close(device);
424                 return;
425         }
426
427         rc = import_device_data(device, &devicedata);
428         if (rc != DEVICE_STATUS_SUCCESS) {
429                 error("Dive data import error");
430                 device_close(device);
431                 return;
432         }
433
434         device_close(device);
435 }
436
437 /*
438  * Taken from 'example.c' in libdivecomputer.
439  *
440  * I really wish there was some way to just have
441  * libdivecomputer tell us what devices it supports,
442  * rather than have the application have to know..
443  */
444 struct device_list {
445         const char *name;
446         device_type_t type;
447 } device_list[] = {
448         { "Suunto Solution",    DEVICE_TYPE_SUUNTO_SOLUTION },
449         { "Suunto Eon",         DEVICE_TYPE_SUUNTO_EON },
450         { "Suunto Vyper",       DEVICE_TYPE_SUUNTO_VYPER },
451         { "Suunto Vyper Air",   DEVICE_TYPE_SUUNTO_VYPER2 },
452         { "Suunto D9",          DEVICE_TYPE_SUUNTO_D9 },
453         { "Uwatec Aladin",      DEVICE_TYPE_UWATEC_ALADIN },
454         { "Uwatec Memo Mouse",  DEVICE_TYPE_UWATEC_MEMOMOUSE },
455         { "Uwatec Smart",       DEVICE_TYPE_UWATEC_SMART },
456         { "ReefNet Sensus",     DEVICE_TYPE_REEFNET_SENSUS },
457         { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
458         { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
459         { "Oceanic VT Pro",     DEVICE_TYPE_OCEANIC_VTPRO },
460         { "Oceanic Veo250",     DEVICE_TYPE_OCEANIC_VEO250 },
461         { "Oceanic Atom 2",     DEVICE_TYPE_OCEANIC_ATOM2 },
462         { "Mares Nemo",         DEVICE_TYPE_MARES_NEMO },
463         { "Mares Puck",         DEVICE_TYPE_MARES_PUCK },
464         { "Mares Icon HD",      DEVICE_TYPE_MARES_ICONHD },
465         { "OSTC",               DEVICE_TYPE_HW_OSTC },
466         { "Cressi Edy",         DEVICE_TYPE_CRESSI_EDY },
467         { "Zeagle N2iTiON 3",   DEVICE_TYPE_ZEAGLE_N2ITION3 },
468         { "Atomics Cobalt",     DEVICE_TYPE_ATOMICS_COBALT },
469         { NULL }
470 };
471
472 static void fill_computer_list(GtkListStore *store)
473 {
474         GtkTreeIter iter;
475         struct device_list *list = device_list;
476
477         for (list = device_list ; list->name ; list++) {
478                 gtk_list_store_append(store, &iter);
479                 gtk_list_store_set(store, &iter,
480                         0, list->name,
481                         1, list->type,
482                         -1);
483         }
484 }
485
486 static GtkComboBox *dive_computer_selector(GtkWidget *dialog)
487 {
488         GtkWidget *hbox, *combo_box;
489         GtkListStore *model;
490         GtkCellRenderer *renderer;
491
492         hbox = gtk_hbox_new(FALSE, 6);
493         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
494
495         model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
496         fill_computer_list(model);
497
498         combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
499         gtk_box_pack_start(GTK_BOX(hbox), combo_box, FALSE, TRUE, 3);
500
501         renderer = gtk_cell_renderer_text_new();
502         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE);
503         gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), renderer, "text", 0, NULL);
504
505         return GTK_COMBO_BOX(combo_box);
506 }
507
508 void import_dialog(GtkWidget *w, gpointer data)
509 {
510         int result;
511         GtkWidget *dialog;
512         GtkComboBox *computer;
513
514         dialog = gtk_dialog_new_with_buttons("Import from dive computer",
515                 GTK_WINDOW(main_window),
516                 GTK_DIALOG_DESTROY_WITH_PARENT,
517                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
518                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
519                 NULL);
520
521         computer = dive_computer_selector(dialog);
522
523         gtk_widget_show_all(dialog);
524         result = gtk_dialog_run(GTK_DIALOG(dialog));
525         switch (result) {
526                 int type;
527                 GtkTreeIter iter;
528                 GtkTreeModel *model;
529                 const char *comp;
530         case GTK_RESPONSE_ACCEPT:
531                 if (!gtk_combo_box_get_active_iter(computer, &iter))
532                         break;
533                 model = gtk_combo_box_get_model(computer);
534                 gtk_tree_model_get(model, &iter,
535                         0, &comp,
536                         1, &type,
537                         -1);
538                 do_import(comp, type);
539                 break;
540         default:
541                 break;
542         }
543         gtk_widget_destroy(dialog);
544 }
545