Changed loops from do..while to for. Changed time-related variable names to more customary names. Changed datetime handling to streamline a clock-tick datetime decoding. Create cochran_commander_read_retry() function to perform reads. Correct device model strings.
Non-code review related:
Added new EMC device model string. Created shorter variables for commonly used structure elements. --- src/cochran_commander.c | 128 ++++++++++++++++++++++------------------- src/cochran_commander_parser.c | 72 ++++++++++++----------- 2 files changed, 108 insertions(+), 92 deletions(-)
diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 1c1009f..27ed67d 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -244,7 +244,10 @@ cochran_commander_get_model (cochran_commander_device_t *device) {"\x11""21", COCHRAN_MODEL_COMMANDER_PRE21000}, {"\x11""22", COCHRAN_MODEL_COMMANDER_AIR_NITROX}, {"730", COCHRAN_MODEL_EMC_14}, + {"731", COCHRAN_MODEL_EMC_14}, + {"\x40""30", COCHRAN_MODEL_EMC_20}, {"A30", COCHRAN_MODEL_EMC_16}, + {"A31", COCHRAN_MODEL_EMC_16}, {"230", COCHRAN_MODEL_EMC_20}, {"231", COCHRAN_MODEL_EMC_20}, }; @@ -473,6 +476,22 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t return DC_STATUS_SUCCESS; }
+static unsigned int +cochran_commander_read_retry (cochran_commander_device_t *device, dc_event_progress_t *progress, unsigned int address, unsigned char data[], unsigned int size) +{ + unsigned int saved_current = progress->current; + dc_status_t rc; + + for (unsigned int tries = 0; tries < 3; tries++) { + rc = cochran_commander_read (device, progress, address, data, size); + if (rc != DC_STATUS_SUCCESS) + progress->current = saved_current; + else + break; + } + return rc; +} +
/* * For corrupt dives the end-of-samples pointer is 0xFFFFFFFF @@ -574,9 +593,8 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d
// Loop through dives to find FP, Accumulate profile data size, // and find the last dive with invalid profile - unsigned int i = head_dive; - do { - i = ringbuffer_decrement(i, 1, 0, device->layout->rb_logbook_entry_count); + for (int x = dive_count; x >= 0; x--) { + unsigned int i = (device->layout->rb_logbook_entry_count + head_dive - (dive_count - x + 1)) % device->layout->rb_logbook_entry_count;
unsigned char *log_entry = data->logbook + i * device->layout->rb_logbook_entry_size;
@@ -600,7 +618,7 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d // Accumulate read size for progress bar sample_read_size += read_size; } - } while (i != tail_dive); + }
return sample_read_size; } @@ -623,7 +641,7 @@ cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_d unsigned int low_offset = 0xFFFFFFFF; unsigned int high_offset = 0;
- for (int i = data->fp_dive_num + 1; i < dive_count; i++) { + for (unsigned int i = data->fp_dive_num + 1; i < dive_count; i++) { pre_dive_offset = array_uint32_le (data->logbook + i * device->layout->rb_logbook_entry_size + device->layout->pt_profile_pre); end_dive_offset = array_uint32_le (data->logbook + i * device->layout->rb_logbook_entry_size @@ -833,15 +851,19 @@ static dc_status_t cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { cochran_commander_device_t *device = (cochran_commander_device_t *) abstract; + const cochran_device_layout_t *layout = device->layout; dc_status_t status = DC_STATUS_SUCCESS;
cochran_data_t data; data.logbook = NULL;
+ // Often used variables + unsigned int rb_logbook_entry_count = layout->rb_logbook_entry_count; + // 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; + unsigned int max_logbook = layout->rb_logbook_end - layout->rb_logbook_begin; + unsigned int max_sample = layout->rb_profile_end - layout->rb_profile_begin;
// setup progress indication dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; @@ -861,19 +883,19 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call 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); + if (layout->endian == ENDIAN_LE) + data.dive_count = array_uint16_le (data.config + layout->cf_dive_count); else - data.dive_count = array_uint16_be (data.config + device->layout->cf_dive_count); + data.dive_count = array_uint16_be (data.config + layout->cf_dive_count);
if (data.dive_count == 0) // No dives to read return DC_STATUS_SUCCESS;
- 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; + if (data.dive_count > rb_logbook_entry_count) { + data.logbook_size = rb_logbook_entry_count * layout->rb_logbook_entry_size; } else { - data.logbook_size = data.dive_count * device->layout->rb_logbook_entry_size; + data.logbook_size = data.dive_count * layout->rb_logbook_entry_size; }
// Update progress indicator with new maximum @@ -904,53 +926,57 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
// Emit a device info event. dc_event_devinfo_t devinfo; - devinfo.model = device->layout->model; + devinfo.model = layout->model; devinfo.firmware = 0; // unknown - if (device->layout->endian == ENDIAN_WORD_BE) - devinfo.serial = array_uint32_word_be(data.config + device->layout->cf_serial_number); + if (layout->endian == ENDIAN_WORD_BE) + devinfo.serial = array_uint32_word_be(data.config + layout->cf_serial_number); else - devinfo.serial = array_uint32_le(data.config + device->layout->cf_serial_number); + devinfo.serial = array_uint32_le(data.config + layout->cf_serial_number);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
// Calculate profile RB effective head pointer // Cochran seems to erase 8K chunks so round up. unsigned int last_start_address; - if (device->layout->endian == ENDIAN_WORD_BE) - last_start_address = (array_uint32_word_be(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + if (layout->endian == ENDIAN_WORD_BE) + last_start_address = (array_uint32_word_be(data.config + layout->cf_last_interdive) & 0xfffff000) + 0x2000; else - last_start_address = (array_uint32_le(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + last_start_address = (array_uint32_le(data.config + layout->cf_last_interdive) & 0xfffff000) + 0x2000;
- if (last_start_address < device->layout->rb_profile_begin || last_start_address > device->layout->rb_profile_end) { + if (last_start_address < layout->rb_profile_begin || last_start_address > layout->rb_profile_end) { ERROR(abstract->context, "Invalid profile ringbuffer head pointer in Cochran config block."); status = DC_STATUS_DATAFORMAT; goto error; }
- unsigned int head_dive = 0, tail_dive = 0; + unsigned int head_dive = 0, tail_dive = 0, dive_count = 0;
- if (data.dive_count <= device->layout->rb_logbook_entry_count) { + if (data.dive_count <= rb_logbook_entry_count) { head_dive = data.dive_count; tail_dive = 0; } else { // Log wrapped - tail_dive = data.dive_count % device->layout->rb_logbook_entry_count; + tail_dive = data.dive_count % rb_logbook_entry_count; head_dive = tail_dive; } + + // Change tail to dive following the fingerprint dive. if (data.fp_dive_num > -1) - tail_dive = data.fp_dive_num + 1; + tail_dive = (data.fp_dive_num + 1) % rb_logbook_entry_count; + + // Number of dives to read + dive_count = (rb_logbook_entry_count + head_dive - tail_dive) % rb_logbook_entry_count;
int invalid_profile_flag = 0;
// Loop through each dive - unsigned int i = head_dive; - do { - i = ringbuffer_decrement(i, 1, 0, device->layout->rb_logbook_entry_count); + for (unsigned int x = dive_count; x > 0; x--) { + unsigned int i = (rb_logbook_entry_count + head_dive - (dive_count - x + 1)) % rb_logbook_entry_count;
- unsigned char *log_entry = data.logbook + i * device->layout->rb_logbook_entry_size; + unsigned char *log_entry = data.logbook + i * layout->rb_logbook_entry_size;
- 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); + unsigned int sample_start_address = array_uint32_le (log_entry + layout->pt_profile_begin); + unsigned int sample_end_address = array_uint32_le (log_entry + layout->pt_profile_end);
int sample_size = 0;
@@ -962,16 +988,15 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call sample_size = cochran_commander_profile_size(device, &data, i, PROFILE_SIZE_READ);
// Build dive blob - unsigned int dive_size = device->layout->rb_logbook_entry_size + sample_size; + unsigned int dive_size = layout->rb_logbook_entry_size + sample_size; unsigned char *dive = (unsigned char *) malloc(dive_size); if (dive == NULL) { status = DC_STATUS_NOMEMORY; goto error; }
- memcpy(dive, log_entry, device->layout->rb_logbook_entry_size); // log + memcpy(dive, log_entry, layout->rb_logbook_entry_size); // log
- int tries = 0; // Read profile data if (sample_size) { if (sample_end_address == 0xFFFFFFFF) @@ -979,55 +1004,42 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call sample_end_address = cochran_commander_guess_sample_end_address(device, &data, i);
if (sample_start_address <= sample_end_address) { - do { - int saved_progress = progress.current; - rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); - if (rc != DC_STATUS_SUCCESS) - progress.current = saved_progress; - } while (rc != DC_STATUS_SUCCESS && tries++ < 3); + rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + layout->rb_logbook_entry_size, sample_size); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; + free(dive); goto error; } } else { // It wrapped the buffer, copy two sections - unsigned int size = device->layout->rb_profile_end - sample_start_address; - - tries = 0; - do { - int saved_progress = progress.current; - rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, size); - if (rc != DC_STATUS_SUCCESS) - progress.current = saved_progress; - } while (rc != DC_STATUS_SUCCESS && tries++ < 3); + unsigned int size = layout->rb_profile_end - sample_start_address; + + rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + layout->rb_logbook_entry_size, size); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; + free(dive); goto error; } - tries = 0; - do { - int saved_progress = progress.current; - 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); - if (rc != DC_STATUS_SUCCESS) - progress.current = saved_progress; - } while (rc != DC_STATUS_SUCCESS && tries++ < 3); + + rc = cochran_commander_read_retry (device, &progress, layout->rb_profile_begin, dive + layout->rb_logbook_entry_size + size, sample_end_address - layout->rb_profile_begin); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; + free(dive); goto error; } } }
- if (callback && !callback (dive, dive_size, dive + device->layout->pt_fingerprint, device->layout->fingerprint_size, userdata)) { + if (callback && !callback (dive, dive_size, dive + layout->pt_fingerprint, layout->fingerprint_size, userdata)) { free(dive); break; }
free(dive); - } while (i != tail_dive); + }
error: free(data.logbook); diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index d9521c0..484b195 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -39,7 +39,7 @@ #define COCHRAN_MODEL_EMC_20 4
// Cochran time stamps start at Jan 1, 1992 -#define COCHRAN_TIME_SHIFT 694242000 +#define COCHRAN_EPOCH 694242000
#define UNSUPPORTED 0xFFFFFFFF
@@ -50,8 +50,9 @@ typedef enum cochran_sample_format_t {
typedef enum cochran_date_encoding_t { - DATE_ENCODING_ELEMENTAL, - DATE_ENCODING_SECONDS_SINCE, + DATE_ENCODING_MSDHYM, + DATE_ENCODING_SMHDMY, + DATE_ENCODING_TICKS, } cochran_date_encoding_t;
typedef struct cochran_parser_layout_t { @@ -59,8 +60,7 @@ typedef struct cochran_parser_layout_t { unsigned int headersize; unsigned int samplesize; cochran_date_encoding_t date_encoding; - unsigned int second, minute, hour, day, month, year; - unsigned int timestamp; + unsigned int datetime; unsigned int pt_profile_begin; unsigned int water_conductivity; unsigned int pt_profile_pre; @@ -118,9 +118,8 @@ static const cochran_parser_layout_t cochran_cmdr_1_parser_layout = { SAMPLE_CMDR, // type 256, // headersize 2, // samplesize - DATE_ENCODING_SECONDS_SINCE, // date_encoding - 1, 0, 3, 2, 5, 4, // second, minute, hour, day, month, year, 1 byte each - 8, // timestamp, 4 bytes + DATE_ENCODING_TICKS, // date_encoding + 8, // datetime, 4 bytes 0, // pt_profile_begin, 4 bytes 24, // water_conductivity, 1 byte, 0=low(fresh), 2=high(sea) 28, // pt_profile_pre, 4 bytes @@ -143,9 +142,8 @@ static const cochran_parser_layout_t cochran_cmdr_parser_layout = { SAMPLE_CMDR, // type 256, // headersize 2, // samplesize - DATE_ENCODING_ELEMENTAL, // date_encoding - 1, 0, 3, 2, 5, 4, // second, minute, hour, day, month, year, 1 byte each - 0, // timestamp, 4 bytes + DATE_ENCODING_MSDHYM, // date_encoding + 0, // datetime, 6 bytes 6, // pt_profile_begin, 4 bytes 24, // water_conductivity, 1 byte, 0=low(fresh), 2=high(sea) 30, // pt_profile_pre, 4 bytes @@ -168,9 +166,8 @@ static const cochran_parser_layout_t cochran_emc_parser_layout = { SAMPLE_EMC, // type 512, // headersize 3, // samplesize - DATE_ENCODING_ELEMENTAL, // date_encoding - 0, 1, 2, 3, 4, 5, // second, minute, hour, day, month, year, 1 byte each - 0, // timestamp, 4 bytes + DATE_ENCODING_SMHDMY, // date_encoding + 0, // datetime, 6 bytes 6, // pt_profile_begin, 4 bytes 24, // water_conductivity, 1 byte 0=low(fresh), 2=high(sea) 30, // pt_profile_pre, 4 bytes @@ -387,25 +384,31 @@ cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dat if (abstract->size < layout->headersize) return DC_STATUS_DATAFORMAT;
+ time_t ts; + if (datetime) { - if (layout->date_encoding == DATE_ENCODING_SECONDS_SINCE) { - time_t ts; - struct tm *t; - ts = array_uint32_le(data + layout->timestamp) + COCHRAN_TIME_SHIFT; - t = localtime(&ts); - datetime->second = t->tm_sec; - datetime->minute = t->tm_min; - datetime->hour = t->tm_hour; - datetime->day = t->tm_mday; - datetime->month = t->tm_mon + 1; - datetime->year = 1900 + t->tm_year; - } else { - datetime->second = data[layout->second]; - datetime->minute = data[layout->minute]; - datetime->hour = data[layout->hour]; - datetime->day = data[layout->day]; - datetime->month = data[layout->month]; - datetime->year = data[layout->year] + (data[layout->year] > 91 ? 1900 : 2000); + switch (layout->date_encoding) + { + case DATE_ENCODING_MSDHYM: + datetime->second = data[layout->datetime + 1]; + datetime->minute = data[layout->datetime + 0]; + datetime->hour = data[layout->datetime + 3]; + datetime->day = data[layout->datetime + 2]; + datetime->month = data[layout->datetime + 5]; + datetime->year = data[layout->datetime + 4] + (data[layout->datetime + 4] > 91 ? 1900 : 2000); + break; + case DATE_ENCODING_SMHDMY: + datetime->second = data[layout->datetime + 0]; + datetime->minute = data[layout->datetime + 1]; + datetime->hour = data[layout->datetime + 2]; + datetime->day = data[layout->datetime + 3]; + datetime->month = data[layout->datetime + 4]; + datetime->year = data[layout->datetime + 5] + (data[layout->datetime + 5] > 91 ? 1900 : 2000); + break; + case DATE_ENCODING_TICKS: + ts = array_uint32_le(data + layout->datetime) + COCHRAN_EPOCH; + dc_datetime_localtime(datetime, ts); + break; } }
@@ -531,10 +534,11 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb // know what the dive summary values are (i.e. max depth, min temp) if (array_uint32_le(data + layout->pt_profile_end) == 0xFFFFFFFF) { corrupt_dive = 1; + dc_datetime_t d; + cochran_commander_parser_get_datetime(abstract, &d);
WARNING(abstract->context, "Incomplete dive on %02d/%02d/%02d at %02d:%02d:%02d, trying to parse samples", - data[layout->year], data[layout->month], data[layout->day], - data[layout->hour], data[layout->minute], data[layout->second]); + d.year, d.month, d.day, d.hour, d.minute, d.second);
// Eliminate inter-dive events size = cochran_commander_backparse(parser, samples, size);