[PATCH] Cochran: Changed profile download to be incremental. It will result in a 30 minute download for full computers but it significantly reduces the time to download partial dives.
John Van Ostrand
john at vanostrand.com
Wed May 31 16:24:47 PDT 2017
---
src/cochran_commander.c | 366 ++++++++++++++++++++++++++++--------------------
1 file changed, 212 insertions(+), 154 deletions(-)
diff --git a/src/cochran_commander.c b/src/cochran_commander.c
index 83dd14a..4c15541 100644
--- a/src/cochran_commander.c
+++ b/src/cochran_commander.c
@@ -41,6 +41,11 @@ typedef enum cochran_endian_t {
ENDIAN_BE,
} cochran_endian_t;
+typedef enum cochran_profile_size_t {
+ PROFILE_FULL_SIZE,
+ PROFILE_READ_SIZE,
+} cochran_profile_size_t;
+
typedef struct cochran_commander_model_t {
unsigned char id[2 + 1];
unsigned int model;
@@ -53,6 +58,7 @@ typedef struct cochran_data_t {
unsigned short int dive_count;
int fp_dive_num;
+ int invalid_profile_dive_num;
unsigned int logbook_size;
@@ -430,23 +436,133 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t
}
-static void
+/*
+ * For corrupt dives the end-of-samples pointer is 0xFFFFFFFF
+ * search for a reasonable size, e.g. using next dive start sample
+ * or end-of-samples to limit searching for recoverable samples
+ */
+static unsigned int
+cochran_commander_guess_sample_end_address(cochran_commander_device_t *device, cochran_data_t *data, unsigned int log_num)
+{
+ const unsigned char *log_entry = data->logbook + device->layout->rb_logbook_entry_size * log_num;
+
+ if (log_num == data->dive_count)
+ // Return next usable address from config page
+ return array_uint32_le(data->config + device->layout->rb_profile_end);
+
+ // Next log's start address
+ return array_uint32_le(log_entry + device->layout->rb_logbook_entry_size + device->layout->pt_profile_begin);
+}
+
+
+static int
+cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_t *data, int dive_num, cochran_profile_size_t type)
+{
+
+ const unsigned char *log_entry = data->logbook + dive_num * device->layout->rb_logbook_entry_size;
+ unsigned int sample_start_address = -1;
+ unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end);
+
+ if (type == PROFILE_FULL_SIZE) {
+ // actual size, includes pre-dive events
+ sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_pre);
+ } else if (type == PROFILE_READ_SIZE) {
+ // read size, only include dive profile
+ sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin);
+ }
+
+ // Validate addresses
+ if (sample_start_address < device->layout->rb_profile_begin ||
+ sample_start_address > device->layout->rb_profile_end ||
+ sample_end_address < device->layout->rb_profile_begin ||
+ (sample_end_address > device->layout->rb_profile_end &&
+ sample_end_address != 0xFFFFFFFF)) {
+ return 0;
+ }
+
+ if (sample_end_address == 0xFFFFFFFF)
+ // Corrupt dive, guess the end address
+ sample_end_address = cochran_commander_guess_sample_end_address(device, data, dive_num);
+
+ // Calculate the size of the profile only
+ int sample_size = sample_end_address - sample_start_address;
+
+ if (sample_size < 0)
+ // Adjust for ring buffer wrap-around
+ sample_size += device->layout->rb_profile_end - device->layout->rb_profile_begin;
+
+ return sample_size;
+}
+
+
+/*
+ * Do several things. Find the log that matches the fingerprint,
+ * calculate the total read size for progress indicator,
+ * Determine the most recent dive without profile data.
+ */
+
+static int
cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_data_t *data)
{
- // Skip to fingerprint to reduce time
+ // We track profile ringbuffer usage to determine which dives have profile data
+ int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin;
+
+ int dive_count = -1;
+
+ // Start at end of log
if (data->dive_count < device->layout->rb_logbook_entry_count)
- data->fp_dive_num = data->dive_count;
+ dive_count = data->dive_count;
else
- data->fp_dive_num = device->layout->rb_logbook_entry_count;
- data->fp_dive_num--;
+ dive_count = device->layout->rb_logbook_entry_count;
+ dive_count--;
+
+ int sample_read_size = 0;
+ data->invalid_profile_dive_num = -1;
+
+ // Remove the pre-dive events that occur after the last dive
+ int rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000;
+ int last_dive_end_address = array_uint32_le(data->logbook + dive_count * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end);
+ if (rb_head_ptr > last_dive_end_address)
+ profile_capacity_remaining -= rb_head_ptr - last_dive_end_address;
+
+ // Loop through dives to find FP, Accumulate profile data size,
+ // and find the last dive with invalid profile
+ for (int i = dive_count; i > 0; i--) {
+ unsigned char *log_entry = data->logbook + i * device->layout->rb_logbook_entry_size;
+
+ // We're done if we find the fingerprint
+ if (!memcmp(device->fingerprint, log_entry, sizeof(device->fingerprint))) {
+ data->fp_dive_num = i;
+ break;
+ }
- while (data->fp_dive_num >= 0 && memcmp(device->fingerprint,
- data->logbook + data->fp_dive_num * device->layout->rb_logbook_entry_size,
- sizeof(device->fingerprint)))
- data->fp_dive_num--;
+ int sample_size = cochran_commander_profile_size(device, data, i, PROFILE_FULL_SIZE);
+ int read_size = cochran_commander_profile_size(device, data, i, PROFILE_READ_SIZE);
+
+ // Determine if sample exists
+ if (profile_capacity_remaining > 0) {
+ // Subtract this dive's profile size including post-dive events
+ profile_capacity_remaining -= sample_size;
+ if (profile_capacity_remaining < 0) {
+ // Save the last dive that is missing profile data
+ data->invalid_profile_dive_num = i;
+ }
+ // Accumulate read size for progress bar
+ sample_read_size += read_size;
+ }
+
+ if (profile_capacity_remaining < 0) {
+ // There is no profile for this dive
+ sample_size = 0;
+ }
+
+ }
+
+ return sample_read_size;
}
+
static void
cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_data_t *data)
{
@@ -509,103 +625,6 @@ cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_d
}
-/*
- * For corrupt dives the end-of-samples pointer is 0xFFFFFFFF
- * search for a reasonable size, e.g. using next dive start sample
- * or end-of-samples to limit searching for recoverable samples
- */
-static unsigned int
-cochran_commander_guess_sample_end_address(cochran_commander_device_t *device, cochran_data_t *data, unsigned int log_num)
-{
- const unsigned char *log_entry = data->logbook + device->layout->rb_logbook_entry_size * log_num;
-
- if (log_num == data->dive_count)
- // Return next usable address from config page
- return array_uint32_le(data->config + device->layout->rb_profile_end);
-
- // Next log's start address
- return array_uint32_le(log_entry + device->layout->rb_logbook_entry_size + device->layout->pt_profile_begin);
-}
-
-
-static dc_status_t
-cochran_commander_read_all (cochran_commander_device_t *device, cochran_data_t *data)
-{
- dc_device_t *abstract = (dc_device_t *) device;
- dc_status_t rc = DC_STATUS_SUCCESS;
-
- // Calculate max data sizes
- unsigned int max_config = sizeof(data->config);
- unsigned int max_logbook = device->layout->rb_logbook_end - device->layout->rb_logbook_begin;
- unsigned int max_sample = device->layout->rb_profile_end - device->layout->rb_profile_begin;
-
- dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
- progress.maximum = max_config + max_logbook + max_sample;
- device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
-
- // Emit ID block
- dc_event_vendor_t vendor;
- vendor.data = device->id;
- vendor.size = sizeof (device->id);
- device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
-
- // Read config
- rc = cochran_commander_read_config(device, &progress, data->config, sizeof(data->config));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
-
- // Determine size of dive list to read.
- if (device->layout->endian == ENDIAN_LE)
- data->dive_count = array_uint16_le (data->config + device->layout->cf_dive_count);
- else
- data->dive_count = array_uint16_be (data->config + device->layout->cf_dive_count);
-
- if (data->dive_count > device->layout->rb_logbook_entry_count) {
- data->logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size;
- } else {
- data->logbook_size = data->dive_count * device->layout->rb_logbook_entry_size;
- }
-
- progress.maximum -= max_logbook - data->logbook_size;
- device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
-
- // Allocate space for log book.
- data->logbook = (unsigned char *) malloc(data->logbook_size);
- if (data->logbook == NULL) {
- ERROR (abstract->context, "Failed to allocate memory.");
- return DC_STATUS_NOMEMORY;
- }
-
- // Request log book
- rc = cochran_commander_read(device, &progress, 0, data->logbook, data->logbook_size);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
-
- // Determine sample memory to read
- cochran_commander_find_fingerprint(device, data);
- cochran_commander_get_sample_parms(device, data);
-
- progress.maximum -= max_sample - data->sample_size;
- device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
-
- if (data->sample_size > 0) {
- data->sample = (unsigned char *) malloc(data->sample_size);
- if (data->sample == NULL) {
- ERROR (abstract->context, "Failed to allocate memory.");
- return DC_STATUS_NOMEMORY;
- }
-
- // Read the sample data
- rc = cochran_commander_read (device, &progress, data->sample_data_offset, data->sample, data->sample_size);
- if (rc != DC_STATUS_SUCCESS) {
- ERROR (abstract->context, "Failed to read the sample data.");
- return rc;
- }
- }
-
- return DC_STATUS_SUCCESS;
-}
-
dc_status_t
cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const char *name)
{
@@ -772,9 +791,64 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
cochran_data_t data;
data.logbook = NULL;
data.sample = NULL;
- status = cochran_commander_read_all (device, &data);
- if (status != DC_STATUS_SUCCESS)
- goto error;
+
+ // Calculate max data sizes
+ unsigned int max_config = sizeof(data.config);
+ unsigned int max_logbook = device->layout->rb_logbook_end - device->layout->rb_logbook_begin;
+ unsigned int max_sample = device->layout->rb_profile_end - device->layout->rb_profile_begin;
+
+ // setup progress indication
+ dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
+ progress.maximum = max_config + max_logbook + max_sample;
+ device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+
+ // Emit ID block
+ dc_event_vendor_t vendor;
+ vendor.data = device->id;
+ vendor.size = sizeof (device->id);
+ device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
+
+ // Read config
+ dc_status_t rc = DC_STATUS_SUCCESS;
+ rc = cochran_commander_read_config(device, &progress, data.config, sizeof(data.config));
+ if (rc != DC_STATUS_SUCCESS)
+ return rc;
+
+ // Determine size of dive list to read.
+ if (device->layout->endian == ENDIAN_LE)
+ data.dive_count = array_uint16_le (data.config + device->layout->cf_dive_count);
+ else
+ data.dive_count = array_uint16_be (data.config + device->layout->cf_dive_count);
+
+ if (data.dive_count > device->layout->rb_logbook_entry_count) {
+ data.logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size;
+ } else {
+ data.logbook_size = data.dive_count * device->layout->rb_logbook_entry_size;
+ }
+
+ // Update progress indicator with new maximum
+ progress.maximum -= max_logbook - data.logbook_size;
+ device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+
+ // Allocate space for log book.
+ data.logbook = (unsigned char *) malloc(data.logbook_size);
+ if (data.logbook == NULL) {
+ ERROR (abstract->context, "Failed to allocate memory.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ // Request log book
+ rc = cochran_commander_read(device, &progress, 0, data.logbook, data.logbook_size);
+ if (rc != DC_STATUS_SUCCESS)
+ return rc;
+
+ // Locate fingerprint, recent dive with invalid profile and calc read size
+ int profile_read_size = cochran_commander_find_fingerprint(device, &data);
+ // Update progress indicator with new maximum
+ progress.maximum -= (max_sample - profile_read_size);
+ device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+
+ cochran_commander_get_sample_parms(device, &data);
// Emit a device info event.
dc_event_devinfo_t devinfo;
@@ -792,9 +866,6 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
goto error;
}
- // We track profile ringbuffer usage to determine which dives have profile data
- int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin;
-
unsigned int dive_count = 0;
if (data.dive_count < device->layout->rb_logbook_entry_count)
dive_count = data.dive_count;
@@ -808,44 +879,11 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
unsigned int sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin);
unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end);
- // Validate
- if (sample_start_address < device->layout->rb_profile_begin ||
- sample_start_address > device->layout->rb_profile_end ||
- sample_end_address < device->layout->rb_profile_begin ||
- (sample_end_address > device->layout->rb_profile_end &&
- sample_end_address != 0xFFFFFFFF)) {
- continue;
- }
-
- if (sample_end_address == 0xFFFFFFFF)
- // Corrupt dive, guess the end address
- sample_end_address = cochran_commander_guess_sample_end_address(device, &data, i);
-
- // Determine if sample exists
- if (profile_capacity_remaining > 0) {
- // Subtract this dive's profile size including post-dive events
- profile_capacity_remaining -= (last_start_address - sample_start_address);
- // Adjust for a dive that wraps the buffer
- if (sample_start_address > last_start_address)
- profile_capacity_remaining -= device->layout->rb_profile_end - device->layout->rb_profile_begin;
- }
- last_start_address = sample_start_address;
-
- unsigned char *sample = NULL;
int sample_size = 0;
- if (profile_capacity_remaining < 0) {
- // There is no profile for this dive
- sample = NULL;
- sample_size = 0;
- } else {
- // Calculate the size of the profile only
- sample = data.sample + sample_start_address - data.sample_data_offset;
- sample_size = sample_end_address - sample_start_address;
-
- if (sample_size < 0)
- // Adjust for ring buffer wrap-around
- sample_size += device->layout->rb_profile_end - device->layout->rb_profile_begin;
- }
+
+ // Determine if profile exists
+ if (i > data.invalid_profile_dive_num)
+ sample_size = cochran_commander_profile_size(device, &data, i, PROFILE_READ_SIZE);
// Build dive blob
unsigned int dive_size = device->layout->rb_logbook_entry_size + sample_size;
@@ -857,17 +895,37 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
memcpy(dive, log_entry, device->layout->rb_logbook_entry_size); // log
- // Copy profile data
+ int tries = 0;
+ // Read profile data
if (sample_size) {
if (sample_start_address <= sample_end_address) {
- memcpy(dive + device->layout->rb_logbook_entry_size, sample, sample_size);
+ do {
+ rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size);
+ } while (rc != DC_STATUS_SUCCESS && tries++ < 3);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to read the sample data.");
+ return rc;
+ }
} else {
// It wrapped the buffer, copy two sections
unsigned int size = device->layout->rb_profile_end - sample_start_address;
- memcpy(dive + device->layout->rb_logbook_entry_size, sample, size);
- memcpy(dive + device->layout->rb_logbook_entry_size + size,
- data.sample, sample_end_address - device->layout->rb_profile_begin);
+ tries = 0;
+ do {
+ rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, size);
+ } while (rc != DC_STATUS_SUCCESS && tries++ < 3);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to read the sample data.");
+ return rc;
+ }
+ tries = 0;
+ do {
+ rc = cochran_commander_read (device, &progress, device->layout->rb_profile_begin, dive + device->layout->rb_logbook_entry_size + size, sample_end_address - device->layout->rb_profile_begin);
+ } while (rc != DC_STATUS_SUCCESS && tries++ < 3);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to read the sample data.");
+ return rc;
+ }
}
}
--
2.4.11
More information about the devel
mailing list