Merge branch 'pc7' of github.com:rclasen/GoldenCheetah into release_3.0.0dev

This commit is contained in:
Mark Liversedge
2011-07-24 20:26:36 +01:00
18 changed files with 1014 additions and 304 deletions

5
src/.gitignore vendored
View File

@@ -20,6 +20,11 @@ qrc_application.cpp
# ignore other object files
*.o
# ignore lex/yacc generated files
*_lex.cpp
*_yacc.cpp
*_yacc.h
# ignore qmake-generated Makefile
Makefile

View File

@@ -48,3 +48,18 @@ CommPort::listCommPorts(QString &err)
return result;
}
QString
CommPort::type() const
{
return commType;
}
QString
CommPort::id() const
{
QString id = commType + ": ";
id += name();
return id;
}

View File

@@ -29,17 +29,24 @@ typedef boost::shared_ptr<CommPort> CommPortPtr;
class CommPort
{
public:
CommPort( const QString type ) : commType( type ) {};
typedef QVector<CommPortPtr> (*ListFunction)(QString &err);
static bool addListFunction(ListFunction f);
static QVector<CommPortPtr> listCommPorts(QString &err);
virtual ~CommPort() {}
virtual ~CommPort() {};
virtual bool isOpen() = 0;
virtual bool open(QString &err) = 0;
virtual void close() = 0;
virtual int read(void *buf, size_t nbyte, QString &err) = 0;
virtual int write(void *buf, size_t nbyte, QString &err) = 0;
virtual QString name() const = 0;
QString type( void ) const;
QString id( void ) const;
private:
QString commType;
};

View File

