diff options
author | Dirk Hohndel <dirk@hohndel.org> | 2012-10-25 20:15:39 -0700 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2012-10-25 20:15:39 -0700 |
commit | b8a4730661be478d890aa3bc327d8af76ef4b86b (patch) | |
tree | 291599cd22c1c6b02308f1bd8fb7956e2cbf170a | |
parent | d5ac38d9edce323a46cb1b757e479cce5379675a (diff) | |
download | subsurface-b8a4730661be478d890aa3bc327d8af76ef4b86b.tar.gz |
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 <dirk@hohndel.org>
-rw-r--r-- | uemis-downloader.c | 294 | ||||
-rw-r--r-- | uemis.c | 2 | ||||
-rw-r--r-- | 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 "<val key=\"%s\">%s" +#define PATTERN2 "\n<int>%d</int>" +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, "</string>"); + if (!eptr) + return FALSE; + *eptr = '\0'; + *value = strdup(ptr + strlen(format) + 9); + *eptr = '<'; + return TRUE; +} + +#define PATTERN4 "\n<float>%lf</float>" +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</%s>\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, "<val key=\"logfilenr\">"); + if (!ptr) + return NULL; + eptr = strstr(ptr + 1, "<val"); + *eptr = '\0'; + buffer_add(&conv_buffer, &conv_size, ptr); + *eptr = '<'; + buffer_add(&conv_buffer, &conv_size, get_xml_for_int(eptr, "altitude")); + buffer_add(&conv_buffer, &conv_size, get_xml_for_int(eptr, "decoindex")); + buffer_add(&conv_buffer, &conv_size, get_xml_for_int(eptr, "consumption")); + if (get_tag_double_value(eptr, "f32Weight", &doublevalue, NULL)) { + weight = hdr[24] ? lbs_to_grams(doublevalue) / 1000.0 : doublevalue; + snprintf(textbuf, sizeof(textbuf), + "<weightsystem weight='%.3lf' description='unknown' />\n", weight); + buffer_add(&conv_buffer, &conv_size, textbuf); + } + if (get_tag_string_value(eptr, "notes", &stringvalue)) { + buffer_add(&conv_buffer, &conv_size, "<notes>"); + buffer_add(&conv_buffer, &conv_size, stringvalue); + buffer_add(&conv_buffer, &conv_size, "</notes>\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), "<suit>%s %s %s</suit>\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, "<val key=\"file_content\">"); + 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, "<bin>") + 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, - "<dive type=\"uemis\" ref=\"divelog\" version=\"1.0\">\n"); + } + if (log) + buffer_add(&conv_buffer, &conv_buffer_size, + "<dive type=\"uemis\" ref=\"divelog\" version=\"1.0\">\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-<section> */ + 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,"<val key=\"%s\">\n<%s>%s</%s>\n</val>\n", + snprintf(tmp, tmp_size, "<val key=\"%s\">\n<%s>%s</%s>\n</val>\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) "</dive>\n<dive type=\"uemis\" ref=\"divelog\" version=\"1.0\">\n"); } } - buffer_add(&conv_buffer, &conv_buffer_size, "</dive>\n"); + if (log) { + buffer_add(&conv_buffer, &conv_buffer_size, "</dive>\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")) { @@ -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; @@ -8,6 +8,7 @@ #include <stdint.h> 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; |