RideFile reading refactoring

With the introduction of the rideSelected signal the RideFile was
opened (as previously) by the RideSummaryWindow::htmlSummary()
member. In some cases, this signal was processed by RideSummary window
AFTER the other charts (AllPlot etc) this results in 'No data' being
shown on other charts.

This patch moves the file reading to RideItem::ride() which was previously
a public RideFile * (that is now a protected member ride_). As a happy by
product it removes the need to check if the file has already been read
across all other functions ensuring in-core values are not accidentally
overwritten. The read errors are made available by a new RideItem::errors()
member.

This modification is required to support the RideImportWizard in freeing
loaded RideFiles during batch import to ensure virtual memory is not
exhausted when large numbers of files are imported at once. This modification
is also included in this patch.
This commit is contained in:
Mark Liversedge
2009-12-18 19:05:13 +00:00
committed by Sean Rhea
parent 71d67e2203
commit feb111a4ff
12 changed files with 68 additions and 60 deletions

View File

@@ -431,8 +431,8 @@ AllPlot::refreshIntervalMarkers()
}
d_mrk.clear();
QRegExp wkoAuto("^(Peak *[0-9]*(s|min)|Entire workout|Find #[0-9]*) *\\([^)]*\\)$");
if (rideItem->ride) {
foreach(const RideFileInterval &interval, rideItem->ride->intervals()) {
if (rideItem->ride()) {
foreach(const RideFileInterval &interval, rideItem->ride()->intervals()) {
// skip WKO autogenerated peak intervals
if (wkoAuto.exactMatch(interval.name))
continue;
@@ -449,7 +449,7 @@ AllPlot::refreshIntervalMarkers()
mrk->setValue(interval.start / 60.0, 0.0);
else
mrk->setValue((useMetricUnits ? 1 : MILES_PER_KM) *
rideItem->ride->timeToDistance(interval.start), 0.0);
rideItem->ride()->timeToDistance(interval.start), 0.0);
mrk->setLabel(text);
}
}
@@ -520,7 +520,7 @@ AllPlot::setData(RideItem *_rideItem)
wattsArray.clear();
RideFile *ride = rideItem->ride;
RideFile *ride = rideItem->ride();
if (ride && ride->deviceType() != QString("Manual CSV")) {
setTitle(ride->startTime().toString(GC_DATETIME_FORMAT));

View File

@@ -232,8 +232,8 @@ AllPlotWindow::setSmoothingFromLineEdit()
void
AllPlotWindow::setAllPlotWidgets(RideItem *ride)
{
if (ride->ride && ride->ride->deviceType() != QString("Manual CSV")) {
const RideFileDataPresent *dataPresent = ride->ride->areDataPresent();
if (ride->ride() && ride->ride()->deviceType() != QString("Manual CSV")) {
const RideFileDataPresent *dataPresent = ride->ride()->areDataPresent();
showPower->setEnabled(dataPresent->watts);
showHr->setEnabled(dataPresent->hr);
showSpeed->setEnabled(dataPresent->kph);
@@ -345,7 +345,7 @@ AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name)
x2 *= KM_PER_MILE;
}
foreach (const RideFilePoint *point, ride->ride->dataPoints()) {
foreach (const RideFilePoint *point, ride->ride()->dataPoints()) {
if ((allPlot->byDistance()==true && point->km>=x1 && point->km<x2) ||
(allPlot->byDistance()==false && point->secs/60>=x1 && point->secs/60<x2)) {
@@ -356,7 +356,7 @@ AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name)
duration2 = point->secs;
if (point->kph > 0.0)
secsMoving += ride->ride->recIntSecs();
secsMoving += ride->ride()->recIntSecs();
wattsTotal += point->watts;
bpmTotal += point->hr;
++arrayLength;
@@ -405,7 +405,7 @@ AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name)
// add average power to the end of the selection name
name += QString("(%1 watts)").arg(round((wattsTotal && arrayLength) ? wattsTotal/arrayLength : 0));
QTreeWidgetItem *last = new IntervalItem(ride->ride, name, duration1, duration2, distance1, distance2);
QTreeWidgetItem *last = new IntervalItem(ride->ride(), name, duration1, duration2, distance1, distance2);
allIntervals->addChild(last);
// now update the RideFileIntervals and all the plots etc

View File

@@ -200,7 +200,7 @@ HistogramWindow::setHistWidgets(RideItem *rideItem)
{
int count = 0;
assert(rideItem);
RideFile *ride = rideItem->ride;
RideFile *ride = rideItem->ride();
// prevent selection from changing during reconstruction of options
disconnect(histParameterCombo, SIGNAL(currentIndexChanged(int)),

View File

@@ -521,7 +521,7 @@ MainWindow::currentRide()
|| (treeWidget->selectedItems().first()->type() != RIDE_TYPE)) {
return NULL;
}
return ((RideItem*) treeWidget->selectedItems().first())->ride;
return ((RideItem*) treeWidget->selectedItems().first())->ride();
}
void
@@ -578,7 +578,7 @@ MainWindow::exportCSV()
return;
}
ride->ride->writeAsCsv(file, useMetricUnits);
ride->ride()->writeAsCsv(file, useMetricUnits);
}
void
@@ -678,17 +678,17 @@ MainWindow::findPowerPeaks()
return;
}
addIntervalForPowerPeaksForSecs(ride->ride, 5, "Peak 5s");
addIntervalForPowerPeaksForSecs(ride->ride, 10, "Peak 10s");
addIntervalForPowerPeaksForSecs(ride->ride, 20, "Peak 20s");
addIntervalForPowerPeaksForSecs(ride->ride, 30, "Peak 30s");
addIntervalForPowerPeaksForSecs(ride->ride, 60, "Peak 1min");
addIntervalForPowerPeaksForSecs(ride->ride, 120, "Peak 2min");
addIntervalForPowerPeaksForSecs(ride->ride, 300, "Peak 5min");
addIntervalForPowerPeaksForSecs(ride->ride, 600, "Peak 10min");
addIntervalForPowerPeaksForSecs(ride->ride, 1200, "Peak 20min");
addIntervalForPowerPeaksForSecs(ride->ride, 1800, "Peak 30min");
addIntervalForPowerPeaksForSecs(ride->ride, 3600, "Peak 60min");
addIntervalForPowerPeaksForSecs(ride->ride(), 5, "Peak 5s");
addIntervalForPowerPeaksForSecs(ride->ride(), 10, "Peak 10s");
addIntervalForPowerPeaksForSecs(ride->ride(), 20, "Peak 20s");
addIntervalForPowerPeaksForSecs(ride->ride(), 30, "Peak 30s");
addIntervalForPowerPeaksForSecs(ride->ride(), 60, "Peak 1min");
addIntervalForPowerPeaksForSecs(ride->ride(), 120, "Peak 2min");
addIntervalForPowerPeaksForSecs(ride->ride(), 300, "Peak 5min");
addIntervalForPowerPeaksForSecs(ride->ride(), 600, "Peak 10min");
addIntervalForPowerPeaksForSecs(ride->ride(), 1200, "Peak 20min");
addIntervalForPowerPeaksForSecs(ride->ride(), 1800, "Peak 30min");
addIntervalForPowerPeaksForSecs(ride->ride(), 3600, "Peak 60min");
// now update the RideFileIntervals
updateRideFileIntervals();
@@ -725,7 +725,7 @@ MainWindow::rideTreeWidgetSelectionChanged()
// now add the intervals for the current ride
if (ride) { // only if we have a ride pointer
RideFile *selected = ride->ride;
RideFile *selected = ride->ride();
if (selected) {
// get all the intervals in the currently selected RideFile
QList<RideFileInterval> intervals = selected->intervals();
@@ -743,7 +743,7 @@ MainWindow::rideTreeWidgetSelectionChanged()
}
// turn off tabs that don't make sense for manual file entry
if (ride->ride && ride->ride->deviceType() == QString("Manual CSV")) {
if (ride->ride() && ride->ride()->deviceType() == QString("Manual CSV")) {
tabWidget->setTabEnabled(3,false); // Power Histogram
tabWidget->setTabEnabled(4,false); // PF/PV Plot
}
@@ -783,7 +783,7 @@ MainWindow::updateRideFileIntervals()
// iterate over allIntervals as they are now defined
// and update the RideFile->intervals
RideItem *which = (RideItem *)treeWidget->selectedItems().first();
RideFile *current = which->ride;
RideFile *current = which->ride();
current->clearIntervals();
for (int i=0; i < allIntervals->childCount(); i++) {
// add the intervals as updated

View File

@@ -313,7 +313,7 @@ PfPvPlot::setData(RideItem *_rideItem)
rideItem = _rideItem;
RideFile *ride = rideItem->ride;
RideFile *ride = rideItem->ride();
if (ride) {
setTitle(ride->startTime().toString(GC_DATETIME_FORMAT));
@@ -409,7 +409,7 @@ PfPvPlot::showIntervals(RideItem *_rideItem)
rideItem = _rideItem;
RideFile *ride = rideItem->ride;
RideFile *ride = rideItem->ride();
if (ride) {
// due to the discrete power and cadence values returned by the

View File

@@ -409,7 +409,7 @@ PowerHist::setData(RideItem *_rideItem)
{
rideItem = _rideItem;
RideFile *ride = rideItem->ride;
RideFile *ride = rideItem->ride();
if (ride) {
setTitle(ride->startTime().toString(GC_DATETIME_FORMAT));
@@ -649,7 +649,7 @@ void
PowerHist::fixSelection() {
Selection s = selected;
RideFile *ride = rideItem->ride;
RideFile *ride = rideItem->ride();
if (ride)
do
@@ -701,7 +701,7 @@ bool PowerHist::shadeZones() const
{
return (
rideItem &&
rideItem->ride &&
rideItem->ride() &&
selected == wattsShaded
);
}

View File

@@ -663,6 +663,9 @@ RideImportWizard::abortClicked()
if (source.copy(fulltarget)) {
tableWidget->item(i,5)->setText(tr("File Saved"));
mainWindow->addRide(QFileInfo(fulltarget).fileName(), true); // add to tree view
// free immediately otherwise all imported rides are cached
// and with large imports this can lead to memory exhaustion
mainWindow->rideItem()->freeMemory();
} else
tableWidget->item(i,5)->setText(tr("Error - copy failed"));
}

View File

@@ -26,10 +26,9 @@
RideItem::RideItem(int type,
QString path, QString fileName, const QDateTime &dateTime,
const Zones *zones, QString notesFileName) :
QTreeWidgetItem(type), path(path), fileName(fileName),
dateTime(dateTime), ride(NULL), zones(zones), notesFileName(notesFileName)
QTreeWidgetItem(type), ride_(NULL), isdirty(false), path(path), fileName(fileName),
dateTime(dateTime), zones(zones), notesFileName(notesFileName)
{
isdirty = false;
setText(0, dateTime.toString("ddd"));
setText(1, dateTime.toString("MMM d, yyyy"));
setText(2, dateTime.toString("h:mm AP"));
@@ -46,6 +45,15 @@ RideItem::~RideItem()
}
}
RideFile *RideItem::ride()
{
if (ride_) return ride_;
// open the ride file
QFile file(path + "/" + fileName);
ride_ = RideFileFactory::instance().openRideFile(file, errors_);
return ride_;
}
void
RideItem::setDirty(bool val)
{
@@ -93,7 +101,7 @@ int RideItem::numZones()
double RideItem::timeInZone(int zone)
{
computeMetrics();
if (!ride)
if (!ride())
return 0.0;
assert(zone < numZones());
return time_in_zone[zone];
@@ -102,9 +110,9 @@ double RideItem::timeInZone(int zone)
void
RideItem::freeMemory()
{
if (ride) {
delete ride;
ride = NULL;
if (ride_) {
delete ride_;
ride_ = NULL;
}
}
@@ -117,13 +125,7 @@ RideItem::computeMetrics()
return;
}
if (!ride) {
QFile file(path + "/" + fileName);
QStringList errors;
ride = RideFileFactory::instance().openRideFile(file, errors);
if (!ride)
return;
}
if (!ride()) return;
computeMetricsTime = QDateTime::currentDateTime();
@@ -135,8 +137,8 @@ RideItem::computeMetrics()
time_in_zone.resize(num_zones);
}
double secs_delta = ride->recIntSecs();
foreach (const RideFilePoint *point, ride->dataPoints()) {
double secs_delta = ride()->recIntSecs();
foreach (const RideFilePoint *point, ride()->dataPoints()) {
if (point->watts >= 0.0) {
if (num_zones > 0) {
int zone = zones->whichZone(zone_range, point->watts);
@@ -169,7 +171,7 @@ RideItem::computeMetrics()
if (!metrics.contains(deps[j]))
goto later;
RideMetric *metric = factory.newMetric(name);
metric->compute(ride, zones, zone_range, metrics);
metric->compute(ride(), zones, zone_range, metrics);
metrics.insert(name, metric);
i.remove();
}

View File

@@ -30,6 +30,8 @@ class RideItem : public QTreeWidgetItem {
protected:
QVector<double> time_in_zone;
RideFile *ride_;
QStringList errors_;
bool isdirty;
public:
@@ -38,7 +40,8 @@ class RideItem : public QTreeWidgetItem {
QString fileName;
QDateTime dateTime;
QDateTime computeMetricsTime;
RideFile *ride;
RideFile *ride();
const QStringList errors() { return errors_; }
const Zones *zones;
QString notesFileName;

View File

@@ -161,18 +161,18 @@ RideSummaryWindow::htmlSummary() const
QString summary;
RideItem *rideItem = mainWindow->rideItem();
QFile file(rideItem->path + "/" + rideItem->fileName);
QStringList errors;
RideFile *ride = rideItem->ride;
if (!ride)
ride = RideFileFactory::instance().openRideFile(file, errors);
RideFile *ride = rideItem->ride();
// ridefile read errors?
if (!ride) {
summary = "<p>Couldn't read file \"" + file.fileName() + "\":";
QListIterator<QString> i(errors);
summary = tr("<p>Couldn't read file \"");
summary += rideItem->fileName + "\":";
QListIterator<QString> i(mainWindow->rideItem()->errors());
while (i.hasNext())
summary += "<br>" + i.next();
return summary;
}
summary = ("<p><center><h2>"
+ rideItem->dateTime.toString(tr("dddd MMMM d, yyyy, h:mm AP"))
+ "</h2><h3>" + tr("Device Type: ") + ride->deviceType() + "</h3>");
@@ -354,9 +354,9 @@ RideSummaryWindow::htmlSummary() const
summary += "</table>";
}
if (!errors.empty()) {
if (!rideItem->errors().empty()) {
summary += tr("<p><h2>Errors reading file:</h2><ul>");
QStringListIterator i(errors);
QStringListIterator i(rideItem->errors());
while(i.hasNext())
summary += " <li>" + i.next();
summary += "</ul>";

View File

@@ -141,7 +141,7 @@ MainWindow::saveSilent(RideItem *rideItem)
// save in GC format
GcFileReader reader;
reader.writeRideFile(rideItem->ride, savedFile);
reader.writeRideFile(rideItem->ride(), savedFile);
// rename the file and update the rideItem list to reflect the change
if (convert) {

View File

@@ -200,7 +200,7 @@ WeeklySummaryWindow::refresh()
) {
item->computeMetrics(); // generates item->ride
if (!item->ride)
if (!item->ride())
continue;
RideMetric *m;