void DivePlannerPointsModel::createPlan(bool replanCopy) { // Ok, so, here the diveplan creates a dive struct deco_state *cache = NULL; bool oldRecalc = setRecalc(false); removeDeco(); createTemporaryPlan(); setRecalc(oldRecalc); //TODO: C-based function here? struct decostop stoptable[60]; plan(&diveplan, &displayed_dive, DECOTIMESTEP, stoptable, &cache, isPlanner(), true); free(cache); if (!current_dive || displayed_dive.id != current_dive->id) { // we were planning a new dive, not re-planning an existing on record_dive(clone_dive(&displayed_dive)); } else if (current_dive && displayed_dive.id == current_dive->id) { // we are replanning a dive - make sure changes are reflected // correctly in the dive structure and copy it back into the dive table displayed_dive.maxdepth.mm = 0; displayed_dive.dc.maxdepth.mm = 0; fixup_dive(&displayed_dive); // Try to identify old planner output and remove only this part // Treat user provided text as plain text. QTextDocument notesDocument; notesDocument.setHtml(current_dive->notes); QString oldnotes(notesDocument.toPlainText()); int disclaimerPosition = oldnotes.indexOf(disclaimer); if (disclaimerPosition == 0) oldnotes.clear(); else if (disclaimerPosition >= 1) oldnotes.truncate(disclaimerPosition-1); // Deal with line breaks oldnotes.replace("\n", "<br>"); oldnotes.append(displayed_dive.notes); displayed_dive.notes = strdup(oldnotes.toUtf8().data()); // If we save as new create a copy of the dive here if (replanCopy) { struct dive *copy = alloc_dive(); copy_dive(current_dive, copy); copy->id = 0; copy->selected = false; copy->divetrip = NULL; if (current_dive->divetrip) add_dive_to_trip(copy, current_dive->divetrip); record_dive(copy); } copy_dive(&displayed_dive, current_dive); } mark_divelist_changed(true); // Remove and clean the diveplan, so we don't delete // the dive by mistake. free_dps(&diveplan); setPlanMode(NOTHING); planCreated(); }
void MainWindow::on_actionAddDive_triggered() { if (DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING || ui.InfoWidget->isEditing()) { QMessageBox::warning(this, tr("Warning"), tr("Please save or cancel the current dive edit before trying to add a dive.")); return; } dive_list()->rememberSelection(); dive_list()->unselectDives(); disableDcShortcuts(); DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD); // now cheat - create one dive that we use to store the info tab data in struct dive *dive = alloc_dive(); dive->when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset(); dive->dc.model = "manually added dive"; // don't translate! this is stored in the XML file dive->latitude.udeg = 0; dive->longitude.udeg = 0; record_dive(dive); // this isn't in the UI yet, so let's call the C helper function - we'll fix this up when // accepting the dive select_dive(get_divenr(dive)); ui.InfoWidget->setCurrentIndex(0); ui.InfoWidget->updateDiveInfo(selected_dive); ui.InfoWidget->addDiveStarted(); ui.stackedWidget->setCurrentIndex(PLANNERPROFILE); // Planner. ui.infoPane->setCurrentIndex(MAINTAB); DivePlannerPointsModel::instance()->clear(); DivePlannerPointsModel::instance()->createSimpleDive(); ui.ListWidget->reload(DiveTripModel::CURRENT); }
void DivePlannerPointsModel::createPlan() { // Ok, so, here the diveplan creates a dive char *cache = NULL; bool oldRecalc = plannerModel->setRecalc(false); removeDeco(); createTemporaryPlan(); plannerModel->setRecalc(oldRecalc); //TODO: C-based function here? plan(&diveplan, &cache, isPlanner(), true); if (!current_dive || displayed_dive.id != current_dive->id) { // we were planning a new dive, not re-planning an existing on record_dive(clone_dive(&displayed_dive)); } else if (current_dive && displayed_dive.id == current_dive->id) { // we are replanning a dive - make sure changes are reflected // correctly in the dive structure and copy it back into the dive table fixup_dive(&displayed_dive); copy_dive(&displayed_dive, current_dive); } mark_divelist_changed(true); // Remove and clean the diveplan, so we don't delete // the dive by mistake. free_dps(&diveplan); setPlanMode(NOTHING); planCreated(); }
bool QMLManager::undoDelete(int id) { if (!deletedDive || deletedDive->id != id) { qDebug() << "can't find the deleted dive"; return false; } if (deletedTrip) insert_trip(&deletedTrip); if (deletedDive->divetrip) { struct dive_trip *trip = deletedDive->divetrip; tripflag_t tripflag = deletedDive->tripflag; // this gets overwritten in add_dive_to_trip() deletedDive->divetrip = NULL; deletedDive->next = NULL; deletedDive->pprev = NULL; add_dive_to_trip(deletedDive, trip); deletedDive->tripflag = tripflag; } record_dive(deletedDive); QList<dive *>diveAsList; diveAsList << deletedDive; DiveListModel::instance()->addDive(diveAsList); changesNeedSaving(); deletedDive = NULL; deletedTrip = NULL; return true; }
void UndoDeleteDive::undo() { for (int i = 0; i < diveList.count(); i++) record_dive(diveList.at(i)); mark_divelist_changed(true); MainWindow::instance()->refreshDisplay(); }
void QMLManager::undoDelete(int id) { if (!deletedDive || deletedDive->id != id) { qDebug() << "can't find the deleted dive"; return; } if (deletedTrip) insert_trip(&deletedTrip); if (deletedDive->divetrip) { struct dive_trip *trip = deletedDive->divetrip; tripflag_t tripflag = deletedDive->tripflag; // this gets overwritten in add_dive_to_trip() deletedDive->divetrip = NULL; deletedDive->next = NULL; deletedDive->pprev = NULL; add_dive_to_trip(deletedDive, trip); deletedDive->tripflag = tripflag; } record_dive(deletedDive); QList<dive *>diveAsList; diveAsList << deletedDive; DiveListModel::instance()->addDive(diveAsList); // make sure the changes get saved if the app is no longer in the foreground // or if the user requests a save mark_divelist_changed(true); deletedDive = NULL; deletedTrip = NULL; }
void DivePlannerPointsModel::createPlan(bool replanCopy) { // Ok, so, here the diveplan creates a dive char *cache = NULL; bool oldRecalc = setRecalc(false); removeDeco(); createTemporaryPlan(); setRecalc(oldRecalc); //TODO: C-based function here? bool did_deco = plan(&diveplan, &cache, isPlanner(), true); free(cache); if (!current_dive || displayed_dive.id != current_dive->id) { // we were planning a new dive, not re-planning an existing on record_dive(clone_dive(&displayed_dive)); } else if (current_dive && displayed_dive.id == current_dive->id) { // we are replanning a dive - make sure changes are reflected // correctly in the dive structure and copy it back into the dive table displayed_dive.maxdepth.mm = 0; displayed_dive.dc.maxdepth.mm = 0; fixup_dive(&displayed_dive); if (replanCopy) { struct dive *copy = alloc_dive(); copy_dive(current_dive, copy); copy->id = 0; copy->selected = false; copy->divetrip = NULL; if (current_dive->divetrip) add_dive_to_trip(copy, current_dive->divetrip); record_dive(copy); QString oldnotes(current_dive->notes); if (oldnotes.indexOf(QString(disclaimer)) >= 0) oldnotes.truncate(oldnotes.indexOf(QString(disclaimer))); if (did_deco) oldnotes.append(displayed_dive.notes); displayed_dive.notes = strdup(oldnotes.toUtf8().data()); } copy_dive(&displayed_dive, current_dive); } mark_divelist_changed(true); // Remove and clean the diveplan, so we don't delete // the dive by mistake. free_dps(&diveplan); setPlanMode(NOTHING); planCreated(); }
static void finish_active_dive(void) { struct dive *dive = active_dive; if (dive) { active_dive = NULL; record_dive(dive); } }
int try_to_open_csv(struct memblock *mem, enum csv_format type) { char *p = mem->buffer; char *header[8]; int i, time; timestamp_t date; struct dive *dive; struct divecomputer *dc; for (i = 0; i < 8; i++) { header[i] = p; p = strchr(p, ','); if (!p) return 0; p++; } date = parse_date(header[2]); if (!date) return 0; dive = alloc_dive(); dive->when = date; dive->number = atoi(header[1]); dc = &dive->dc; time = 0; for (;;) { char *end; double val; struct sample *sample; errno = 0; val = strtod(p, &end); // FIXME == localization issue if (end == p) break; if (errno) break; sample = prepare_sample(dc); sample->time.seconds = time; add_sample_data(sample, type, val); finish_sample(dc); time++; dc->duration.seconds = time; if (*end != ',') break; p = end + 1; } record_dive(dive); return 1; }
void MainWindow::on_actionAddDive_triggered() { struct dive *dive; dive = alloc_dive(); record_dive(dive); process_dives(FALSE, FALSE); ui->InfoWidget->reload(); ui->globe->reload(); ui->ListWidget->reload(DiveTripModel::TREE); ui->ListWidget->setFocus(); }
void DivePlannerPointsModel::createPlan() { // Ok, so, here the diveplan creates a dive char *cache = NULL; bool oldRecalc = plannerModel->setRecalc(false); removeDeco(); createTemporaryPlan(); plannerModel->setRecalc(oldRecalc); //TODO: C-based function here? plan(&diveplan, &cache, isPlanner(), true); record_dive(clone_dive(&displayed_dive)); mark_divelist_changed(true); // Remove and clean the diveplan, so we don't delete // the dive by mistake. diveplan.dp = NULL; setPlanMode(NOTHING); planCreated(); }
static void finish_active_dive(void) { struct dive *dive = active_dive; if (dive) { /* check if we need to save pictures */ FOR_EACH_PICTURE(dive) { if (!picture_exists(picture)) save_picture_from_git(picture); } /* free any memory we allocated to track pictures */ while (pel) { free(pel->data); void *lastone = pel; pel = pel->next; free(lastone); } active_dive = NULL; record_dive(dive); } }
void MainWindow::on_actionAddDive_triggered() { // clear the selection for (int i = 0; i < dive_table.nr; i++) { struct dive *d = get_dive(i); if (d && d->selected) deselect_dive(i); } disableDcShortcuts(); DivePlannerPointsModel::instance()->setPlanMode(false); // now cheat - create one dive that we use to store the info tab data in struct dive *dive = alloc_dive(); dive->when = QDateTime::currentMSecsSinceEpoch() / 1000L; const char* model = strdup(tr("manually added dive").toLocal8Bit().constData()); dive->dc.model = model; // do not use tr here since it expects a char*. record_dive(dive); select_dive(get_divenr(dive)); ui.InfoWidget->updateDiveInfo(selected_dive); ui.stackedWidget->setCurrentIndex(PLANNERPROFILE); // Planner. ui.infoPane->setCurrentIndex(MAINTAB); DivePlannerPointsModel::instance()->createSimpleDive(); refreshDisplay(); ui.InfoWidget->addDiveStarted(); }
void UndoDeleteDive::undo() { // first bring back the trip(s) Q_FOREACH(struct dive_trip *trip, tripList) insert_trip(&trip); // now walk the list of deleted dives for (int i = 0; i < diveList.count(); i++) { struct dive *d = diveList.at(i); // we adjusted the divetrip to point to the "new" divetrip if (d->divetrip) { struct dive_trip *trip = d->divetrip; tripflag_t tripflag = d->tripflag; // this gets overwritten in add_dive_to_trip() d->divetrip = NULL; d->next = NULL; d->pprev = NULL; add_dive_to_trip(d, trip); d->tripflag = tripflag; } record_dive(diveList.at(i)); } mark_divelist_changed(true); MainWindow::instance()->refreshDisplay(); }
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, he; int hh = 0, mm = 0, ss = 0; int prev_depth = 0, cur_sampletime = 0, prev_setpoint = -1, prev_ndl = -1; bool has_depth = false, has_setpoint = false, has_ndl = false; char *lineptr, *key, *value; int cur_cylinder_index = 0; unsigned int prev_time = 0; struct dive *dive; struct divecomputer *dc; struct tm cur_tm; value = parse_mkvi_value(memtxt.buffer, "Dive started at"); if (sscanf(value, "%d-%d-%d %d:%d:%d", &y, &m, &d, &hh, &mm, &ss) != 6) { free(value); return -1; } free(value); 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"); value = parse_mkvi_value(memtxt.buffer, "Rig Serial number"); dive->dc.deviceid = atoi(value); free(value); dive->dc.divemode = CCR; dive->dc.no_o2sensors = 2; dive->cylinder[cur_cylinder_index].cylinder_use = OXYGEN; 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"); dive->cylinder[cur_cylinder_index].gasmix.o2.permille = 1000; cur_cylinder_index++; dive->cylinder[cur_cylinder_index].cylinder_use = DILUENT; 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"); value = parse_mkvi_value(memtxt.buffer, "Helium percentage"); he = atoi(value); free(value); value = parse_mkvi_value(memtxt.buffer, "Nitrogen percentage"); dive->cylinder[cur_cylinder_index].gasmix.o2.permille = (100 - atoi(value) - he) * 10; free(value); dive->cylinder[cur_cylinder_index].gasmix.he.permille = he * 10; cur_cylinder_index++; lineptr = strstr(memtxt.buffer, "Dive started at"); while (!empty_string(lineptr) && (lineptr = strchr(lineptr, '\n'))) { ++lineptr; // Skip over '\n' key = next_mkvi_key(lineptr); if (!key) break; value = parse_mkvi_value(lineptr, key); if (!value) { free(key); break; } add_extra_data(&dive->dc, key, value); free(key); free(value); } 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) { free(dive); return report_error(translate("gettextFromC", "Poseidon import failed: unable to read '%s'"), csv); } lineptr = memcsv.buffer; for (;;) { struct sample *sample; int type; int value; int sampletime; int gaschange = 0; /* Collect all the information for one sample */ sscanf(lineptr, "%d,%d,%d", &cur_sampletime, &type, &value); has_depth = false; has_setpoint = false; has_ndl = false; sample = prepare_sample(dc); /* * There was a bug in MKVI download tool that resulted in erroneous sample * times. This fix should work similarly as the vendor's own. */ sample->time.seconds = cur_sampletime < 0xFFFF * 3 / 4 ? cur_sampletime : prev_time; prev_time = sample->time.seconds; do { int i = sscanf(lineptr, "%d,%d,%d", &sampletime, &type, &value); switch (i) { case 3: switch (type) { case 0: //Mouth piece position event: 0=OC, 1=CC, 2=UN, 3=NC switch (value) { case 0: add_event(dc, cur_sampletime, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position OC")); break; case 1: add_event(dc, cur_sampletime, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position CC")); break; case 2: add_event(dc, cur_sampletime, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position unknown")); break; case 3: add_event(dc, cur_sampletime, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position not connected")); break; } break; case 3: //Power Off event add_event(dc, cur_sampletime, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Power off")); break; case 4: //Battery State of Charge in % #ifdef SAMPLE_EVENT_BATTERY add_event(dc, cur_sampletime, SAMPLE_EVENT_BATTERY, 0, value, QT_TRANSLATE_NOOP("gettextFromC", "battery")); #endif break; case 6: //PO2 Cell 1 Average add_sample_data(sample, POSEIDON_SENSOR1, value); break; case 7: //PO2 Cell 2 Average add_sample_data(sample, POSEIDON_SENSOR2, value); break; case 8: //Depth * 2 has_depth = true; prev_depth = value; add_sample_data(sample, POSEIDON_DEPTH, value); break; //9 Max Depth * 2 //10 Ascent/Descent Rate * 2 case 11: //Ascent Rate Alert >10 m/s add_event(dc, cur_sampletime, SAMPLE_EVENT_ASCENT, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "ascent")); break; case 13: //O2 Tank Pressure add_sample_pressure(sample, 0, lrint(value * 1000)); break; case 14: //Diluent Tank Pressure add_sample_pressure(sample, 1, lrint(value * 1000)); break; //16 Remaining dive time #1? //17 related to O2 injection case 20: //PO2 Setpoint has_setpoint = true; prev_setpoint = value; add_sample_data(sample, POSEIDON_SETPOINT, value); break; case 22: //End of O2 calibration Event: 0 = OK, 2 = Failed, rest of dive setpoint 1.0 if (value == 2) add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_END, 0, QT_TRANSLATE_NOOP("gettextFromC", "O₂ calibration failed")); add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_END, 0, QT_TRANSLATE_NOOP("gettextFromC", "O₂ calibration")); break; case 25: //25 Max Ascent depth add_sample_data(sample, POSEIDON_CEILING, value); break; case 31: //Start of O2 calibration Event add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_BEGIN, 0, QT_TRANSLATE_NOOP("gettextFromC", "O₂ calibration")); break; case 37: //Remaining dive time #2? has_ndl = true; prev_ndl = value; add_sample_data(sample, POSEIDON_NDL, value); break; case 39: // Water Temperature in Celcius add_sample_data(sample, POSEIDON_TEMP, value); break; case 85: //He diluent part in % gaschange += value << 16; break; case 86: //O2 diluent part in % gaschange += value; break; //239 Unknown, maybe PO2 at sensor validation? //240 Unknown, maybe PO2 at sensor validation? //247 Unknown, maybe PO2 Cell 1 during pressure test //248 Unknown, maybe PO2 Cell 2 during pressure test //250 PO2 Cell 1 //251 PO2 Cell 2 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 (gaschange) add_event(dc, cur_sampletime, SAMPLE_EVENT_GASCHANGE2, 0, gaschange, QT_TRANSLATE_NOOP("gettextFromC", "gaschange")); if (!has_depth) add_sample_data(sample, POSEIDON_DEPTH, prev_depth); if (!has_setpoint && prev_setpoint >= 0) add_sample_data(sample, POSEIDON_SETPOINT, prev_setpoint); if (!has_ndl && prev_ndl >= 0) add_sample_data(sample, POSEIDON_NDL, prev_ndl); finish_sample(dc); if (!lineptr || !*lineptr) break; } record_dive(dive); return 1; } else { return 0; } return 0; }
static int dive_cb(const unsigned char *data, unsigned int size, const unsigned char *fingerprint, unsigned int fsize, void *userdata) { int rc; dc_parser_t *parser = NULL; device_data_t *devdata = userdata; dc_datetime_t dt = {0}; struct tm tm; struct dive *dive; rc = create_parser(devdata, &parser); if (rc != DC_STATUS_SUCCESS) { dev_info(devdata, _("Unable to create parser for %s %s"), devdata->vendor, devdata->product); return rc; } rc = dc_parser_set_data(parser, data, size); if (rc != DC_STATUS_SUCCESS) { dev_info(devdata, _("Error registering the data")); dc_parser_destroy(parser); return rc; } import_dive_number++; dive = alloc_dive(); rc = dc_parser_get_datetime(parser, &dt); if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { dev_info(devdata, _("Error parsing the datetime")); dc_parser_destroy(parser); return rc; } tm.tm_year = dt.year; tm.tm_mon = dt.month-1; tm.tm_mday = dt.day; tm.tm_hour = dt.hour; tm.tm_min = dt.minute; tm.tm_sec = dt.second; dive->when = utc_mktime(&tm); // Parse the divetime. dev_info(devdata, _("Dive %d: %s %d %04d"), import_dive_number, monthname(tm.tm_mon), tm.tm_mday, year(tm.tm_year)); unsigned int divetime = 0; rc = dc_parser_get_field (parser, DC_FIELD_DIVETIME, 0, &divetime); if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { dev_info(devdata, _("Error parsing the divetime")); dc_parser_destroy(parser); return rc; } dive->duration.seconds = divetime; // Parse the maxdepth. double maxdepth = 0.0; rc = dc_parser_get_field(parser, DC_FIELD_MAXDEPTH, 0, &maxdepth); if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { dev_info(devdata, _("Error parsing the maxdepth")); dc_parser_destroy(parser); return rc; } dive->maxdepth.mm = maxdepth * 1000 + 0.5; // Parse the gas mixes. unsigned int ngases = 0; rc = dc_parser_get_field(parser, DC_FIELD_GASMIX_COUNT, 0, &ngases); if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { dev_info(devdata, _("Error parsing the gas mix count")); dc_parser_destroy(parser); return rc; } // Check if the libdivecomputer version already supports salinity double salinity = 1.03; #ifdef DC_FIELD_SALINITY rc = dc_parser_get_field(parser, DC_FIELD_SALINITY, 0, &salinity); if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { dev_info(devdata, _("Error obtaining water salinity")); dc_parser_destroy(parser); return rc; } #endif dive->salinity = salinity * 10000.0 + 0.5; rc = parse_gasmixes(devdata, dive, parser, ngases); if (rc != DC_STATUS_SUCCESS) { dev_info(devdata, _("Error parsing the gas mix")); dc_parser_destroy(parser); return rc; } // Initialize the sample data. rc = parse_samples(devdata, &dive, parser); if (rc != DC_STATUS_SUCCESS) { dev_info(devdata, _("Error parsing the samples")); dc_parser_destroy(parser); return rc; } dc_parser_destroy(parser); /* If we already saw this dive, abort. */ if (!devdata->force_download && find_dive(dive, devdata)) return 0; dive->downloaded = TRUE; record_dive(dive); mark_divelist_changed(TRUE); return 1; }
/* This function is called for both divelog and dive information that we get * from the SDA (what an insane design, btw). The object_id in the divelog * matches the logfilenr in the dive information (which has its own, often * different object_id) - we use this as the diveid. * We create the dive when parsing the divelog and then later, when we parse * the dive information we locate the already created dive via its diveid. * Most things just get parsed and converted into our internal data structures, * but the dive location API is even more crazy. We just get an id that is an * index into yet another data store that we read out later. In order to * correctly populate the location and gps data from that we need to remember * the adresses of those fields for every dive that references the divespot. */ static bool process_raw_buffer(uint32_t deviceid, char *inbuf, char **max_divenr, bool keep_number, int *for_dive) { char *buf = strdup(inbuf); char *tp, *bp, *tag, *type, *val; bool done = false; int inbuflen = strlen(inbuf); char *endptr = buf + inbuflen; bool log = false; char *sections[10]; int s, nr_sections = 0; struct dive *dive = NULL; #if UEMIS_DEBUG & 4 fprintf(debugfile, "p_r_b %s\n", inbuf); #endif if (for_dive) *for_dive = -1; bp = buf + 1; tp = next_token(&bp); if (strcmp(tp, "divelog") == 0) { /* this is a divelog */ log = true; tp = next_token(&bp); /* is it a valid entry or nothing ? */ if (strcmp(tp, "1.0") != 0 || strstr(inbuf, "divelog{1.0{{{{")) { free(buf); return false; } } else if (strcmp(tp, "dive") == 0) { /* this is dive detail */ tp = next_token(&bp); if (strcmp(tp, "1.0") != 0) { free(buf); return false; } } else { /* don't understand the buffer */ free(buf); return false; } if (log) { dive = uemis_start_dive(deviceid); } else { /* remember, we don't know if this is the right entry, * so first test if this is even a valid entry */ if (strstr(inbuf, "deleted{bool{true")) { #if UEMIS_DEBUG & 4 fprintf(debugfile, "p_r_b entry deleted\n"); #endif /* oops, this one isn't valid, suggest to try the previous one */ return false; } } while (!done) { /* the valid buffer ends with a series of delimiters */ if (bp >= endptr - 2 || !strcmp(bp, "{{")) break; tag = next_token(&bp); /* we also end if we get an empty tag */ if (*tag == '\0') break; for (s = 0; s < nr_sections; s++) if (!strcmp(tag, sections[s])) { tag = next_token(&bp); break; } type = next_token(&bp); if (!strcmp(type, "1.0")) { /* this tells us the sections that will follow; the tag here * is of the format dive-<section> */ sections[nr_sections] = strchr(tag, '-') + 1; #if UEMIS_DEBUG & 4 fprintf(debugfile, "Expect to find section %s\n", sections[nr_sections]); #endif if (nr_sections < sizeof(sections) - 1) nr_sections++; continue; } val = next_token(&bp); if (log && !strcmp(tag, "object_id")) { free(*max_divenr); *max_divenr = strdup(val); dive->dc.diveid = atoi(val); if (keep_number) dive->number = atoi(val); } else if (!log && !strcmp(tag, "logfilenr")) { /* this one tells us which dive we are adding data to */ dive = get_dive_by_uemis_diveid(atoi(val), deviceid); if (for_dive) *for_dive = atoi(val); } else if (!log && dive && !strcmp(tag, "divespot_id")) { track_divespot(val, dive->dc.diveid, &dive->location, &dive->latitude, &dive->longitude); } else if (dive) { parse_tag(dive, tag, val); } if (log && !strcmp(tag, "file_content")) done = true; /* done with one dive (got the file_content tag), but there could be more: * a '{' indicates the end of the record - but we need to see another "{{" * later in the buffer to know that the next record is complete (it could * be a short read because of some error */ if (done && ++bp < endptr && *bp != '{' && strstr(bp, "{{")) { done = false; record_dive(dive); mark_divelist_changed(true); dive = uemis_start_dive(deviceid); } } if (log) { if (dive->dc.diveid) { record_dive(dive); mark_divelist_changed(true); } else { /* partial dive */ free(dive); free(buf); return false; } } free(buf); return true; }
static int dive_cb(const unsigned char *data, unsigned int size, const unsigned char *fingerprint, unsigned int fsize, void *userdata) { int rc; parser_t *parser = NULL; device_data_t *devdata = userdata; dc_datetime_t dt = {0}; struct tm tm; struct dive *dive; rc = create_parser(devdata, &parser); if (rc != PARSER_STATUS_SUCCESS) { fprintf(stderr, "Unable to create parser for %s", devdata->name); return rc; } rc = parser_set_data(parser, data, size); if (rc != PARSER_STATUS_SUCCESS) { fprintf(stderr, "Error registering the data."); parser_destroy(parser); return rc; } dive = alloc_dive(); rc = parser_get_datetime(parser, &dt); if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) { fprintf(stderr, "Error parsing the datetime."); parser_destroy (parser); return rc; } tm.tm_year = dt.year; tm.tm_mon = dt.month-1; tm.tm_mday = dt.day; tm.tm_hour = dt.hour; tm.tm_min = dt.minute; tm.tm_sec = dt.second; dive->when = utc_mktime(&tm); // Parse the divetime. printf("Parsing the divetime.\n"); unsigned int divetime = 0; rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime); if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) { fprintf(stderr, "Error parsing the divetime."); parser_destroy(parser); return rc; } dive->duration.seconds = divetime; // Parse the maxdepth. printf("Parsing the maxdepth.\n"); double maxdepth = 0.0; rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth); if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) { fprintf(stderr, "Error parsing the maxdepth."); parser_destroy(parser); return rc; } dive->maxdepth.mm = maxdepth * 1000 + 0.5; // Parse the gas mixes. printf("Parsing the gas mixes.\n"); unsigned int ngases = 0; rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases); if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) { fprintf(stderr, "Error parsing the gas mix count."); parser_destroy(parser); return rc; } rc = parse_gasmixes(dive, parser, ngases); if (rc != PARSER_STATUS_SUCCESS) { fprintf(stderr, "Error parsing the gas mix."); parser_destroy(parser); return rc; } // Initialize the sample data. rc = parse_samples(&dive, parser); if (rc != PARSER_STATUS_SUCCESS) { fprintf(stderr, "Error parsing the samples."); parser_destroy(parser); return rc; } parser_destroy(parser); /* If we already saw this dive, abort. */ if (find_dive(dive, devdata)) return 0; record_dive(dive); return 1; }