diff options
author | Dirk Hohndel <dirk@hohndel.org> | 2012-09-26 11:04:50 -0700 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2012-09-26 11:04:50 -0700 |
commit | 85925afe43ff14fe44b919dbd2bc5f4570d105a0 (patch) | |
tree | 130898128371f78859a55037f54d09b0af3de065 | |
parent | fcfc5dd57c2fe8cca49e4e2f3296c49cc9308520 (diff) | |
parent | cb48db275e3877314c6da102990bed6c6980bd7f (diff) | |
download | subsurface-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-- | Makefile | 5 | ||||
-rw-r--r-- | display-gtk.h | 2 | ||||
-rw-r--r-- | dive.h | 4 | ||||
-rw-r--r-- | gtk-gui.c | 77 | ||||
-rw-r--r-- | main.c | 16 | ||||
-rw-r--r-- | uemis-downloader.c | 597 | ||||
-rw-r--r-- | uemis.c | 3 | ||||
-rw-r--r-- | uemis.h | 10 |
8 files changed, 707 insertions, 7 deletions
@@ -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 @@ -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 */ @@ -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: @@ -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; +} @@ -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; @@ -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]; |