@@ -95,27 +95,33 @@ static D2XXWrapper *lib; // singleton lib instance
bool D2XXRegistered = CommPort::addListFunction(&D2XX::myListCommPorts);
D2XX::D2XX(const FT_DEVICE_LIST_INFO_NODE &info) :
info(info), isOpen(false)
CommPort( "D2XX" ), info(info), _isOpen(false)
{
}
D2XX::~D2XX()
{
if (isOpen)
if (_isOpen)
close();
}
bool
D2XX::isOpen()
{
return _isOpen;
}
bool
D2XX::open(QString &err)
{
assert(!isOpen);
assert(!_isOpen);
FT_STATUS ftStatus =
lib->open_ex(info.Description, FT_OPEN_BY_DESCRIPTION, &ftHandle);
if (ftStatus != FT_OK) {
err = QString("FT_Open: %1").arg(ftStatus);
return false;
}
isOpen = true;
_isOpen = true;
ftStatus = lib->set_baud_rate(ftHandle, 9600);
if (ftStatus != FT_OK) {
err = QString("FT_SetBaudRate: %1").arg(ftStatus);
@@ -142,15 +148,15 @@ D2XX::open(QString &err)
void
D2XX::close()
{
assert(isOpen);
assert(_isOpen);
lib->close(ftHandle);
isOpen = false;
_isOpen = false;
}
int
D2XX::read(void *buf, size_t nbyte, QString &err)
{
assert(isOpen);
assert(_isOpen);
DWORD rxbytes;
FT_STATUS ftStatus = lib->get_queue_status(ftHandle, &rxbytes);
if (ftStatus != FT_OK) {
@@ -174,7 +180,7 @@ D2XX::read(void *buf, size_t nbyte, QString &err)
int
D2XX::write(void *buf, size_t nbyte, QString &err)
{
assert(isOpen);
assert(_isOpen);
DWORD n;
FT_STATUS ftStatus = lib->write(ftHandle, buf, nbyte, &n);
if (ftStatus == FT_OK)
@@ -186,7 +192,7 @@ D2XX::write(void *buf, size_t nbyte, QString &err)
QString
D2XX::name() const
{
return QString("D2XX: ") + info.Description;
return info.Description;
}
QVector<CommPortPtr>

View File

@@ -33,7 +33,7 @@ class D2XX : public CommPort
FT_DEVICE_LIST_INFO_NODE info;
FT_HANDLE ftHandle;
bool isOpen;
bool _isOpen;
D2XX(const FT_DEVICE_LIST_INFO_NODE &info);
public:
@@ -41,6 +41,7 @@ class D2XX : public CommPort
static QVector<CommPortPtr> myListCommPorts(QString &err);
virtual ~D2XX();
virtual bool isOpen();
virtual bool open(QString &err);
virtual void close();
virtual int read(void *buf, size_t nbyte, QString &err);

View File

@@ -18,7 +18,9 @@
#include "Device.h"
typedef QMap<QString,Device*> DevicesMap;
#define tr(s) QObject::tr(s)
typedef QMap<QString,DevicesPtr> DevicesMap;
static DevicesMap *devicesPtr;
@@ -26,28 +28,59 @@ inline DevicesMap &
devices()
{
if (devicesPtr == NULL)
devicesPtr = new QMap<QString,Device*>;
devicesPtr = new QMap<QString,DevicesPtr>;
return *devicesPtr;
}
Device::~Device()
{
if( dev->isOpen() )
dev->close();
}
bool
Device::preview( StatusCallback statusCallback, QString &err )
{
(void) statusCallback;
(void) err;
return true;
}
QList<DeviceRideItemPtr> &Device::rides()
{
return rideList;
}
bool
Device::cleanup( QString &err )
{
(void) dev;
err = tr("cleanup is not supported");
return false;
}
QList<QString>
Device::deviceTypes()
Devices::typeNames()
{
return devices().keys();
}
Device &
Device::device(const QString &deviceType)
DevicesPtr
Devices::getType(const QString &deviceTypeName )
{
assert(devices().contains(deviceType));
return *devices().value(deviceType);
assert(devices().contains(deviceTypeName));
return devices().value(deviceTypeName);
}
bool
Device::addDevice(const QString &deviceType, Device *device)
Devices::addType(const QString &deviceTypeName, DevicesPtr p )
{
assert(!devices().contains(deviceType));
devices().insert(deviceType, device);
assert(!devices().contains(deviceTypeName));
devices().insert(deviceTypeName, p);
return true;
}

View File

@@ -23,22 +23,67 @@
#include "CommPort.h"
#include <boost/function.hpp>
struct DeviceDownloadFile
{
QString name;
QDateTime startTime;
QString extension;
};
struct DeviceRideItem
{
bool wanted;
QDateTime startTime;
unsigned work;
};
typedef boost::shared_ptr<DeviceRideItem> DeviceRideItemPtr;
struct Device;
typedef boost::shared_ptr<Device> DevicePtr;
struct Device
{
virtual ~Device() {}
Device( CommPortPtr dev ) : dev( dev ) {};
virtual ~Device();
typedef boost::function<bool (const QString &statusText)> StatusCallback;
typedef boost::function<bool (void)> CancelCallback;
typedef boost::function<void (const QString &statusText)> StatusCallback;
typedef boost::function<void (const QString &progressText)> ProgressCallback;
virtual QString downloadInstructions() const = 0;
virtual bool download(CommPortPtr dev, const QDir &tmpdir,
QString &tmpname, QString &filename,
StatusCallback statusCallback, QString &err) = 0;
virtual void cleanup(CommPortPtr dev) { (void) dev; }
virtual bool preview( StatusCallback statusCallback, QString &err );
virtual QList<DeviceRideItemPtr> &rides();
virtual bool download( const QDir &tmpdir,
QList<DeviceDownloadFile> &files,
CancelCallback cancelCallback,
StatusCallback statusCallback,
ProgressCallback progressCallback,
QString &err) = 0;
virtual bool cleanup( QString &err );
protected:
QList<DeviceRideItemPtr> rideList;
CommPortPtr dev;
static QList<QString> deviceTypes();
static Device &device(const QString &deviceType);
static bool addDevice(const QString &deviceType, Device *device);
};
struct Devices;
typedef boost::shared_ptr<Devices> DevicesPtr;
struct Devices
{
virtual DevicePtr newDevice( CommPortPtr ) = 0;
virtual bool canCleanup() { return false; };
virtual QString downloadInstructions() const { return ""; };
static QList<QString> typeNames();
static DevicesPtr getType(const QString &deviceTypeName );
static bool addType(const QString &deviceTypeName, DevicesPtr p );
};
#endif // _GC_Device_h

View File

@@ -26,51 +26,66 @@
#include <QtGui>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <stdio.h>
DownloadRideDialog::DownloadRideDialog(MainWindow *mainWindow,
const QDir &home) :
mainWindow(mainWindow), home(home), cancelled(false),
downloadInProgress(false)
action(actionIdle)
{
setAttribute(Qt::WA_DeleteOnClose);
setWindowTitle("Download Ride Data");
portCombo = new QComboBox(this);
QLabel *instructLabel = new QLabel(tr("Instructions:"), this);
label = new QLabel(this);
label->setIndent(10);
statusLabel = new QTextEdit(this);
statusLabel->setReadOnly(true);
statusLabel->setAcceptRichText(false);
// would prefer a progress bar, but some devices (eg. PTap) don't give
// a hint about the total work, so this isn't possible.
progressLabel = new QLabel(this);
progressLabel->setIndent(10);
progressLabel->setTextFormat(Qt::PlainText);
deviceCombo = new QComboBox(this);
QList<QString> deviceTypes = Device::deviceTypes();
QList<QString> deviceTypes = Devices::typeNames();
assert(deviceTypes.size() > 0);
BOOST_FOREACH(QString device, deviceTypes) {
deviceCombo->addItem(device);
}
connect(deviceCombo, SIGNAL(currentIndexChanged(QString)), this, SLOT(deviceChanged(QString)));
downloadButton = new QPushButton(tr("&Download"), this);
eraseRideButton = new QPushButton(tr("&Erase Ride(s)"), this);
rescanButton = new QPushButton(tr("&Rescan"), this);
cancelButton = new QPushButton(tr("&Cancel"), this);
closeButton = new QPushButton(tr("&Close"), this);
downloadButton->setDefault( true );
connect(downloadButton, SIGNAL(clicked()), this, SLOT(downloadClicked()));
connect(eraseRideButton, SIGNAL(clicked()), this, SLOT(eraseClicked()));
connect(rescanButton, SIGNAL(clicked()), this, SLOT(scanCommPorts()));
connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancelClicked()));
connect(closeButton, SIGNAL(clicked()), this, SLOT(closeClicked()));
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(downloadButton);
buttonLayout->addWidget(eraseRideButton);
buttonLayout->addWidget(rescanButton);
buttonLayout->addWidget(cancelButton);
buttonLayout->addWidget(closeButton);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(new QLabel(tr("Select port:"), this));
mainLayout->addWidget(portCombo);
mainLayout->addWidget(new QLabel(tr("Select device type:"), this));
mainLayout->addWidget(deviceCombo);
mainLayout->addWidget(instructLabel);
mainLayout->addWidget(label);
mainLayout->addWidget(new QLabel(tr("Select port:"), this));
mainLayout->addWidget(portCombo);
mainLayout->addWidget(new QLabel(tr("Info:"), this));
mainLayout->addWidget(statusLabel, 1);
mainLayout->addWidget(progressLabel);
mainLayout->addLayout(buttonLayout);
scanCommPorts();
@@ -79,23 +94,23 @@ DownloadRideDialog::DownloadRideDialog(MainWindow *mainWindow,
void
DownloadRideDialog::setReadyInstruct()
{
progressLabel->setText("");
if (portCombo->count() == 0) {
label->setText(tr("No devices found. Make sure the device\n"
statusLabel->setPlainText(tr("No devices found. Make sure the device\n"
"unit is plugged into the computer,\n"
"then click \"Rescan\" to check again."));
downloadButton->setEnabled(false);
eraseRideButton->setEnabled(false);
updateAction( actionMissing );
}
else {
Device &device = Device::device(deviceCombo->currentText());
QString inst = device.downloadInstructions();
DevicesPtr devtype = Devices::getType(deviceCombo->currentText());
QString inst = devtype->downloadInstructions();
if (inst.size() == 0)
label->setText("Click Download to begin downloading.");
statusLabel->setPlainText("Click Download to begin downloading.");
else
label->setText(inst + ", \nthen click Download.");
downloadButton->setEnabled(true);
if (deviceCombo->currentText() == "SRM") // only SRM supports erase ride for now
eraseRideButton->setEnabled(true);
statusLabel->setPlainText(inst + ", \nthen click Download.");
updateAction( actionIdle );
}
}
@@ -112,8 +127,8 @@ DownloadRideDialog::scanCommPorts()
QMessageBox::Ok, QMessageBox::NoButton);
}
for (int i = 0; i < devList.size(); ++i) {
portCombo->addItem(devList[i]->name());
// Hack: SRM PCV download cables use the PL2203 chipset. If the
portCombo->addItem(devList[i]->id());
// XXX Hack: SRM PCV download cables use the PL2203 chipset. If the
// first device name contains "PL2303", then, we're probably dealing
// with an SRM, so go ahead and select the SRM device. Generalize?
if ((i == 0) && devList[i]->name().contains("PL2303")) {
@@ -128,34 +143,125 @@ DownloadRideDialog::scanCommPorts()
}
bool
DownloadRideDialog::statusCallback(const QString &statusText)
DownloadRideDialog::isCancelled()
{
label->setText(statusText);
return cancelled;
}
void
DownloadRideDialog::updateAction( downloadAction newAction )
{
switch( newAction ){
case actionMissing:
downloadButton->setEnabled(false);
eraseRideButton->setEnabled(false);
rescanButton->setEnabled(true);
cancelButton->setEnabled(false);
closeButton->setEnabled(true);
portCombo->setEnabled(false);
deviceCombo->setEnabled(true);
break;
case actionIdle: {
DevicesPtr devtype = Devices::getType(deviceCombo->currentText());
downloadButton->setEnabled(true);
eraseRideButton->setEnabled(devtype->canCleanup());
rescanButton->setEnabled(true);
cancelButton->setEnabled(false);
closeButton->setEnabled(true);
portCombo->setEnabled(true);
deviceCombo->setEnabled(true);
break;
}
case actionDownload:
case actionCleaning:
downloadButton->setEnabled(false);
eraseRideButton->setEnabled(false);
rescanButton->setEnabled(false);
cancelButton->setEnabled(true);
closeButton->setEnabled(false);
portCombo->setEnabled(false);
deviceCombo->setEnabled(false);
break;
}
action = newAction;
cancelled = false;
QCoreApplication::processEvents();
return !cancelled;
}
void
DownloadRideDialog::updateStatus(const QString &statusText)
{
statusLabel->append(statusText);
QCoreApplication::processEvents();
}
void
DownloadRideDialog::updateProgress( const QString &progressText )
{
progressLabel->setText(progressText);
QCoreApplication::processEvents();
}
void
DownloadRideDialog::deviceChanged( QString deviceType )
{
(void)deviceType;
updateAction(action);
setReadyInstruct();
}
void
DownloadRideDialog::downloadClicked()
{
downloadButton->setEnabled(false);
eraseRideButton->setEnabled(false);
rescanButton->setEnabled(false);
downloadInProgress = true;
updateAction( actionDownload );
updateProgress( "" );
statusLabel->setPlainText( "" );
CommPortPtr dev;
for (int i = 0; i < devList.size(); ++i) {
if (devList[i]->name() == portCombo->currentText()) {
if (devList[i]->id() == portCombo->currentText()) {
dev = devList[i];
break;
}
}
assert(dev);
QString err;
QString tmpname, filename;
Device &device = Device::device(deviceCombo->currentText());
if (!device.download(
dev, home, tmpname, filename,
boost::bind(&DownloadRideDialog::statusCallback, this, _1), err))
QList<DeviceDownloadFile> files;
DevicesPtr devtype = Devices::getType(deviceCombo->currentText());
DevicePtr device = devtype->newDevice( dev );
if( ! device->preview(
boost::bind(&DownloadRideDialog::updateStatus, this, _1),
err ) ){
QMessageBox::information(this, tr("Preview failed"), err);
updateAction( actionIdle );
return;
}
QList<DeviceRideItemPtr> &rides( device->rides() );
if( ! rides.empty() ){
// XXX: let user select, which rides he wants to download
for( int i = 0; i < rides.size(); ++i ){
rides.at(i)->wanted = true;
}
}
if (!device->download( home, files,
boost::bind(&DownloadRideDialog::isCancelled, this),
boost::bind(&DownloadRideDialog::updateStatus, this, _1),
boost::bind(&DownloadRideDialog::updateProgress, this, _1),
err))
{
if (cancelled) {
QMessageBox::information(this, tr("Download canceled"),
@@ -165,86 +271,129 @@ DownloadRideDialog::downloadClicked()
else {
QMessageBox::information(this, tr("Download failed"), err);
}
downloadInProgress = false;
reject();
updateStatus(tr("Download failed"));
updateAction( actionIdle );
return;
}
QString filepath = home.absolutePath() + "/" + filename;
if (QFile::exists(filepath)) {
if (QMessageBox::warning(
this,
tr("Ride Already Downloaded"),
tr("This ride appears to have already ")
+ tr("been downloaded. Do you want to ")
+ tr("overwrite the previous download?"),
tr("&Overwrite"), tr("&Cancel"),
QString(), 1, 1) == 1) {
reject();
return;
updateProgress( "" );
int failures = 0;
for( int i = 0; i < files.size(); ++i ){
QString filename( files.at(i).startTime
.toString("yyyy_MM_dd_hh_mm_ss")
+ "." + files.at(i).extension );
QString filepath( home.absoluteFilePath(filename) );
if (QFile::exists(filepath)) {
if (QMessageBox::warning( this,
tr("Ride Already Downloaded"),
tr("The ride starting at %1 appears to have already "
"been downloaded. Do you want to overwrite the "
"previous download?")
.arg(files.at(i).startTime.toString()),
tr("&Overwrite"), tr("&Skip"),
QString(), 1, 1) == 1) {
QFile::remove(files.at(i).name);
updateStatus(tr("skipped file %1")
.arg( files.at(i).name ));
continue;
}
}
}
#ifdef __WIN32__
// Windows ::rename won't overwrite an existing file.
if (QFile::exists(filepath)) {
QFile old(filepath);
if (!old.remove()) {
QMessageBox::critical(this, tr("Error"),
tr("Failed to remove existing file ")
+ filepath + ": " + old.error());
QFile::remove(tmpname);
reject();
// Windows ::rename won't overwrite an existing file.
if (QFile::exists(filepath)) {
QFile old(filepath);
if (!old.remove()) {
QMessageBox::critical(this, tr("Error"),
tr("Failed to remove existing file %1: %2")
.arg(filepath)
.arg(old.error()) );
QFile::remove(files.at(i).name);
updateStatus(tr("failed to rename %1 to %2")
.arg( files.at(i).name )
.arg( filename ));
++failures;
continue;
}
}
}
#endif
// Use ::rename() instead of QFile::rename() to get forced overwrite.
if (rename(QFile::encodeName(tmpname), QFile::encodeName(filepath)) < 0) {
QMessageBox::critical(this, tr("Error"),
tr("Failed to rename ") + tmpname + tr(" to ")
+ filepath + ": " + strerror(errno));
QFile::remove(tmpname);
reject();
return;
// Use ::rename() instead of QFile::rename() to get forced overwrite.
if (rename(QFile::encodeName(files.at(i).name),
QFile::encodeName(filepath)) < 0) {
QMessageBox::critical(this, tr("Error"),
tr("Failed to rename %1 to %2: %3")
.arg(files.at(i).name)
.arg(filepath)
.arg(strerror(errno)) );
updateStatus(tr("failed to rename %1 to %2")
.arg( files.at(i).name )
.arg( filename ));
QFile::remove(files.at(i).name);
++failures;
continue;
}
QFile::remove(files.at(i).name);
mainWindow->addRide(filename);
}
QMessageBox::information(this, tr("Success"), tr("Download complete."));
mainWindow->addRide(filename);
if( ! failures )
updateStatus( tr("download completed successfully") );
device.cleanup(dev);
downloadInProgress = false;
accept();
updateAction( actionIdle );
}
void
DownloadRideDialog::eraseClicked()
{
downloadButton->setEnabled(false);
eraseRideButton->setEnabled(false);
rescanButton->setEnabled(false);
downloadInProgress = true;
updateAction( actionCleaning );
statusLabel->setPlainText( "" );
updateProgress( "" );
CommPortPtr dev;
for (int i = 0; i < devList.size(); ++i) {
if (devList[i]->name() == portCombo->currentText()) {
if (devList[i]->id() == portCombo->currentText()) {
dev = devList[i];
break;
}
}
assert(dev);
Device &device = Device::device(deviceCombo->currentText());
device.cleanup(dev);
downloadInProgress = false;
accept();
DevicesPtr devtype = Devices::getType(deviceCombo->currentText());
DevicePtr device = devtype->newDevice( dev );
QString err;
if( device->cleanup( err) )
updateStatus( tr("cleaned data") );
else
updateStatus( err );
updateAction( actionIdle );
}
void
DownloadRideDialog::cancelClicked()
{
if (!downloadInProgress)
reject();
else
switch( action ){
case actionIdle:
case actionMissing:
// do nothing
break;
default:
cancelled = true;
break;
}
}
void
DownloadRideDialog::closeClicked()
{
accept();
}

View File

@@ -34,26 +34,42 @@ class DownloadRideDialog : public QDialog
public:
DownloadRideDialog(MainWindow *mainWindow, const QDir &home);
void downloadFinished();
bool statusCallback(const QString &statusText);
bool isCancelled();
void updateStatus(const QString &statusText);
void updateProgress(const QString &progressText);
private slots:
void downloadClicked();
void eraseClicked();
void cancelClicked();
void closeClicked();
void setReadyInstruct();
void scanCommPorts();
void deviceChanged(QString);
private:
MainWindow *mainWindow;
QDir home;
QPushButton *downloadButton, *eraseRideButton, *rescanButton, *cancelButton;
QPushButton *downloadButton, *eraseRideButton, *rescanButton,
*cancelButton, *closeButton;
QComboBox *portCombo, *deviceCombo;
QLabel *label;
QTextEdit *statusLabel;
QLabel *progressLabel;
QVector<CommPortPtr> devList;
bool cancelled, downloadInProgress;
bool cancelled;
typedef enum {
actionIdle,
actionMissing,
actionDownload,
actionCleaning,
} downloadAction;
downloadAction action;
void updateAction( downloadAction action );
};
#endif // _GC_DownloadRideDialog_h

View File

@@ -41,15 +41,21 @@
#define LAST_PAGE 0xFA0A // LastPage number
static bool macroRegistered =
Device::addDevice("O-Synce Macro PC-Link", new MacroDevice());
Devices::addType("O-Synce Macro PC-Link", DevicesPtr(new MacroDevices()) );
QString
MacroDevice::downloadInstructions() const
MacroDevices::downloadInstructions() const
{
return ("Make sure the Macro unit is turned\n"
"on and that its display says, \"PC Link\"");
}
DevicePtr
MacroDevices::newDevice( CommPortPtr dev )
{
return DevicePtr( new MacroDevice( dev ));
}
static QString
cEscape(char *buf, int len)
{
@@ -85,9 +91,12 @@ hexHex2Int(char c, char c2)
}
bool
MacroDevice::download(CommPortPtr dev, const QDir &tmpdir,
QString &tmpname, QString &filename,
StatusCallback statusCallback, QString &err)
MacroDevice::download( const QDir &tmpdir,
QList<DeviceDownloadFile> &files,
CancelCallback cancelCallback,
StatusCallback statusCallback,
ProgressCallback progressCallback,
QString &err)
{
if (MACRO_DEBUG) printf("download O-Synce Macro");
@@ -96,8 +105,7 @@ MacroDevice::download(CommPortPtr dev, const QDir &tmpdir,
return false;
}
QString cbtext;
cbtext = "Request number of training...";
statusCallback("Request number of training...");
if (MACRO_DEBUG) printf("Request number of training\n");
MacroPacket cmd(NUMBER_OF_TRAINING_REQUESTS);
@@ -105,8 +113,7 @@ MacroDevice::download(CommPortPtr dev, const QDir &tmpdir,
if (!cmd.write(dev, err)) return false;
cbtext = "Reading number of training...";
if (!statusCallback(cbtext))
if (cancelCallback())
{
err = "download cancelled";
return false;
@@ -130,31 +137,12 @@ MacroDevice::download(CommPortPtr dev, const QDir &tmpdir,
return false;
}
if (!statusCallback(cbtext))
if (cancelCallback())
{
err = "download cancelled";
return false;
}
if (MACRO_DEBUG) printf("Acknowledge");
cmd= MacroPacket(ACKNOWLEDGE);
cmd.addToPayload(response.command);
if (!cmd.write(dev, err)) return false;
// filename from the first training
int sec = bcd2Int(response.payload.at(2));
int min = bcd2Int(response.payload.at(3));
int hour = bcd2Int(response.payload.at(4));
int day = bcd2Int(response.payload.at(5));
int month = hex2Int(response.payload.at(6));
int year = bcd2Int(response.payload.at(7));
char *filename_tmp = new char[32];
sprintf(filename_tmp, "%04d_%02d_%02d_%02d_%02d_%02d.osyn",
year + 2000, month, day, hour, min, sec);
filename = filename_tmp;
// create temporary file
QString tmpl = tmpdir.absoluteFilePath(".macrodl.XXXXXX");
QTemporaryFile tmp(tmpl);
@@ -166,14 +154,37 @@ MacroDevice::download(CommPortPtr dev, const QDir &tmpdir,
return false;
}
if (MACRO_DEBUG) printf("Acknowledge");
cmd= MacroPacket(ACKNOWLEDGE);
cmd.addToPayload(response.command);
if (!cmd.write(dev, err)) return false;
// timestamp from the first training
struct tm start;
start.tm_sec = bcd2Int(response.payload.at(2));
start.tm_min = bcd2Int(response.payload.at(3));
start.tm_hour = bcd2Int(response.payload.at(4));
start.tm_mday = bcd2Int(response.payload.at(5));
start.tm_mon = hex2Int(response.payload.at(6)) -1;
start.tm_year = bcd2Int(response.payload.at(7)) -100;
start.tm_isdst = -1;
DeviceDownloadFile file;
file.extension = "osyn";
file.name = tmp.fileName();
file.startTime.setTime_t( mktime( &start ));
files.append(file);
QTextStream os(&tmp);
os << hex;
for (int i = 0; i < count; i++)
{
if (MACRO_DEBUG) printf("Request training %d\n",i);
cbtext = QString("Request datas of training %1 / %2...").arg(i+1).arg((int)count);
if (!statusCallback(cbtext))
statusCallback( QString("Request datas of training %1 / %2...")
.arg(i+1).arg((int)count) );
if (cancelCallback())
{
err = "download cancelled";
return false;
@@ -183,8 +194,7 @@ MacroDevice::download(CommPortPtr dev, const QDir &tmpdir,
cmd.addToPayload(i);
if (!cmd.write(dev, err)) return false;
cbtext = QString("Read datas of training %1 / %2...").arg(i+1).arg((int)count);
if (!statusCallback(cbtext))
if (cancelCallback())
{
err = "download cancelled";
return false;
@@ -206,8 +216,11 @@ MacroDevice::download(CommPortPtr dev, const QDir &tmpdir,
//int training_flag = hex2Int(response2.payload.at(43));
tmp.write(response2.dataArray());
cbtext = QString("Read datas of training %1 / %2... (%3 bytes)").arg(i+1).arg((int)count).arg(tmp.size());
if (!statusCallback(cbtext))
progressCallback( QString("training %1/%2... (%3 Bytes)")
.arg(i+1)
.arg((int)count)
.arg(tmp.size()) );
if (cancelCallback())
{
err = "download cancelled";
return false;
@@ -223,7 +236,6 @@ MacroDevice::download(CommPortPtr dev, const QDir &tmpdir,
}
tmpname = tmp.fileName(); // after close(), tmp.fileName() is ""
tmp.close();
dev->close();
@@ -238,12 +250,10 @@ MacroDevice::download(CommPortPtr dev, const QDir &tmpdir,
return true;
}
void
MacroDevice::cleanup(CommPortPtr dev){
bool
MacroDevice::cleanup( QString &err ){
if (MACRO_DEBUG) printf("Erase all records on computer\n");
QString err;
if (!dev->open(err)) {
err = "ERROR: open failed: " + err;
}
@@ -262,6 +272,8 @@ MacroDevice::cleanup(CommPortPtr dev){
}
dev->close();
return true;
}
// --------------------------------------

View File

@@ -6,15 +6,26 @@
class DeviceFileInfo;
struct MacroDevices : public Devices
{
virtual DevicePtr newDevice( CommPortPtr dev );
virtual QString downloadInstructions() const;
virtual bool canCleanup( void ) {return true; };
};
struct MacroDevice : public Device
{
virtual QString downloadInstructions() const;
MacroDevice( CommPortPtr dev ) :
Device( dev ) {};
virtual bool download(CommPortPtr dev, const QDir &tmpdir,
QString &tmpname, QString &filename,
StatusCallback statusCallback, QString &err);
virtual bool download( const QDir &tmpdir,
QList<DeviceDownloadFile> &files,
CancelCallback cancelCallback,
StatusCallback statusCallback,
ProgressCallback progressCallback,
QString &err);
virtual void cleanup(CommPortPtr dev);
virtual bool cleanup( QString &err );
};
class MacroPacket

View File

@@ -23,10 +23,16 @@
#define PT_DEBUG false
static bool powerTapRegistered =
Device::addDevice("PowerTap", new PowerTapDevice());
Devices::addType("PowerTap", DevicesPtr(new PowerTapDevices()) );
DevicePtr
PowerTapDevices::newDevice( CommPortPtr dev )
{
return DevicePtr( new PowerTapDevice( dev ));
}
QString
PowerTapDevice::downloadInstructions() const
PowerTapDevices::downloadInstructions() const
{
return ("Make sure the PowerTap unit is turned\n"
"on and that its display says, \"Host\"");
@@ -114,9 +120,12 @@ readUntilNewline(CommPortPtr dev, char *buf, int len, QString &err)
}
bool
PowerTapDevice::download(CommPortPtr dev, const QDir &tmpdir,
QString &tmpname, QString &filename,
StatusCallback statusCallback, QString &err)
PowerTapDevice::download( const QDir &tmpdir,
QList<DeviceDownloadFile> &files,
CancelCallback cancelCallback,
StatusCallback statusCallback,
ProgressCallback progressCallback,
QString &err)
{
if (!dev->open(err)) {
err = "ERROR: open failed: " + err;
@@ -124,7 +133,6 @@ PowerTapDevice::download(CommPortPtr dev, const QDir &tmpdir,
}
// make several attempts at reading the version
int attempts = 3;
QString cbtext;
int veridx = -1;
int version_len;
char vbuf[256];
@@ -134,8 +142,8 @@ PowerTapDevice::download(CommPortPtr dev, const QDir &tmpdir,
if (!doWrite(dev, 0x56, false, err)) // 'V'
return false;
cbtext = "Reading version...";
if (!statusCallback(cbtext)) {
statusCallback( "Reading version..." );
if (cancelCallback()) {
err = "download cancelled";
return false;
}
@@ -166,8 +174,8 @@ PowerTapDevice::download(CommPortPtr dev, const QDir &tmpdir,
bool hwecho = version.indexOf('V') < veridx;
if (PT_DEBUG) printf("hwecho=%s\n", hwecho ? "true" : "false");
cbtext += "done.\nReading header...";
if (!statusCallback(cbtext)) {
statusCallback( "Reading header..." );
if (cancelCallback()) {
err = "download cancelled";
return false;
}
@@ -192,12 +200,11 @@ PowerTapDevice::download(CommPortPtr dev, const QDir &tmpdir,
for (size_t i = 0; i < sizeof(header); ++i)
records.append(header[i]);
cbtext += "done.\nReading ride data...\n";
if (!statusCallback(cbtext)) {
statusCallback( "Reading ride data..." );
if (cancelCallback()) {
err = "download cancelled";
return false;
}
int cbtextlen = cbtext.length();
double recIntSecs = 0.0;
fflush(stdout);
@@ -259,11 +266,11 @@ PowerTapDevice::download(CommPortPtr dev, const QDir &tmpdir,
}
if (recIntSecs != 0.0) {
int min = (int) round(records.size() / 6 * recIntSecs);
cbtext.chop(cbtext.size() - cbtextlen);
cbtext.append(QString("Ride data read: %1:%2").arg(min / 60)
.arg(min % 60, 2, 10, QLatin1Char('0')));
progressCallback( QString("progress: %1:%2")
.arg(min / 60)
.arg(min % 60, 2, 10, QLatin1Char('0')));
}
if (!statusCallback(cbtext)) {
if (cancelCallback()) {
err = "download cancelled";
return false;
}
@@ -279,11 +286,20 @@ PowerTapDevice::download(CommPortPtr dev, const QDir &tmpdir,
+ tmpl + ": " + tmp.error();
return false;
}
// QTemporaryFile initially has permissions set to 0600.
// Make it readable by everyone.
tmp.setPermissions(tmp.permissions()
| QFile::ReadOwner | QFile::ReadUser
| QFile::ReadGroup | QFile::ReadOther);
DeviceDownloadFile file;
file.extension = "raw";
file.name = tmp.fileName();
QTextStream os(&tmp);
os << hex;
os.setPadChar('0');
struct tm time;
bool time_set = false;
unsigned char *data = records.data();
bool bIsVer81 = PowerTapUtil::is_Ver81(data);
@@ -298,7 +314,9 @@ PowerTapDevice::download(CommPortPtr dev, const QDir &tmpdir,
os << ((j == 5) ? "\n" : " ");
}
if (!time_set && PowerTapUtil::is_time(data + i, bIsVer81)) {
PowerTapUtil::unpack_time(data + i, &time, bIsVer81);
struct tm time;
time_t timet = PowerTapUtil::unpack_time(data + i, &time, bIsVer81);
file.startTime.fromTime_t( timet );
time_set = true;
}
}
@@ -307,21 +325,8 @@ PowerTapDevice::download(CommPortPtr dev, const QDir &tmpdir,
tmp.setAutoRemove(true);
return false;
}
tmpname = tmp.fileName(); // after close(), tmp.fileName() is ""
tmp.close();
// QTemporaryFile initially has permissions set to 0600.
// Make it readable by everyone.
tmp.setPermissions(tmp.permissions()
| QFile::ReadOwner | QFile::ReadUser
| QFile::ReadGroup | QFile::ReadOther);
char filename_tmp[32];
sprintf(filename_tmp, "%04d_%02d_%02d_%02d_%02d_%02d.raw",
time.tm_year + 1900, time.tm_mon + 1,
time.tm_mday, time.tm_hour, time.tm_min,
time.tm_sec);
filename = filename_tmp;
files << file;
return true;
}

View File

@@ -20,15 +20,25 @@
#define _GC_PowerTapDevice_h 1
#include "GoldenCheetah.h"
#include "CommPort.h"
#include "Device.h"
struct PowerTapDevices : public Devices
{
virtual DevicePtr newDevice( CommPortPtr dev );
virtual QString downloadInstructions() const;
};
struct PowerTapDevice : public Device
{
virtual QString downloadInstructions() const;
virtual bool download(CommPortPtr dev, const QDir &tmpdir,
QString &tmpname, QString &filename,
StatusCallback statusCallback, QString &err);
PowerTapDevice( CommPortPtr dev ) :
Device( dev ) {};
virtual bool download( const QDir &tmpdir,
QList<DeviceDownloadFile> &files,
CancelCallback cancelCallback,
StatusCallback statusCallback,
ProgressCallback progressCallback,
QString &err);
};
#endif // _GC_PowerTapDevice_h

View File

@@ -46,23 +46,28 @@
bool SerialRegistered = CommPort::addListFunction(&Serial::myListCommPorts);
#ifdef Q_OS_WIN32
Serial::Serial(const QString &path) : path(path), isOpen(false)
Serial::Serial(const QString &path) : CommPort("Serial"), path(path), _isOpen(false)
{
}
#else
Serial::Serial(const QString &path) : path(path), fd(-1)
Serial::Serial(const QString &path) : CommPort("Serial"), path(path), fd(-1)
{
}
#endif
Serial::~Serial()
{
#ifdef Q_OS_WIN32
if (isOpen == true) {
if( isOpen() )
close();
}
}
bool
Serial::isOpen()
{
#ifdef Q_OS_WIN32
return _isOpen;
#else
if (fd >= 0) close();
return fd >= 0;
#endif
}
@@ -129,9 +134,9 @@ Serial::open(QString &err)
fd = CreateFile (deviceFilenameW, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_DELETE|FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (fd == INVALID_HANDLE_VALUE) return isOpen = false;
if (fd == INVALID_HANDLE_VALUE) return _isOpen = false;
if (GetCommState (fd, &deviceSettings) == false) return isOpen = false;
if (GetCommState (fd, &deviceSettings) == false) return _isOpen = false;
// so we've opened the comm port lets set it up for
deviceSettings.BaudRate = CBR_9600;
@@ -148,7 +153,7 @@ Serial::open(QString &err)
if (SetCommState(fd, &deviceSettings) == false) {
CloseHandle(fd);
return isOpen = false;
return _isOpen = false;
}
timeouts.ReadIntervalTimeout = 0;
@@ -158,7 +163,7 @@ Serial::open(QString &err)
timeouts.WriteTotalTimeoutMultiplier = 0;
SetCommTimeouts(fd, &timeouts);
return isOpen = true;
return _isOpen = true;
#endif
}
@@ -170,9 +175,9 @@ Serial::close()
::close(fd);
fd = -1;
#else
if (isOpen == true) {
if (_isOpen == true) {
CloseHandle(fd);
isOpen = false;
_isOpen = false;
}
#endif
}
@@ -309,7 +314,7 @@ Serial::write(void *buf, size_t nbyte, QString &err)
QString
Serial::name() const
{
return QString("Serial: ") + path;
return path;
}
#ifndef Q_OS_WIN32

View File

@@ -36,7 +36,7 @@ class Serial : public CommPort
#ifndef Q_OS_WIN32
int fd;
#else
bool isOpen; // don't rely on fd value to determine if
bool _isOpen; // don't rely on fd value to determine if
// the COM port is open
HANDLE fd; // file descriptor for reading from com3
#endif
@@ -47,6 +47,7 @@ class Serial : public CommPort
static QVector<CommPortPtr> myListCommPorts(QString &err);
virtual ~Serial();
virtual bool isOpen();
virtual bool open(QString &err);
virtual void close();
virtual int read(void *buf, size_t nbyte, QString &err);

View File

@@ -23,23 +23,19 @@
#include <boost/scoped_ptr.hpp>
#include <boost/foreach.hpp>
#include <errno.h>
#include <stdio.h>
static bool srmRegistered = Device::addDevice("SRM", new SrmDevice());
#define tr(s) QObject::tr(s)
QString
SrmDevice::downloadInstructions() const
static bool srm5Registered =
Devices::addType("SRM PCV", DevicesPtr(new SrmDevices( 5 )) );
static bool srm7Registered =
Devices::addType("SRM PCVI/7", DevicesPtr(new SrmDevices( 7 )));
DevicePtr
SrmDevices::newDevice( CommPortPtr dev )
{
return ""; // no particular instructions for SRM
}
static Device::StatusCallback cb;
static void
logfunc(const char *msg)
{
// XXX: better log function
// fprintf(stderr, "%s\n", msg);
cb(QString(msg).left(40));
return DevicePtr( new SrmDevice( dev, protoVersion));
}
static bool
@@ -58,90 +54,450 @@ get_tmpname(const QDir &tmpdir, QString &tmpname, QString &err)
return true;
}
struct SrmpcConn : public boost::noncopyable
SrmDevice::~SrmDevice()
{
srmpc_conn_t d;
SrmpcConn(const QString &path, srmpc_log_callback_t logfunc = NULL) {
int opt_force = 0; // setting this to 1 is potentially dangerous
d = srmpc_open(path.toAscii().constData(), opt_force, logfunc);
}
~SrmpcConn() { if (d) srmpc_close(d); }
};
close();
}
struct SrmioData : public boost::noncopyable
bool
SrmDevice::open( QString &err )
{
srm_data_t d;
SrmioData(srm_data_t d) : d(d) {}
~SrmioData() { if (d) srm_data_free(d); }
};
if( dev->type() == "Serial" ){
io = srmio_ios_new( dev->name().toAscii().constData() );
if( ! io ){
err = tr("failed to allocate device handle: %1")
.arg(strerror(errno));
return false;
}
static bool
dev2path(CommPortPtr dev, QString &path, QString &err)
{
// Read device path out of device name. Sketchy.
QRegExp rx("^Serial: (.+)$");
if (!rx.exactMatch(dev->name())) {
err = "SRM download not supported by device " + dev->name();
} else {
err = tr("device type %1 is unsupported")
.arg(dev->type());
return false;
}
path = rx.cap(1);
switch( protoVersion ){
case 5:
pc = srmio_pc5_new();
break;
case 6:
case 7:
pc = srmio_pc7_new();
break;
default:
err = tr("unsupported SRM Protocl version: %1")
.arg(protoVersion);
goto fail;
}
if( ! pc ){
err = tr("failed to allocate Powercontrol handle: %1")
.arg(strerror(errno));
goto fail;
}
if( ! srmio_io_open(io ) ){
err = tr("Couldn't open device %1: %2")
.arg(dev->name())
.arg(strerror(errno));
goto fail;
}
if( ! srmio_pc_set_device( pc, io ) ){
err = tr("failed to set Powercontrol io handle: %1")
.arg(strerror(errno));
goto fail;
}
if( ! srmio_pc_open( pc ) ){
err = tr("failed to initialize Powercontrol communication: %1")
.arg(strerror(errno));
goto fail;
}
is_open = true;
return true;
fail:
if( pc ){
srmio_pc_free( pc );
pc = NULL;
}
if( io ){
srmio_io_free( io );
io = NULL;
}
return false;
}
bool
SrmDevice::close( void )
{
rideList.clear();
if( pc ){
srmio_pc_free( pc );
pc = NULL;
}
if( io ){
srmio_io_free( io );
io = NULL;
}
is_open = false;
return true;
}
bool
SrmDevice::download(CommPortPtr dev, const QDir &tmpdir,
QString &tmpname, QString &filename,
StatusCallback statusCallback, QString &err)
SrmDevice::preview( StatusCallback statusCallback, QString &err )
{
// Totally ghetto, proof-of-concept integration with srmio.
cb = statusCallback;
QString path;
if (!dev2path(dev, path, err))
return false;
if (!get_tmpname(tmpdir, tmpname, err))
return false;
SrmpcConn srm(path, logfunc);
if (!srm.d) {
err = "Couldn't open device " + path + ": " + strerror(errno);
return false;
struct _srmio_pc_xfer_block_t block;
if( ! is_open ){
statusCallback( tr("opening device %1").arg(dev->name()) );
if( ! open( err ) )
return false;
}
int opt_all = 0; // only get new data
int opt_fixup = 1; // fix bad data like srmwin.exe does
SrmioData srmdata(srmpc_get_data(srm.d, opt_all, opt_fixup));
if (!srmdata.d) {
err = "srmpc_get_data failed: ";
err += strerror(errno);
return false;
rideList.clear();
if( ! srmio_pc_can_preview( pc ) )
// nothing to do
return true;
if( ! srmio_pc_xfer_start( pc ) ){
err = tr("failed to start download: %1")
.arg(strerror(errno));
goto fail;
}
if( ! srmdata.d->cused ){
err = "no data available";
return false;
while( srmio_pc_xfer_block_next( pc, &block )){
DeviceRideItemPtr ride( new DeviceRideItem );
ride->startTime.setTime_t( 0.1 * block.start );
ride->work = block.total;
rideList.append( ride );
if( block.athlete )
free( block.athlete );
block.athlete = NULL;
}
if (srm_data_write_srm7(srmdata.d, tmpname.toAscii().constData()) < 0) {
err = "Couldn't write to file " + tmpname + ": " + strerror(errno);
return false;
if( ! srmio_pc_xfer_finish( pc ) ){
err = tr("download failed: %1")
.arg(strerror(errno));
goto fail;
}
QDateTime startTime;
startTime.setTime_t( srmdata.d->chunks[0]->time / 10 );
filename = startTime.toString("yyyy_MM_dd_hh_mm_ss") + ".srm";
return true;
fail:
rideList.clear();
return false;
}
void
SrmDevice::cleanup(CommPortPtr dev)
bool
SrmDevice::download( const QDir &tmpdir,
QList<DeviceDownloadFile> &files,
CancelCallback cancelCallback,
StatusCallback statusCallback,
ProgressCallback progressCallback,
QString &err)
{
QString path, err;
if (!dev2path(dev, path, err))
assert(false);
if (QMessageBox::question(0, "Powercontrol",
"Erase ride from device memory?",
"&Erase", "&Cancel", "", 1, 1) == 0) {
SrmpcConn srm(path);
if(!srm.d || (srmpc_clear_chunks(srm.d) < 0)) {
QMessageBox::warning(0, "Error",
"Error communicating with device.");
}
unsigned firmware;
srmio_io_baudrate_t baudrateId;
unsigned baudrate;
struct _srmio_pc_xfer_block_t block;
srmio_data_t data( NULL );
srmio_data_t *splitList( NULL );
srmio_data_t *split;
int mfirst( -1 );
size_t block_cnt, block_num( 0 );
size_t prog_sum( 0 ), prog_prev( 0 );
size_t chunks_done( 0 );
srmio_time_t splitGap( 72000 ); // 2h - XXX: make this configurable
if( ! is_open ){
statusCallback( tr("opening device %1").arg(dev->name()) );
if( ! open( err ) )
return false;
}
if( ! srmio_pc_get_version( pc, &firmware ) ){
err = tr("failed to get firmware version: %1")
.arg(strerror(errno));
goto fail;
}
if( ! srmio_pc_get_baudrate( pc, &baudrateId ) ){
err = tr("failed to get baud rate: %1")
.arg(strerror(errno));
goto fail;
}
if( ! srmio_io_baud2name( baudrateId, &baudrate ) ){
err = tr("failed to get baud rate name: %1")
.arg(strerror(errno));
}
statusCallback(tr("found Powercontrol version 0x%1 using %2 baud")
.arg(firmware, 4, 16 )
.arg(baudrate));
// fetch preview in case user didn't
if( srmio_pc_can_preview(pc) && rideList.size() == 0 ){
if( ! preview( statusCallback, err ) )
return false;
}
data = srmio_data_new();
if( ! data ){
err = tr("failed to allocate data handle: %1")
.arg(strerror(errno));
goto fail;
}
if( cancelCallback() ){
err = tr("download cancelled");
goto fail;
}
if( ! srmio_pc_xfer_start( pc )){
err = tr("failed to start download: %1")
.arg(strerror(errno));
goto fail;
}
if( ! srmio_pc_xfer_get_blocks( pc, &block_cnt ) ){
err = tr("failed to get number of data blocks: %1")
.arg(strerror(errno));
goto fail1;
}
statusCallback(tr("found %1 ride blocks").arg(block_cnt));
for( int i = 0; i < rideList.size(); ++i ){
if( rideList.at(i)->wanted )
prog_sum += rideList.at(i)->work;
}
while( srmio_pc_xfer_block_next( pc, &block )){
bool wanted = false;
struct _srmio_chunk_t chunk;
bool is_int;
bool is_first;
size_t prog_total;
if( rideList.empty() ){
wanted = true;
} else {
for( int i = 0; i < rideList.size(); ++i ){
if( rideList.at(i)->startTime.toTime_t() == block.start / 10 ){
wanted = rideList.at(i)->wanted;
break;
}
}
}
if( ! wanted ){
statusCallback(tr("skipping unselected ride block %1")
.arg(block_num +1));
continue;
}
statusCallback(tr("downloading ride block %1/%2")
.arg(block_num +1)
.arg(block_cnt) );
data->slope = block.slope;
data->zeropos = block.zeropos;
data->circum = block.circum;
if( block.athlete ){
if( data->athlete )
free( data->athlete );
data->athlete = strdup( block.athlete );
}
if( ! rideList.empty() ){
prog_total = prog_sum;
} else if( block_cnt == 1 ){
prog_total = block.total;
} else {
prog_total = block_cnt * 1000;
}
while( srmio_pc_xfer_chunk_next( pc, &chunk, &is_int, &is_first ) ){
if( cancelCallback() ){
err = tr("download cancelled");
goto fail1;
}
if( chunks_done % 16 == 0 ){
size_t block_done;
srmio_pc_xfer_block_progress( pc, &block_done );
if( ! rideList.empty() ){
block_done += prog_prev;
} else if( block_cnt == 1 ){
// unchanged
} else {
block_done = (double)block_num * 1000
+ 1000 * block.total / block_done;
}
progressCallback( tr("progress: %1/%2")
.arg(block_done)
.arg(prog_total));
}
if( ! srmio_data_add_chunk( data, &chunk ) )
goto fail1;
++chunks_done;
/* finish previous marker */
if( mfirst >= 0 && ( ! is_int || is_first ) )
srmio_data_add_marker( data, mfirst, data->cused -1 );
/* start marker */
if( is_first ){
mfirst = (int)data->cused;
} else if( ! is_int ){
mfirst = -1;
}
}
/* finalize marker at block end */
if( mfirst >= 0 ){
srmio_data_add_marker( data, mfirst, data->cused -1 );
mfirst = -1;
}
if( ! rideList.empty() )
prog_prev += block.total;
else
prog_prev += 1000;
if( block.athlete )
free( block.athlete );
block.athlete = NULL;
++block_num;
}
if( ! srmio_pc_xfer_finish( pc ) ){
err = tr( "download failed: %1")
.arg(strerror(errno));
goto fail;
}
statusCallback( tr("got %1 records").arg(data->cused) );
if( cancelCallback() ){
err = tr("download cancelled");
goto fail;
}
if( ! data->cused ){
err = tr("no data available");
goto fail;
}
splitList = srmio_data_split( data, splitGap, 1000 );
if( ! splitList ){
err = tr("Couldn't split data: %1")
.arg(strerror(errno));
goto fail;
}
for( split = splitList; *split; ++split ){
FILE *fh( NULL );
srmio_time_t stime;
DeviceDownloadFile file;
file.extension = "srm";
if (!get_tmpname(tmpdir, file.name, err))
goto fail;
if( ! srmio_data_time_start( *split, &stime ) ){
err = tr("Couldn't get start time of data: %1")
.arg(strerror(errno));
goto fail;
}
file.startTime.setTime_t( 0.1 * stime );
fh = fopen( file.name.toAscii().constData(), "w" );
if( ! fh ){
err = tr( "failed to open file %1: %2")
.arg(file.name)
.arg(strerror(errno));
goto fail;
}
if( ! srmio_file_srm7_write(*split, fh) ){
err = tr("Couldn't write to file %1: %2")
.arg(file.name)
.arg(strerror(errno));
fclose(fh);
goto fail;
}
files.append(file);
fclose( fh );
}
for( split = splitList; *split; ++split )
srmio_data_free( *split );
free(splitList);
srmio_data_free( data );
return true;
fail1:
srmio_pc_xfer_finish(pc);
fail:
if( data ) srmio_data_free( data );
if( splitList ){
for( split = splitList; *split; ++split )
srmio_data_free( *split );
free(splitList);
}
close();
return false;
}
bool
SrmDevice::cleanup( QString &err )
{
if( ! is_open ){
if( ! open( err ) )
goto cleanup;
}
if( ! srmio_pc_cmd_clear( pc ) ){
err = tr("failed to clear Powercontrol memory: %1")
.arg(strerror(errno));
goto cleanup;
}
return true;
cleanup:
close();
return false;
}

View File

@@ -18,18 +18,50 @@
#ifndef _GC_SrmDevice_h
#define _GC_SrmDevice_h 1
#include <srmio.h>
#include "GoldenCheetah.h"
#include "CommPort.h"
#include "Device.h"
struct SrmDevices : public Devices
{
SrmDevices( int protoVersion ) : protoVersion( protoVersion ) {}
virtual DevicePtr newDevice( CommPortPtr dev );
virtual bool canCleanup( void ) {return true; };
private:
int protoVersion;
};
struct SrmDevice : public Device
{
virtual QString downloadInstructions() const;
virtual bool download(CommPortPtr dev, const QDir &tmpdir,
QString &tmpname, QString &filename,
StatusCallback statusCallback, QString &err);
virtual void cleanup(CommPortPtr dev);
SrmDevice( CommPortPtr dev, int protoVersion ) :
Device( dev ),
protoVersion( protoVersion ),
is_open( false ),
io( NULL ), pc( NULL ) { };
~SrmDevice();
virtual bool preview( StatusCallback statusCallback, QString &err );
virtual bool download( const QDir &tmpdir,
QList<DeviceDownloadFile> &files,
CancelCallback cancelCallback,
StatusCallback statusCallback,
ProgressCallback progressCallback,
QString &err);
virtual bool cleanup( QString &err );
private:
int protoVersion;
bool is_open;
srmio_io_t io;
srmio_pc_t pc;
bool open ( QString &err );
bool close( void );
};
#endif // _GC_SrmDevice_h

View File

@@ -178,11 +178,12 @@ struct SyncFileReaderState
QDateTime t = QDateTime(QDate(2000+year,month,day), QTime(hour,min,sec));
if (secs == 0.0 || rideFile->startTime().toMSecsSinceEpoch()<0) {
if (secs == 0.0 || rideFile->startTime().toTime_t() == (unsigned int)-1 ) {
rideFile->setStartTime(t);
}
else {
int gap = (t.toMSecsSinceEpoch() - rideFile->startTime().toMSecsSinceEpoch())/1000 - secs;
int gap = (t.toTime_t() - rideFile->startTime().toTime_t()) - secs;
secs += gap;
}