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:
Mark Liversedge
2014-11-03 20:30:35 +00:00
parent 00d2e989d4
commit 66570337b3
7 changed files with 408 additions and 22 deletions

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -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
View 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
View 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

View File

@@ -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;
}

View File

@@ -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 \