Files
GoldenCheetah/src/SrmDevice.cpp
Mark Liversedge aa8605e8d5 QT5 -- 1 of 3
Porting the codebase to QT 5 (5.2) to get the
latest bug fixes, performance and improved platform
support.

This first part is to fixup the codebase to compile
on Qt 5, but some aspects have been broken (video).

The second part is to migrate from Qwt 6.0.1 to the
latest Qwt for multiaxis support.

The third part will be to fixup any platform specific
issues or issues identified at runtime.
2013-12-09 09:57:13 +00:00

634 lines
15 KiB
C++

/*
* Copyright (c) 2008 Sean C. Rhea (srhea@srhea.net)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "SrmDevice.h"
#include "SrmRideFile.h"
#include <srmio.h>
#include <QMessageBox>
#include <errno.h>
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 DevicePtr( new SrmDevice( dev, protoVersion));
}
bool
SrmDevices::supportsPort( CommPortPtr dev )
{
#if defined(SRMIO_HAVE_TERMIOS) || defined(SRMIO_HAVE_WINCOM)
// we could check device name starts with "com" or "/dev"
// but we wouldn't have got here unless it was a supported
// serial port anyway.
if( dev->type() == "Serial" )
return true;
#endif
#ifdef SRMIO_HAVE_D2XX
if( dev->type() == "D2XX" ){
switch( protoVersion ){
case 6:
case 7:
return true;
}
}
#endif
return false;
}
bool
SrmDevices::exclusivePort( CommPortPtr dev )
{
switch( protoVersion ){
case 5:
if( dev->type() == "Serial" && dev->name().contains( "PL2303" ) )
return true;
break;
case 6:
case 7:
if( dev->type() == "D2XX" && dev->name().startsWith( "POWERCONTROL" ) )
return true;
break;
}
return false;
}
bool
SrmDevice::get_tmpname(const QDir &tmpdir, QString &tmpname, QString &err)
{
QString tmpl = tmpdir.absoluteFilePath(".srmdl.XXXXXX.srm");
QTemporaryFile tmp(tmpl);
tmp.setAutoRemove(false);
if (!tmp.open()) {
err = tr("Failed to create temporary file ")
+ tmpl + ": " + tmp.error();
return false;
}
tmpname = tmp.fileName(); // after close(), tmp.fileName() is ""
tmp.close();
return true;
}
SrmDevice::~SrmDevice()
{
close();
}
// translate C callback function to method emitting signal
static void logfunc( const char *msg, void *data )
{
SrmDevice *dev = reinterpret_cast<SrmDevice*>(data);
dev->_emit_updateStatus( msg );
}
void
SrmDevice::_emit_updateStatus( QString statusText )
{
emit updateStatus( statusText );
}
bool
SrmDevice::open( QString &err )
{
srmio_error_t serr;
if( dev->type() == "Serial" ){
// no need to check device name starts with "com" or "/dev"
// since we wouldn't get this far unless it was a supported
// serial port device anyway.
#ifdef SRMIO_HAVE_WINCOM
io = srmio_iow32_new( dev->name().toLatin1().constData(), &serr );
if( ! io ){
err = tr("failed to allocate device handle: %1")
.arg(serr.message);
return false;
}
#elif defined(SRMIO_HAVE_TERMIOS)
io = srmio_ios_new( dev->name().toLatin1().constData(), &serr );
if( ! io ){
err = tr("failed to allocate device handle: %1")
.arg(serr.message);
return false;
}
#else
err = tr("device type %1 is unsupported")
.arg(dev->type());
return false;
#endif
} else if( dev->type() == "D2XX" ){
#ifdef SRMIO_HAVE_D2XX
io = srmio_d2xx_description_new(
dev->name().toLatin1().constData(), &serr );
if( ! io ){
err = tr("failed to allocate device handle: %1")
.arg(serr.message);
return false;
}
#else
err = tr("device type %1 is unsupported")
.arg(dev->type());
return false;
#endif
} else {
err = tr("device type %1 is unsupported")
.arg(dev->type());
return false;
}
switch( protoVersion ){
case 5:
emit updateStatus( tr("opening PCV at %1").arg(dev->name()) );
pc = srmio_pc5_new( &serr );
break;
case 6:
case 7:
emit updateStatus( tr("opening PC6/7 at %1").arg(dev->name()) );
pc = srmio_pc7_new( &serr );
break;
default:
err = tr("unsupported SRM Protocl version: %1")
.arg(protoVersion);
goto fail;
}
if( ! pc ){
err = tr("failed to allocate Powercontrol handle: %1")
.arg(serr.message);
goto fail;
}
if( ! srmio_io_open( io, &serr ) ){
err = tr("Couldn't open device %1: %2")
.arg(dev->name())
.arg(serr.message);
goto fail;
}
if( ! srmio_pc_set_logfunc( pc, logfunc,
reinterpret_cast<void*>( this ), &serr ) ){
err = tr("Couldn't set logging function: %1")
.arg(serr.message);
goto fail;
}
if( ! srmio_pc_set_device( pc, io, &serr ) ){
err = tr("failed to set Powercontrol io handle: %1")
.arg(serr.message);
goto fail;
}
if( ! srmio_pc_open( pc, &serr ) ){
err = tr("failed to initialize Powercontrol communication: %1")
.arg(serr.message);
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::preview( QString &err )
{
srmio_error_t serr;
size_t block_cnt;
struct _srmio_pc_xfer_block_t block;
if( ! is_open ){
if( ! open( err ) )
return false;
}
rideList.clear();
if( ! srmio_pc_can_preview( pc ) )
// nothing to do
return true;
if( ! srmio_pc_xfer_start( pc, &serr ) ){
err = tr("failed to start download: %1")
.arg(serr.message);
goto fail;
}
if( ! srmio_pc_xfer_get_blocks( pc, &block_cnt, &serr ) ){
err = tr("failed to get number of data blocks: %1")
.arg(serr.message);
goto fail;
}
emit updateStatus(tr("found %1 ride blocks").arg(block_cnt));
while( srmio_pc_xfer_block_next( pc, &block )){
DeviceRideItemPtr ride( new DeviceRideItem );
if( block.start )
ride->startTime.setTime_t( 0.1 * block.start );
if( block.end )
ride->endTime.setTime_t( 0.1 * block.end );
ride->work = block.total;
ride->wanted = false;
rideList.append( ride );
if( block.athlete )
free( block.athlete );
block.athlete = NULL;
}
if( srmio_pc_xfer_state_success != srmio_pc_xfer_status( pc, &serr ) ){
err = tr( "preview failed: %1")
.arg(serr.message);
goto fail;
}
if( ! srmio_pc_xfer_finish( pc, &serr ) ){
err = tr("preview failed: %1")
.arg(serr.message);
goto fail;
}
return true;
fail:
rideList.clear();
return false;
}
bool
SrmDevice::download( const QDir &tmpdir,
QList<DeviceDownloadFile> &files,
QString &err)
{
srmio_error_t serr;
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 - NOTE: we could make this configurable
if( ! is_open ){
if( ! open( err ) )
return false;
}
// fetch preview in case user didn't
if( srmio_pc_can_preview(pc) && rideList.size() == 0 ){
if( ! preview( err ) )
return false;
}
data = srmio_data_new( &serr );
if( ! data ){
err = tr("failed to allocate data handle: %1")
.arg(serr.message);
goto fail;
}
if( m_Cancelled ){
err = tr("download cancelled");
goto fail;
}
if( ! srmio_pc_xfer_start( pc, &serr )){
err = tr("failed to start download: %1")
.arg(serr.message);
goto fail;
}
if( ! srmio_pc_xfer_get_blocks( pc, &block_cnt, &serr ) ){
err = tr("failed to get number of data blocks: %1")
.arg(serr.message);
goto fail1;
}
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 ){
emit updateStatus(tr("skipping unselected ride block %1")
.arg(block_num +1));
++block_num;
continue;
}
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( m_Cancelled ){
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;
}
emit updateProgress( tr("progress: %1/%2")
.arg(block_done)
.arg(prog_total));
}
if( ! srmio_data_add_chunk( data, &chunk, &serr ) ){
err = tr("adding chunk failed: %1")
.arg(serr.message);
goto fail1;
}
++chunks_done;
/* finish previous marker */
if( mfirst >= 0 && ( ! is_int || is_first ) )
if( ! srmio_data_add_marker( data, mfirst, data->cused -2, &serr ) ){
err = tr("adding marker failed: %1")
.arg(serr.message);
goto fail1;
}
/* start marker */
if( is_first ){
mfirst = (int)data->cused -1;
} else if( ! is_int ){
mfirst = -1;
}
}
/* finalize marker at block end */
if( mfirst >= 0 ){
if( ! srmio_data_add_marker( data, mfirst, data->cused -1, &serr ) ){;
err = tr("adding marker failed: %1")
.arg(serr.message);
goto fail1;
}
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_state_success != srmio_pc_xfer_status( pc, &serr ) ){
err = tr( "download failed: %1")
.arg(serr.message);
goto fail;
}
if( ! srmio_pc_xfer_finish( pc, &serr ) ){
err = tr( "download failed: %1")
.arg(serr.message);
goto fail;
}
emit updateStatus( tr("got %1 records").arg(data->cused) );
if( m_Cancelled ){
err = tr("download cancelled");
goto fail;
}
if( ! data->cused ){
err = tr("no data available");
goto fail;
}
splitList = srmio_data_split( data, splitGap, 1000, &serr );
if( ! splitList ){
err = tr("Couldn't split data: %1")
.arg(serr.message);
goto fail;
}
for( split = splitList; *split; ++split ){
FILE *fh( NULL );
srmio_time_t stime;
srmio_data_t fixed;
DeviceDownloadFile file;
// skip empty hunks ... shouldn't happen, just to be safe
if( ! (*split)->cused ){
continue;
}
fixed = srmio_data_fixup( *split, &serr );
if( ! fixed ){
err = tr("Couldn't fixup data: %1")
.arg(serr.message);
goto fail;
}
// skip empty hunks ... shouldn't happen, just to be safe
if( ! fixed->cused ){
srmio_data_free(fixed);
continue;
}
file.extension = "srm";
if (!get_tmpname(tmpdir, file.name, err))
goto fail;
if( ! srmio_data_time_start( fixed, &stime, &serr ) ){
srmio_data_free(fixed);
err = tr("Couldn't get start time of data: %1")
.arg(serr.message);
goto fail;
}
file.startTime.setTime_t( 0.1 * stime );
fh = fopen( file.name.toLatin1().constData(), "wb" );
if( ! fh ){
srmio_data_free(fixed);
err = tr( "failed to open file %1: %2")
.arg(file.name)
.arg(strerror(errno));
goto fail;
}
if( ! srmio_file_srm7_write(fixed, fh, &serr) ){
srmio_data_free(fixed);
err = tr("Couldn't write to file %1: %2")
.arg(file.name)
.arg(serr.message);
fclose(fh);
goto fail;
}
files.append(file);
fclose( fh );
srmio_data_free(fixed);
}
for( split = splitList; *split; ++split )
srmio_data_free( *split );
free(splitList);
srmio_data_free( data );
return true;
fail1:
srmio_pc_xfer_finish(pc, NULL);
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 )
{
srmio_error_t serr;
if( ! is_open ){
if( ! open( err ) )
goto cleanup;
}
emit updateStatus( tr("cleaning device ..."));
if( ! srmio_pc_cmd_clear( pc, &serr ) ){
err = tr("failed to clear Powercontrol memory: %1")
.arg(serr.message);
goto cleanup;
}
return true;
cleanup:
close();
return false;
}