mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Basic Moxy Download 1 of 2
.. device IO is working ok .. need to add in processing and creation of a ridefile in part 2
This commit is contained in:
@@ -100,6 +100,9 @@ struct Devices
|
||||
// cleanup for this device type is implemented:
|
||||
virtual bool canCleanup() { return false; };
|
||||
|
||||
// can preview ride list
|
||||
virtual bool canPreview() { return true; }; // assume it can !
|
||||
|
||||
virtual QString downloadInstructions() const { return ""; };
|
||||
|
||||
static QList<QString> typeNames();
|
||||
|
||||
@@ -109,7 +109,7 @@ DownloadRideDialog::setReadyInstruct()
|
||||
if (inst.size() == 0)
|
||||
statusLabel->setPlainText(tr("Click Download to begin downloading."));
|
||||
else
|
||||
statusLabel->setPlainText(inst + tr(", \nthen click Download."));
|
||||
statusLabel->setPlainText(inst + tr(", then click Download."));
|
||||
|
||||
updateAction( actionIdle );
|
||||
}
|
||||
@@ -287,12 +287,14 @@ DownloadRideDialog::downloadClicked()
|
||||
connect( this, SIGNAL(cancel()), device.data(), SLOT(cancelled()) );
|
||||
connect( device.data(), SIGNAL(updateProgress(QString)), this, SLOT(updateProgress(QString)));
|
||||
|
||||
updateStatus(tr("getting summary ..."));
|
||||
if( ! device->preview( err ) ){
|
||||
if (devtype->canPreview()) {
|
||||
updateStatus(tr("Getting ride list ..."));
|
||||
if( ! device->preview( err ) ){
|
||||
|
||||
QMessageBox::information(this, tr("Preview failed"), err);
|
||||
updateAction( actionIdle );
|
||||
return;
|
||||
QMessageBox::information(this, tr("Get ride list failed"), err);
|
||||
updateAction( actionIdle );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QList<DeviceRideItemPtr> &rides( device->rides() );
|
||||
@@ -304,11 +306,11 @@ DownloadRideDialog::downloadClicked()
|
||||
}
|
||||
}
|
||||
|
||||
updateStatus(tr("getting data ..."));
|
||||
updateStatus(tr("Starting Download ..."));
|
||||
if (!device->download( context->athlete->home->downloads(), files, err))
|
||||
{
|
||||
if (cancelled) {
|
||||
QMessageBox::information(this, tr("Download canceled"),
|
||||
QMessageBox::information(this, tr("Download cancelled"),
|
||||
tr("Cancel clicked by user."));
|
||||
cancelled = false;
|
||||
}
|
||||
@@ -325,7 +327,7 @@ DownloadRideDialog::downloadClicked()
|
||||
int failures = 0;
|
||||
for( int i = 0; i < files.size(); ++i ){
|
||||
if( ! files.at(i).startTime.isValid() ){
|
||||
updateStatus(tr("file %1 has no valid timestamp, falling back to 'now'")
|
||||
updateStatus(tr("File %1 has no valid timestamp, falling back to 'now'")
|
||||
.arg(files.at(i).name));
|
||||
files[i].startTime = QDateTime::currentDateTime();
|
||||
}
|
||||
@@ -379,7 +381,7 @@ DownloadRideDialog::downloadClicked()
|
||||
.arg(files.at(i).name)
|
||||
.arg(filepath)
|
||||
.arg(strerror(errno)) );
|
||||
updateStatus(tr("failed to rename %1 to %2")
|
||||
updateStatus(tr("Failed to rename %1 to %2")
|
||||
.arg( files.at(i).name )
|
||||
.arg( filename ));
|
||||
QFile::remove(files.at(i).name);
|
||||
@@ -429,7 +431,7 @@ DownloadRideDialog::downloadClicked()
|
||||
}
|
||||
|
||||
if( ! failures )
|
||||
updateStatus( tr("download completed successfully") );
|
||||
updateStatus( tr("Download completed") );
|
||||
|
||||
updateAction( actionIdle );
|
||||
}
|
||||
@@ -458,7 +460,7 @@ DownloadRideDialog::eraseClicked()
|
||||
|
||||
QString err;
|
||||
if( device->cleanup( err) )
|
||||
updateStatus( tr("cleaned data") );
|
||||
updateStatus( tr("Cleaned data") );
|
||||
else
|
||||
updateStatus( err );
|
||||
|
||||
@@ -486,5 +488,3 @@ DownloadRideDialog::closeClicked()
|
||||
{
|
||||
accept();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ static bool jouleRegistered =
|
||||
QString
|
||||
JouleDevices::downloadInstructions() const
|
||||
{
|
||||
return (tr("Make sure the Joule (1.0 or GPS) unit is turned ON\n"));
|
||||
return (tr("Make sure the Joule (1.0 or GPS) unit is turned ON"));
|
||||
}
|
||||
|
||||
DevicePtr
|
||||
|
||||
304
src/MoxyDevice.cpp
Normal file
304
src/MoxyDevice.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Damien Grauser (Damien.Grauser@pev-geneve.ch)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "MoxyDevice.h"
|
||||
#include "DownloadRideDialog.h"
|
||||
#include "PowerTapUtil.h"
|
||||
#include "Device.h"
|
||||
#include "RideFile.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <QThread> // for msleep
|
||||
|
||||
static bool moxyRegistered =
|
||||
Devices::addType("Moxy Muscle Oxygen Monitor", DevicesPtr(new MoxyDevices()) );
|
||||
|
||||
QString
|
||||
MoxyDevices::downloadInstructions() const
|
||||
{
|
||||
return (tr("NOT WORKING YET .. DO NOT USE\nMake sure the Moxy is connected via USB"));
|
||||
}
|
||||
|
||||
DevicePtr
|
||||
MoxyDevices::newDevice( CommPortPtr dev )
|
||||
{
|
||||
return DevicePtr( new MoxyDevice( dev ));
|
||||
}
|
||||
|
||||
bool
|
||||
MoxyDevice::download( const QDir &tmpdir,
|
||||
QList<DeviceDownloadFile> &files,
|
||||
QString &err)
|
||||
{ Q_UNUSED(tmpdir);
|
||||
Q_UNUSED(files);
|
||||
|
||||
int bytes=0;
|
||||
char vbuf[256];
|
||||
|
||||
QString deviceInfo;
|
||||
|
||||
if (!dev->open(err)) {
|
||||
err = tr("ERROR: open failed: ") + err;
|
||||
return false;
|
||||
}
|
||||
dev->setBaudRate(115200, err);
|
||||
|
||||
// get into engineering mode
|
||||
if (writeCommand(dev, "\r", err) == false) {
|
||||
emit updateStatus(QString(tr("Write error: %1\n")).arg(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((bytes=readUntilPrompt(dev, vbuf, 256, err)) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writeCommand(dev, "\r", err) == false) {
|
||||
emit updateStatus(QString(tr("Write error: %1\n")).arg(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((bytes=readUntilPrompt(dev, vbuf, 256, err)) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// now get a prompt
|
||||
if (writeCommand(dev, "\r", err) == false) {
|
||||
emit updateStatus(QString(tr("Write error: %1\n")).arg(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// get a prompt back ?
|
||||
if ((bytes=readUntilPrompt(dev, vbuf, 256, err)) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// now get version
|
||||
if (writeCommand(dev, "ver\r", err) == false) {
|
||||
emit updateStatus(QString(tr("Write error: %1\n")).arg(err));
|
||||
return false;
|
||||
} else if ((bytes=readUntilPrompt(dev, vbuf, 256, err)) > 0) {
|
||||
vbuf[bytes-1] = '\0';
|
||||
deviceInfo += vbuf;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// now get time on unit
|
||||
if (writeCommand(dev, "time\r", err) == false) {
|
||||
emit updateStatus(QString(tr("Write error: %1\n")).arg(err));
|
||||
return false;
|
||||
} else if ((bytes=readUntilPrompt(dev, vbuf, 256, err)) > 0) {
|
||||
vbuf[bytes-1] = '\0';
|
||||
deviceInfo += vbuf;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// now get battery status
|
||||
if (writeCommand(dev, "batt\r", err) == false) {
|
||||
emit updateStatus(QString(tr("Write error: %1\n")).arg(err));
|
||||
return false;
|
||||
} else if ((bytes=readUntilPrompt(dev, vbuf, 256, err)) > 0) {
|
||||
vbuf[bytes-1] = '\0';
|
||||
deviceInfo += vbuf;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// now get update mode
|
||||
if (writeCommand(dev, "um\r", err) == false) {
|
||||
emit updateStatus(QString(tr("Write error: %1\n")).arg(err));
|
||||
return false;
|
||||
} else if ((bytes=readUntilPrompt(dev, vbuf, 256, err)) > 0) {
|
||||
vbuf[bytes-1] = '\0';
|
||||
deviceInfo += vbuf;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
emit updateStatus(deviceInfo);
|
||||
|
||||
// now lets get the data
|
||||
if (writeCommand(dev, "gd\r", err) == false) {
|
||||
emit updateStatus(QString(tr("Write error: %1\n")).arg(err));
|
||||
return false;
|
||||
}
|
||||
emit updateStatus(deviceInfo);
|
||||
emit updateStatus(QString(tr("Downloading ... \n")));
|
||||
|
||||
do {
|
||||
if ((bytes=readData(dev, vbuf, 256, err)) > 0) {
|
||||
vbuf[bytes] = '\0';
|
||||
qDebug()<<vbuf;
|
||||
} else break;
|
||||
} while(vbuf[0] != '>');
|
||||
|
||||
// exit
|
||||
if (writeCommand(dev, "exit\r", err) == false) {
|
||||
emit updateStatus(QString(tr("Write error: %1\n")).arg(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// close device
|
||||
dev->close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MoxyDevice::cleanup( QString &err ) {
|
||||
emit updateStatus(tr("Erase all records on computer"));
|
||||
|
||||
// open device
|
||||
if (!dev->open(err)) {
|
||||
err = tr("ERROR: open failed: ") + err;
|
||||
}
|
||||
|
||||
dev->setBaudRate(115200, err);
|
||||
|
||||
dev->close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
hasNewline(const char *buf, int len)
|
||||
{
|
||||
static char newline[] = { 0x0d, 0x0a };
|
||||
if (len < 2)
|
||||
return false;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
bool success = true;
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
if (buf[i+j] != newline[j]) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (success)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static QString
|
||||
cEscape(const char *buf, int len)
|
||||
{
|
||||
char *result = new char[4 * len + 1];
|
||||
char *tmp = result;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (buf[i] == '"')
|
||||
tmp += sprintf(tmp, "\\\"");
|
||||
else if (isprint(buf[i]))
|
||||
*(tmp++) = buf[i];
|
||||
else
|
||||
tmp += sprintf(tmp, "\\x%02x", 0xff & (unsigned) buf[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// read the entire response after a command issued it ends with
|
||||
// a command prompt
|
||||
int
|
||||
MoxyDevice::readUntilPrompt(CommPortPtr dev, char *buf, int len, QString &err)
|
||||
{
|
||||
|
||||
int sofar = 0;
|
||||
while (sofar < len) {
|
||||
// Read one byte at a time to avoid waiting for timeout.
|
||||
int n = dev->read(buf + sofar, 1, err);
|
||||
if (n <= 0) {
|
||||
err = (n < 0) ? (tr("read error: ") + err) : tr("read timeout");
|
||||
err += QString(tr(", read %1 bytes so far: \"%2\""))
|
||||
.arg(sofar).arg(sofar > 0 ? cEscape(buf, sofar) : "");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sofar += n;
|
||||
|
||||
// we got our prompt
|
||||
if (*(buf+sofar-1) == '>') break;
|
||||
}
|
||||
return sofar;
|
||||
}
|
||||
|
||||
// just read whatever we get before the new line
|
||||
int
|
||||
MoxyDevice::readUntilNewline(CommPortPtr dev, char *buf, int len, QString &err)
|
||||
{
|
||||
int sofar = 0;
|
||||
while (!hasNewline(buf, sofar) && sofar < len) {
|
||||
// Read one byte at a time to avoid waiting for timeout.
|
||||
int n = dev->read(buf + sofar, 1, err);
|
||||
if (n <= 0) {
|
||||
err = (n < 0) ? (tr("read error: ") + err) : tr("read timeout");
|
||||
err += QString(tr(", read %1 bytes so far: \"%2\""))
|
||||
.arg(sofar).arg(cEscape(buf, sofar));
|
||||
return -1;
|
||||
}
|
||||
sofar += n;
|
||||
}
|
||||
return sofar;
|
||||
}
|
||||
|
||||
// read a line of data, if it starts with a prompt ">" then its done
|
||||
int
|
||||
MoxyDevice::readData(CommPortPtr dev, char *buf, int len, QString &err)
|
||||
{
|
||||
int sofar = 0;
|
||||
while (sofar < len) {
|
||||
// Read one byte at a time to avoid waiting for timeout.
|
||||
int n = dev->read(buf + sofar, 1, err);
|
||||
if (n <= 0) {
|
||||
err = (n < 0) ? (tr("read error: ") + err) : tr("read timeout");
|
||||
err += QString(tr(", read %1 bytes so far: \"%2\""))
|
||||
.arg(sofar).arg(cEscape(buf, sofar));
|
||||
return -1;
|
||||
}
|
||||
|
||||
sofar += n;
|
||||
|
||||
if (hasNewline(buf, sofar) || *buf == '>') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sofar;
|
||||
}
|
||||
|
||||
bool
|
||||
MoxyDevice::writeCommand(CommPortPtr dev, const char *command, QString &err)
|
||||
{
|
||||
QThread::msleep(100); // wait a tenth of a second
|
||||
|
||||
int len = strlen(command);
|
||||
int n = dev->write(const_cast<char*>(command), len, err);
|
||||
|
||||
if (n != len) {
|
||||
if (n < 0)
|
||||
err = QString(tr("failed to write '%1' to device: %2")).arg(const_cast<char*>(command)).arg(err);
|
||||
else
|
||||
err = QString(tr("timeout writing '%1' to device")).arg(const_cast<char*>(command));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
45
src/MoxyDevice.h
Normal file
45
src/MoxyDevice.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef JOULEDEVICE_H
|
||||
#define JOULEDEVICE_H
|
||||
|
||||
#include "CommPort.h"
|
||||
#include "Device.h"
|
||||
#include "stdint.h"
|
||||
|
||||
class DeviceFileInfo;
|
||||
|
||||
struct MoxyDevices : public Devices
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(MoxyDevices)
|
||||
|
||||
public:
|
||||
virtual DevicePtr newDevice( CommPortPtr dev );
|
||||
virtual QString downloadInstructions() const;
|
||||
virtual bool canCleanup( void ) {return true; };
|
||||
virtual bool canPreview() { return false; }; // Moxy is dumb ass
|
||||
};
|
||||
|
||||
struct MoxyDevice : public Device
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(MoxyDevice)
|
||||
|
||||
public:
|
||||
MoxyDevice( CommPortPtr dev ) : Device( dev ) {};
|
||||
|
||||
// io with the moxy is text based lines
|
||||
// they even give you a prompt ">" when
|
||||
// the command completes
|
||||
int readUntilPrompt(CommPortPtr dev, char *buf, int len, QString &err);
|
||||
int readUntilNewline(CommPortPtr dev, char *buf, int len, QString &err);
|
||||
int readData(CommPortPtr dev, char *buf, int len, QString &err);
|
||||
|
||||
bool writeCommand(CommPortPtr dev, const char *command, QString &err);
|
||||
|
||||
virtual bool download( const QDir &tmpdir,
|
||||
QList<DeviceDownloadFile> &files,
|
||||
QString &err);
|
||||
|
||||
virtual bool cleanup( QString &err );
|
||||
|
||||
};
|
||||
|
||||
#endif // JOULEDEVICE_H
|
||||
@@ -112,6 +112,7 @@ Serial::open(QString &err)
|
||||
perror("tcsetattr");
|
||||
assert(0);
|
||||
}
|
||||
tcflush(fd, TCIOFLUSH); // clear out the garbage
|
||||
return true;
|
||||
#else
|
||||
Q_UNUSED(err);
|
||||
@@ -171,6 +172,7 @@ Serial::close()
|
||||
{
|
||||
#ifndef Q_OS_WIN32
|
||||
assert(fd >= 0);
|
||||
tcflush(fd, TCIOFLUSH); // clear out the garbage
|
||||
::close(fd);
|
||||
fd = -1;
|
||||
#else
|
||||
@@ -319,15 +321,45 @@ Serial::name() const
|
||||
|
||||
bool
|
||||
Serial::setBaudRate(int speed, QString &err)
|
||||
{ Q_UNUSED(speed); Q_UNUSED(err);
|
||||
/* not yet implemented
|
||||
{
|
||||
|
||||
struct termios tty;
|
||||
if (cfsetspeed(&tty, speed) == -1) {
|
||||
perror("cfsetspeed");
|
||||
assert(0);
|
||||
// only really needed for Moxy
|
||||
// so not doing a big old switch/case
|
||||
if (speed == 115200) {
|
||||
|
||||
#ifndef Q_OS_WIN32
|
||||
|
||||
// LINUX / MAC
|
||||
struct termios tty;
|
||||
if (tcgetattr(fd, &tty) == -1) {
|
||||
perror("tcgetattr");
|
||||
}
|
||||
cfsetspeed(&tty, B115200);
|
||||
|
||||
/* put terminal in raw mode after flushing */
|
||||
if (tcsetattr(fd,TCSAFLUSH,&tty) < 0) {
|
||||
qDebug()<<"cannot set raw mode";
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
|
||||
// WINDOWS
|
||||
|
||||
DCB deviceSettings; // serial port settings baud rate et al
|
||||
|
||||
// get current settings
|
||||
if (GetCommState (fd, &deviceSettings) == false)
|
||||
return false;
|
||||
|
||||
// set the baud rate then
|
||||
deviceSettings.BaudRate = CBR_115200;
|
||||
|
||||
// apply
|
||||
if (SetCommState(fd, &deviceSettings) == false)
|
||||
return false;
|
||||
#endif
|
||||
return true; // this worked
|
||||
}
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -363,6 +363,7 @@ HEADERS += \
|
||||
MergeActivityWizard.h \
|
||||
MetadataWindow.h \
|
||||
MetricAggregator.h \
|
||||
MoxyDevice.h \
|
||||
NewCyclistDialog.h \
|
||||
NullController.h \
|
||||
PaceZones.h \
|
||||
@@ -574,6 +575,7 @@ SOURCES += \
|
||||
MergeActivityWizard.cpp \
|
||||
MetadataWindow.cpp \
|
||||
MetricAggregator.cpp \
|
||||
MoxyDevice.cpp \
|
||||
NewCyclistDialog.cpp \
|
||||
NullController.cpp \
|
||||
PaceTimeInZone.cpp \
|
||||
|
||||
Reference in New Issue
Block a user