mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Merge branch 'pc7' of github.com:rclasen/GoldenCheetah into release_3.0.0dev
This commit is contained in:
5
src/.gitignore
vendored
5
src/.gitignore
vendored
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
24
src/D2XX.cpp
24
src/D2XX.cpp
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
65
src/Device.h
65
src/Device.h
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
// --------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user