PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/libdivecomputer.c

http://github.com/torvalds/subsurface
C | 481 lines | 376 code | 64 blank | 41 comment | 71 complexity | 2c810eb9078d8ee23ab23578ce34130c MD5 | raw file
Possible License(s): GPL-2.0
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <unistd.h>
  4. #include <inttypes.h>
  5. #include <glib/gi18n.h>
  6. #include "dive.h"
  7. #include "divelist.h"
  8. #include "display.h"
  9. #include "display-gtk.h"
  10. #include "libdivecomputer.h"
  11. /* Christ. Libdivecomputer has the worst configuration system ever. */
  12. #ifdef HW_FROG_H
  13. #define NOT_FROG , 0
  14. #define LIBDIVECOMPUTER_SUPPORTS_FROG
  15. #else
  16. #define NOT_FROG
  17. #endif
  18. static const char *progress_bar_text = "";
  19. static double progress_bar_fraction = 0.0;
  20. static GError *error(const char *fmt, ...)
  21. {
  22. va_list args;
  23. GError *error;
  24. va_start(args, fmt);
  25. error = g_error_new_valist(
  26. g_quark_from_string("subsurface"),
  27. DIVE_ERROR_PARSE, fmt, args);
  28. va_end(args);
  29. return error;
  30. }
  31. static dc_status_t create_parser(device_data_t *devdata, dc_parser_t **parser)
  32. {
  33. return dc_parser_new(parser, devdata->device);
  34. }
  35. static int parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_parser_t *parser, int ngases)
  36. {
  37. int i;
  38. for (i = 0; i < ngases; i++) {
  39. int rc;
  40. dc_gasmix_t gasmix = {0};
  41. int o2, he;
  42. rc = dc_parser_get_field(parser, DC_FIELD_GASMIX, i, &gasmix);
  43. if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED)
  44. return rc;
  45. if (i >= MAX_CYLINDERS)
  46. continue;
  47. o2 = gasmix.oxygen * 1000 + 0.5;
  48. he = gasmix.helium * 1000 + 0.5;
  49. /* Ignore bogus data - libdivecomputer does some crazy stuff */
  50. if (o2 + he <= AIR_PERMILLE || o2 >= 1000)
  51. o2 = 0;
  52. if (he < 0 || he >= 800 || o2+he >= 1000)
  53. he = 0;
  54. dive->cylinder[i].gasmix.o2.permille = o2;
  55. dive->cylinder[i].gasmix.he.permille = he;
  56. }
  57. return DC_STATUS_SUCCESS;
  58. }
  59. static void handle_event(struct dive *dive, struct sample *sample, dc_sample_value_t value)
  60. {
  61. int type, time;
  62. /* we mark these for translation here, but we store the untranslated strings
  63. * and only translate them when they are displayed on screen */
  64. static const char *events[] = {
  65. N_("none"), N_("deco"), N_("rbt"), N_("ascent"), N_("ceiling"), N_("workload"),
  66. N_("transmitter"), N_("violation"), N_("bookmark"), N_("surface"), N_("safety stop"),
  67. N_("gaschange"), N_("safety stop (voluntary)"), N_("safety stop (mandatory)"),
  68. N_("deepstop"), N_("ceiling (safety stop)"), N_("unknown"), N_("divetime"),
  69. N_("maxdepth"), N_("OLF"), N_("PO2"), N_("airtime"), N_("rgbm"), N_("heading"),
  70. N_("tissue level warning")
  71. };
  72. const int nr_events = sizeof(events) / sizeof(const char *);
  73. const char *name;
  74. /*
  75. * Just ignore surface events. They are pointless. What "surface"
  76. * means depends on the dive computer (and possibly even settings
  77. * in the dive computer). It does *not* necessarily mean "depth 0",
  78. * so don't even turn it into that.
  79. */
  80. if (value.event.type == SAMPLE_EVENT_SURFACE)
  81. return;
  82. /*
  83. * Other evens might be more interesting, but for now we just print them out.
  84. */
  85. type = value.event.type;
  86. name = N_("invalid event number");
  87. if (type < nr_events)
  88. name = events[type];
  89. time = value.event.time;
  90. if (sample)
  91. time += sample->time.seconds;
  92. add_event(dive, time, type, value.event.flags, value.event.value, name);
  93. }
  94. void
  95. sample_cb(dc_sample_type_t type, dc_sample_value_t value, void *userdata)
  96. {
  97. int i;
  98. struct dive **divep = userdata;
  99. struct dive *dive = *divep;
  100. struct sample *sample;
  101. /*
  102. * We fill in the "previous" sample - except for DC_SAMPLE_TIME,
  103. * which creates a new one.
  104. */
  105. sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
  106. switch (type) {
  107. case DC_SAMPLE_TIME:
  108. sample = prepare_sample(divep);
  109. sample->time.seconds = value.time;
  110. finish_sample(*divep);
  111. break;
  112. case DC_SAMPLE_DEPTH:
  113. sample->depth.mm = value.depth * 1000 + 0.5;
  114. break;
  115. case DC_SAMPLE_PRESSURE:
  116. sample->cylinderindex = value.pressure.tank;
  117. sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
  118. break;
  119. case DC_SAMPLE_TEMPERATURE:
  120. sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
  121. break;
  122. case DC_SAMPLE_EVENT:
  123. handle_event(dive, sample, value);
  124. break;
  125. case DC_SAMPLE_RBT:
  126. printf(" <rbt>%u</rbt>\n", value.rbt);
  127. break;
  128. case DC_SAMPLE_HEARTBEAT:
  129. printf(" <heartbeat>%u</heartbeat>\n", value.heartbeat);
  130. break;
  131. case DC_SAMPLE_BEARING:
  132. printf(" <bearing>%u</bearing>\n", value.bearing);
  133. break;
  134. case DC_SAMPLE_VENDOR:
  135. printf(" <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
  136. for (i = 0; i < value.vendor.size; ++i)
  137. printf("%02X", ((unsigned char *) value.vendor.data)[i]);
  138. printf("</vendor>\n");
  139. break;
  140. default:
  141. break;
  142. }
  143. }
  144. static void dev_info(device_data_t *devdata, const char *fmt, ...)
  145. {
  146. static char buffer[256];
  147. va_list ap;
  148. va_start(ap, fmt);
  149. vsnprintf(buffer, sizeof(buffer), fmt, ap);
  150. va_end(ap);
  151. progress_bar_text = buffer;
  152. }
  153. static int import_dive_number = 0;
  154. static int parse_samples(device_data_t *devdata, struct dive **divep, dc_parser_t *parser)
  155. {
  156. // Parse the sample data.
  157. return dc_parser_samples_foreach(parser, sample_cb, divep);
  158. }
  159. /*
  160. * Check if this dive already existed before the import
  161. */
  162. static int find_dive(struct dive *dive, device_data_t *devdata)
  163. {
  164. int i;
  165. for (i = 0; i < dive_table.preexisting; i++) {
  166. struct dive *old = dive_table.dives[i];
  167. if (dive->when > old->when + 60)
  168. continue;
  169. if (dive->when + 60 < old->when)
  170. continue;
  171. return 1;
  172. }
  173. return 0;
  174. }
  175. static inline int year(int year)
  176. {
  177. if (year < 70)
  178. return year + 2000;
  179. if (year < 100)
  180. return year + 1900;
  181. return year;
  182. }
  183. static int dive_cb(const unsigned char *data, unsigned int size,
  184. const unsigned char *fingerprint, unsigned int fsize,
  185. void *userdata)
  186. {
  187. int rc;
  188. dc_parser_t *parser = NULL;
  189. device_data_t *devdata = userdata;
  190. dc_datetime_t dt = {0};
  191. struct tm tm;
  192. struct dive *dive;
  193. rc = create_parser(devdata, &parser);
  194. if (rc != DC_STATUS_SUCCESS) {
  195. dev_info(devdata, _("Unable to create parser for %s %s"), devdata->vendor, devdata->product);
  196. return rc;
  197. }
  198. rc = dc_parser_set_data(parser, data, size);
  199. if (rc != DC_STATUS_SUCCESS) {
  200. dev_info(devdata, _("Error registering the data"));
  201. dc_parser_destroy(parser);
  202. return rc;
  203. }
  204. import_dive_number++;
  205. dive = alloc_dive();
  206. rc = dc_parser_get_datetime(parser, &dt);
  207. if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
  208. dev_info(devdata, _("Error parsing the datetime"));
  209. dc_parser_destroy(parser);
  210. return rc;
  211. }
  212. tm.tm_year = dt.year;
  213. tm.tm_mon = dt.month-1;
  214. tm.tm_mday = dt.day;
  215. tm.tm_hour = dt.hour;
  216. tm.tm_min = dt.minute;
  217. tm.tm_sec = dt.second;
  218. dive->when = utc_mktime(&tm);
  219. // Parse the divetime.
  220. dev_info(devdata, _("Dive %d: %s %d %04d"), import_dive_number,
  221. monthname(tm.tm_mon), tm.tm_mday, year(tm.tm_year));
  222. unsigned int divetime = 0;
  223. rc = dc_parser_get_field (parser, DC_FIELD_DIVETIME, 0, &divetime);
  224. if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
  225. dev_info(devdata, _("Error parsing the divetime"));
  226. dc_parser_destroy(parser);
  227. return rc;
  228. }
  229. dive->duration.seconds = divetime;
  230. // Parse the maxdepth.
  231. double maxdepth = 0.0;
  232. rc = dc_parser_get_field(parser, DC_FIELD_MAXDEPTH, 0, &maxdepth);
  233. if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
  234. dev_info(devdata, _("Error parsing the maxdepth"));
  235. dc_parser_destroy(parser);
  236. return rc;
  237. }
  238. dive->maxdepth.mm = maxdepth * 1000 + 0.5;
  239. // Parse the gas mixes.
  240. unsigned int ngases = 0;
  241. rc = dc_parser_get_field(parser, DC_FIELD_GASMIX_COUNT, 0, &ngases);
  242. if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
  243. dev_info(devdata, _("Error parsing the gas mix count"));
  244. dc_parser_destroy(parser);
  245. return rc;
  246. }
  247. // Check if the libdivecomputer version already supports salinity
  248. double salinity = 1.03;
  249. #ifdef DC_FIELD_SALINITY
  250. rc = dc_parser_get_field(parser, DC_FIELD_SALINITY, 0, &salinity);
  251. if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
  252. dev_info(devdata, _("Error obtaining water salinity"));
  253. dc_parser_destroy(parser);
  254. return rc;
  255. }
  256. #endif
  257. dive->salinity = salinity * 10000.0 + 0.5;
  258. rc = parse_gasmixes(devdata, dive, parser, ngases);
  259. if (rc != DC_STATUS_SUCCESS) {
  260. dev_info(devdata, _("Error parsing the gas mix"));
  261. dc_parser_destroy(parser);
  262. return rc;
  263. }
  264. // Initialize the sample data.
  265. rc = parse_samples(devdata, &dive, parser);
  266. if (rc != DC_STATUS_SUCCESS) {
  267. dev_info(devdata, _("Error parsing the samples"));
  268. dc_parser_destroy(parser);
  269. return rc;
  270. }
  271. dc_parser_destroy(parser);
  272. /* If we already saw this dive, abort. */
  273. if (!devdata->force_download && find_dive(dive, devdata))
  274. return 0;
  275. dive->downloaded = TRUE;
  276. record_dive(dive);
  277. mark_divelist_changed(TRUE);
  278. return 1;
  279. }
  280. static dc_status_t import_device_data(dc_device_t *device, device_data_t *devicedata)
  281. {
  282. return dc_device_foreach(device, dive_cb, devicedata);
  283. }
  284. static void event_cb(dc_device_t *device, dc_event_type_t event, const void *data, void *userdata)
  285. {
  286. const dc_event_progress_t *progress = data;
  287. const dc_event_devinfo_t *devinfo = data;
  288. const dc_event_clock_t *clock = data;
  289. device_data_t *devdata = userdata;
  290. switch (event) {
  291. case DC_EVENT_WAITING:
  292. dev_info(devdata, _("Event: waiting for user action"));
  293. break;
  294. case DC_EVENT_PROGRESS:
  295. if (!progress->maximum)
  296. break;
  297. progress_bar_fraction = (double) progress->current / (double) progress->maximum;
  298. break;
  299. case DC_EVENT_DEVINFO:
  300. dev_info(devdata, _("model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)"),
  301. devinfo->model, devinfo->model,
  302. devinfo->firmware, devinfo->firmware,
  303. devinfo->serial, devinfo->serial);
  304. break;
  305. case DC_EVENT_CLOCK:
  306. dev_info(devdata, _("Event: systime=%"PRId64", devtime=%u\n"),
  307. (uint64_t)clock->systime, clock->devtime);
  308. break;
  309. default:
  310. break;
  311. }
  312. }
  313. static int import_thread_done = 0, import_thread_cancelled;
  314. static int
  315. cancel_cb(void *userdata)
  316. {
  317. return import_thread_cancelled;
  318. }
  319. static const char *do_device_import(device_data_t *data)
  320. {
  321. dc_status_t rc;
  322. dc_device_t *device = data->device;
  323. // Register the event handler.
  324. int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK;
  325. rc = dc_device_set_events(device, events, event_cb, data);
  326. if (rc != DC_STATUS_SUCCESS)
  327. return _("Error registering the event handler.");
  328. // Register the cancellation handler.
  329. rc = dc_device_set_cancel(device, cancel_cb, data);
  330. if (rc != DC_STATUS_SUCCESS)
  331. return _("Error registering the cancellation handler.");
  332. rc = import_device_data(device, data);
  333. if (rc != DC_STATUS_SUCCESS)
  334. return _("Dive data import error");
  335. /* All good */
  336. return NULL;
  337. }
  338. static const char *do_libdivecomputer_import(device_data_t *data)
  339. {
  340. dc_status_t rc;
  341. const char *err;
  342. import_dive_number = 0;
  343. data->device = NULL;
  344. data->context = NULL;
  345. rc = dc_context_new(&data->context);
  346. if (rc != DC_STATUS_SUCCESS)
  347. return _("Unable to create libdivecomputer context");
  348. err = _("Unable to open %s %s (%s)");
  349. rc = dc_device_open(&data->device, data->context, data->descriptor, data->devname);
  350. if (rc == DC_STATUS_SUCCESS) {
  351. err = do_device_import(data);
  352. dc_device_close(data->device);
  353. }
  354. dc_context_free(data->context);
  355. return err;
  356. }
  357. static void *pthread_wrapper(void *_data)
  358. {
  359. device_data_t *data = _data;
  360. const char *err_string = do_libdivecomputer_import(data);
  361. import_thread_done = 1;
  362. return (void *)err_string;
  363. }
  364. /* this simply ends the dialog without a response and asks not to be fired again
  365. * as we set this function up in every loop while uemis_download is waiting for
  366. * the download to finish */
  367. static gboolean timeout_func(gpointer _data)
  368. {
  369. GtkDialog *dialog = _data;
  370. if (!import_thread_cancelled)
  371. gtk_dialog_response(dialog, GTK_RESPONSE_NONE);
  372. return FALSE;
  373. }
  374. GError *do_import(device_data_t *data)
  375. {
  376. pthread_t pthread;
  377. void *retval;
  378. GtkDialog *dialog = data->dialog;
  379. /* I'm sure there is some better interface for waiting on a thread in a UI main loop */
  380. import_thread_done = 0;
  381. progress_bar_text = "";
  382. progress_bar_fraction = 0.0;
  383. pthread_create(&pthread, NULL, pthread_wrapper, data);
  384. /* loop here until the import is done or was cancelled by the user;
  385. * in order to get control back from gtk we register a timeout function
  386. * that ends the dialog with no response every 100ms; we then update the
  387. * progressbar and setup the timeout again - unless of course the user
  388. * pressed cancel, in which case we just wait for the download thread
  389. * to react to that and exit */
  390. while (!import_thread_done) {
  391. if (!import_thread_cancelled) {
  392. int result;
  393. g_timeout_add(100, timeout_func, dialog);
  394. update_progressbar(&data->progress, progress_bar_fraction);
  395. update_progressbar_text(&data->progress, progress_bar_text);
  396. result = gtk_dialog_run(dialog);
  397. switch (result) {
  398. case GTK_RESPONSE_CANCEL:
  399. import_thread_cancelled = TRUE;
  400. progress_bar_text = "Cancelled...";
  401. break;
  402. default:
  403. /* nothing */
  404. break;
  405. }
  406. } else {
  407. update_progressbar(&data->progress, progress_bar_fraction);
  408. update_progressbar_text(&data->progress, progress_bar_text);
  409. usleep(100000);
  410. }
  411. }
  412. if (pthread_join(pthread, &retval) < 0)
  413. retval = _("Odd pthread error return");
  414. if (retval)
  415. return error(retval, data->vendor, data->product, data->devname);
  416. return NULL;
  417. }