From b8a4730661be478d890aa3bc327d8af76ef4b86b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 25 Oct 2012 20:15:39 -0700 Subject: Expand Uemis Zurich download support With this commit we not only use the getDivelogs command but also the getDive command for each of the dives that was downloaded. Oddly, that makes quite a bit of redundant (and at times slightly contradictory) data available, but also many new things. We now get weight, suit and notes that were stored with a dive in the logbook on the divecomputer. There are a ton more data available that we don't use, yet. For example information about altitude, a decoindex, dive type and dive activity, other equipment information, etc. I still need to decide how much of this I want to make available in Subsurface (and how I want to present this - after all most of this is not available from other dive computers). Signed-off-by: Dirk Hohndel --- uemis-downloader.c | 294 ++++++++++++++++++++++++++++++++++++++++++++++++----- uemis.c | 2 +- uemis.h | 1 + 3 files changed, 270 insertions(+), 27 deletions(-) diff --git a/uemis-downloader.c b/uemis-downloader.c index 5d7881291..5267a0b00 100644 --- a/uemis-downloader.c +++ b/uemis-downloader.c @@ -29,7 +29,7 @@ #define ERR_FS_FULL N_("Uemis Zurich: File System is full\nDisconnect/reconnect the dive computer\nand try again") #define ERR_FS_SHORT_WRITE N_("Short write to req.txt file\nIs the Uemis Zurich plugged in correctly?") #define BUFLEN 2048 -#define NUM_PARAM_BUFS 6 +#define NUM_PARAM_BUFS 10 #define UEMIS_TIMEOUT 100000 static char *param_buff[NUM_PARAM_BUFS]; static char *reqtxt_path; @@ -223,19 +223,197 @@ static char *next_segment(char *buf, int *offset, int size) * The binary data block can be more than 100k in size (base64 encoded) */ static void buffer_add(char **buffer, int *buffer_size, char *buf) { - if (buf) { - if (! *buffer) { - *buffer = strdup(buf); - *buffer_size = strlen(*buffer) + 1; - } else { - *buffer_size += strlen(buf); - *buffer = realloc(*buffer, *buffer_size); - strcat(*buffer, buf); - } + if (!buf) + return; + if (! *buffer) { + *buffer = strdup(buf); + *buffer_size = strlen(*buffer) + 1; + } else { + *buffer_size += strlen(buf); + *buffer = realloc(*buffer, *buffer_size); + strcat(*buffer, buf); + } #if UEMIS_DEBUG > 5 - fprintf(debugfile,"added \"%s\" to buffer - new length %d\n", buf, *buffer_size); + fprintf(debugfile,"added \"%s\" to buffer - new length %d\n", buf, *buffer_size); #endif +} + +#define PATTERN1 "%s" +#define PATTERN2 "\n%d" +static gboolean get_tag_int_value(char *buffer, char *tag, int *value, char **next_ptr) +{ + char *ptr = buffer; + int len; + char *format; + + if (!ptr || !value) + return FALSE; + len = strlen(PATTERN1) + strlen(PATTERN2) + strlen(tag); + format = malloc(len); + snprintf(format, len, PATTERN1, tag, ""); + ptr = strstr(ptr, format); + if (!ptr) + return FALSE; + snprintf(format, len, PATTERN1, tag, PATTERN2); + if (sscanf(ptr, format, value) != 1) + return FALSE; + if (next_ptr) + *next_ptr = ptr + 1; + return TRUE; +} + +static gboolean get_tag_string_value(char *buffer, char *tag, char **value) +{ + char *eptr, *ptr = buffer; + int len; + char *format; + + if (!ptr || !value) + return FALSE; + len = strlen(PATTERN1) + strlen(tag); + format = malloc(len); + snprintf(format, len, PATTERN1, tag, ""); + ptr = strstr(ptr, format); + if (!ptr) + return FALSE; + eptr = strstr(ptr, ""); + if (!eptr) + return FALSE; + *eptr = '\0'; + *value = strdup(ptr + strlen(format) + 9); + *eptr = '<'; + return TRUE; +} + +#define PATTERN4 "\n%lf" +static gboolean get_tag_double_value(char *buffer, char *tag, double *value, char **next_ptr) +{ + char *ptr = buffer; + int len; + char *format; + + if (!ptr || !value) + return FALSE; + len = strlen(PATTERN1) + strlen(PATTERN4) + strlen(tag); + format = malloc(len); + snprintf(format, len, PATTERN1, tag, ""); + ptr = strstr(ptr, format); + if (!ptr) + return FALSE; + snprintf(format, len, PATTERN1, tag, PATTERN4); + if (sscanf(ptr, format, value) != 1) + return FALSE; + if (next_ptr) + *next_ptr = ptr + 1; + return TRUE; +} + +static char *get_xml_for_int(char *buf, char *tag) +{ + int value; + char *xml; + if (get_tag_int_value(buf, tag, &value, NULL)) { + int len = 20 + 2 * strlen(tag); + xml = malloc(len); + snprintf(xml, len, "<%s>%d\n", tag, value, tag); } + return xml; +} + +static const char *suit[] = { "", "wetsuit", "semidry", "drysuit" }; +static const char *suit_type[] = { "", "shorty", "vest", "long john", "jacket", "full suit", "2 pcs full suit" }; +static const char *suit_thickness[] = { "", "0.5-2mm", "2-3mm", "3-5mm", "5-7mm", "8mm+", "membrane" }; +static char *convert_dive_details(char *buf, uint8_t *hdr) +{ + char *conv_buffer = NULL; + int conv_size = 0; + char *ptr, *eptr; + double doublevalue; + double weight; + char *stringvalue; + char textbuf[200]; + int suit_idx, suit_type_idx, suit_thickness_idx; + + /* we want to throw away what is duplicate and clean up and + * parse the userful entries */ + ptr = strstr(buf, ""); + if (!ptr) + return NULL; + eptr = strstr(ptr + 1, "\n", weight); + buffer_add(&conv_buffer, &conv_size, textbuf); + } + if (get_tag_string_value(eptr, "notes", &stringvalue)) { + buffer_add(&conv_buffer, &conv_size, ""); + buffer_add(&conv_buffer, &conv_size, stringvalue); + buffer_add(&conv_buffer, &conv_size, "\n"); + } + if (get_tag_int_value(eptr, "u8DiveSuit", &suit_idx, NULL) && + get_tag_int_value(eptr, "u8DiveSuitType", &suit_type_idx, NULL) && + get_tag_int_value(eptr, "u8SuitThickness", &suit_thickness_idx, NULL) && + suit_idx >= 0 && suit_idx < sizeof(suit) && + suit_type_idx >= 0 && suit_type_idx < sizeof(suit_type) && + suit_thickness_idx >= 0 && suit_thickness_idx < sizeof(suit_thickness)) { + snprintf(textbuf, sizeof(textbuf), "%s %s %s\n", + suit[suit_idx], suit_type[suit_type_idx], suit_thickness[suit_thickness_idx]); + buffer_add(&conv_buffer, &conv_size, textbuf); + } + return conv_buffer; +} + +/* this is more interesting - insert the additional data in the dive + * it belongs to - no idea why Uemis splits out the dive data in this + * odd way, but we have to re-assemble it here */ +static void buffer_insert(char **buffer, int *buffer_size, char *buf) +{ + char *ptr, *endptr, *b64, *cbuf; + int obj_dive; + int obj_log; + int offset, len; + uint8_t hdr[24]; + + /* since we want to insert into the buffer... if there's + * nothing there, this makes absolutely no sense so just + * bail */ + if (!buf || !*buffer) + return; + endptr = *buffer + strlen(*buffer); + /* now figure out the object_id of buf and find the matching + * spot in buffer */ + if (!get_tag_int_value(buf, "logfilenr", &obj_dive, NULL)) + return; + ptr = *buffer; + while (ptr < endptr) { + if (!get_tag_int_value(ptr, "object_id", &obj_log, &ptr)) + return; + if (obj_dive == obj_log) + break; + } + ptr = strstr(ptr, ""); + if (!ptr) + return; + /* this is where the base64 data starts; we need to extract + * some info from that in order to make sense of the data in + * the dive info */ + b64 = strstr(ptr, "") + 5; + decode(b64, hdr, 32); + cbuf = convert_dive_details(buf, hdr); + offset = ptr - *buffer; + len = strlen(cbuf); + *buffer_size += len; + *buffer = realloc(*buffer, *buffer_size); + ptr = *buffer + offset; + memmove(ptr + len, ptr, strlen(*buffer) - offset); + memmove(ptr, cbuf, len); } /* are there more ANS files we can check? */ @@ -291,7 +469,7 @@ static gboolean uemis_get_answer(const char *path, char *request, int n_param_in 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")) { + if (! strcmp(request, "getDivelogs") || ! strcmp(request, "getDeviceData") || ! strcmp(request, "getDirectory")) { answer_in_mbuf = TRUE; str_append_with_delim(sb, ""); } @@ -414,9 +592,9 @@ static gboolean uemis_get_answer(const char *path, char *request, int n_param_in 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 +/* Turn what we get from the dive computer into something that we can + * pass to the parse_xml function. If this is a divelog, then 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) { @@ -427,34 +605,71 @@ static char *process_raw_buffer(char *inbuf, char **max_divenr) char *endptr = buf + inbuflen; char *conv_buffer = NULL; int conv_buffer_size = 0; + gboolean log = FALSE; + char *sections[10]; + int s, nr_sections = 0; bp = buf + 1; tp = next_token(&bp); - if (strcmp(tp,"divelog") != 0) - return NULL; - tp = next_token(&bp); - if (strcmp(tp,"1.0") != 0) + if (strcmp(tp, "divelog") == 0) { + /* this is a divelog */ + log = TRUE; + tp = next_token(&bp); + if (strcmp(tp,"1.0") != 0) + return NULL; + } else if (strcmp(tp, "dive") == 0) { + /* this is dive detail */ + tp = next_token(&bp); + if (strcmp(tp,"1.0") != 0) + return NULL; + } else { + /* don't understand the buffer */ return NULL; - buffer_add(&conv_buffer, &conv_buffer_size, - "\n"); + } + if (log) + buffer_add(&conv_buffer, &conv_buffer_size, + "\n"); while (!done) { char *tmp; int tmp_size; + + /* the valid buffer ends with a series of delimiters */ + if (bp >= endptr - 2 || !strcmp(bp, "{{")) + break; tag = next_token(&bp); + /* we also end if we get an empty tag */ + if (*tag == '\0') + break; + for (s = 0; s < nr_sections; s++) + if (!strcmp(tag, sections[s])) { + tag = next_token(&bp); + break; + } type = next_token(&bp); + if (!strcmp(type, "1.0")) { + /* this tells us the sections that will follow; the tag here + * is of the format dive-
*/ + sections[nr_sections] = strchr(tag, '-') + 1; +#if UEMIS_DEBUG > 2 + fprintf(debugfile, "Expect to find section %s\n", sections[nr_sections]); +#endif + if (nr_sections < sizeof(sections) - 1) + nr_sections++; + continue; + } val = next_token(&bp); - if (! strcmp(tag, "object_id")) { + if (log && ! strcmp(tag, "object_id")) { free(*max_divenr); *max_divenr = strdup(val); } if (! strcmp(tag, "file_content")) { - tmp_size = 44 + strlen(tag) + strlen(val); + tmp_size = 45 + strlen(tag) + strlen(val); done = TRUE; } else { - tmp_size = 27 + strlen(tag) + 2 * strlen(type) + strlen(val); + tmp_size = 28 + strlen(tag) + 2 * strlen(type) + strlen(val); } tmp = malloc(tmp_size); - snprintf(tmp, tmp_size,"\n<%s>%s\n\n", + snprintf(tmp, tmp_size, "\n<%s>%s\n\n", tag, type, val, type); buffer_add(&conv_buffer, &conv_buffer_size, tmp); free(tmp); @@ -468,8 +683,13 @@ static char *process_raw_buffer(char *inbuf, char **max_divenr) "\n\n"); } } - buffer_add(&conv_buffer, &conv_buffer_size, "\n"); + if (log) { + buffer_add(&conv_buffer, &conv_buffer_size, "\n"); + } free(buf); +#if UEMIS_DEBUG > 2 + fprintf(debugfile,"converted to \"%s\"\n", conv_buffer); +#endif return strdup(conv_buffer); } @@ -548,6 +768,8 @@ static char *do_uemis_download(struct argument_block *args) char **xml_buffer = args->xml_buffer; int xml_buffer_size; char *newmax = NULL; + int start, end, i; + char objectid[10]; char *deviceid = NULL; char *result = NULL; char *endptr; @@ -571,6 +793,8 @@ static char *do_uemis_download(struct argument_block *args) goto bail; param_buff[1] = "notempty"; newmax = get_divenr(*max_dive_data, deviceid); + if (sscanf(newmax, "%d", &start) != 1) + start = 0; for (;;) { param_buff[2] = newmax; param_buff[3] = 0; @@ -598,7 +822,25 @@ static char *do_uemis_download(struct argument_block *args) *(endptr + 2) = '\0'; } *args->max_dive_data = update_max_dive_data(*max_dive_data, deviceid, newmax); + if (sscanf(newmax, "%d", &end) != 1) + end = start; +#if UEMIS_DEBUG > 2 + fprintf(debugfile, "done: read from object_id %d to %d\n", start, end); +#endif free(newmax); + for (i = start; i < end; i++) { + snprintf(objectid, sizeof(objectid), "%d", i); + param_buff[2] = objectid; + param_buff[3] = 0; + success = uemis_get_answer(mountpath, "getDive", 3, 0, &result); + if (mbuf) { + char *next_detail = process_raw_buffer(mbuf, &newmax); + buffer_insert(xml_buffer, &xml_buffer_size, next_detail); + free(next_detail); + } + if (!success) + break; + } if (! uemis_get_answer(mountpath, "terminateSync", 0, 3, &result)) goto bail; if (! strcmp(param_buff[0], "error")) { diff --git a/uemis.c b/uemis.c index d4c166b88..237fd6c16 100644 --- a/uemis.c +++ b/uemis.c @@ -43,7 +43,7 @@ static void decodeblock( unsigned char in[4], unsigned char out[3] ) { /* * decode a base64 encoded stream discarding padding, line breaks and noise */ -static void decode( uint8_t *inbuf, uint8_t *outbuf, int inbuf_len ) { +void decode( uint8_t *inbuf, uint8_t *outbuf, int inbuf_len ) { uint8_t in[4], out[3], v; int i,len,indx_in=0,indx_out=0; diff --git a/uemis.h b/uemis.h index baa6783a4..4cb5c2b47 100644 --- a/uemis.h +++ b/uemis.h @@ -8,6 +8,7 @@ #include void uemis_parse_divelog_binary(char *base64, void *divep); +void decode(uint8_t *in, uint8_t *out, int len); typedef struct { uint16_t dive_time; -- cgit v1.2.3-70-g09d2