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;
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 +};