[PATCH 1/3] Cochran Commander: get_field would return bad values (xFF) when dive was incomplete.
The commander parser wasn't detecting when dives were incomplete (i.e. dive end block would be set to all 0xFF). So it returned large max depths, temps and other fields. --- src/cochran_cmdr_parser.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/cochran_cmdr_parser.c b/src/cochran_cmdr_parser.c index b4e8fb7..73b344f 100644 --- a/src/cochran_cmdr_parser.c +++ b/src/cochran_cmdr_parser.c @@ -49,22 +49,40 @@ cochran_cmdr_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, switch (type) { case DC_FIELD_TEMPERATURE_SURFACE: *((unsigned int*) value) = ((float) log[CMD_START_TEMP] - 32) / 1.8; + break; case DC_FIELD_TEMPERATURE_MINIMUM: - *((unsigned int*) value) = ((float) log[CMD_MIN_TEMP] / 2 - + 20 - 32) / 1.8; + if (array_uint16_le(log + CMD_MIN_TEMP) == 0xFFFF) + *((double *) value) = 0; + else + *((unsigned int*) value) = ((float) log[CMD_MIN_TEMP] / 2 + + 20 - 32) / 1.8; + break; case DC_FIELD_TEMPERATURE_MAXIMUM: - *((unsigned int*) value) = ((float) log[CMD_MAX_TEMP] / 2 - + 20 - 32) / 1.8; + if (array_uint16_le(log + CMD_MAX_TEMP) == 0xFFFF) + *((double *) value) = 0; + else + *((unsigned int*) value) = ((float) log[CMD_MAX_TEMP] / 2 + + 20 - 32) / 1.8; + break; case DC_FIELD_DIVETIME: - *((unsigned int *) value) = array_uint16_le (log + CMD_BT) * 60; + if (array_uint16_le(log + CMD_BT) == 0xFFFF) + *((double *) value) = 0; + else + *((unsigned int *) value) = array_uint16_le (log + CMD_BT) * 60; break; case DC_FIELD_MAXDEPTH: - *((double *) value) = (float) array_uint16_le (log + CMD_MAX_DEPTH) - / 4 * FEET; + if (array_uint16_le(log + CMD_MAX_DEPTH) == 0xFFFF) + *((double *) value) = 0; + else + *((double *) value) = (float) array_uint16_le (log + + CMD_MAX_DEPTH) / 4 * FEET; break; case DC_FIELD_AVGDEPTH: - *((double *) value) = (float) array_uint16_le (log + CMD_AVG_DEPTH) - / 4 * FEET; + if (array_uint16_le(log + CMD_AVG_DEPTH) == 0xFFFF) + *((double *) value) = 0; + else + *((double *) value) = (float) array_uint16_le (log + + CMD_AVG_DEPTH) / 4 * FEET; break; case DC_FIELD_GASMIX_COUNT: *((unsigned int *) value) = 2; -- 2.4.3
An extract command was added to dctool to store a DC's individual dive data into files. This is helpful for developing parsing algorithms without having to wait for a slow DC or simulate a DC. --- examples/Makefile.am | 1 + examples/dctool.c | 1 + examples/dctool.h | 1 + examples/dctool_extract.c | 357 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 360 insertions(+) create mode 100644 examples/dctool_extract.c diff --git a/examples/Makefile.am b/examples/Makefile.am index 12bca6f..b1e1e42 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -18,5 +18,6 @@ dctool_SOURCES = \ dctool_read.c \ dctool_write.c \ dctool_fwupdate.c \ + dctool_extract.c \ utils.h \ utils.c diff --git a/examples/dctool.c b/examples/dctool.c index 4d70c0e..127352e 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -56,6 +56,7 @@ static const dctool_command_t *g_commands[] = { &dctool_read, &dctool_write, &dctool_fwupdate, + &dctool_extract, NULL }; diff --git a/examples/dctool.h b/examples/dctool.h index a0e09d7..6ee58a9 100644 --- a/examples/dctool.h +++ b/examples/dctool.h @@ -50,6 +50,7 @@ extern const dctool_command_t dctool_dump; extern const dctool_command_t dctool_read; extern const dctool_command_t dctool_write; extern const dctool_command_t dctool_fwupdate; +extern const dctool_command_t dctool_extract; const dctool_command_t * dctool_command_find (const char *name); diff --git a/examples/dctool_extract.c b/examples/dctool_extract.c new file mode 100644 index 0000000..4ed604e --- /dev/null +++ b/examples/dctool_extract.c @@ -0,0 +1,357 @@ +/* + * libdivecomputer + * + * Copyright (C) 2015 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +#include <libdivecomputer/context.h> +#include <libdivecomputer/descriptor.h> +#include <libdivecomputer/device.h> +#include <libdivecomputer/parser.h> + +#include "dctool.h" +#include "common.h" +#include "utils.h" + +typedef struct event_data_t { + const char *cachedir; + dc_event_devinfo_t devinfo; +} event_data_t; + +typedef struct dive_data_t { + const char *path; + dc_device_t *device; + dc_buffer_t **fingerprint; + unsigned int number; +} dive_data_t; + + +static int +dive_cb (const unsigned char *data, unsigned int size, const unsigned char *fingerprint, unsigned int fsize, void *userdata) +{ + dive_data_t *divedata = (dive_data_t *) userdata; + + divedata->number++; + + char *fp_string = malloc(fsize * 0 + 1); + if (!fp_string) { + message ("Unable to allocate memory in dive_cb\n"); + return 0; + } + + message ("Dive: number=%u, size=%u, fingerprint=", divedata->number, size); + for (unsigned int i = 0; i < fsize; ++i) { + message ("%02X", fingerprint[i]); + sprintf(fp_string + i * 2, "%02X", fingerprint[i]); + } + message ("\n"); + + // Keep a copy of the most recent fingerprint. Because dives are + // guaranteed to be downloaded in reverse order, the most recent + // dive is always the first dive. + if (divedata->number == 1) { + dc_buffer_t *fp = dc_buffer_new (fsize); + dc_buffer_append (fp, fingerprint, fsize); + *divedata->fingerprint = fp; + } + + // Write out dive into new file. + char *path = malloc(strlen(divedata->path) + (fsize * 2) + 2); + sprintf(path, "%s/%s", divedata->path, fp_string); + free(fp_string); + + int fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + free(path); + + if (fd == -1) { + message ("ERROR: %s creating file %s\n", strerror(errno), path); + return 0; + } + + int bytes_written = write(fd, data, size); + + if (bytes_written == -1) { + message ("ERROR: %s while writing %s\n", strerror(errno), path); + return 0; + } else if (bytes_written != size) { + message ("ERROR: unable to write %s\n", path); + } + + return 1; +} + +static void +event_cb (dc_device_t *device, dc_event_type_t event, const void *data, void *userdata) +{ + const dc_event_devinfo_t *devinfo = (const dc_event_devinfo_t *) data; + + event_data_t *eventdata = (event_data_t *) userdata; + + // Forward to the default event handler. + dctool_event_cb (device, event, data, userdata); + + switch (event) { + case DC_EVENT_DEVINFO: + // Load the fingerprint from the cache. If there is no + // fingerprint present in the cache, a NULL buffer is returned, + // and the registered fingerprint will be cleared. + if (eventdata->cachedir) { + char filename[1024] = {0}; + dc_family_t family = DC_FAMILY_NULL; + dc_buffer_t *fingerprint = NULL; + + // Generate the fingerprint filename. + family = dc_device_get_type (device); + snprintf (filename, sizeof (filename), "%s/%s-%08X.bin", + eventdata->cachedir, dctool_family_name (family), devinfo->serial); + + // Read the fingerprint file. + fingerprint = dctool_file_read (filename); + + // Register the fingerprint data. + dc_device_set_fingerprint (device, + dc_buffer_get_data (fingerprint), + dc_buffer_get_size (fingerprint)); + + // Free the buffer again. + dc_buffer_free (fingerprint); + } + + // Keep a copy of the event data. It will be used for generating + // the fingerprint filename again after a (successful) extract. + eventdata->devinfo = *devinfo; + break; + default: + break; + } +} + +static dc_status_t +extract (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, const char *cachedir, dc_buffer_t *fingerprint, const char *path) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + dc_device_t *device = NULL; + dc_buffer_t *ofingerprint = NULL; + + // Open the device. + message ("Opening the device (%s %s, %s).\n", + dc_descriptor_get_vendor (descriptor), + dc_descriptor_get_product (descriptor), + devname ? devname : "null"); + rc = dc_device_open (&device, context, descriptor, devname); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error opening the device."); + goto cleanup; + } + + // Initialize the event data. + event_data_t eventdata = {0}; + if (fingerprint) { + eventdata.cachedir = NULL; + } else { + eventdata.cachedir = cachedir; + } + + // Register the event handler. + message ("Registering the event handler.\n"); + int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR; + rc = dc_device_set_events (device, events, event_cb, &eventdata); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the event handler."); + goto cleanup; + } + + // Register the cancellation handler. + message ("Registering the cancellation handler.\n"); + rc = dc_device_set_cancel (device, dctool_cancel_cb, NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the cancellation handler."); + goto cleanup; + } + + // Register the fingerprint data. + if (fingerprint) { + message ("Registering the fingerprint data.\n"); + rc = dc_device_set_fingerprint (device, dc_buffer_get_data (fingerprint), dc_buffer_get_size (fingerprint)); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error registering the fingerprint data."); + goto cleanup; + } + } + + // Initialize the dive data. + dive_data_t divedata = {0}; + divedata.device = device; + divedata.path = path; + divedata.fingerprint = &ofingerprint; + divedata.number = 0; + + // Download the dives. + message ("Downloading the dives.\n"); + rc = dc_device_foreach (device, dive_cb, &divedata); + if (rc != DC_STATUS_SUCCESS) { + ERROR ("Error extracting the dives."); + goto cleanup; + } + + // Store the fingerprint data. + if (cachedir && ofingerprint) { + char filename[1024] = {0}; + dc_family_t family = DC_FAMILY_NULL; + + // Generate the fingerprint filename. + family = dc_device_get_type (device); + snprintf (filename, sizeof (filename), "%s/%s-%08X.bin", + cachedir, dctool_family_name (family), eventdata.devinfo.serial); + + // Write the fingerprint file. + dctool_file_write (filename, ofingerprint); + } + +cleanup: + dc_buffer_free (ofingerprint); + dc_device_close (device); + return rc; +} + +static int +dctool_extract_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t *descriptor) +{ + int exitcode = EXIT_SUCCESS; + dc_status_t status = DC_STATUS_SUCCESS; + dc_buffer_t *fingerprint = NULL; + + // Default option values. + unsigned int help = 0; + const char *fphex = NULL; + const char *path = NULL; + const char *cachedir = NULL; + + // Parse the command-line options. + int opt = 0; + const char *optstring = "ho:p:c:"; +#ifdef HAVE_GETOPT_LONG + struct option options[] = { + {"help", no_argument, 0, 'h'}, + {"output", required_argument, 0, 'o'}, + {"fingerprint", required_argument, 0, 'p'}, + {"cache", required_argument, 0, 'c'}, + {0, 0, 0, 0 } + }; + while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) { +#else + while ((opt = getopt (argc, argv, optstring)) != -1) { +#endif + switch (opt) { + case 'h': + help = 1; + break; + case 'o': + path = optarg; + break; + case 'p': + fphex = optarg; + break; + case 'c': + cachedir = optarg; + break; + default: + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + // Show help message. + if (help) { + dctool_command_showhelp (&dctool_extract); + return EXIT_SUCCESS; + } + + // Convert the fingerprint to binary. + fingerprint = dctool_convert_hex2bin (fphex); + + // Create directory if needed + struct stat ds; + if (stat(path, &ds)) { + // Assume it doesn't exist + if (mkdir(path, 0777)) { + message ("ERROR: %s while creating directory\n", strerror(errno)); + exitcode = EXIT_FAILURE; + goto cleanup; + } + } else { + if (!(ds.st_mode && S_IFDIR)) { + message ("ERROR: Path given is not a directory\n"); + exitcode = EXIT_FAILURE; + goto cleanup; + } + } + + // Extract the dives. + status = extract (context, descriptor, argv[0], cachedir, fingerprint, path); + if (status != DC_STATUS_SUCCESS) { + message ("ERROR: %s\n", dctool_errmsg (status)); + exitcode = EXIT_FAILURE; + goto cleanup; + } + +cleanup: + dc_buffer_free (fingerprint); + return exitcode; +} + +const dctool_command_t dctool_extract = { + dctool_extract_run, + DCTOOL_CONFIG_DESCRIPTOR, + "extract", + "Extract dive data into files", + "Usage:\n" + " dctool extract [options] <devname>\n" + "\n" + "Options:\n" +#ifdef HAVE_GETOPT_LONG + " -h, --help Show help message\n" + " -o, --output <dir> Directory to store dive data\n" + " -p, --fingerprint <data> Fingerprint data (hexadecimal)\n" + " -c, --cache <directory> Cache directory\n" +#else + " -h Show help message\n" + " -o <dir> Directory to store dive data\n" + " -p <fingerprint> Fingerprint data (hexadecimal)\n" + " -c <directory> Cache directory\n" +#endif +}; -- 2.4.3
participants (1)
-
John Van Ostrand