diff options
author | Miika Turkia <miika.turkia@gmail.com> | 2014-05-28 09:55:46 +0300 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2014-10-26 20:33:50 -0700 |
commit | 8fcc074b493970fe45c7592d65bf216bc8bd8c42 (patch) | |
tree | ecc7fb237305a9c33e8dfee802c67b6413360268 | |
parent | bb020ae918593b5241e64582847e8d54ede7243d (diff) | |
download | subsurface-8fcc074b493970fe45c7592d65bf216bc8bd8c42.tar.gz |
Support for importing Poseidon MK6 logs
This patch adds support for importing the logs from a Poseidon MK6
rebreather. This DC produces logs that contain of a .txt file that has
all the meta data and a .csv file that contains the sample readings. The
CSV file is different from the others in that it has a line per each
sample reading at given time. Thus we have to merge all the lines from
one point in time into one sample reading of ours.
[Dirk Hohndel: addressed some compiler warnings]
Signed-off-by: Miika Turkia <miika.turkia@gmail.com>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r-- | dive.h | 1 | ||||
-rw-r--r-- | file.c | 206 | ||||
-rw-r--r-- | parse-xml.c | 8 | ||||
-rw-r--r-- | qt-ui/mainwindow.cpp | 26 | ||||
-rw-r--r-- | qt-ui/mainwindow.h | 1 |
5 files changed, 239 insertions, 3 deletions
@@ -603,6 +603,7 @@ extern int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char extern int parse_file(const char *filename); extern int parse_csv_file(const char *filename, int time, int depth, int temp, int po2f, int cnsf, int ndlf, int ttsf, int stopdepthf, int pressuref, int sepidx, const char *csvtemplate, int units); +extern int parse_txt_file(const char *filename, const char *csv); extern int parse_manual_file(const char *filename, int separator_index, int units, int number, int date, int time, int duration, int location, int gps, int maxdepth, int meandepth, int buddy, int notes, int weight, int tags); extern int save_dives(const char *filename); @@ -226,7 +226,14 @@ timestamp_t parse_date(const char *date) enum csv_format { CSV_DEPTH, CSV_TEMP, - CSV_PRESSURE + CSV_PRESSURE, + POSEIDON_DEPTH, + POSEIDON_TEMP, + POSEIDON_SETPOINT, + POSEIDON_SENSOR1, + POSEIDON_SENSOR2, + POSEIDON_PRESSURE, + POSEIDON_DILUENT }; static void add_sample_data(struct sample *sample, enum csv_format type, double val) @@ -241,6 +248,27 @@ static void add_sample_data(struct sample *sample, enum csv_format type, double case CSV_PRESSURE: sample->cylinderpressure.mbar = psi_to_mbar(val * 4); break; + case POSEIDON_DEPTH: + sample->depth.mm = val * 0.5 *1000; + break; + case POSEIDON_TEMP: + sample->temperature.mkelvin = C_to_mkelvin(val * 0.2); + break; + case POSEIDON_SETPOINT: + sample->setpoint.mbar = val * 10; + break; + case POSEIDON_SENSOR1: + sample->o2sensor[0].mbar = val * 10; + break; + case POSEIDON_SENSOR2: + sample->o2sensor[1].mbar = val * 10; + break; + case POSEIDON_PRESSURE: + sample->cylinderpressure.mbar = val * 1000; + break; + case POSEIDON_DILUENT: + sample->diluentpressure.mbar = val * 1000; + break; } } @@ -385,6 +413,182 @@ int parse_file(const char *filename) return 0; } +#define MATCH(buffer, pattern) \ + memcmp(buffer, pattern, strlen(pattern)) + +char *parse_mkvi_value(const char *haystack, const char *needle) +{ + char *lineptr, *valueptr, *endptr, *ret = NULL; + + if ((lineptr = strstr(haystack, needle)) != NULL) { + if ((valueptr = strstr(lineptr, ": ")) != NULL) { + valueptr += 2; + } + if ((endptr = strstr(lineptr, "\n")) != NULL) { + *endptr = 0; + ret = strdup(valueptr); + *endptr = '\n'; + + } + } + return ret; +} + +static int cur_cylinder_index; +int parse_txt_file(const char *filename, const char *csv) +{ + struct memblock memtxt, memcsv; + + if (readfile(filename, &memtxt) < 0) { + return report_error(translate("gettextFromC", "Failed to read '%s'"), filename); + } + + /* + * MkVI stores some information in .txt file but the whole profile and events are stored in .csv file. First + * make sure the input .txt looks like proper MkVI file, then start parsing the .csv. + */ + if (MATCH(memtxt.buffer, "MkVI_Config") == 0) { + int d, m, y; + int hh = 0, mm = 0, ss = 0; + int prev_depth = 0, cur_sampletime = 0; + bool has_depth = false; + char *lineptr; + + struct dive *dive; + struct divecomputer *dc; + struct tm cur_tm; + timestamp_t date; + + if (sscanf(parse_mkvi_value(memtxt.buffer, "Dive started at"), "%d-%d-%d %d:%d:%d", + &y, &m, &d, &hh, &mm, &ss) != 6) { + return -1; + } + + cur_tm.tm_year = y; + cur_tm.tm_mon = m - 1; + cur_tm.tm_mday = d; + cur_tm.tm_hour = hh; + cur_tm.tm_min = mm; + cur_tm.tm_sec = ss; + + dive = alloc_dive(); + dive->when = utc_mktime(&cur_tm);; + dive->dc.model = strdup("Poseidon MkVI Discovery"); + dive->dc.deviceid = atoi(parse_mkvi_value(memtxt.buffer, "Rig Serial number")); + dive->dc.dctype = CCR; + + dive->cylinder[cur_cylinder_index].type.size.mliter = 3000; + dive->cylinder[cur_cylinder_index].type.workingpressure.mbar = 200000; + dive->cylinder[cur_cylinder_index].type.description = strdup("3l Mk6"); + cur_cylinder_index++; + + dive->cylinder[cur_cylinder_index].type.size.mliter = 3000; + dive->cylinder[cur_cylinder_index].type.workingpressure.mbar = 200000; + dive->cylinder[cur_cylinder_index].type.description = strdup("3l Mk6"); + cur_cylinder_index++; + + dc = &dive->dc; + + /* + * Read samples from the CSV file. A sample contains all the lines with same timestamp. The CSV file has + * the following format: + * + * timestamp, type, value + * + * And following fields are of interest to us: + * + * 6 sensor1 + * 7 sensor2 + * 8 depth + * 13 o2 tank pressure + * 14 diluent tank pressure + * 20 o2 setpoint + * 39 water temp + */ + + if (readfile(csv, &memcsv) < 0) { + return report_error(translate("gettextFromC", "Poseidon import failed: unable to read '%s'"), csv); + } + lineptr = memcsv.buffer; + for (;;) { + char *end; + double val; + struct sample *sample; + int type; + int value; + int sampletime; + + /* Collect all the information for one sample */ + sscanf(lineptr, "%d,%d,%d", &cur_sampletime, &type, &value); + + has_depth = false; + sample = prepare_sample(dc); + sample->time.seconds = cur_sampletime; + + do { + int i = sscanf(lineptr, "%d,%d,%d", &sampletime, &type, &value); + switch (i) { + case 3: + switch (type) { + case 6: + add_sample_data(sample, POSEIDON_SENSOR1, value); + break; + case 7: + add_sample_data(sample, POSEIDON_SENSOR2, value); + break; + case 8: + has_depth = true; + prev_depth = value; + add_sample_data(sample, POSEIDON_DEPTH, value); + break; + case 13: + add_sample_data(sample, POSEIDON_PRESSURE, value); + break; + case 14: + add_sample_data(sample, POSEIDON_DILUENT, value); + break; + case 20: + add_sample_data(sample, POSEIDON_SETPOINT, value); + break; + case 39: + add_sample_data(sample, POSEIDON_TEMP, value); + break; + default: + break; + } /* sample types */ + break; + case EOF: + break; + default: + printf("Unable to parse input: %s\n", lineptr); + break; + } + + lineptr = strchr(lineptr, '\n'); + if (!lineptr || !*lineptr) + break; + lineptr++; + + /* Grabbing next sample time */ + sscanf(lineptr, "%d,%d,%d", &cur_sampletime, &type, &value); + } while (sampletime == cur_sampletime); + + if (!has_depth) + add_sample_data(sample, POSEIDON_DEPTH, prev_depth); + finish_sample(dc); + + if (!lineptr || !*lineptr) + break; + } + record_dive(dive); + return 1; + } else { + return report_error(translate("gettextFromC", "No matching DC found for file '%s'"), csv); + } + + return 0; +} + #define MAXCOLDIGITS 3 #define MAXCOLS 100 int parse_csv_file(const char *filename, int timef, int depthf, int tempf, int po2f, int cnsf, int ndlf, int ttsf, int stopdepthf, int pressuref, int sepidx, const char *csvtemplate, int unitidx) diff --git a/parse-xml.c b/parse-xml.c index d54456e92..6da99b378 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -17,6 +17,7 @@ #include "dive.h" #include "device.h" +#include "membuffer.h" int verbose, quit; int metric = 1; @@ -1738,6 +1739,13 @@ void parse_xml_buffer(const char *url, const char *buffer, int size, xmlFreeDoc(doc); } +void parse_mkvi_buffer(struct membuffer *txt, struct membuffer *csv, const char *starttime) +{ + dive_start(); + divedate(starttime, &cur_dive->when); + dive_end(); +} + extern int dm4_events(void *handle, int columns, char **data, char **column) { event_start(); diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index dfe8a2380..cb1ffea2d 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -1174,6 +1174,23 @@ void MainWindow::importFiles(const QStringList fileNames) refreshDisplay(); } +void MainWindow::importTxtFiles(const QStringList fileNames) +{ + if (fileNames.isEmpty()) + return; + + QByteArray fileNamePtr, csv; + + for (int i = 0; i < fileNames.size(); ++i) { + fileNamePtr = QFile::encodeName(fileNames.at(i)); + csv = fileNamePtr.data(); + csv.replace(strlen(csv.data()) - 3, 3, "csv"); + parse_txt_file(fileNamePtr.data(), csv); + } + process_dives(true, false); + refreshDisplay(); +} + void MainWindow::loadFiles(const QStringList fileNames) { if (fileNames.isEmpty()) @@ -1208,14 +1225,15 @@ void MainWindow::on_actionImportDiveLog_triggered() QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open dive log file"), lastUsedDir(), tr("Dive log files (*.xml *.uddf *.udcf *.csv *.jlb *.dld *.sde *.db);;" "XML files (*.xml);;UDDF/UDCF files(*.uddf *.udcf);;JDiveLog files(*.jlb);;" - "Suunto Files(*.sde *.db);;CSV Files(*.csv);;All Files(*)")); + "Suunto Files(*.sde *.db);;CSV Files(*.csv);;MkVI Files(*.txt);;All Files(*)")); if (fileNames.isEmpty()) return; updateLastUsedDir(QFileInfo(fileNames[0]).dir().path()); - QStringList logFiles = fileNames.filter(QRegExp("^.*\\.(?!csv)", Qt::CaseInsensitive)); + QStringList logFiles = fileNames.filter(QRegExp("^.*\\.(?!csv|?!txt)", Qt::CaseInsensitive)); QStringList csvFiles = fileNames.filter(".csv", Qt::CaseInsensitive); + QStringList txtFiles = fileNames.filter(".txt", Qt::CaseInsensitive); if (logFiles.size()) { importFiles(logFiles); } @@ -1226,6 +1244,10 @@ void MainWindow::on_actionImportDiveLog_triggered() process_dives(true, false); refreshDisplay(); } + + if (txtFiles.size()) { + importTxtFiles(txtFiles); + } } void MainWindow::editCurrentDive() diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index edf33a500..26f4176ac 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -73,6 +73,7 @@ public: void enableDcShortcuts(); void loadFiles(const QStringList files); void importFiles(const QStringList importFiles); + void importTxtFiles(const QStringList fileNames); void cleanUpEmpty(); void setToolButtonsEnabled(bool enabled); ProfileWidget2 *graphics() const; |