]> git.tdb.fi Git - ext/subsurface.git/blob - libdivecomputer.c
da9b35bd3c04dd7ff321dd6ac568884f9e81046a
[ext/subsurface.git] / libdivecomputer.c
1 #include <stdio.h>
2 #include <gtk/gtk.h>
3
4 #include "dive.h"
5 #include "divelist.h"
6 #include "display.h"
7
8 /* libdivecomputer */
9 #include <device.h>
10 #include <suunto.h>
11 #include <reefnet.h>
12 #include <uwatec.h>
13 #include <oceanic.h>
14 #include <mares.h>
15 #include <hw.h>
16 #include <cressi.h>
17 #include <zeagle.h>
18 #include <atomics.h>
19 #include <utils.h>
20
21 /*
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.
24  *
25  * I may need to spawn a new thread to do the computer
26  * reading stuff..
27  */
28 static int run_gtk_mainloop(void)
29 {
30         return gtk_main_iteration_do(0);
31 }
32
33 static void error(const char *fmt, ...)
34 {
35         va_list args;
36         GError *error;
37
38         va_start(args, fmt);
39         error = g_error_new_valist(
40                 g_quark_from_string("divelog"),
41                 DIVE_ERROR_PARSE, fmt, args);
42         va_end(args);
43         report_error(error);
44         g_error_free(error);
45 }
46
47 typedef struct device_data_t {
48         device_type_t type;
49         const char *name, *devname;
50         GtkWidget *progressbar;
51         device_devinfo_t devinfo;
52         device_clock_t clock;
53 } device_data_t;
54
55 static parser_status_t create_parser(device_data_t *devdata, parser_t **parser)
56 {
57         switch (devdata->type) {
58         case DEVICE_TYPE_SUUNTO_SOLUTION:
59                 return suunto_solution_parser_create(parser);
60
61         case DEVICE_TYPE_SUUNTO_EON:
62                 return suunto_eon_parser_create(parser, 0);
63
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);
68
69         case DEVICE_TYPE_SUUNTO_VYPER2:
70         case DEVICE_TYPE_SUUNTO_D9:
71                 return suunto_d9_parser_create(parser, devdata->devinfo.model);
72
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);
76
77         case DEVICE_TYPE_UWATEC_SMART:
78                 return uwatec_smart_parser_create(parser, devdata->devinfo.model, devdata->clock.devtime, devdata->clock.systime);
79
80         case DEVICE_TYPE_REEFNET_SENSUS:
81                 return reefnet_sensus_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
82
83         case DEVICE_TYPE_REEFNET_SENSUSPRO:
84                 return reefnet_sensuspro_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
85
86         case DEVICE_TYPE_REEFNET_SENSUSULTRA:
87                 return reefnet_sensusultra_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
88
89         case DEVICE_TYPE_OCEANIC_VTPRO:
90                 return oceanic_vtpro_parser_create(parser);
91
92         case DEVICE_TYPE_OCEANIC_VEO250:
93                 return oceanic_veo250_parser_create(parser, devdata->devinfo.model);
94
95         case DEVICE_TYPE_OCEANIC_ATOM2:
96                 return oceanic_atom2_parser_create(parser, devdata->devinfo.model);
97
98         case DEVICE_TYPE_MARES_NEMO:
99         case DEVICE_TYPE_MARES_PUCK:
100                 return mares_nemo_parser_create(parser, devdata->devinfo.model);
101
102         case DEVICE_TYPE_MARES_ICONHD:
103                 return mares_iconhd_parser_create(parser);
104
105         case DEVICE_TYPE_HW_OSTC:
106                 return hw_ostc_parser_create(parser);
107
108         case DEVICE_TYPE_CRESSI_EDY:
109         case DEVICE_TYPE_ZEAGLE_N2ITION3:
110                 return cressi_edy_parser_create(parser, devdata->devinfo.model);
111
112         case DEVICE_TYPE_ATOMICS_COBALT:
113                 return atomics_cobalt_parser_create(parser);
114
115         default:
116                 return PARSER_STATUS_ERROR;
117         }
118 }
119
120 static int parse_gasmixes(struct dive *dive, parser_t *parser, int ngases)
121 {
122         int i;
123
124         for (i = 0; i < ngases; i++) {
125                 int rc;
126                 gasmix_t gasmix = {0};
127
128                 rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
129                 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
130                         return rc;
131
132                 if (i >= MAX_CYLINDERS)
133                         continue;
134
135                 dive->cylinder[i].gasmix.o2.permille = gasmix.oxygen * 1000 + 0.5;
136                 dive->cylinder[i].gasmix.he.permille = gasmix.helium * 1000 + 0.5;
137         }
138         return PARSER_STATUS_SUCCESS;
139 }
140
141 void
142 sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
143 {
144         int i;
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;
154
155         /*
156          * We fill in the "previous" sample - except for SAMPLE_TYPE_TIME,
157          * which creates a new one.
158          */
159         sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
160
161         switch (type) {
162         case SAMPLE_TYPE_TIME:
163                 sample = prepare_sample(divep);
164                 sample->time.seconds = value.time;
165                 finish_sample(*divep, sample);
166                 break;
167         case SAMPLE_TYPE_DEPTH:
168                 sample->depth.mm = value.depth * 1000 + 0.5;
169                 break;
170         case SAMPLE_TYPE_PRESSURE:
171                 sample->cylinderindex = value.pressure.tank;
172                 sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
173                 break;
174         case SAMPLE_TYPE_TEMPERATURE:
175                 sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
176                 break;
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]);
180                 break;
181         case SAMPLE_TYPE_RBT:
182                 printf("   <rbt>%u</rbt>\n", value.rbt);
183                 break;
184         case SAMPLE_TYPE_HEARTBEAT:
185                 printf("   <heartbeat>%u</heartbeat>\n", value.heartbeat);
186                 break;
187         case SAMPLE_TYPE_BEARING:
188                 printf("   <bearing>%u</bearing>\n", value.bearing);
189                 break;
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");
195                 break;
196         default:
197                 break;
198         }
199 }
200
201
202 static int parse_samples(struct dive **divep, parser_t *parser)
203 {
204         // Parse the sample data.
205         printf("Parsing the sample data.\n");
206         return parser_samples_foreach(parser, sample_cb, divep);
207 }
208
209 static int dive_cb(const unsigned char *data, unsigned int size,
210         const unsigned char *fingerprint, unsigned int fsize,
211         void *userdata)
212 {
213         int rc;
214         parser_t *parser = NULL;
215         device_data_t *devdata = userdata;
216         dc_datetime_t dt = {0};
217         struct tm tm;
218         struct dive *dive;
219
220         /* Christ, this is hacky */
221         run_gtk_mainloop();
222
223         rc = create_parser(devdata, &parser);
224         if (rc != PARSER_STATUS_SUCCESS) {
225                 error("Unable to create parser for %s", devdata->name);
226                 return rc;
227         }
228
229         rc = parser_set_data(parser, data, size);
230         if (rc != PARSER_STATUS_SUCCESS) {
231                 error("Error registering the data.");
232                 parser_destroy(parser);
233                 return rc;
234         }
235
236         dive = alloc_dive();
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);
241                 return rc;
242         }
243
244         tm.tm_year = dt.year;
245         tm.tm_mon = dt.month-1;
246         tm.tm_mday = dt.day;
247         tm.tm_hour = dt.hour;
248         tm.tm_min = dt.minute;
249         tm.tm_sec = dt.second;
250         dive->when = utc_mktime(&tm);
251
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);
259                 return rc;
260         }
261         dive->duration.seconds = divetime;
262
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);
270                 return rc;
271         }
272         dive->maxdepth.mm = maxdepth * 1000 + 0.5;
273
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);
281                 return rc;
282         }
283
284         rc = parse_gasmixes(dive, parser, ngases);
285         if (rc != PARSER_STATUS_SUCCESS) {
286                 error("Error parsing the gas mix.");
287                 parser_destroy(parser);
288                 return rc;
289         }
290
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);
296                 return rc;
297         }
298         record_dive(dive);
299
300         parser_destroy(parser);
301         return 1;
302 }
303
304
305 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
306 {
307         return device_foreach(device, dive_cb, devicedata);
308 }
309
310 static device_status_t device_open(const char *devname,
311         device_type_t type,
312         device_t **device)
313 {
314         switch (type) {
315         case DEVICE_TYPE_SUUNTO_SOLUTION:
316                 return suunto_solution_device_open(device, devname);
317
318         case DEVICE_TYPE_SUUNTO_EON:
319                 return suunto_eon_device_open(device, devname);
320
321         case DEVICE_TYPE_SUUNTO_VYPER:
322                 return suunto_vyper_device_open(device, devname);
323
324         case DEVICE_TYPE_SUUNTO_VYPER2:
325                 return suunto_vyper2_device_open(device, devname);
326
327         case DEVICE_TYPE_SUUNTO_D9:
328                 return suunto_d9_device_open(device, devname);
329
330         case DEVICE_TYPE_UWATEC_ALADIN:
331                 return uwatec_aladin_device_open(device, devname);
332
333         case DEVICE_TYPE_UWATEC_MEMOMOUSE:
334                 return uwatec_memomouse_device_open(device, devname);
335
336         case DEVICE_TYPE_UWATEC_SMART:
337                 return uwatec_smart_device_open(device);
338
339         case DEVICE_TYPE_REEFNET_SENSUS:
340                 return reefnet_sensus_device_open(device, devname);
341
342         case DEVICE_TYPE_REEFNET_SENSUSPRO:
343                 return reefnet_sensuspro_device_open(device, devname);
344
345         case DEVICE_TYPE_REEFNET_SENSUSULTRA:
346                 return reefnet_sensusultra_device_open(device, devname);
347
348         case DEVICE_TYPE_OCEANIC_VTPRO:
349                 return oceanic_vtpro_device_open(device, devname);
350
351         case DEVICE_TYPE_OCEANIC_VEO250:
352                 return oceanic_veo250_device_open(device, devname);
353
354         case DEVICE_TYPE_OCEANIC_ATOM2:
355                 return oceanic_atom2_device_open(device, devname);
356
357         case DEVICE_TYPE_MARES_NEMO:
358                 return mares_nemo_device_open(device, devname);
359
360         case DEVICE_TYPE_MARES_PUCK:
361                 return mares_puck_device_open(device, devname);
362
363         case DEVICE_TYPE_MARES_ICONHD:
364                 return mares_iconhd_device_open(device, devname);
365
366         case DEVICE_TYPE_HW_OSTC:
367                 return hw_ostc_device_open(device, devname);
368
369         case DEVICE_TYPE_CRESSI_EDY:
370                 return cressi_edy_device_open(device, devname);
371
372         case DEVICE_TYPE_ZEAGLE_N2ITION3:
373                 return zeagle_n2ition3_device_open(device, devname);
374
375         case DEVICE_TYPE_ATOMICS_COBALT:
376                 return atomics_cobalt_device_open(device);
377
378         default:
379                 return DEVICE_STATUS_ERROR;
380         }
381 }
382
383 static void
384 event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
385 {
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;
390
391         /* Christ, this is hacky */
392         run_gtk_mainloop();
393
394         switch (event) {
395         case DEVICE_EVENT_WAITING:
396                 printf("Event: waiting for user action\n");
397                 break;
398         case DEVICE_EVENT_PROGRESS:
399                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(devdata->progressbar),
400                         (double) progress->current / (double) progress->maximum);
401                 break;
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);
408                 break;
409         case DEVICE_EVENT_CLOCK:
410                 devdata->clock = *clock;
411                 printf("Event: systime=%lld, devtime=%u\n",
412                         clock->systime, clock->devtime);
413                 break;
414         default:
415                 break;
416         }
417 }
418
419 static int
420 cancel_cb(void *userdata)
421 {
422         return run_gtk_mainloop();
423 }
424
425 static void do_import(device_data_t *data)
426 {
427         /* FIXME! Needs user input! */
428         const char *devname = "/dev/ttyUSB0";
429         device_t *device = NULL;
430         device_status_t rc;
431
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);
435                 return;
436         }
437
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);
444                 return;
445         }
446
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);
452                 return;
453         }
454
455         rc = import_device_data(device, data);
456         if (rc != DEVICE_STATUS_SUCCESS) {
457                 error("Dive data import error");
458                 device_close(device);
459                 return;
460         }
461
462         device_close(device);
463 }
464
465 /*
466  * Taken from 'example.c' in libdivecomputer.
467  *
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..
471  */
472 struct device_list {
473         const char *name;
474         device_type_t type;
475 } device_list[] = {
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 },
497         { NULL }
498 };
499
500 static void fill_computer_list(GtkListStore *store)
501 {
502         GtkTreeIter iter;
503         struct device_list *list = device_list;
504
505         for (list = device_list ; list->name ; list++) {
506                 gtk_list_store_append(store, &iter);
507                 gtk_list_store_set(store, &iter,
508                         0, list->name,
509                         1, list->type,
510                         -1);
511         }
512 }
513
514 static GtkComboBox *dive_computer_selector(GtkWidget *dialog)
515 {
516         GtkWidget *hbox, *combo_box;
517         GtkListStore *model;
518         GtkCellRenderer *renderer;
519
520         hbox = gtk_hbox_new(FALSE, 6);
521         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 3);
522
523         model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
524         fill_computer_list(model);
525
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);
528
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);
532
533         return GTK_COMBO_BOX(combo_box);
534 }
535
536 void import_dialog(GtkWidget *w, gpointer data)
537 {
538         int result;
539         GtkWidget *dialog, *hbox;
540         GtkComboBox *computer;
541         device_data_t devicedata = {
542                 .devname = "/dev/ttyUSB0",
543         };
544
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,
550                 NULL);
551
552         computer = dive_computer_selector(dialog);
553
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);
558
559         gtk_widget_show_all(dialog);
560         result = gtk_dialog_run(GTK_DIALOG(dialog));
561         switch (result) {
562                 int type;
563                 GtkTreeIter iter;
564                 GtkTreeModel *model;
565                 const char *comp;
566         case GTK_RESPONSE_ACCEPT:
567                 if (!gtk_combo_box_get_active_iter(computer, &iter))
568                         break;
569                 model = gtk_combo_box_get_model(computer);
570                 gtk_tree_model_get(model, &iter,
571                         0, &comp,
572                         1, &type,
573                         -1);
574                 devicedata.type = type;
575                 devicedata.name = comp;
576                 do_import(&devicedata);
577                 break;
578         default:
579                 break;
580         }
581         gtk_widget_destroy(dialog);
582
583         report_dives();
584         dive_list_update_dives(dive_list);
585 }