summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Dirk Hohndel <dirk@hohndel.org>2012-09-26 11:04:50 -0700
committerGravatar Dirk Hohndel <dirk@hohndel.org>2012-09-26 11:04:50 -0700
commit85925afe43ff14fe44b919dbd2bc5f4570d105a0 (patch)
tree130898128371f78859a55037f54d09b0af3de065
parentfcfc5dd57c2fe8cca49e4e2f3296c49cc9308520 (diff)
parentcb48db275e3877314c6da102990bed6c6980bd7f (diff)
downloadsubsurface-85925afe43ff14fe44b919dbd2bc5f4570d105a0.tar.gz
Merge branch 'uemis-native'
This brings in the code to download dive information directly from a Uemis Zurich dive computer. The implementation contains a major hack that hooks the uemis code into the same data structures used to setup libdivecomputer. This gives the best result for the user, but is not something that I like as a long term solution as it relies on internal libdivecomputer data structures.
-rw-r--r--Makefile5
-rw-r--r--display-gtk.h2
-rw-r--r--dive.h4
-rw-r--r--gtk-gui.c77
-rw-r--r--main.c16
-rw-r--r--uemis-downloader.c597
-rw-r--r--uemis.c3
-rw-r--r--uemis.h10
8 files changed, 707 insertions, 7 deletions
diff --git a/Makefile b/Makefile
index 94e23909a..7340a2af8 100644
--- a/Makefile
+++ b/Makefile
@@ -119,7 +119,7 @@ endif
LIBS = $(LIBXML2) $(LIBXSLT) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) $(EXTRALIBS) $(LIBZIP) -lpthread -lm
OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o \
- parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o \
+ parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \
gtk-gui.o statistics.o file.o cochran.o $(OSSUPPORT).o $(RESFILE)
$(NAME): $(OBJS)
@@ -205,6 +205,9 @@ gtk-gui.o: gtk-gui.c dive.h display.h divelist.h display-gtk.h libdivecomputer.h
uemis.o: uemis.c dive.h uemis.h
$(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) -c uemis.c
+uemis-downloader.o: uemis-downloader.c dive.h uemis.h
+ $(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) -c uemis-downloader.c
+
$(OSSUPPORT).o: $(OSSUPPORT).c display-gtk.h
$(CC) $(CFLAGS) $(OSSUPPORT_CFLAGS) -c $(OSSUPPORT).c
diff --git a/display-gtk.h b/display-gtk.h
index e4cf9b800..b75515232 100644
--- a/display-gtk.h
+++ b/display-gtk.h
@@ -98,4 +98,6 @@ typedef gint (*sort_func_t)(GtkTreeModel *model,
extern GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title,
data_func_t data_func, unsigned int flags);
+GError *uemis_download(const char *path, char **divenr, char **xml_buffer, progressbar_t *progress);
+
#endif
diff --git a/dive.h b/dive.h
index 4b00b4eba..4d736b119 100644
--- a/dive.h
+++ b/dive.h
@@ -437,4 +437,8 @@ extern const char *subsurface_default_filename(void);
#define FRACTION(n,x) ((unsigned)(n)/(x)),((unsigned)(n)%(x))
+#ifdef DEBUGFILE
+extern char *debugfilename;
+extern FILE *debugfile;
+#endif
#endif /* DIVE_H */
diff --git a/gtk-gui.c b/gtk-gui.c
index 0787fed63..8704771c7 100644
--- a/gtk-gui.c
+++ b/gtk-gui.c
@@ -14,6 +14,7 @@
#include "divelist.h"
#include "display.h"
#include "display-gtk.h"
+#include "uemis.h"
#include "libdivecomputer.h"
@@ -38,6 +39,7 @@ visible_cols_t visible_cols = {TRUE, FALSE};
static const char *default_dive_computer_vendor;
static const char *default_dive_computer_product;
static const char *default_dive_computer_device;
+static char *uemis_max_dive_data;
static int is_default_dive_computer(const char *vendor, const char *product)
{
@@ -76,6 +78,12 @@ static void set_default_dive_computer_device(const char *name)
subsurface_set_conf("dive_computer_device", PREF_STRING, name);
}
+static void set_uemis_last_dive(char *data)
+{
+ uemis_max_dive_data = data;
+ subsurface_set_conf("uemis_max_dive_data", PREF_STRING, data);
+}
+
void repaint_dive(void)
{
update_dive(current_dive);
@@ -982,6 +990,7 @@ void init_ui(int *argcp, char ***argvp)
GtkIconTheme *icon_theme=NULL;
GtkSettings *settings;
GtkUIManager *ui_manager;
+ const char *conf_value;
gtk_init(argcp, argvp);
settings = gtk_settings_get_default();
@@ -1016,7 +1025,11 @@ void init_ui(int *argcp, char ***argvp)
default_dive_computer_vendor = subsurface_get_conf("dive_computer_vendor", PREF_STRING);
default_dive_computer_product = subsurface_get_conf("dive_computer_product", PREF_STRING);
default_dive_computer_device = subsurface_get_conf("dive_computer_device", PREF_STRING);
-
+ conf_value = subsurface_get_conf("uemis_max_dive_data", PREF_STRING);
+ if (!conf_value)
+ uemis_max_dive_data = strdup("");
+ else
+ uemis_max_dive_data = strdup(conf_value);
error_info_bar = NULL;
win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_set_application_name ("subsurface");
@@ -1209,12 +1222,20 @@ int process_ui_events(void)
return ret;
}
+struct mydescriptor {
+ const char *vendor;
+ const char *product;
+ dc_family_t type;
+ unsigned int model;
+};
+
static int fill_computer_list(GtkListStore *store)
{
int index = -1, i;
GtkTreeIter iter;
dc_iterator_t *iterator = NULL;
dc_descriptor_t *descriptor = NULL;
+ struct mydescriptor *mydescriptor;
i = 0;
dc_descriptor_iterator(&iterator);
@@ -1231,6 +1252,21 @@ static int fill_computer_list(GtkListStore *store)
i++;
}
dc_iterator_free(iterator);
+ /* and add the Uemis Zurich which we are handling internally
+ THIS IS A HACK as we use the internal of libdivecomputer
+ data structures... eventually the UEMIS code needs to move
+ into libdivecomputer, I guess */
+ mydescriptor = malloc(sizeof(mydescriptor));
+ mydescriptor->vendor = "Uemis";
+ mydescriptor->product = "Zurich";
+ mydescriptor->type = DC_FAMILY_NULL;
+ mydescriptor->model = 0;
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, mydescriptor, -1);
+ if (is_default_dive_computer("Uemis", "Zurich"))
+ index = i;
+
return index;
}
@@ -1376,12 +1412,48 @@ void import_files(GtkWidget *w, gpointer data)
gtk_widget_destroy(fs_dialog);
}
+static GError *setup_uemis_import(device_data_t *data)
+{
+ GError *error = NULL;
+
+ for (;;) {
+ char *buf;
+ error = uemis_download(data->devname, &uemis_max_dive_data, &buf, &data->progress);
+ if (buf && strlen(buf) > 1) {
+#ifdef DEBUGFILE
+ fprintf(debugfile, "xml buffer \"%s\"\n\n", buf);
+#endif
+ parse_xml_buffer("Uemis Download", buf, strlen(buf), &error);
+ set_uemis_last_dive(uemis_max_dive_data);
+#if UEMIS_DEBUG
+ fprintf(debugfile, "%s\n", uemis_max_dive_data);
+#endif
+ /* this function is set up to download all the remaining dives
+ * yet this can fail in odd ways if we run out of ANS files on
+ * the dive computer (basically, its file system is only 6MB and
+ * no more than 2MB can be used for communication responses).
+ * So in order to avoid this issue we break out here as well,
+ * but once we understand how to reset the Uemis Zurich from
+ * software the following break statement should be removed */
+ break;
+ } else {
+ break;
+ }
+ }
+ return error;
+}
+
static GtkWidget *import_dive_computer(device_data_t *data, GtkDialog *dialog)
{
GError *error;
GtkWidget *vbox, *info, *container, *label, *button;
- error = do_import(data);
+ /* HACK to simply include the Uemis Zurich in the list */
+ if (! strcmp(data->vendor, "Uemis") && ! strcmp(data->product, "Zurich")) {
+ error = setup_uemis_import(data);
+ } else {
+ error = do_import(data);
+ }
if (!error)
return NULL;
@@ -1463,7 +1535,6 @@ repeat:
info = import_dive_computer(&devicedata, GTK_DIALOG(dialog));
if (info)
goto repeat;
-
report_dives(TRUE);
break;
default:
diff --git a/main.c b/main.c
index d3bd44335..95f63713d 100644
--- a/main.c
+++ b/main.c
@@ -7,6 +7,11 @@
#include "dive.h"
#include "divelist.h"
+#ifdef DEBUGFILE
+char *debugfilename;
+FILE *debugfile;
+#endif
+
struct units output_units;
/* random helper functions, used here or elsewhere */
@@ -222,6 +227,13 @@ int main(int argc, char **argv)
init_ui(&argc, &argv);
+#ifdef DEBUGFILE
+ debugfilename = (char *)subsurface_default_filename();
+ strncpy(debugfilename + strlen(debugfilename) - 3, "log", 3);
+ if (g_mkdir_with_parents(g_path_get_dirname(debugfilename), 0664) != 0 ||
+ (debugfile = g_fopen(debugfilename, "w")) == NULL)
+ printf("oh boy, can't create debugfile");
+#endif
for (i = 1; i < argc; i++) {
const char *a = argv[i];
@@ -256,5 +268,9 @@ int main(int argc, char **argv)
parse_xml_exit();
+#ifdef DEBUGFILE
+ if (debugfile)
+ fclose(debugfile);
+#endif
return 0;
}
diff --git a/uemis-downloader.c b/uemis-downloader.c
new file mode 100644
index 000000000..89ffb8e91
--- /dev/null
+++ b/uemis-downloader.c
@@ -0,0 +1,597 @@
+/*
+ * uemis-downloader.c
+ *
+ * Copyright (c) Dirk Hohndel <dirk@hohndel.org>
+ * released under GPL2
+ *
+ * very (VERY) loosely based on the algorithms found in Java code by Fabian Gast <fgast@only640k.net>
+ * which was released under the BSD-STYLE BEER WARE LICENSE
+ * I believe that I only used the information about HOW to do this (download data from the Uemis
+ * Zurich) but did not actually use any of his copyrighted code, therefore the license under which
+ * he released his code does not apply to this new implementation in C
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include "uemis.h"
+#include "dive.h"
+#include "divelist.h"
+#include "display.h"
+#include "display-gtk.h"
+
+#define BUFLEN 2048
+#define NUM_PARAM_BUFS 6
+static char *param_buff[NUM_PARAM_BUFS];
+static char *reqtxt_path;
+static int reqtxt_file;
+static int filenr;
+static int number_of_files;
+static char *mbuf = NULL;
+static int mbuf_size = 0;
+
+struct argument_block {
+ const char *mountpath;
+ char **max_dive_data;
+ char **xml_buffer;
+ progressbar_t *progress;
+};
+
+static int import_thread_done = 0, import_thread_cancelled;
+static const char *progress_bar_text = "";
+static double progress_bar_fraction = 0.0;
+
+static GError *error(const char *fmt, ...)
+{
+ va_list args;
+ GError *error;
+
+ va_start(args, fmt);
+ error = g_error_new_valist(
+ g_quark_from_string("subsurface"),
+ DIVE_ERROR_PARSE, fmt, args);
+ va_end(args);
+ return error;
+}
+
+/* send text to the importer progress bar */
+static void uemis_info(const char *fmt, ...)
+{
+ static char buffer[32];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ va_end(ap);
+ progress_bar_text = buffer;
+}
+
+static long bytes_available(int file)
+{
+ long result;
+ long now = lseek(file, 0, SEEK_CUR);
+ result = lseek(file, 0, SEEK_END);
+ lseek(file, now, SEEK_SET);
+ return result;
+}
+
+static int number_of_file(char *path)
+{
+ int count = 0;
+ GDir *dir = g_dir_open(path, 0, NULL);
+ while (g_dir_read_name(dir))
+ count++;
+ return count;
+}
+
+/* Check if there's a req.txt file and get the starting filenr from it.
+ * Test for the maximum number of ANS files (I believe this is always
+ * 4000 but in case there are differences depending on firmware, this
+ * code is easy enough */
+static gboolean uemis_init(const char *path)
+{
+ char *ans_path;
+ int i;
+
+ if (!path)
+ return FALSE;
+ /* let's check if this is indeed a Uemis DC */
+ reqtxt_path = g_build_filename(path, "/req.txt", NULL);
+ reqtxt_file = g_open(reqtxt_path, O_RDONLY, 0666);
+ if (!reqtxt_file)
+ return FALSE;
+ if (bytes_available(reqtxt_file) > 5) {
+ char tmp[6];
+ read(reqtxt_file, tmp, 5);
+ tmp[5] = '\0';
+#if UEMIS_DEBUG > 2
+ fprintf(debugfile, "::r req.txt \"%s\"\n", tmp);
+#endif
+ if (sscanf(tmp + 1, "%d", &filenr) != 1)
+ return FALSE;
+ }
+#if UEMIS_DEBUG > 2
+ else {
+ fprintf(debugfile, "::r req.txt skipped as there were fewer than 5 bytes\n");
+ }
+#endif
+ close (reqtxt_file);
+
+ /* It would be nice if we could simply go back to the first set of
+ * ANS files. Something like the below - unfortunately this is known
+ * to fail - more information from Uemis is needed here.
+ * Without code like this it is very easy when downloading large amounts
+ * of dives to run out of space on the dive computer - which can only
+ * be fixed by unmounting and rebooting the DC
+ * reqtxt_file = g_open(reqtxt_path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ * write(reqtxt_file, "n0001", 5);
+ * close(reqtxt_file);
+ * filenr = 1;
+ */
+ ans_path = g_build_filename(path, "ANS", NULL);
+ number_of_files = number_of_file(ans_path);
+ g_free(ans_path);
+ /* initialize the array in which we collect the answers */
+ for (i = 0; i < NUM_PARAM_BUFS; i++)
+ param_buff[i] = "";
+ return TRUE;
+}
+
+static void str_append_with_delim(char *s, char *t)
+{
+ int len = strlen(s);
+ snprintf(s + len, BUFLEN - len, "%s{", t);
+}
+
+/* The communication protocoll with the DC is truly funky.
+ * After you write your request to the req.txt file you call this function.
+ * It writes the number of the next ANS file at the beginning of the req.txt
+ * file (prefixed by 'n' or 'r') and then again at the very end of it, after
+ * the full request (this time without the prefix).
+ * Then it syncs (not needed on Windows) and closes the file. */
+static void trigger_response(int file, char *command, int nr, long tailpos)
+{
+ char fl[10];
+
+ snprintf(fl, 8, "%s%04d", command, nr);
+#if UEMIS_DEBUG > 2
+ fprintf(debugfile,"::: %s (after seeks)\n", fl);
+#endif
+ lseek(file, 0, SEEK_SET);
+ write(file, fl, strlen(fl));
+ lseek(file, tailpos, SEEK_SET);
+ write(file, fl + 1, strlen(fl + 1));
+#ifndef WIN32
+ fsync(file);
+#endif
+ close(file);
+}
+
+static char *next_token(char **buf)
+{
+ char *q, *p = strchr(*buf, '{');
+ *p = '\0';
+ q = *buf;
+ *buf = p + 1;
+ return q;
+}
+
+/* poor man's tokenizer that understands a quoted delimter ('{') */
+static char *next_segment(char *buf, int *offset, int size)
+{
+ int i = *offset;
+ int seg_size;
+ gboolean done = FALSE;
+ char *segment;
+
+ while (!done) {
+ if (i < size) {
+ if (buf[i] == '\\' && i < size - 1 &&
+ (buf[i+1] == '\\' || buf[i+1] == '{'))
+ memcpy(buf + i, buf + i + 1, size - i - 1);
+ else if (buf[i] == '{')
+ done = TRUE;
+ i++;
+ } else {
+ done = TRUE;
+ }
+ }
+ seg_size = i - *offset - 1;
+ if (seg_size < 0)
+ seg_size = 0;
+ segment = malloc(seg_size + 1);
+ memcpy(segment, buf + *offset, seg_size);
+ segment[seg_size] = '\0';
+ *offset = i;
+ return segment;
+}
+
+/* a dynamically growing buffer to store the potentially massive responses.
+ * The binary data block can be more than 100k in size (base64 encoded) */
+static void mbuf_add(char *buf)
+{
+ if (buf) {
+ if (!mbuf) {
+ mbuf = strdup(buf);
+ mbuf_size = strlen(mbuf) + 1;
+ } else {
+ mbuf_size += strlen(buf);
+ mbuf = realloc(mbuf, mbuf_size);
+ strcat(mbuf, buf);
+ }
+#if UEMIS_DEBUG > 5
+ fprintf(debugfile,"added \"%s\" to mbuf - new length %d\n", buf, mbuf_size);
+#endif
+ }
+}
+
+/* are there more ANS files we can check? */
+static gboolean next_file(int max)
+{
+ if (filenr >= max)
+ return FALSE;
+ filenr++;
+ return TRUE;
+}
+
+/* ultra-simplistic; it doesn't deal with the case when the object_id is
+ * split across two chunks. It also doesn't deal with the discrepancy between
+ * object_id and dive number as understood by the dive computer */
+static void show_progress(char *buf)
+{
+ char *object;
+ object = strstr(buf, "object_id");
+ if (object) {
+ /* let the user know which dive we are working on */
+ char tmp[10];
+ char *p = object + 14;
+ char *t = tmp;
+
+ if (p < buf + strlen(buf)) {
+ while (*p != '{' && t < tmp + 9)
+ *t++ = *p++;
+ *t = '\0';
+ uemis_info("Reading dive %s", tmp);
+ }
+ }
+}
+
+/* send a request to the dive computer and collect the answer */
+static gboolean uemis_get_answer(const char *path, char *request, int n_param_in,
+ int n_param_out, char **error_text)
+{
+ int i = 0, file_length;
+ char sb[BUFLEN];
+ char fl[13];
+ char tmp[100];
+ gboolean searching = TRUE;
+ gboolean assembling_mbuf = FALSE;
+ gboolean ismulti = FALSE;
+ gboolean found_answer = FALSE;
+ gboolean more_files = TRUE;
+ gboolean answer_in_mbuf = FALSE;
+ char *ans_path;
+ int ans_file;
+
+ reqtxt_file = g_open(reqtxt_path, O_RDWR | O_CREAT, 0666);
+ snprintf(sb, BUFLEN, "n%04d12345678", filenr);
+ str_append_with_delim(sb, request);
+ for (i = 0; i < n_param_in; i++)
+ str_append_with_delim(sb, param_buff[i]);
+ if (! strcmp(request, "getDivelogs") || ! strcmp(request, "getDeviceData")) {
+ answer_in_mbuf = TRUE;
+ str_append_with_delim(sb, "");
+ }
+ str_append_with_delim(sb, "");
+ file_length = strlen(sb);
+ snprintf(fl, 10, "%08d", file_length - 13);
+ memcpy(sb + 5, fl, strlen(fl));
+#ifdef UEMIS_DEBUG
+ fprintf(debugfile,"::w req.txt \"%s\"\n", sb);
+#endif
+ if (write(reqtxt_file, sb, strlen(sb)) != strlen(sb)) {
+ *error_text = "short write to req.txt file";
+ return FALSE;
+ }
+ if (! next_file(number_of_files)) {
+ *error_text = "out of files";
+ more_files = FALSE;
+ }
+ trigger_response(reqtxt_file, "n", filenr, file_length);
+ usleep(100000);
+ mbuf = NULL;
+ mbuf_size = 0;
+ while (searching || assembling_mbuf) {
+ progress_bar_fraction = filenr / 4000.0;
+ snprintf(fl, 13, "ANS%d.TXT", filenr - 1);
+ ans_path = g_build_filename(path, "ANS", fl, NULL);
+ ans_file = g_open(ans_path, O_RDONLY, 0666);
+ read(ans_file, tmp, 100);
+ close(ans_file);
+#if UEMIS_DEBUG > 3
+ fprintf(debugfile, "::t %s \"%s\"\n", ans_path, tmp);
+#endif
+ g_free(ans_path);
+ if (tmp[0] == '1') {
+ searching = FALSE;
+ if (tmp[1] == 'm') {
+ assembling_mbuf = TRUE;
+ ismulti = TRUE;
+ }
+ if (tmp[2] == 'e')
+ assembling_mbuf = FALSE;
+ if (assembling_mbuf) {
+ if (! next_file(number_of_files)) {
+ *error_text = "Out of files";
+ more_files = FALSE;
+ assembling_mbuf = FALSE;
+ }
+ reqtxt_file = g_open(reqtxt_path, O_RDWR | O_CREAT, 0666);
+ trigger_response(reqtxt_file, "n", filenr, file_length);
+ }
+ } else {
+ if (! next_file(number_of_files - 1)) {
+ *error_text = "Out of files";
+ more_files = FALSE;
+ assembling_mbuf = FALSE;
+ searching = FALSE;
+ }
+ reqtxt_file = g_open(reqtxt_path, O_RDWR | O_CREAT, 0666);
+ trigger_response(reqtxt_file, "r", filenr, file_length);
+ usleep(100000);
+ }
+ if (ismulti && more_files && tmp[0] == '1') {
+ int size;
+ snprintf(fl, 13, "ANS%d.TXT", assembling_mbuf ? filenr - 2 : filenr - 1);
+ ans_path = g_build_filename(path, "ANS", fl, NULL);
+ ans_file = g_open(ans_path, O_RDONLY, 0666);
+ size = bytes_available(ans_file);
+ if (size > 3) {
+ char *buf = malloc(size - 2);
+ lseek(ans_file, 3, SEEK_CUR);
+ read(ans_file, buf, size - 3);
+ buf[size -3 ] = '\0';
+ mbuf_add(buf);
+ show_progress(buf);
+ free(buf);
+ }
+ close(ans_file);
+ usleep(100000);
+ }
+ }
+ if (more_files) {
+ int size = 0, j = 0;
+ char *buf = NULL;
+
+ if (!ismulti) {
+ snprintf(fl, 13, "ANS%d.TXT", filenr - 1);
+ ans_path = g_build_filename(path, "ANS", fl, NULL);
+ ans_file = g_open(ans_path, O_RDONLY, 0666);
+ size = bytes_available(ans_file);
+ if (size > 3) {
+ buf = malloc(size - 2);
+ lseek(ans_file, 3, SEEK_CUR);
+ read(ans_file, buf, size - 3);
+ buf[size - 3] = '\0';
+#if UEMIS_DEBUG > 2
+ fprintf(debugfile, "::r %s \"%s\"\n", ans_path, buf);
+#endif
+ }
+ close(ans_file);
+ free(ans_path);
+ } else {
+ ismulti = FALSE;
+ }
+#if UEMIS_DEBUG > 1
+ fprintf(debugfile,":r: %s\n", buf);
+#endif
+ if (!answer_in_mbuf)
+ for (i = 0; i < n_param_out && j < size; i++)
+ param_buff[i] = next_segment(buf, &j, size);
+ found_answer = TRUE;
+ }
+#if UEMIS_DEBUG
+ for (i = 0; i < n_param_out; i++)
+ fprintf(debugfile,"::: %d: %s\n", i, paarm_buff[i]);
+#endif
+ return found_answer;
+}
+
+/* Turn what we get from the dive computer into something
+ * that we can pass to the parse_xml function.
+ * The last 'object_id' that we see is returned as our current
+ * approximation of a last dive number */
+static char *process_raw_buffer(char *inbuf, char **max_divenr)
+{
+ /* we'll just reuse the mbuf infrastructure to assemble the xml buffer */
+ char *buf = strdup(inbuf);
+ char *tp, *bp, *tag, *type, *val;
+ gboolean done = FALSE;
+ int inbuflen = strlen(inbuf);
+ char *endptr = buf + inbuflen;
+
+ mbuf = NULL;
+ mbuf_size = 0;
+ mbuf_add("<dives type='uemis'><string></string>\n<list>\n");
+ bp = buf + 1;
+ tp = next_token(&bp);
+ if (strcmp(tp,"divelog") != 0)
+ return NULL;
+ tp = next_token(&bp);
+ if (strcmp(tp,"1.0") != 0)
+ return NULL;
+ mbuf_add("<dive type=\"uemis\" ref=\"divelog\" version=\"1.0\">\n");
+ while (!done) {
+ char *tmp;
+ int tmp_size;
+ tag = next_token(&bp);
+ type = next_token(&bp);
+ val = next_token(&bp);
+ if (! strcmp(tag, "object_id")) {
+ free(*max_divenr);
+ *max_divenr = strdup(val);
+ }
+ if (! strcmp(tag, "file_content")) {
+ tmp_size = 44 + strlen(tag) + strlen(val);
+ done = TRUE;
+ } else {
+ tmp_size = 27 + strlen(tag) + 2 * strlen(type) + strlen(val);
+ }
+ tmp = malloc(tmp_size);
+ snprintf(tmp, tmp_size,"<val key=\"%s\">\n<%s>%s</%s>\n</val>\n",
+ tag, type, val, type);
+ mbuf_add(tmp);
+ free(tmp);
+ /* done with one dive (got the file_content tag), but there could be more */
+ if (done && ++bp < endptr && *bp != '{') {
+ done = FALSE;
+ mbuf_add("</dive>\n");
+ mbuf_add("<dive type=\"uemis\" ref=\"divelog\" version=\"1.0\">\n");
+ }
+ }
+ mbuf_add("</dive>\n</list></dives>");
+ free(buf);
+ return strdup(mbuf);
+}
+
+/* to keep track of multiple computers we simply encode the last dive read
+ in tuples "{deviceid,nr},{deviceid,nr}..." no spaces to make parsing easier */
+
+static char *find_deviceid(char *max_dive_data, char *deviceid)
+{
+ char *pattern;
+ char *result;
+ if (! deviceid || *deviceid == '\0')
+ return NULL;
+ pattern = malloc(3 + strlen(deviceid));
+ sprintf(pattern, "{%s,", deviceid);
+ result = strstr(max_dive_data, pattern);
+ free(pattern);
+ return result;
+}
+
+static char *get_divenr(char *max_dive_data, char *deviceid)
+{
+ char *q, *p = max_dive_data;
+ char *result = "0";
+
+ if (!p || !deviceid)
+ return result;
+ p = find_deviceid(max_dive_data, deviceid);
+ if (p) {
+ p += strlen(deviceid) + 2;
+ q = strchr(p, '}');
+ if (!q)
+ return result;
+ result = malloc(q - p + 1);
+ strncpy(result, p, q - p);
+ result[q - p] = '\0';
+ }
+ return result;
+}
+
+static char *update_max_dive_data(char *max_dive_data, char *deviceid, char *newmax)
+{
+ char *p;
+ char *result;
+ int len;
+
+ if (! newmax || *newmax == '\0')
+ return max_dive_data;
+ p = find_deviceid(max_dive_data, deviceid);
+ if (p) {
+ /* if there are more entries after this one, copy them,
+ otherwise just remove the existing entry for this device */
+ char *q = strstr(p, "},{");
+ if (q) {
+ memcpy(p + 1, q + 3, strlen(q + 3) + 1);
+ } else {
+ if (p > max_dive_data)
+ *(p-1) = '\0';
+ else
+ *p = '\0';
+ }
+ }
+ /* now add the new one at the end */
+ len = strlen(max_dive_data) + strlen(deviceid) + strlen(newmax) + 4 + (strlen(max_dive_data) ? 1 : 0);
+ result = malloc(len);
+ snprintf(result, len, "%s%s{%s,%s}", max_dive_data, strlen(max_dive_data) ? "," : "", deviceid, newmax);
+ free(max_dive_data);
+ return result;
+}
+
+static char *do_uemis_download(struct argument_block *args)
+{
+ const char *mountpath = args->mountpath;
+ char **max_dive_data = args->max_dive_data;
+ char **xml_buffer = args->xml_buffer;
+ char *error_text = "";
+ char *newmax = NULL;
+ char *deviceid;
+
+ *xml_buffer = NULL;
+ uemis_info("Init Communication");
+ if (! uemis_init(mountpath))
+ return "Uemis init failed";
+ if (! uemis_get_answer(mountpath, "getDeviceId", 0, 1, &error_text))
+ return error_text;
+ deviceid = strdup(param_buff[0]);
+ /* the answer from the DeviceId call becomes the input parameter for getDeviceData */
+ if (! uemis_get_answer(mountpath, "getDeviceData", 1, 0, &error_text))
+ return error_text;
+ /* param_buff[0] is still valid */
+ if (! uemis_get_answer(mountpath, "initSession", 1, 6, &error_text))
+ return error_text;
+ uemis_info("Start download");
+ if (! uemis_get_answer(mountpath, "processSync", 0, 2, &error_text))
+ return error_text;
+ param_buff[1] = "notempty";
+ param_buff[2] = get_divenr(*max_dive_data, deviceid);
+ if (uemis_get_answer(mountpath, "getDivelogs", 3, 0, &error_text)) {
+ if (mbuf)
+ *xml_buffer = process_raw_buffer(mbuf, &newmax);
+ } else {
+ return error_text;
+ }
+ *args->max_dive_data = update_max_dive_data(*max_dive_data, deviceid, newmax);
+ free(newmax);
+ if (! uemis_get_answer(mountpath, "terminateSync", 0, 1, &error_text))
+ return error_text;
+
+ return NULL;
+}
+
+static void *pthread_wrapper(void *_data)
+{
+ struct argument_block *args = _data;
+ const char *err_string = do_uemis_download(args);
+ import_thread_done = 1;
+ return (void *)err_string;
+}
+
+GError *uemis_download(const char *mountpath, char **max_dive_data, char **xml_buffer, progressbar_t *progress)
+{
+ pthread_t pthread;
+ void *retval;
+ struct argument_block args = {mountpath, max_dive_data, xml_buffer, progress};
+
+ /* I'm sure there is some better interface for waiting on a thread in a UI main loop */
+ import_thread_done = 0;
+ progress_bar_text = "";
+ progress_bar_fraction = 0.0;
+ pthread_create(&pthread, NULL, pthread_wrapper, &args);
+ while (!import_thread_done) {
+ import_thread_cancelled = process_ui_events();
+ update_progressbar(args.progress, progress_bar_fraction);
+ update_progressbar_text(args.progress, progress_bar_text);
+ usleep(100000);
+ }
+ if (pthread_join(pthread, &retval) < 0)
+ return error("Pthread return with error");
+ if (retval)
+ return error(retval);
+ return NULL;
+}
diff --git a/uemis.c b/uemis.c
index 06ef5b423..557c419f3 100644
--- a/uemis.c
+++ b/uemis.c
@@ -229,7 +229,8 @@ void uemis_parse_divelog_binary(char *base64, void *datap) {
sample->depth.mm = pressure_to_depth(u_sample->water_pressure);
sample->temperature.mkelvin = (u_sample->dive_temperature * 100) + 273150;
sample->cylinderindex = u_sample->active_tank;
- sample->cylinderpressure.mbar= u_sample->tank_pressure * 10;
+ sample->cylinderpressure.mbar =
+ (u_sample->tank_pressure_high * 256 + u_sample->tank_pressure_low) * 10;
uemis_event(dive, sample, u_sample);
finish_sample(dive);
i += 0x25;
diff --git a/uemis.h b/uemis.h
index ba9a23402..baa6783a4 100644
--- a/uemis.h
+++ b/uemis.h
@@ -24,8 +24,14 @@ typedef struct {
uint16_t hold_depth;
uint16_t hold_time;
uint8_t active_tank;
- uint16_t tank_pressure; // (in cbar)
- uint16_t consumption; // (units unclear)
+ // bloody glib, when compiled for Windows, forces the whole program to use
+ // the Windows packing rules. So to avoid problems on Windows (and since
+ // only tank_pressure is currently used and that exactly once) I give in and
+ // make this silly low byte / high byte 8bit entries
+ uint8_t tank_pressure_low; // (in cbar)
+ uint8_t tank_pressure_high;
+ uint8_t consumption_low; // (units unclear)
+ uint8_t consumption_high;
uint8_t rgt; // (remaining gas time in minutes)
uint8_t cns;
uint8_t flags[8];