From da8ae52b50111dca4bd89e92b5e330225eb976a5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 24 Jun 2020 17:09:34 -0700 Subject: [PATCH] Oceans S1: actually download all dives and parse them This isn't perfect - we don't do the whole dive fingerprint etc, so right now it always downloads all dives. To make matters worse, it downloads dives oldest first, which then confuses the subsurface downloader that expects newest first. So there's stuff to clean up, but the basic profile data is all there. Signed-off-by: Linus Torvalds --- src/oceans_s1.c | 107 +++++++++++++++++++++++++++---- src/oceans_s1_parser.c | 142 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 227 insertions(+), 22 deletions(-) diff --git a/src/oceans_s1.c b/src/oceans_s1.c index 3cd7496..7e126a2 100644 --- a/src/oceans_s1.c +++ b/src/oceans_s1.c @@ -167,7 +167,7 @@ static dc_status_t oceans_s1_get_sequence(oceans_s1_device_t *s1, unsigned char return DC_STATUS_SUCCESS; } -static dc_status_t oceans_s1_get_blob(oceans_s1_device_t *s1, unsigned char **result) +static dc_status_t oceans_s1_get_blob(oceans_s1_device_t *s1, const unsigned char **result) { dc_status_t status; dc_buffer_t *res; @@ -441,7 +441,7 @@ oceans_s1_device_set_fingerprint(dc_device_t *abstract, const unsigned char data } static dc_status_t -get_dive_list(oceans_s1_device_t *s1, unsigned char **list) +get_dive_list(oceans_s1_device_t *s1, const unsigned char **list) { dc_status_t status; @@ -457,7 +457,7 @@ get_dive_list(oceans_s1_device_t *s1, unsigned char **list) } static dc_status_t -get_one_dive(oceans_s1_device_t *s1, int nr, unsigned char **dive) +get_one_dive(oceans_s1_device_t *s1, int nr, const unsigned char **dive) { dc_status_t status; @@ -472,10 +472,52 @@ get_one_dive(oceans_s1_device_t *s1, int nr, unsigned char **dive) return oceans_s1_get_blob(s1, dive); } +static const unsigned char *get_string_line(const unsigned char *in, const unsigned char **next) +{ + const unsigned char *line; + unsigned char c; + + if (!in) { + *next = NULL; + return NULL; + } + + while (isspace(*in)) + in++; + + if (!*in) { + *next = NULL; + return NULL; + } + + line = in; + while ((c = *in) != 0) { + if (c == '\r' || c == '\n') + break; + in++; + } + *next = in; + return line; +} + +static int count_dives(const unsigned char *divelist) +{ + int dives = 0; + const unsigned char *line; + + while ((line = get_string_line(divelist, &divelist)) != NULL) { + if (strncmp(line, "dive ", 5)) + continue; + dives++; + } + return dives; +} + static dc_status_t oceans_s1_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { - unsigned char *divelist, *dive; + int nr; + const unsigned char *divelist, *line; dc_status_t status = DC_STATUS_SUCCESS; oceans_s1_device_t *s1 = (oceans_s1_device_t*)abstract; @@ -485,19 +527,60 @@ oceans_s1_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback, voi status = get_dive_list(s1, &divelist); if (status != DC_STATUS_SUCCESS) return status; - fprintf(stderr, "divelist = %s\n", divelist); + + nr = count_dives(divelist); + if (!nr) + return DC_STATUS_SUCCESS; progress.current = 0; - progress.maximum = 100; + progress.maximum = nr; device_event_emit(abstract, DC_EVENT_PROGRESS, &progress); - // Just force dive 4 for now - status = get_one_dive(s1, 4, &dive); - if (status != DC_STATUS_SUCCESS) - return status; - fprintf(stderr, "dive 4 = %s\n", dive); + int dive_nr = 0, dive_unknown = 0, dive_o2 = 0; + int dive_depth, dive_time; + long long dive_date = -1; + char fingerprint[32]; - // Fill in + while ((line = get_string_line(divelist, &divelist)) != NULL) { + int linelen = divelist - line; + const unsigned char *dive; + + /* We only care about 'dive' and 'enddive' lines */ + if (linelen < 8 || linelen >= 32) + continue; + + if (!memcmp(line, "dive ", 5)) { + int nr, unknown, o2; + long long date; + + if (sscanf(line, "dive %d,%d,%d,%lld", &nr, &unknown, &o2, &date) != 4) + continue; + dive_nr = nr; + dive_unknown = unknown; + dive_o2 = o2; + dive_date = date; + + memset(fingerprint, 0, sizeof(fingerprint)); + memcpy(fingerprint, line, linelen); + continue; + } + + if (memcmp(line, "enddive ", 8)) + continue; + + if (sscanf(line, "enddive %d,%d", &dive_depth, &dive_time) != 2) + continue; + + status = get_one_dive(s1, dive_nr, &dive); + if (status != DC_STATUS_SUCCESS) + return status; + + progress.current++; + device_event_emit(abstract, DC_EVENT_PROGRESS, &progress); + + if (callback && !callback(dive, strlen(dive), fingerprint, sizeof(fingerprint), userdata)) + break; + } return status; } diff --git a/src/oceans_s1_parser.c b/src/oceans_s1_parser.c index f545c15..dd30f06 100644 --- a/src/oceans_s1_parser.c +++ b/src/oceans_s1_parser.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "oceans_s1.h" #include "context-private.h" @@ -16,6 +18,9 @@ typedef struct oceans_s1_parser_t oceans_s1_parser_t; struct oceans_s1_parser_t { dc_parser_t base; + int divenr; + unsigned int maxdepth, duration; + long long date; struct dc_field_cache cache; }; @@ -37,7 +42,7 @@ static const dc_parser_vtable_t oceans_s1_parser_vtable = { dc_status_t oceans_s1_parser_create(dc_parser_t **out, dc_context_t *context) { - oceans_s1_parser_t* parser = NULL; + oceans_s1_parser_t *parser = NULL; if (out == NULL) return DC_STATUS_INVALIDARGS; @@ -54,23 +59,139 @@ oceans_s1_parser_create(dc_parser_t **out, dc_context_t *context) return DC_STATUS_SUCCESS; } +static const unsigned char *get_string_line(const unsigned char *in, const unsigned char **next) +{ + const unsigned char *line; + unsigned char c; + + if (!in) { + *next = NULL; + return NULL; + } + + while (isspace(*in)) + in++; + + if (!*in) { + *next = NULL; + return NULL; + } + + line = in; + while ((c = *in) != 0) { + if (c == '\r' || c == '\n') + break; + in++; + } + *next = in; + return line; +} + +static dc_status_t +oceans_s1_parse_dive(struct oceans_s1_parser_t *s1, const unsigned char *data, dc_sample_callback_t callback, void *userdata) +{ + const unsigned char *line; + unsigned int sample_interval = 10; + unsigned int sample_time = 0; + + memset(&s1->cache, 0, sizeof(s1->cache)); + + while ((line = get_string_line(data, &data)) != NULL) { + dc_sample_value_t sample = {0}; + int depth = 0, temp = 0, flags = 0; + + if (!strncmp(line, "divelog ", 8)) { + sscanf(line, "divelog v1,%us/sample", &sample_interval); + continue; + } + if (!strncmp(line, "dive ", 5)) { + int nr, unknown, o2; + long long date; + + sscanf(line, "dive %d,%d,%d,%lld", &nr, &unknown, &o2, &date); + s1->divenr = nr; + s1->date = date; + // I think "unknown" is dive mode + if (o2) { + dc_gasmix_t mix = { 0 }; + mix.oxygen = o2 / 100.0; + DC_ASSIGN_FIELD(s1->cache, GASMIX_COUNT, 1); + DC_ASSIGN_IDX(s1->cache, GASMIX, 0, mix); + } + continue; + } + if (!strncmp(line, "continue ", 9)) { + int depth = 0, seconds = 0; + sscanf(line, "continue %d,%d", &depth, &seconds); + + // Create surface samples for the surface time, + // and then a depth sample at the stated depth + if (callback) { + if (seconds >= sample_interval*2) { + dc_sample_value_t sample = {0}; + sample.time = sample_time + sample_interval; + callback(DC_SAMPLE_TIME, sample, userdata); + sample.depth = 0; + callback(DC_SAMPLE_DEPTH, sample, userdata); + + sample.time = sample_time + seconds - sample_interval; + callback(DC_SAMPLE_TIME, sample, userdata); + sample.depth = 0; + callback(DC_SAMPLE_DEPTH, sample, userdata); + } + sample.time = sample_time + seconds; + callback(DC_SAMPLE_TIME, sample, userdata); + sample.depth = depth / 100.0; + callback(DC_SAMPLE_DEPTH, sample, userdata); + } + sample_time += seconds; + continue; + } + if (!strncmp(line, "enddive ", 8)) { + int maxdepth = 0, duration = 0; + sscanf(line, "enddive %d,%d", &maxdepth, &duration); + DC_ASSIGN_FIELD(s1->cache, MAXDEPTH, maxdepth / 100.0); + DC_ASSIGN_FIELD(s1->cache, DIVETIME, duration); + s1->maxdepth = maxdepth; + s1->duration = duration; + continue; + } + if (sscanf(line, "%d,%d,%d", &depth, &temp, &flags) != 3) + continue; + + sample_time += sample_interval; + if (callback) { + dc_sample_value_t sample = {0}; + sample.time = sample_time; + callback(DC_SAMPLE_TIME, sample, userdata); + sample.depth = depth / 100.0; + callback(DC_SAMPLE_DEPTH, sample, userdata); + sample.temperature = temp; + callback (DC_SAMPLE_TEMPERATURE, sample, userdata); + } + } + return DC_STATUS_SUCCESS; +} + static dc_status_t oceans_s1_parser_set_data(dc_parser_t *abstract, const unsigned char *data, unsigned int size) { - dc_status_t status = DC_STATUS_SUCCESS; - // Fill me - return DC_STATUS_SUCCESS; + struct oceans_s1_parser_t *s1 = (struct oceans_s1_parser_t *)abstract; + + return oceans_s1_parse_dive(s1, data, NULL, NULL); } static dc_status_t -oceans_s1_parser_get_datetime(dc_parser_t* abstract, dc_datetime_t* datetime) +oceans_s1_parser_get_datetime(dc_parser_t *abstract, dc_datetime_t *datetime) { - // Fill me + oceans_s1_parser_t *s1 = (oceans_s1_parser_t *)abstract; + + dc_datetime_gmtime(datetime, s1->date); return DC_STATUS_SUCCESS; } static dc_status_t -oceans_s1_parser_get_field(dc_parser_t* abstract, dc_field_type_t type, unsigned int flags, void* value) +oceans_s1_parser_get_field(dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) { oceans_s1_parser_t *s1 = (oceans_s1_parser_t *)abstract; @@ -78,8 +199,9 @@ oceans_s1_parser_get_field(dc_parser_t* abstract, dc_field_type_t type, unsigned } static dc_status_t -oceans_s1_parser_samples_foreach(dc_parser_t* abstract, dc_sample_callback_t callback, void* userdata) +oceans_s1_parser_samples_foreach(dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) { - // Fill me - return DC_STATUS_SUCCESS; + struct oceans_s1_parser_t *s1 = (struct oceans_s1_parser_t *)abstract; + + return oceans_s1_parse_dive(s1, s1->base.data, callback, userdata); }