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