mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 08:38:45 +00:00
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.
634 lines
15 KiB
C++
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;
|
|
}
|
|
|