mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
added zones to the weekly summary, consolidated and cleaned up code
This commit is contained in:
@@ -26,6 +26,7 @@ HEADERS += \
|
||||
PowerHist.h \
|
||||
RawFile.h \
|
||||
RideItem.h \
|
||||
Time.h \
|
||||
Zones.h
|
||||
|
||||
SOURCES += \
|
||||
@@ -39,6 +40,7 @@ SOURCES += \
|
||||
PowerHist.cpp \
|
||||
RawFile.cpp \
|
||||
RideItem.cpp \
|
||||
Time.cpp \
|
||||
Zones.cpp \
|
||||
\
|
||||
main.cpp
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include "RawFile.h"
|
||||
#include "RideItem.h"
|
||||
#include "Settings.h"
|
||||
#include "Time.h"
|
||||
#include "Zones.h"
|
||||
#include <assert.h>
|
||||
#include <QApplication>
|
||||
#include <QtGui>
|
||||
@@ -47,6 +49,17 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
setWindowTitle(home.dirName());
|
||||
settings.setValue(GC_SETTINGS_LAST, home.dirName());
|
||||
|
||||
QFile zonesFile(home.absolutePath() + "/power.zones");
|
||||
if (zonesFile.exists()) {
|
||||
zones = new Zones();
|
||||
if (!zones->read(zonesFile)) {
|
||||
QMessageBox::warning(this, tr("Zones File Error"),
|
||||
zones->errorString());
|
||||
delete zones;
|
||||
zones = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
QVariant geom = settings.value(GC_SETTINGS_MAIN_GEOM);
|
||||
if (geom == QVariant())
|
||||
resize(640, 480);
|
||||
@@ -80,7 +93,8 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
QDate date(rx.cap(1).toInt(), rx.cap(2).toInt(),rx.cap(3).toInt());
|
||||
QTime time(rx.cap(4).toInt(), rx.cap(5).toInt(),rx.cap(6).toInt());
|
||||
QDateTime dt(date, time);
|
||||
last = new RideItem(allRides, RIDE_TYPE, home.path(), name, dt);
|
||||
last = new RideItem(allRides, RIDE_TYPE, home.path(),
|
||||
name, dt, zones);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +294,8 @@ MainWindow::addRide(QString name)
|
||||
QDate date(rx.cap(1).toInt(), rx.cap(2).toInt(),rx.cap(3).toInt());
|
||||
QTime time(rx.cap(4).toInt(), rx.cap(5).toInt(),rx.cap(6).toInt());
|
||||
QDateTime dt(date, time);
|
||||
RideItem *last = new RideItem(allRides, RIDE_TYPE, home.path(), name, dt);
|
||||
RideItem *last = new RideItem(allRides, RIDE_TYPE, home.path(),
|
||||
name, dt, zones);
|
||||
cpintPlot->needToScanRides = true;
|
||||
tabWidget->setCurrentIndex(0);
|
||||
treeWidget->setCurrentItem(last);
|
||||
@@ -447,6 +462,11 @@ MainWindow::rideSelected()
|
||||
double weeklyDistance = 0.0;
|
||||
double weeklyWork = 0.0;
|
||||
|
||||
double *time_in_zone = NULL;
|
||||
int zone_range = -1;
|
||||
int num_zones = -1;
|
||||
bool zones_ok = true;
|
||||
|
||||
for (int i = 0; i < allRides->childCount(); ++i) {
|
||||
if (allRides->child(i)->type() == RIDE_TYPE) {
|
||||
RideItem *item = (RideItem*) allRides->child(i);
|
||||
@@ -455,6 +475,18 @@ MainWindow::rideSelected()
|
||||
weeklySeconds += item->secsMovingOrPedaling();
|
||||
weeklyDistance += item->totalDistance();
|
||||
weeklyWork += item->totalWork();
|
||||
if (zone_range == -1) {
|
||||
zone_range = item->zoneRange();
|
||||
num_zones = item->numZones();
|
||||
time_in_zone = new double[num_zones];
|
||||
}
|
||||
else if (item->zoneRange() != zone_range) {
|
||||
zones_ok = false;
|
||||
}
|
||||
if (zone_range != -1) {
|
||||
for (int j = 0; j < num_zones; ++j)
|
||||
time_in_zone[j] += item->timeInZone(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -464,7 +496,7 @@ MainWindow::rideSelected()
|
||||
minutes %= 60;
|
||||
|
||||
const char *dateFormat = "MM/dd/yyyy";
|
||||
weeklySummary->setHtml(tr(
|
||||
QString summary = tr(
|
||||
"<center>"
|
||||
"<h2>Week of %1 through %2</h2>"
|
||||
"<h2>Summary</h2>"
|
||||
@@ -477,15 +509,30 @@ MainWindow::rideSelected()
|
||||
"<tr><td>Total work (kJ):</td>"
|
||||
" <td align=\"right\">%6</td></tr>"
|
||||
"</table>"
|
||||
"</center>"
|
||||
// TODO: add averages
|
||||
)
|
||||
.arg(wstart.toString(dateFormat))
|
||||
.arg(wstart.addDays(6).toString(dateFormat))
|
||||
.arg(hours)
|
||||
.arg(minutes, 2, 10, QLatin1Char('0'))
|
||||
.arg((unsigned) round(weeklyDistance))
|
||||
.arg((unsigned) round(weeklyWork))
|
||||
);
|
||||
.arg((unsigned) round(weeklyWork));
|
||||
|
||||
if (zone_range != -1) {
|
||||
summary += "<h2>Power Zones</h2>";
|
||||
if (!zones_ok)
|
||||
summary += "Error: Week spans more than one zone range.";
|
||||
else {
|
||||
summary +=
|
||||
zones->summarize(zone_range, time_in_zone, num_zones);
|
||||
}
|
||||
}
|
||||
|
||||
summary += "</center>";
|
||||
|
||||
// TODO: add daily breakdown
|
||||
|
||||
weeklySummary->setHtml(summary);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -564,31 +611,6 @@ MainWindow::tabChanged(int index)
|
||||
}
|
||||
}
|
||||
|
||||
static QString
|
||||
time_to_string(double secs) {
|
||||
if (secs < 60.0)
|
||||
return QString("%1s").arg(secs, 0, 'f', 2, QLatin1Char('0'));
|
||||
QString result;
|
||||
unsigned rounded = (unsigned) round(secs);
|
||||
bool needs_colon = false;
|
||||
if (rounded >= 3600) {
|
||||
result += QString("%1h").arg(rounded / 3600);
|
||||
rounded %= 3600;
|
||||
needs_colon = true;
|
||||
}
|
||||
if (needs_colon || rounded >= 60) {
|
||||
if (needs_colon)
|
||||
result += " ";
|
||||
result += QString("%1m").arg(rounded / 60, 2, 10, QLatin1Char('0'));
|
||||
rounded %= 60;
|
||||
needs_colon = true;
|
||||
}
|
||||
if (needs_colon)
|
||||
result += " ";
|
||||
result += QString("%1s").arg(rounded, 2, 10, QLatin1Char('0'));
|
||||
return result;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
curve_to_point(double x, const QwtPlotCurve *curve)
|
||||
{
|
||||
@@ -617,10 +639,10 @@ MainWindow::pickerMoved(const QPoint &pos)
|
||||
{
|
||||
double minutes = cpintPlot->invTransform(QwtPlot::xBottom, pos.x());
|
||||
cpintTimeLabel->setText(tr("Interval Duration: %1")
|
||||
.arg(time_to_string(60.0*minutes)));
|
||||
cpintAllLabel->setText(tr("All Rides: %1 watts").arg(
|
||||
curve_to_point(minutes, cpintPlot->getAllCurve())));
|
||||
.arg(interval_to_str(60.0*minutes)));
|
||||
cpintTodayLabel->setText(tr("Today: %1 watts").arg(
|
||||
curve_to_point(minutes, cpintPlot->getThisCurve())));
|
||||
cpintAllLabel->setText(tr("All Rides: %1 watts").arg(
|
||||
curve_to_point(minutes, cpintPlot->getAllCurve())));
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ class AllPlot;
|
||||
class CpintPlot;
|
||||
class PowerHist;
|
||||
class QwtPlotPicker;
|
||||
class Zones;
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
@@ -78,6 +79,7 @@ class MainWindow : public QMainWindow
|
||||
QLineEdit *binWidthLineEdit;
|
||||
QTreeWidgetItem *allRides;
|
||||
PowerHist *powerHist;
|
||||
Zones *zones;
|
||||
};
|
||||
|
||||
#endif // _GC_MainWindow_h
|
||||
|
||||
@@ -21,41 +21,26 @@
|
||||
#include "RideItem.h"
|
||||
#include "RawFile.h"
|
||||
#include "Settings.h"
|
||||
#include "Time.h"
|
||||
#include "Zones.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
static QString
|
||||
time_to_string(double secs) {
|
||||
QString result;
|
||||
unsigned rounded = (unsigned) round(secs);
|
||||
bool needs_colon = false;
|
||||
if (rounded >= 3600) {
|
||||
result += QString("%1").arg(rounded / 3600);
|
||||
rounded %= 3600;
|
||||
needs_colon = true;
|
||||
}
|
||||
if (needs_colon || rounded >= 60) {
|
||||
if (needs_colon)
|
||||
result += ":";
|
||||
result += QString("%1").arg(rounded / 60, 2, 10, QLatin1Char('0'));
|
||||
rounded %= 60;
|
||||
needs_colon = true;
|
||||
}
|
||||
if (needs_colon)
|
||||
result += ":";
|
||||
result += QString("%1").arg(rounded, 2, 10, QLatin1Char('0'));
|
||||
return result;
|
||||
}
|
||||
|
||||
RideItem::RideItem(QTreeWidgetItem *parent, int type, QString path,
|
||||
QString fileName, const QDateTime &dateTime) :
|
||||
QString fileName, const QDateTime &dateTime,
|
||||
const Zones *zones) :
|
||||
QTreeWidgetItem(parent, type), path(path),
|
||||
fileName(fileName), dateTime(dateTime)
|
||||
fileName(fileName), dateTime(dateTime), zones(zones)
|
||||
{
|
||||
setText(0, dateTime.toString("ddd"));
|
||||
setText(1, dateTime.toString("MMM d, yyyy"));
|
||||
setText(2, dateTime.toString("h:mm AP"));
|
||||
setTextAlignment(1, Qt::AlignRight);
|
||||
setTextAlignment(2, Qt::AlignRight);
|
||||
|
||||
time_in_zone = NULL;
|
||||
num_zones = -1;
|
||||
zone_range = -1;
|
||||
}
|
||||
|
||||
static void summarize(QString &intervals,
|
||||
@@ -122,6 +107,30 @@ double RideItem::totalWork()
|
||||
return total_work;
|
||||
}
|
||||
|
||||
int RideItem::zoneRange()
|
||||
{
|
||||
if (summary.isEmpty())
|
||||
htmlSummary();
|
||||
return zone_range;
|
||||
}
|
||||
|
||||
int RideItem::numZones()
|
||||
{
|
||||
if (summary.isEmpty())
|
||||
htmlSummary();
|
||||
assert(zone_range >= 0);
|
||||
return num_zones;
|
||||
}
|
||||
|
||||
double RideItem::timeInZone(int zone)
|
||||
{
|
||||
if (summary.isEmpty())
|
||||
htmlSummary();
|
||||
assert(zone_range >= 0);
|
||||
assert(zone < num_zones);
|
||||
return time_in_zone[zone];
|
||||
}
|
||||
|
||||
QString
|
||||
RideItem::htmlSummary()
|
||||
{
|
||||
@@ -137,6 +146,16 @@ RideItem::htmlSummary()
|
||||
return summary;
|
||||
}
|
||||
|
||||
if (zones) {
|
||||
zone_range = zones->whichRange(dateTime.date());
|
||||
if (zone_range >= 0) {
|
||||
num_zones = zones->numZones(zone_range);
|
||||
time_in_zone = new double[num_zones];
|
||||
for (int i = 0; i < num_zones; ++i)
|
||||
time_in_zone[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
secs_moving_or_pedaling = 0.0;
|
||||
double secs_moving = 0.0;
|
||||
double total_watts = 0.0;
|
||||
@@ -174,14 +193,20 @@ RideItem::htmlSummary()
|
||||
}
|
||||
|
||||
double secs_delta = raw->rec_int_ms / 1000.0;
|
||||
if ((point->mph > 0.0) || (point->cad > 0.0))
|
||||
if ((point->mph > 0.0) || (point->cad > 0.0)) {
|
||||
secs_moving_or_pedaling += secs_delta;
|
||||
}
|
||||
if (point->mph > 0.0)
|
||||
secs_moving += secs_delta;
|
||||
if (point->watts >= 0.0) {
|
||||
total_watts += point->watts * secs_delta;
|
||||
secs_watts += secs_delta;
|
||||
int_watts_sum += point->watts * secs_delta;
|
||||
if (zones) {
|
||||
int zone = zones->whichZone(zone_range, point->watts);
|
||||
if (zone >= 0)
|
||||
time_in_zone[zone] += secs_delta;
|
||||
}
|
||||
}
|
||||
if (point->hr > 0) {
|
||||
total_hr += point->hr * secs_delta;
|
||||
@@ -211,35 +236,48 @@ RideItem::htmlSummary()
|
||||
total_distance = raw->points.back()->miles;
|
||||
total_work = total_watts / 1000.0;
|
||||
|
||||
summary += "<p><table align=\"center\" width=\"60%\" border=0>";
|
||||
summary += "<tr><td>Total workout time:</td><td align=\"right\">" +
|
||||
summary += "<p>";
|
||||
summary += "<table align=\"center\" width=\"90%\" border=0>";
|
||||
summary += "<tr><td align=\"center\"><h2>Totals</h2></td>";
|
||||
summary += "<td align=\"center\"><h2>Averages</h2></td></tr>";
|
||||
summary += "<tr><td>";
|
||||
summary += "<table align=\"center\" width=\"70%\" border=0>";
|
||||
summary += "<tr><td>Workout time:</td><td align=\"right\">" +
|
||||
time_to_string(raw->points.back()->secs);
|
||||
summary += "<tr><td>Total time riding:</td><td align=\"right\">" +
|
||||
summary += "<tr><td>Time riding:</td><td align=\"right\">" +
|
||||
time_to_string(secs_moving_or_pedaling) + "</td></tr>";
|
||||
summary += QString("<tr><td>Total distance (miles):</td>"
|
||||
summary += QString("<tr><td>Distance (miles):</td>"
|
||||
"<td align=\"right\">%1</td></tr>")
|
||||
.arg(total_distance, 0, 'f', 1);
|
||||
summary += QString("<tr><td>Total work (kJ):</td>"
|
||||
summary += QString("<tr><td>Work (kJ):</td>"
|
||||
"<td align=\"right\">%1</td></tr>")
|
||||
.arg((unsigned) round(total_work));
|
||||
summary += QString("<tr><td>Average speed (mph):</td>"
|
||||
summary += "</table></td><td>";
|
||||
summary += "<table align=\"center\" width=\"70%\" border=0>";
|
||||
summary += QString("<tr><td>Speed (mph):</td>"
|
||||
"<td align=\"right\">%1</td></tr>")
|
||||
.arg(((secs_moving == 0.0) ? 0.0
|
||||
: raw->points.back()->miles / secs_moving * 3600.0),
|
||||
0, 'f', 1);
|
||||
summary += QString("<tr><td>Average power (watts):</td>"
|
||||
summary += QString("<tr><td>Power (watts):</td>"
|
||||
"<td align=\"right\">%1</td></tr>")
|
||||
.arg((unsigned) avg_watts);
|
||||
summary +=QString("<tr><td>Average heart rate (bpm):</td>"
|
||||
summary +=QString("<tr><td>Heart rate (bpm):</td>"
|
||||
"<td align=\"right\">%1</td></tr>")
|
||||
.arg((unsigned) ((secs_hr == 0.0) ? 0.0
|
||||
: round(total_hr / secs_hr)));
|
||||
summary += QString("<tr><td>Average cadence (rpm):</td>"
|
||||
summary += QString("<tr><td>Cadence (rpm):</td>"
|
||||
"<td align=\"right\">%1</td></tr>")
|
||||
.arg((unsigned) ((secs_cad == 0.0) ? 0.0
|
||||
: round(total_cad / secs_cad)));
|
||||
summary += "</table></td></tr>";
|
||||
summary += "</table>";
|
||||
|
||||
if (zones) {
|
||||
summary += "<h2>Power Zones</h2>";
|
||||
summary += zones->summarize(zone_range, time_in_zone, num_zones);
|
||||
}
|
||||
|
||||
if (last_interval > 0) {
|
||||
summary += "<p><h2>Intervals</h2>\n<p>\n";
|
||||
summary += "<table align=\"center\" width=\"90%\" ";
|
||||
@@ -278,4 +316,3 @@ RideItem::htmlSummary()
|
||||
return summary;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <QtGui>
|
||||
|
||||
class RawFile;
|
||||
class Zones;
|
||||
|
||||
class RideItem : public QTreeWidgetItem {
|
||||
|
||||
@@ -32,6 +33,9 @@ class RideItem : public QTreeWidgetItem {
|
||||
double secs_moving_or_pedaling;
|
||||
double total_distance;
|
||||
double total_work;
|
||||
double *time_in_zone;
|
||||
int num_zones;
|
||||
int zone_range;
|
||||
|
||||
public:
|
||||
|
||||
@@ -40,14 +44,20 @@ class RideItem : public QTreeWidgetItem {
|
||||
QDateTime dateTime;
|
||||
QString summary;
|
||||
RawFile *raw;
|
||||
const Zones *zones;
|
||||
|
||||
RideItem(QTreeWidgetItem *parent, int type, QString path,
|
||||
QString fileName, const QDateTime &dateTime);
|
||||
QString fileName, const QDateTime &dateTime,
|
||||
const Zones *zones);
|
||||
|
||||
QString htmlSummary();
|
||||
double secsMovingOrPedaling();
|
||||
double totalDistance();
|
||||
double totalWork();
|
||||
|
||||
int zoneRange();
|
||||
int numZones();
|
||||
double timeInZone(int zone);
|
||||
};
|
||||
|
||||
#endif // _GC_RideItem_h
|
||||
|
||||
48
src/gui/Time.cpp
Normal file
48
src/gui/Time.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
#include "Time.h"
|
||||
#include <math.h>
|
||||
|
||||
QString time_to_string(double secs)
|
||||
{
|
||||
QString result;
|
||||
unsigned rounded = (unsigned) round(secs);
|
||||
bool needs_colon = false;
|
||||
if (rounded >= 3600) {
|
||||
result += QString("%1").arg(rounded / 3600);
|
||||
rounded %= 3600;
|
||||
needs_colon = true;
|
||||
}
|
||||
if (needs_colon)
|
||||
result += ":";
|
||||
result += QString("%1").arg(rounded / 60, 2, 10, QLatin1Char('0'));
|
||||
rounded %= 60;
|
||||
result += ":";
|
||||
result += QString("%1").arg(rounded, 2, 10, QLatin1Char('0'));
|
||||
return result;
|
||||
}
|
||||
|
||||
QString interval_to_str(double secs)
|
||||
{
|
||||
if (secs < 60.0)
|
||||
return QString("%1s").arg(secs, 0, 'f', 2, QLatin1Char('0'));
|
||||
QString result;
|
||||
unsigned rounded = (unsigned) round(secs);
|
||||
bool needs_colon = false;
|
||||
if (rounded >= 3600) {
|
||||
result += QString("%1h").arg(rounded / 3600);
|
||||
rounded %= 3600;
|
||||
needs_colon = true;
|
||||
}
|
||||
if (needs_colon || rounded >= 60) {
|
||||
if (needs_colon)
|
||||
result += " ";
|
||||
result += QString("%1m").arg(rounded / 60, 2, 10, QLatin1Char('0'));
|
||||
rounded %= 60;
|
||||
needs_colon = true;
|
||||
}
|
||||
if (needs_colon)
|
||||
result += " ";
|
||||
result += QString("%1s").arg(rounded, 2, 10, QLatin1Char('0'));
|
||||
return result;
|
||||
}
|
||||
|
||||
30
src/gui/Time.h
Normal file
30
src/gui/Time.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* $Id: RideItem.cpp,v 1.3 2006/07/09 15:30:34 srhea Exp $
|
||||
*
|
||||
* Copyright (c) 2006 Sean C. Rhea (srhea@srhea.net)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _Time_h
|
||||
#define _Time_h
|
||||
|
||||
#include <QString>
|
||||
|
||||
QString interval_to_str(double secs); // output like 1h 2m 3s
|
||||
QString time_to_string(double secs); // output like 1:02:03
|
||||
|
||||
#endif // _Time_h
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
*/
|
||||
|
||||
#include "Zones.h"
|
||||
#include "Time.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
bool Zones::read(QFile &file)
|
||||
{
|
||||
@@ -51,17 +53,17 @@ bool Zones::read(QFile &file)
|
||||
int pos = commentrx.indexIn(line, 0);
|
||||
if (pos != -1) {
|
||||
line = line.left(pos);
|
||||
fprintf(stderr, "removing comment line %d; now: \"%s\"\n",
|
||||
lineno, line.toAscii().constData());
|
||||
// fprintf(stderr, "removing comment line %d; now: \"%s\"\n",
|
||||
// lineno, line.toAscii().constData());
|
||||
}
|
||||
if (blankrx.indexIn(line, 0) == 0) {
|
||||
fprintf(stderr, "line %d: blank\n", lineno);
|
||||
// fprintf(stderr, "line %d: blank\n", lineno);
|
||||
}
|
||||
else if (rangerx.indexIn(line, 0) != -1) {
|
||||
fprintf(stderr, "line %d: matched range: %s to %s\n",
|
||||
lineno,
|
||||
rangerx.cap(1).toAscii().constData(),
|
||||
rangerx.cap(5).toAscii().constData());
|
||||
// fprintf(stderr, "line %d: matched range: %s to %s\n",
|
||||
// lineno,
|
||||
// rangerx.cap(1).toAscii().constData(),
|
||||
// rangerx.cap(5).toAscii().constData());
|
||||
QDate begin, end;
|
||||
if (rangerx.cap(1) == "BEGIN")
|
||||
begin = QDate::currentDate().addYears(-1000);
|
||||
@@ -77,9 +79,9 @@ bool Zones::read(QFile &file)
|
||||
rangerx.cap(7).toInt(),
|
||||
rangerx.cap(8).toInt());
|
||||
}
|
||||
fprintf(stderr, "begin=%s, end=%s\n",
|
||||
begin.toString().toAscii().constData(),
|
||||
end.toString().toAscii().constData());
|
||||
// fprintf(stderr, "begin=%s, end=%s\n",
|
||||
// begin.toString().toAscii().constData(),
|
||||
// end.toString().toAscii().constData());
|
||||
if (range) {
|
||||
if (range->zones.empty()) {
|
||||
err = tr("line %1: read new range without reading "
|
||||
@@ -106,12 +108,12 @@ bool Zones::read(QFile &file)
|
||||
hi = zonerx.cap(4).toInt();
|
||||
ZoneInfo *zone =
|
||||
new ZoneInfo(zonerx.cap(1), zonerx.cap(2), lo, hi);
|
||||
fprintf(stderr, "line %d: matched zones: "
|
||||
"\"%s\", \"%s\", %s, %s\n", lineno,
|
||||
zonerx.cap(1).toAscii().constData(),
|
||||
zonerx.cap(2).toAscii().constData(),
|
||||
zonerx.cap(3).toAscii().constData(),
|
||||
zonerx.cap(4).toAscii().constData());
|
||||
// fprintf(stderr, "line %d: matched zones: "
|
||||
// "\"%s\", \"%s\", %s, %s\n", lineno,
|
||||
// zonerx.cap(1).toAscii().constData(),
|
||||
// zonerx.cap(2).toAscii().constData(),
|
||||
// zonerx.cap(3).toAscii().constData(),
|
||||
// zonerx.cap(4).toAscii().constData());
|
||||
range->zones.append(zone);
|
||||
}
|
||||
}
|
||||
@@ -128,39 +130,86 @@ bool Zones::read(QFile &file)
|
||||
return true;
|
||||
}
|
||||
|
||||
int Zones::whichZone(const QDate &date, double value) const
|
||||
int Zones::whichRange(const QDate &date) const
|
||||
{
|
||||
int rnum = 0;
|
||||
QListIterator<ZoneRange*> i(ranges);
|
||||
while (i.hasNext()) {
|
||||
ZoneRange *range = i.next();
|
||||
if ((date >= range->begin) && (date < range->end)) {
|
||||
for (int j = 0; j < range->zones.size(); ++j) {
|
||||
ZoneInfo *info = range->zones[j];
|
||||
if ((value >= info->lo) && (value < info->hi))
|
||||
return j;
|
||||
}
|
||||
}
|
||||
if ((date >= range->begin) && (date < range->end))
|
||||
return rnum;
|
||||
++rnum;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Zones::zoneInfo(const QDate &date, int zone,
|
||||
QString &name, QString &description,
|
||||
int &low, int &high)
|
||||
int Zones::numZones(int rnum) const
|
||||
{
|
||||
QListIterator<ZoneRange*> i(ranges);
|
||||
while (i.hasNext()) {
|
||||
ZoneRange *range = i.next();
|
||||
if ((date >= range->begin) && (date < range->end)) {
|
||||
assert(zone < range->zones.size());
|
||||
ZoneInfo *info = range->zones[zone];
|
||||
name = info->name;
|
||||
description = info->desc;
|
||||
low = info->lo;
|
||||
high = info->hi;
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
assert(rnum < ranges.size());
|
||||
return ranges[rnum]->zones.size();
|
||||
}
|
||||
|
||||
int Zones::whichZone(int rnum, double value) const
|
||||
{
|
||||
assert(rnum < ranges.size());
|
||||
ZoneRange *range = ranges[rnum];
|
||||
for (int j = 0; j < range->zones.size(); ++j) {
|
||||
ZoneInfo *info = range->zones[j];
|
||||
if ((value >= info->lo) && (value < info->hi))
|
||||
return j;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Zones::zoneInfo(int rnum, int znum,
|
||||
QString &name, QString &description,
|
||||
int &low, int &high) const
|
||||
{
|
||||
assert(rnum < ranges.size());
|
||||
ZoneRange *range = ranges[rnum];
|
||||
assert(znum < range->zones.size());
|
||||
ZoneInfo *zone = range->zones[znum];
|
||||
name = zone->name;
|
||||
description = zone->desc;
|
||||
low = zone->lo;
|
||||
high = zone->hi;
|
||||
}
|
||||
|
||||
QString Zones::summarize(int rnum, double *time_in_zone, int num_zones) const
|
||||
{
|
||||
assert(rnum < ranges.size());
|
||||
ZoneRange *range = ranges[rnum];
|
||||
assert(num_zones == range->zones.size());
|
||||
QString summary;
|
||||
summary += "<table align=\"center\" width=\"70%\" ";
|
||||
summary += "border=\"0\">";
|
||||
summary += "<tr>";
|
||||
summary += "<td align=\"center\">Zone</td>";
|
||||
summary += "<td align=\"center\">Description</td>";
|
||||
summary += "<td align=\"center\">Low</td>";
|
||||
summary += "<td align=\"center\">High</td>";
|
||||
summary += "<td align=\"center\">Time</td>";
|
||||
summary += "</tr>";
|
||||
for (int zone = 0; zone < num_zones; ++zone) {
|
||||
if (time_in_zone[zone] > 0.0) {
|
||||
QString name, desc;
|
||||
int lo, hi;
|
||||
zoneInfo(rnum, zone, name, desc, lo, hi);
|
||||
summary += "<tr>";
|
||||
summary += QString("<td align=\"center\">%1</td>").arg(name);
|
||||
summary += QString("<td align=\"center\">%1</td>").arg(desc);
|
||||
summary += QString("<td align=\"center\">%1</td>").arg(lo);
|
||||
if (hi == INT_MAX)
|
||||
summary += "<td align=\"center\">MAX</td>";
|
||||
else
|
||||
summary += QString("<td align=\"center\">%1</td>").arg(hi);
|
||||
summary += QString("<td align=\"center\">%1</td>")
|
||||
.arg(time_to_string((unsigned) round(time_in_zone[zone])));
|
||||
summary += "</tr>";
|
||||
}
|
||||
}
|
||||
summary += "</table>";
|
||||
return summary;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -62,10 +62,14 @@ class Zones : public QObject
|
||||
|
||||
bool read(QFile &file);
|
||||
const QString &errorString() const { return err; }
|
||||
int whichZone(const QDate &date, double value) const;
|
||||
void zoneInfo(const QDate &date, int zone,
|
||||
|
||||
int whichRange(const QDate &date) const;
|
||||
int numZones(int range) const;
|
||||
int whichZone(int range, double value) const;
|
||||
void zoneInfo(int range, int zone,
|
||||
QString &name, QString &description,
|
||||
int &low, int &high);
|
||||
int &low, int &high) const;
|
||||
QString summarize(int rnum, double *time_in_zone, int num_zones) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user