mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Remove WFAPI support
This commit is contained in:
@@ -63,10 +63,6 @@
|
||||
#include "kqoauthmanager.h"
|
||||
#endif
|
||||
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
#include "WFApi.h"
|
||||
#endif
|
||||
|
||||
#ifdef GC_HAVE_SAMPLERATE
|
||||
#include <samplerate.h>
|
||||
#endif
|
||||
@@ -266,12 +262,6 @@ QString GcCrashDialog::versionHTML()
|
||||
vlc = "yes";
|
||||
#endif
|
||||
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
QString wfapi = WFApi::getInstance()->apiVersion();
|
||||
#else
|
||||
QString wfapi = QString("none");
|
||||
#endif
|
||||
|
||||
#ifdef GC_HAVE_SAMPLERATE
|
||||
QString src = QString(src_get_version()).mid(14,6);
|
||||
#else
|
||||
@@ -332,7 +322,6 @@ QString GcCrashDialog::versionHTML()
|
||||
.arg(ical)
|
||||
.arg(usbxpress)
|
||||
.arg(libusb)
|
||||
.arg(wfapi)
|
||||
.arg(vlc)
|
||||
#if defined GC_VIDEO_QUICKTIME
|
||||
.arg("quicktime")
|
||||
|
||||
@@ -108,10 +108,6 @@
|
||||
// LTM CHART DRAG/DROP PARSE
|
||||
#include "LTMChartParser.h"
|
||||
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
#include "WFApi.h"
|
||||
#endif
|
||||
|
||||
// CloudDB
|
||||
#ifdef GC_HAS_CLOUD_DB
|
||||
#include "CloudDBCommon.h"
|
||||
@@ -155,10 +151,6 @@ MainWindow::MainWindow(const QDir &home)
|
||||
setContentsMargins(0,0,0,0);
|
||||
setAcceptDrops(true);
|
||||
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
WFApi *w = WFApi::getInstance(); // ensure created on main thread
|
||||
w->apiVersion();//shutup compiler
|
||||
#endif
|
||||
Library::initialise(context->athlete->home->root());
|
||||
QNetworkProxyQuery npq(QUrl("http://www.google.com"));
|
||||
QList<QNetworkProxy> listOfProxies = QNetworkProxyFactory::systemProxyForQuery(npq);
|
||||
|
||||
@@ -153,12 +153,6 @@ DeviceScanner::run()
|
||||
active = true;
|
||||
bool result = false;
|
||||
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
void *pool;
|
||||
// get an autorelease pool for this thread!
|
||||
if (wizard->deviceTypes.Supported[wizard->current].connector == DEV_BTLE) pool = WFApi::getInstance()->getPool();
|
||||
#endif
|
||||
|
||||
for (int i=0; active && !result && i<10; i++) { // search for longer
|
||||
|
||||
// better to wait a while, esp. if its just a USB device
|
||||
@@ -170,10 +164,6 @@ DeviceScanner::run()
|
||||
result = quickScan(false);
|
||||
}
|
||||
if (active) emit finished(result); // only signal if we weren't aborted!
|
||||
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
WFApi::getInstance()->freePool(pool);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@@ -208,8 +198,7 @@ DeviceScanner::quickScan(bool deep) // scan quickly or if true scan forever, as
|
||||
#endif
|
||||
case DEV_NULL : wizard->controller = new NullController(NULL, NULL); break;
|
||||
case DEV_ANTLOCAL : wizard->controller = new ANTlocalController(NULL, NULL); break;
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
case DEV_KICKR : wizard->controller = new KickrController(NULL, NULL); break;
|
||||
#ifdef QT_BLUETOOTH_LIB
|
||||
case DEV_BT40 : wizard->controller = new BT40Controller(NULL, NULL); break;
|
||||
#endif
|
||||
|
||||
@@ -260,12 +249,6 @@ DeviceScanner::quickScan(bool deep) // scan quickly or if true scan forever, as
|
||||
|
||||
} while (!isfound && deep && count++ < 2);
|
||||
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
// save away the device UUID, so we can choose it when connecting.
|
||||
if (isfound && wizard->deviceTypes.Supported[wizard->current].connector == DEV_BTLE)
|
||||
wizard->portSpec = ((KickrController*)(wizard->controller))->id();
|
||||
#endif
|
||||
|
||||
return isfound;
|
||||
|
||||
}
|
||||
@@ -453,7 +436,6 @@ AddSearch::nextId() const
|
||||
case DEV_ANTLOCAL : return 50; break; // pair
|
||||
case DEV_BT40 : return 55; break; // pair BT devices
|
||||
default:
|
||||
case DEV_KICKR :
|
||||
case DEV_CT : return 60; break; // confirm and add
|
||||
case DEV_MONARK : return 60; break; // confirm and add
|
||||
case DEV_KETTLER : return 60; break; // confirm and add
|
||||
@@ -887,10 +869,6 @@ AddPairBTLE::cleanupPage()
|
||||
void
|
||||
AddPairBTLE::initializePage()
|
||||
{
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
qDebug()<<"found this many devices:"<<WFApi::getInstance()->deviceCount();
|
||||
#endif
|
||||
|
||||
// setup the controller and start it off so we can
|
||||
// manipulate it
|
||||
if (wizard->controller) delete wizard->controller;
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
#include "DeviceTypes.h"
|
||||
#include "Serial.h"
|
||||
#include "RealtimeController.h"
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
#include "KickrController.h"
|
||||
#ifdef QT_BLUETOOTH_LIB
|
||||
#include "BT40Controller.h"
|
||||
#endif
|
||||
#ifdef GC_HAVE_LIBUSB
|
||||
|
||||
@@ -1,276 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Mark Liversedge (liversedge@gmail.com)
|
||||
*
|
||||
* 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 "BT40.h"
|
||||
|
||||
BT40::BT40(QObject *parent, DeviceConfiguration *devConf) : QThread(parent)
|
||||
{
|
||||
this->parent = parent;
|
||||
this->devConf = devConf;
|
||||
scanned = false;
|
||||
mode = -1;
|
||||
load = 100;
|
||||
slope = 1.0;
|
||||
|
||||
connect(WFApi::getInstance(), SIGNAL(discoveredDevices(int,bool)), this, SLOT(discoveredDevices(int,bool)));
|
||||
}
|
||||
|
||||
BT40::~BT40()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
// not required
|
||||
void BT40::setDevice(QString) { }
|
||||
|
||||
void BT40::setMode(int mode, double load, double gradient)
|
||||
{
|
||||
pvars.lock();
|
||||
this->mode = mode;
|
||||
this->load = load;
|
||||
this->slope = gradient;
|
||||
pvars.unlock();
|
||||
}
|
||||
|
||||
void BT40::setLoad(double load)
|
||||
{
|
||||
pvars.lock();
|
||||
if (load > 1500) load = 1500;
|
||||
if (load < 50) load = 50;
|
||||
this->load = load;
|
||||
pvars.unlock();
|
||||
}
|
||||
|
||||
void BT40::setGradient(double gradient)
|
||||
{
|
||||
pvars.lock();
|
||||
this->slope = gradient;
|
||||
pvars.unlock();
|
||||
}
|
||||
|
||||
|
||||
int BT40::getMode()
|
||||
{
|
||||
int tmp;
|
||||
pvars.lock();
|
||||
tmp = mode;
|
||||
pvars.unlock();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
double BT40::getLoad()
|
||||
{
|
||||
double tmp;
|
||||
pvars.lock();
|
||||
tmp = load;
|
||||
pvars.unlock();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
double BT40::getGradient()
|
||||
{
|
||||
double tmp;
|
||||
pvars.lock();
|
||||
tmp = slope;
|
||||
pvars.unlock();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void
|
||||
BT40::getRealtimeData(RealtimeData &rtData)
|
||||
{
|
||||
pvars.lock();
|
||||
rtData = rt;
|
||||
pvars.unlock();
|
||||
}
|
||||
|
||||
int
|
||||
BT40::start()
|
||||
{
|
||||
QThread::start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// does nothing - neither does pause
|
||||
int BT40::restart() { return 0; }
|
||||
int BT40::pause() { return 0; }
|
||||
|
||||
int BT40::stop()
|
||||
{
|
||||
running = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// used by thread to set variables and emit event if needed
|
||||
// on unexpected exit
|
||||
int BT40::quit(int code)
|
||||
{
|
||||
// event code goes here!
|
||||
exit(code);
|
||||
return 0; // never gets here obviously but shuts up the compiler!
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
* MAIN THREAD - READS TELEMETRY AND UPDATES LOAD/GRADIENT ON KICKR
|
||||
*----------------------------------------------------------------------*/
|
||||
void BT40::run()
|
||||
{
|
||||
int currentmode = -1;
|
||||
int currentload = -1;
|
||||
double currentslope= -1;
|
||||
|
||||
// Connect to the device
|
||||
if (connectBT40()) {
|
||||
quit(2);
|
||||
return; // open failed!
|
||||
}
|
||||
|
||||
running = true;
|
||||
while(running) {
|
||||
|
||||
// only get busy if we're actually connected
|
||||
if (WFApi::getInstance()->isConnected(sd)) {
|
||||
|
||||
// We ALWAYS set load for each loop. This is because
|
||||
// even though the device reports as connected we need
|
||||
// to wait before it really is. So we just keep on
|
||||
// sending the current mode/load. It doesn't cost us
|
||||
// anything since all devices are powered.
|
||||
|
||||
// it does generate a few error messages though..
|
||||
// and the connection takes about 25 secs to get
|
||||
// up to speed.
|
||||
|
||||
// set load - reset it if generated watts don't match ..
|
||||
if (mode == RT_MODE_ERGO) {
|
||||
WFApi::getInstance()->setErgoMode(sd);
|
||||
WFApi::getInstance()->setLoad(sd, load);
|
||||
currentload = load;
|
||||
currentmode = mode;
|
||||
}
|
||||
|
||||
// set slope
|
||||
if (mode == RT_MODE_SLOPE && currentslope) {
|
||||
WFApi::getInstance()->setSlopeMode(sd);
|
||||
WFApi::getInstance()->setSlope(sd, slope);
|
||||
currentslope = slope;
|
||||
currentmode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
if (WFApi::getInstance()->hasData(sd)) {
|
||||
pvars.lock();
|
||||
WFApi::getInstance()->getRealtimeData(sd, &rt);
|
||||
|
||||
// set speed from wheelRpm and configured wheelsize
|
||||
double x = rt.getWheelRpm();
|
||||
if (devConf) rt.setSpeed(x * devConf->wheelSize / 1000 * 60 / 1000);
|
||||
else rt.setSpeed(x * 2.10 * 60 / 1000);
|
||||
pvars.unlock();
|
||||
}
|
||||
|
||||
// lets not hog cpu
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
disconnectBT40();
|
||||
quit(0);
|
||||
}
|
||||
|
||||
void
|
||||
BT40::discoveredDevices(int n, bool finished)
|
||||
{
|
||||
WFApi *w = WFApi::getInstance();
|
||||
|
||||
// need to emit signal with device uuid and type
|
||||
// this is used by the add device wizard.
|
||||
// but only emit as they are found, not at the end
|
||||
// when search times out -- we want them as they
|
||||
// arrive.
|
||||
if (!finished && w->deviceSubType(n-1) != WFApi::WF_SENSOR_SUBTYPE_BIKE_POWER_KICKR) {
|
||||
|
||||
for(int i=0; i<n; i++) {
|
||||
qDebug()<<this<<"BT40 discovered a bluetooth device.."<<i
|
||||
<<w->deviceUUID(i)
|
||||
<<w->deviceType(i);
|
||||
emit foundDevice(w->deviceUUID(i), w->deviceType(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BT40::find()
|
||||
{
|
||||
WFApi *w = WFApi::getInstance();
|
||||
|
||||
// do we even have BTLE hardware available?
|
||||
if (w->isBTLEEnabled() ==false) {
|
||||
|
||||
// lets try and enable it
|
||||
// won't complete for a while but hopefully before next attempt to find is made
|
||||
// by user or device scanner in AddDeviceWizard
|
||||
w->enableBTLE(true, false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// can we kick off a search?
|
||||
if (w->discoverDevicesOfType(WFApi::WF_SENSORTYPE_NONE) == false) return false;
|
||||
|
||||
// wait for it to return something, anything
|
||||
QEventLoop loop;
|
||||
connect(w, SIGNAL(discoverFinished()), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
|
||||
scanned = true;
|
||||
|
||||
// what did we get?
|
||||
if (w->deviceCount()) {
|
||||
deviceUUID = w->deviceUUID(0);
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
int
|
||||
BT40::connectBT40()
|
||||
{
|
||||
// get a pool for this thread
|
||||
pool = WFApi::getInstance()->getPool();
|
||||
sd = WFApi::getInstance()->connectDevice(devConf->portSpec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
BT40::disconnectBT40()
|
||||
{
|
||||
// disconnect
|
||||
WFApi::getInstance()->disconnectDevice(sd);
|
||||
connected = false;
|
||||
|
||||
// clear that pool now we're done
|
||||
WFApi::getInstance()->freePool(pool);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check to see of there is a port at the device specified
|
||||
// returns true if the device exists and false if not
|
||||
bool BT40::discover(QString)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
101
src/Train/BT40.h
101
src/Train/BT40.h
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Mark Liversedge (liversedge@gmail.com)
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _GC_BT40_h
|
||||
#define _GC_BT40_h 1
|
||||
#include "GoldenCheetah.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QFile>
|
||||
#include "RealtimeController.h"
|
||||
#include "TrainSidebar.h"
|
||||
#include "DeviceConfiguration.h"
|
||||
|
||||
#include "WFApi.h"
|
||||
|
||||
class BT40 : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BT40(QObject *parent=0, DeviceConfiguration * devConf=0); // pass device
|
||||
~BT40();
|
||||
|
||||
QObject *parent;
|
||||
|
||||
// HIGH-LEVEL FUNCTIONS
|
||||
int start(); // Calls QThread to start
|
||||
int restart(); // restart after paused
|
||||
int pause(); // pauses data collection, inbound telemetry is discarded
|
||||
int stop(); // stops data collection thread
|
||||
int quit(int error); // called by thread before exiting
|
||||
bool discover(QString deviceFilename); // confirm CT is attached to device
|
||||
|
||||
// SET
|
||||
void setDevice(QString deviceFilename); // setup the device filename
|
||||
void setLoad(double load); // set the load to generate in ERGOMODE
|
||||
void setGradient(double gradient); // set the load to generate in SSMODE
|
||||
void setMode(int mode,
|
||||
double load=100, // set mode to CT_ERGOMODE or CT_SSMODE
|
||||
double gradient=1);
|
||||
|
||||
bool find();
|
||||
int connectBT40();
|
||||
int disconnectBT40();
|
||||
|
||||
int getMode();
|
||||
double getGradient();
|
||||
double getLoad();
|
||||
void getRealtimeData(RealtimeData &rtData);
|
||||
|
||||
QString id() { return deviceUUID; }
|
||||
|
||||
signals:
|
||||
void foundDevice(QString uuid, int type);
|
||||
|
||||
private slots:
|
||||
void discoveredDevices(int,bool);
|
||||
|
||||
private:
|
||||
void run(); // called by start to kick off the CT comtrol thread
|
||||
|
||||
// device configuration
|
||||
DeviceConfiguration *devConf;
|
||||
|
||||
// Mutex for controlling accessing private data
|
||||
QMutex pvars;
|
||||
|
||||
bool scanned, running, connected;
|
||||
|
||||
volatile int mode;
|
||||
volatile double load;
|
||||
volatile double slope;
|
||||
|
||||
QString deviceUUID;
|
||||
int sd; // sensor descriptor aka an index into the connections array
|
||||
// mimics the fd index used by open/close syscalls.
|
||||
RealtimeData rt;
|
||||
|
||||
void *pool;
|
||||
};
|
||||
|
||||
#endif // _GC_BT40_h
|
||||
|
||||
@@ -42,15 +42,10 @@ DeviceTypes::DeviceTypes()
|
||||
"speed or cadence meters via a Garmin ANT+ USB1 or USB2 stick") ,
|
||||
":images/devices/garminusb.png" },
|
||||
#endif
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
#if 0 //!!! Deferred until v3.1 or as an update to v3.0 when Wahoo support ANT+
|
||||
{ DEV_BT40, DEV_BTLE, (char *) "Bluetooth 4.0", true, false,
|
||||
tr("Bluetooth Low Energy devices such as KK Inride, Stages PM, Blue HR and Blue SC"),
|
||||
":images/devices/btle.png" },
|
||||
#endif
|
||||
{ DEV_KICKR, DEV_BTLE, (char *) "Wahoo Kickr", true, false,
|
||||
tr("The Wahoo Fitness Kickr cycling trainer via its Bluetooth smart interface. "),
|
||||
":images/devices/kickr.png" },
|
||||
#endif
|
||||
{ DEV_CT, DEV_SERIAL, (char *) "Racermate Computrainer",true, false,
|
||||
tr("Racermate Computrainer Lab or Pro bike trainer with the handlebar controller "
|
||||
|
||||
@@ -34,8 +34,7 @@
|
||||
#define DEV_GSERVER 0x0100 // NOT IMPLEMENTED IN THIS RELEASE XXX
|
||||
#define DEV_GCLIENT 0x0200 // NOT IMPLEMENTED IN THIS RELEASE XXX
|
||||
#define DEV_FORTIUS 0x0800 // Tacx Fortius
|
||||
#define DEV_KICKR 0x1000 // Wahoo Kickr
|
||||
#define DEV_BT40 0x2000 // Wahoo Kickr
|
||||
#define DEV_BT40 0x2000 // QT Bluetooth support
|
||||
#define DEV_MONARK 0x4000 // Monark USB
|
||||
#define DEV_KETTLER 0x8000 // Kettler Serial
|
||||
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Mark Liversedge (liversedge@gmail.com)
|
||||
*
|
||||
* 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 "Kickr.h"
|
||||
|
||||
Kickr::Kickr(QObject *parent, DeviceConfiguration *devConf) : QThread(parent)
|
||||
{
|
||||
this->parent = parent;
|
||||
this->devConf = devConf;
|
||||
scanned = false;
|
||||
mode = -1;
|
||||
load = 100;
|
||||
slope = 1.0;
|
||||
|
||||
connect(WFApi::getInstance(), SIGNAL(discoveredDevices(int,bool)), this, SLOT(discoveredDevices(int,bool)));
|
||||
}
|
||||
|
||||
Kickr::~Kickr()
|
||||
{
|
||||
}
|
||||
|
||||
// not required
|
||||
void Kickr::setDevice(QString) { }
|
||||
|
||||
void Kickr::setMode(int mode, double load, double gradient)
|
||||
{
|
||||
pvars.lock();
|
||||
this->mode = mode;
|
||||
this->load = load;
|
||||
this->slope = gradient;
|
||||
pvars.unlock();
|
||||
}
|
||||
|
||||
void Kickr::setLoad(double load)
|
||||
{
|
||||
pvars.lock();
|
||||
if (load > 1500) load = 1500;
|
||||
if (load < 50) load = 50;
|
||||
this->load = load;
|
||||
pvars.unlock();
|
||||
}
|
||||
|
||||
void Kickr::setGradient(double gradient)
|
||||
{
|
||||
pvars.lock();
|
||||
this->slope = gradient;
|
||||
pvars.unlock();
|
||||
}
|
||||
|
||||
|
||||
int Kickr::getMode()
|
||||
{
|
||||
int tmp;
|
||||
pvars.lock();
|
||||
tmp = mode;
|
||||
pvars.unlock();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
double Kickr::getLoad()
|
||||
{
|
||||
double tmp;
|
||||
pvars.lock();
|
||||
tmp = load;
|
||||
pvars.unlock();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
double Kickr::getGradient()
|
||||
{
|
||||
double tmp;
|
||||
pvars.lock();
|
||||
tmp = slope;
|
||||
pvars.unlock();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void
|
||||
Kickr::getRealtimeData(RealtimeData &rtData)
|
||||
{
|
||||
pvars.lock();
|
||||
rtData = rt;
|
||||
pvars.unlock();
|
||||
}
|
||||
|
||||
int
|
||||
Kickr::start()
|
||||
{
|
||||
QThread::start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// does nothing - neither does pause
|
||||
int Kickr::restart() { return 0; }
|
||||
int Kickr::pause() { return 0; }
|
||||
|
||||
int Kickr::stop()
|
||||
{
|
||||
running = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// used by thread to set variables and emit event if needed
|
||||
// on unexpected exit
|
||||
int Kickr::quit(int code)
|
||||
{
|
||||
// event code goes here!
|
||||
exit(code);
|
||||
return 0; // never gets here obviously but shuts up the compiler!
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
* MAIN THREAD - READS TELEMETRY AND UPDATES LOAD/GRADIENT ON KICKR
|
||||
*----------------------------------------------------------------------*/
|
||||
void Kickr::run()
|
||||
{
|
||||
int currentmode = -1;
|
||||
int currentload = -1;
|
||||
double currentslope= -1;
|
||||
|
||||
// Connect to the device
|
||||
if (connectKickr()) {
|
||||
quit(2);
|
||||
return; // open failed!
|
||||
}
|
||||
|
||||
int connectionloops =0;
|
||||
|
||||
running = true;
|
||||
while(running) {
|
||||
|
||||
// only get busy if we're actually connected
|
||||
if (WFApi::getInstance()->isConnected(sd)) {
|
||||
|
||||
connectionloops = 0;
|
||||
|
||||
// We ALWAYS set load for each loop. This is because
|
||||
// even though the device reports as connected we need
|
||||
// to wait before it really is. So we just keep on
|
||||
// sending the current mode/load. It doesn't cost us
|
||||
// anything since all devices are powered.
|
||||
|
||||
// it does generate a few error messages though..
|
||||
// and the connection takes about 25 secs to get
|
||||
// up to speed.
|
||||
|
||||
// set load - reset it if generated watts don't match ..
|
||||
if (mode == RT_MODE_ERGO) {
|
||||
WFApi::getInstance()->setErgoMode(sd);
|
||||
WFApi::getInstance()->setLoad(sd, load);
|
||||
currentload = load;
|
||||
currentmode = mode;
|
||||
}
|
||||
|
||||
// set slope
|
||||
if (mode == RT_MODE_SLOPE && currentslope) {
|
||||
WFApi::getInstance()->setSlopeMode(sd);
|
||||
WFApi::getInstance()->setSlope(sd, slope);
|
||||
currentslope = slope;
|
||||
currentmode = mode;
|
||||
}
|
||||
|
||||
// get telemetry
|
||||
if (WFApi::getInstance()->hasData(sd)) {
|
||||
pvars.lock();
|
||||
WFApi::getInstance()->getRealtimeData(sd, &rt);
|
||||
|
||||
// set speed from wheelRpm and configured wheelsize
|
||||
double x = rt.getWheelRpm();
|
||||
if (devConf) rt.setSpeed(x * devConf->wheelSize / 1000 * 60 / 1000);
|
||||
else rt.setSpeed(x * 2.10 * 60 / 1000);
|
||||
pvars.unlock();
|
||||
}
|
||||
|
||||
} else {
|
||||
// 100ms in each loop, 30secs is 300 loops
|
||||
if (++connectionloops > 300) {
|
||||
// give up waiting for connection
|
||||
quit(-1);
|
||||
}
|
||||
}
|
||||
// lets not hog cpu
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
disconnectKickr();
|
||||
quit(0);
|
||||
}
|
||||
|
||||
void
|
||||
Kickr::discoveredDevices(int n, bool finished)
|
||||
{
|
||||
WFApi *w = WFApi::getInstance();
|
||||
|
||||
// need to emit signal with device uuid and type
|
||||
// this is used by the add device wizard.
|
||||
// but only emit as they are found, not at the end
|
||||
// when search times out -- we want them as they
|
||||
// arrive.
|
||||
if (!finished && w->deviceSubType(n-1) == WFApi::WF_SENSOR_SUBTYPE_BIKE_POWER_KICKR) {
|
||||
qDebug()<<"KIKCR? discovered a device.."
|
||||
<<w->deviceUUID(n-1)
|
||||
<<w->deviceType(n-1);
|
||||
emit foundDevice(w->deviceUUID(n-1), w->deviceType(n-1));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Kickr::find()
|
||||
{
|
||||
WFApi *w = WFApi::getInstance();
|
||||
|
||||
if (w->discoverDevicesOfType(WFApi::WF_SENSORTYPE_BIKE_POWER) == false) return false;
|
||||
|
||||
QEventLoop loop;
|
||||
connect(w, SIGNAL(discoveredDevices(int,bool)), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
|
||||
scanned = true;
|
||||
|
||||
if (w->deviceCount()) {
|
||||
deviceUUID = w->deviceUUID(0);
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
int
|
||||
Kickr::connectKickr()
|
||||
{
|
||||
// is BTLE even enabled?
|
||||
if (WFApi::getInstance()->isBTLEEnabled() == false) return -1;
|
||||
|
||||
// get a pool for this thread
|
||||
pool = WFApi::getInstance()->getPool();
|
||||
|
||||
// kick off connection and assume it is gonna be ok
|
||||
sd = WFApi::getInstance()->connectDevice(devConf->portSpec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Kickr::disconnectKickr()
|
||||
{
|
||||
// disconnect
|
||||
WFApi::getInstance()->disconnectDevice(sd);
|
||||
connected = false;
|
||||
|
||||
// clear that pool now we're done
|
||||
WFApi::getInstance()->freePool(pool);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check to see of there is a port at the device specified
|
||||
// returns true if the device exists and false if not
|
||||
bool Kickr::discover(QString)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Mark Liversedge (liversedge@gmail.com)
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _GC_Kickr_h
|
||||
#define _GC_Kickr_h 1
|
||||
#include "GoldenCheetah.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QFile>
|
||||
#include "RealtimeController.h"
|
||||
#include "TrainSidebar.h"
|
||||
#include "DeviceConfiguration.h"
|
||||
|
||||
#include "WFApi.h"
|
||||
|
||||
class Kickr : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Kickr(QObject *parent=0, DeviceConfiguration * devConf=0); // pass device
|
||||
~Kickr();
|
||||
|
||||
QObject *parent;
|
||||
|
||||
// HIGH-LEVEL FUNCTIONS
|
||||
int start(); // Calls QThread to start
|
||||
int restart(); // restart after paused
|
||||
int pause(); // pauses data collection, inbound telemetry is discarded
|
||||
int stop(); // stops data collection thread
|
||||
int quit(int error); // called by thread before exiting
|
||||
bool discover(QString deviceFilename); // confirm CT is attached to device
|
||||
|
||||
// SET
|
||||
void setDevice(QString deviceFilename); // setup the device filename
|
||||
void setLoad(double load); // set the load to generate in ERGOMODE
|
||||
void setGradient(double gradient); // set the load to generate in SSMODE
|
||||
void setMode(int mode,
|
||||
double load=100, // set mode to CT_ERGOMODE or CT_SSMODE
|
||||
double gradient=1);
|
||||
|
||||
bool find();
|
||||
int connectKickr();
|
||||
int disconnectKickr();
|
||||
|
||||
int getMode();
|
||||
double getGradient();
|
||||
double getLoad();
|
||||
void getRealtimeData(RealtimeData &rtData);
|
||||
|
||||
QString id() { return deviceUUID; }
|
||||
|
||||
signals:
|
||||
void foundDevice(QString uuid, int type);
|
||||
|
||||
private slots:
|
||||
void discoveredDevices(int,bool);
|
||||
|
||||
private:
|
||||
void run(); // called by start to kick off the CT comtrol thread
|
||||
|
||||
// device configuration
|
||||
DeviceConfiguration *devConf;
|
||||
|
||||
// Mutex for controlling accessing private data
|
||||
QMutex pvars;
|
||||
|
||||
bool scanned, running, connected;
|
||||
|
||||
volatile int mode;
|
||||
volatile double load;
|
||||
volatile double slope;
|
||||
|
||||
QString deviceUUID;
|
||||
int sd; // sensor descriptor aka an index into the connections array
|
||||
// mimics the fd index used by open/close syscalls.
|
||||
RealtimeData rt;
|
||||
|
||||
void *pool;
|
||||
};
|
||||
|
||||
#endif // _GC_Kickr_h
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Mark Liversedge (liversedge@gmail.com)
|
||||
*
|
||||
* 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 <QProgressDialog>
|
||||
#include "KickrController.h"
|
||||
#include "RealtimeData.h"
|
||||
|
||||
KickrController::KickrController(TrainSidebar *parent, DeviceConfiguration *dc) : RealtimeController(parent, dc)
|
||||
{
|
||||
myKickr = new Kickr(parent, dc);
|
||||
connect(myKickr, SIGNAL(foundDevice(QString,int)), this, SIGNAL(foundDevice(QString,int)));
|
||||
}
|
||||
|
||||
void
|
||||
KickrController::setDevice(QString)
|
||||
{
|
||||
// not required
|
||||
}
|
||||
|
||||
int
|
||||
KickrController::start()
|
||||
{
|
||||
myKickr->start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
KickrController::restart()
|
||||
{
|
||||
return myKickr->restart();
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
KickrController::pause()
|
||||
{
|
||||
return myKickr->pause();
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
KickrController::stop()
|
||||
{
|
||||
return myKickr->stop();
|
||||
}
|
||||
|
||||
bool
|
||||
KickrController::find()
|
||||
{
|
||||
return myKickr->find();
|
||||
}
|
||||
|
||||
bool
|
||||
KickrController::discover(QString name)
|
||||
{
|
||||
return myKickr->discover(name);
|
||||
}
|
||||
|
||||
|
||||
bool KickrController::doesPush() { return false; }
|
||||
bool KickrController::doesPull() { return true; }
|
||||
bool KickrController::doesLoad() { return true; }
|
||||
|
||||
/*
|
||||
* gets called from the GUI to get updated telemetry.
|
||||
* so whilst we are at it we check button status too and
|
||||
* act accordingly.
|
||||
*
|
||||
*/
|
||||
void
|
||||
KickrController::getRealtimeData(RealtimeData &rtData)
|
||||
{
|
||||
if(!myKickr->isRunning())
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(tr("Cannot Connect to Kickr"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.exec();
|
||||
parent->Stop(1);
|
||||
return;
|
||||
}
|
||||
// get latest telemetry
|
||||
myKickr->getRealtimeData(rtData);
|
||||
processRealtimeData(rtData);
|
||||
}
|
||||
|
||||
void KickrController::pushRealtimeData(RealtimeData &) { } // update realtime data with current values
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Mark Liversedge (liversedge@gmail.com)
|
||||
*
|
||||
* 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 "GoldenCheetah.h"
|
||||
|
||||
#include "RealtimeController.h"
|
||||
#include "DeviceConfiguration.h"
|
||||
#include "ConfigDialog.h"
|
||||
|
||||
#include "Kickr.h"
|
||||
|
||||
#ifndef _GC_KickrController_h
|
||||
#define _GC_KickrController_h 1
|
||||
|
||||
class KickrController : public RealtimeController
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
KickrController (TrainSidebar *parent =0, DeviceConfiguration *dc =0);
|
||||
|
||||
Kickr *myKickr; // the device itself
|
||||
|
||||
int start();
|
||||
int restart(); // restart after paused
|
||||
int pause(); // pauses data collection, inbound telemetry is discarded
|
||||
int stop(); // stops data collection thread
|
||||
|
||||
bool find();
|
||||
bool discover(QString name);
|
||||
void setDevice(QString);
|
||||
|
||||
// telemetry push pull
|
||||
bool doesPush(), doesPull(), doesLoad();
|
||||
void getRealtimeData(RealtimeData &rtData);
|
||||
void pushRealtimeData(RealtimeData &rtData);
|
||||
|
||||
void setLoad(double x) { myKickr->setLoad(x); }
|
||||
void setGradient(double x) { myKickr->setGradient(x); }
|
||||
void setMode(int x) { myKickr->setMode(x); }
|
||||
|
||||
QString id() { return myKickr->id(); }
|
||||
|
||||
signals:
|
||||
void foundDevice(QString uuid, int type);
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
#endif // _GC_KickrController_h
|
||||
@@ -46,8 +46,8 @@
|
||||
#endif
|
||||
#include "ANTlocalController.h"
|
||||
#include "NullController.h"
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
#include "KickrController.h"
|
||||
#ifdef QT_BLUETOOTH_LIB
|
||||
#include "BT40Controller.h"
|
||||
#endif
|
||||
#ifdef GC_HAVE_LIBUSB
|
||||
#include "FortiusController.h"
|
||||
@@ -641,9 +641,9 @@ TrainSidebar::configChanged(qint32)
|
||||
Devices[i].controller = new ANTlocalController(this, &Devices[i]);
|
||||
// connect slot for receiving remote control commands
|
||||
connect(Devices[i].controller, SIGNAL(remoteControl(uint16_t)), this, SLOT(remoteControl(uint16_t)));
|
||||
#ifdef GC_HAVE_WFAPI
|
||||
} else if (Devices.at(i).type == DEV_KICKR) {
|
||||
Devices[i].controller = new KickrController(this, &Devices[i]);
|
||||
#ifdef QT_BLUETOOTH_LIB
|
||||
} else if (Devices.at(i).type == DEV_BT40) {
|
||||
Devices[i].controller = new BT40Controller(this, &Devices[i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Mark Liversedge (liversedge@gmail.com)
|
||||
*
|
||||
* 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 WARRWFApiY; without even the implied warranty of MERCHWFApiABILITY 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
|
||||
*/
|
||||
|
||||
#ifndef gc_WFApi_h
|
||||
#define gc_WFApi_h
|
||||
|
||||
// GC
|
||||
#include "GoldenCheetah.h"
|
||||
#include "Settings.h"
|
||||
#include "RealtimeData.h"
|
||||
#include "DeviceConfiguration.h"
|
||||
#include "RealtimeData.h"
|
||||
|
||||
// QT
|
||||
#include <QPointer>
|
||||
#include <QObject>
|
||||
|
||||
// global singleton
|
||||
class WFApi;
|
||||
extern WFApi *_gc_wfapi;
|
||||
|
||||
// declare the provate implementation for objc
|
||||
#ifdef __OBJC__
|
||||
@class WFBridge;
|
||||
#else
|
||||
// irritating dependencies
|
||||
#endif
|
||||
|
||||
class WFApi : public QObject // QOBject for signals
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WFApi(); // single instance
|
||||
~WFApi();
|
||||
|
||||
static WFApi *getInstance() { if (_gc_wfapi) return _gc_wfapi;
|
||||
else return (_gc_wfapi = new WFApi); } // singleton
|
||||
|
||||
// what version?
|
||||
QString apiVersion();
|
||||
|
||||
// status of BTLE
|
||||
bool hasBTLESupport();
|
||||
bool isBTLEEnabled();
|
||||
bool enableBTLE(bool enable, bool bondingmode);
|
||||
bool isCommunicationHWReady();
|
||||
|
||||
// we don't use the WF API header "hardware_connector_types.h" because it
|
||||
// references Objc types (NSUInteger) that fail when compiling C++.
|
||||
// Instead we redeclare them here, but only the ones we absolutely need
|
||||
// and hopefully they won't change too often
|
||||
|
||||
// current state
|
||||
typedef enum {
|
||||
WFAPI_HWCONN_STATE_NOT_CONNECTED = 0,
|
||||
WFAPI_HWCONN_STATE_CONNECTED = 0x01,
|
||||
WFAPI_HWCONN_STATE_ACTIVE = 0x02,
|
||||
WFAPI_HWCONN_STATE_RESET = 0x04,
|
||||
WFAPI_HWCONN_STATE_BT40_ENABLED = 0x08,
|
||||
WFAPI_HWCONN_STATE_BT_BONDING_MODE = 0x10 } _state;
|
||||
int currentState();
|
||||
|
||||
// connection state
|
||||
typedef enum {
|
||||
WF_SENSOR_CONNECTION_STATUS_IDLE = 0,
|
||||
WF_SENSOR_CONNECTION_STATUS_CONNECTING = 1,
|
||||
WF_SENSOR_CONNECTION_STATUS_CONNECTED = 2,
|
||||
WF_SENSOR_CONNECTION_STATUS_INTERRUPTED = 3,
|
||||
WF_SENSOR_CONNECTION_STATUS_DISCONNECTING = 4 } _connstate;
|
||||
int connectionStatus(int sd);
|
||||
bool isConnected(int sd);
|
||||
|
||||
// all the sensor types just the types we need
|
||||
enum {
|
||||
WF_SENSORTYPE_NONE = 0,
|
||||
WF_SENSORTYPE_BIKE_POWER = 0x00000001, // kickr, inride etc
|
||||
WF_SENSORTYPE_BIKE_SPEED = 0x00000002,
|
||||
WF_SENSORTYPE_BIKE_CADENCE = 0x00000004,
|
||||
WF_SENSORTYPE_BIKE_SPEED_CADENCE = 0x00000008,
|
||||
WF_SENSORTYPE_FOOTPOD = 0x00000010,
|
||||
WF_SENSORTYPE_HEARTRATE = 0x00000020,
|
||||
WF_SENSORTYPE_WEIGHT_SCALE = 0x00000040,
|
||||
WF_SENSORTYPE_ANT_FS = 0x00000080,
|
||||
WF_SENSORTYPE_LOCATION = 0x00000100,
|
||||
WF_SENSORTYPE_CALORIMETER = 0x00000200,
|
||||
WF_SENSORTYPE_GEO_CACHE = 0x00000400,
|
||||
WF_SENSORTYPE_FITNESS_EQUIPMENT = 0x00000800,
|
||||
WF_SENSORTYPE_MULTISPORT_SPEED_DISTANCE = 0x00001000,
|
||||
WF_SENSORTYPE_PROXIMITY = 0x00002000,
|
||||
WF_SENSORTYPE_HEALTH_THERMOMETER = 0x00004000,
|
||||
WF_SENSORTYPE_BLOOD_PRESSURE = 0x00008000,
|
||||
WF_SENSORTYPE_BTLE_GLUCOSE = 0x00010000,
|
||||
WF_SENSORTYPE_GLUCOSE = 0x00020000,
|
||||
WF_SENSORTYPE_DISPLAY = 0x00800000 } _sensortypes; // rflkt
|
||||
|
||||
const QString sensorDescription(int id, int sub) const {
|
||||
QString returning(tr("Unknown"));
|
||||
switch (id) {
|
||||
case 0x0 : returning = tr("None"); break;
|
||||
case 0x1 :
|
||||
{
|
||||
switch(sub) {
|
||||
|
||||
case 0:
|
||||
default:
|
||||
returning = tr("Power Meter");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
returning = tr("Wahoo KICKR trainer");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
returning = tr("Stage ONE Crank Power Meter");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
returning = tr("Kurt Kinetic InRide Power Meter");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x2 : returning = tr("Bike Speed"); break;
|
||||
case 0x4 : returning = tr("Bike Cadence"); break;
|
||||
case 0x8 : returning = tr("Speed and Cadence"); break;
|
||||
case 0x10 : returning = tr("FootPod"); break;
|
||||
case 0x20 : returning = tr("Heart Rate"); break;
|
||||
case 0x800000 : returning = tr("RFKLT Display"); break;
|
||||
}
|
||||
return returning;
|
||||
}
|
||||
|
||||
|
||||
// subtypes -- esp. power
|
||||
enum {
|
||||
WF_SENSOR_SUBTYPE_UNSPECIFIED = 0,
|
||||
WF_SENSOR_SUBTYPE_BIKE_POWER_KICKR = 1,
|
||||
WF_SENSOR_SUBTYPE_BIKE_POWER_STAGE_ONE = 2,
|
||||
WF_SENSOR_SUBTYPE_BIKE_POWER_IN_RIDE = 3 } _sensorsubtype;
|
||||
|
||||
// scan
|
||||
bool discoverDevicesOfType(int eSensorType);
|
||||
int deviceCount();
|
||||
QString deviceUUID(int); // return the UUID for device n
|
||||
int deviceType(int); // return the device type
|
||||
int deviceSubType(int); // return the subtype found
|
||||
|
||||
// connect and disconnect
|
||||
int connectDevice(QString uuid); // connect the device n
|
||||
bool disconnectDevice(int sd); // disconnect
|
||||
|
||||
// has data?
|
||||
bool hasData(int sd);
|
||||
void getRealtimeData(int sd, RealtimeData *p);
|
||||
|
||||
// set slope or ergo mode
|
||||
void setSlopeMode(int sd);
|
||||
void setErgoMode(int sd);
|
||||
|
||||
// set resistance slope or load
|
||||
void setSlope(int sd, double slope);
|
||||
void setLoad(int sd, int watts);
|
||||
|
||||
// NOTE: There is an application wide NSAutoreleasePool maintained
|
||||
// in cocoa initialiser, but it is only to support activity on
|
||||
// the main thread.
|
||||
// The application code (e.g. Kickr.cpp) needs to get and free a
|
||||
// pool for each thread, this is why we have a getPool/freePool
|
||||
// method in WFApi, but never allocate a pool ourselves.
|
||||
void *getPool();
|
||||
void freePool(void*);
|
||||
|
||||
signals:
|
||||
void currentStateChanged(int); // hardware conncector state changed
|
||||
void connectionStateChanged(int status);
|
||||
void discoveredDevices(int,bool);
|
||||
void discoverFinished();
|
||||
void connectionHasData();
|
||||
|
||||
public slots:
|
||||
|
||||
// connecting...
|
||||
void stateChanged();
|
||||
void connectionState(int status);
|
||||
void connectionTimeout();
|
||||
|
||||
void connectedSensor(void*);
|
||||
void didDiscoverDevices(int count, bool finished);
|
||||
void disconnectedSensor(void*);
|
||||
void hasFirmwareUpdateAvalableForConnection();
|
||||
void connectorHasData();
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
public:
|
||||
|
||||
// the native api bridge -- private implementation in
|
||||
// WFApi.mm -- bridge between the QT/C++ world and the
|
||||
// WF/Objc world
|
||||
#ifdef __OBJC__
|
||||
WFBridge *wf; // when included in objc sources
|
||||
#else /* __OBJC__ */
|
||||
void *wf; // when included in C++ sources
|
||||
#endif /* __OBJC__ */
|
||||
QVector <void *> connections;
|
||||
};
|
||||
#endif
|
||||
@@ -1,327 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Mark Liversedge (liversedge@gmail.com)
|
||||
*
|
||||
* 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 WARRWFApiY; without even the implied warranty of MERCHWFApiABILITY 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 "WFApi.h"
|
||||
|
||||
// WF API Headers
|
||||
#import <WFConnector/WFHardwareConnector.h>
|
||||
#import <WFConnector/WFConnectionParams.h>
|
||||
#import <WFConnector/WFDeviceParams.h>
|
||||
#import <WFConnector/WFSensorConnection.h>
|
||||
#import <WFConnector/WFSensorData.h>
|
||||
#import <WFConnector/WFBikePowerData.h>
|
||||
#import <WFConnector/WFBikePowerConnection.h>
|
||||
#import <WFConnector/hardware_connector_types.h>
|
||||
|
||||
// Utility
|
||||
static QString toQString(const NSString *nsstr)
|
||||
{
|
||||
NSRange range;
|
||||
range.location = 0;
|
||||
range.length = [nsstr length];
|
||||
QString result(range.length, QChar(0));
|
||||
|
||||
unichar *chars = new unichar[range.length];
|
||||
[nsstr getCharacters:chars range:range];
|
||||
result = QString::fromUtf16(chars, range.length);
|
||||
delete[] chars;
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline NSString* fromQString(const QString &string)
|
||||
{
|
||||
const QByteArray utf8 = string.toUtf8();
|
||||
const char* cString = utf8.constData();
|
||||
return [[NSString alloc] initWithUTF8String:cString];
|
||||
}
|
||||
|
||||
// Thi source file contains the private objc interface (WFBridge) that
|
||||
// sits atop the Wahoo Fitness APIs at the top of the source file.
|
||||
//
|
||||
// This is then follwoed by the C++ public interface implementation that
|
||||
// sits atop that private interface (WFBridge)
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Objective C -- Private interface
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
@interface WFBridge : NSObject <WFHardwareConnectorDelegate, WFSensorConnectionDelegate> {
|
||||
@public
|
||||
QPointer<WFApi> qtw; // the QT QObject public class
|
||||
NSMutableArray *discoveredSensors;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation WFBridge
|
||||
|
||||
//============================================================================
|
||||
// Hardware Connector Methods
|
||||
//============================================================================
|
||||
|
||||
// retreive the API version
|
||||
-(NSString *) apiVersion { return [[WFHardwareConnector sharedConnector] apiVersion]; }
|
||||
|
||||
// BTLE state and enablement
|
||||
-(BOOL)hasBTLESupport { return [[WFHardwareConnector sharedConnector] hasBTLESupport]; }
|
||||
-(BOOL)isBTLEEnabled { return [[WFHardwareConnector sharedConnector] isBTLEEnabled]; }
|
||||
-(BOOL)enableBTLE:(BOOL)bEnable inBondingMode:(BOOL)bBondingMode {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
|
||||
bool result = [[WFHardwareConnector sharedConnector] enableBTLE:bEnable inBondingMode:bBondingMode];
|
||||
[pool drain];
|
||||
return result;
|
||||
}
|
||||
-(BOOL)isCommunicationHWReady { return [[WFHardwareConnector sharedConnector] isCommunicationHWReady]; }
|
||||
-(int)currentState { return [[WFHardwareConnector sharedConnector] currentState]; }
|
||||
|
||||
// Initialise the WFBridge singleton
|
||||
-(id)init
|
||||
{
|
||||
// initialise
|
||||
discoveredSensors = [[NSMutableArray alloc] init];
|
||||
[[WFHardwareConnector sharedConnector] setDelegate:self];
|
||||
[self enableBTLE:TRUE inBondingMode:false];
|
||||
return self;
|
||||
}
|
||||
|
||||
// scan for devices and stored details
|
||||
-(BOOL)discoverDevicesOfType:(WFSensorType_t)eSensorType onNetwork:(WFNetworkType_t)eNetworkType searchTimeout:(NSTimeInterval)timeout
|
||||
{
|
||||
// get rid of past scans / stop any in progress
|
||||
[discoveredSensors removeAllObjects];
|
||||
[[WFHardwareConnector sharedConnector] cancelDiscoveryOnNetwork:WF_NETWORKTYPE_BTLE];
|
||||
|
||||
// go looking
|
||||
[[WFHardwareConnector sharedConnector] discoverDevicesOfType:eSensorType onNetwork:eNetworkType searchTimeout:timeout]; //XXX ignoringreturn
|
||||
return true;
|
||||
}
|
||||
-(int)deviceCount { return [discoveredSensors count]; }
|
||||
-(NSString*)deviceUUID:(int)n
|
||||
{
|
||||
WFConnectionParams* connParams = (WFConnectionParams*)[discoveredSensors objectAtIndex:n];
|
||||
return connParams.device1.deviceUUIDString;
|
||||
}
|
||||
-(int)deviceType:(int)n
|
||||
{
|
||||
WFConnectionParams* connParams = (WFConnectionParams*)[discoveredSensors objectAtIndex:n];
|
||||
return (int)connParams.sensorType;
|
||||
}
|
||||
-(int)deviceSubType:(int)n
|
||||
{
|
||||
WFConnectionParams* connParams = (WFConnectionParams*)[discoveredSensors objectAtIndex:n];
|
||||
return (int)connParams.sensorSubType;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// Sensor Connection Methods
|
||||
//============================================================================
|
||||
|
||||
// connect and disconnect a device
|
||||
-(WFSensorConnection *)connectDevice: (NSString *)uuid
|
||||
{
|
||||
// just in case there is a discovery in action, lets cancel it...
|
||||
[[WFHardwareConnector sharedConnector] cancelDiscoveryOnNetwork:WF_NETWORKTYPE_BTLE];
|
||||
|
||||
WFDeviceParams* dev = [[WFDeviceParams alloc] init];
|
||||
dev.deviceUUIDString = uuid;
|
||||
dev.networkType = WF_NETWORKTYPE_BTLE;
|
||||
|
||||
WFConnectionParams* params = [[WFConnectionParams alloc] init];
|
||||
params.sensorType = WF_SENSORTYPE_BIKE_POWER;
|
||||
params.networkType = WF_NETWORKTYPE_BTLE;
|
||||
params.sensorSubType = WF_SENSOR_SUBTYPE_BIKE_POWER_KICKR;
|
||||
params.searchTimeout = 5;
|
||||
params.device1 = dev;
|
||||
|
||||
// request the sensor connection.
|
||||
WFSensorConnection *sensorConnection = (WFBikePowerConnection*)[[WFHardwareConnector sharedConnector] requestSensorConnection:params];
|
||||
|
||||
// set delegate to receive connection status changes.
|
||||
sensorConnection.delegate = self;
|
||||
|
||||
|
||||
return sensorConnection;
|
||||
}
|
||||
-(int)connectionStatus:(WFSensorConnection*)sensorConnection { return (int)[sensorConnection connectionStatus]; }
|
||||
- (BOOL)disconnectDevice:(WFSensorConnection*)sensorConnection { [sensorConnection disconnect]; return true; }
|
||||
- (BOOL)isConnected:(WFSensorConnection*)sensorConnection { return [sensorConnection isConnected]; }
|
||||
|
||||
// get telemetry
|
||||
- (WFBikePowerData*) getData:(WFSensorConnection*)sensorConnection { return (WFBikePowerData*)[sensorConnection getData]; }
|
||||
|
||||
// trainer setup / load
|
||||
- (void) setSlopeMode:(WFBikePowerConnection*)sensorConnection { [sensorConnection trainerSetSimMode:85 rollingResistance:0.0004 windResistance:0.6]; }
|
||||
- (void) setErgoMode:(WFBikePowerConnection*)sensorConnection { [sensorConnection trainerSetErgMode:100]; }
|
||||
- (void) setSlope:(WFBikePowerConnection*)sensorConnection slope:(double)slope { [sensorConnection trainerSetGrade:slope]; }
|
||||
- (void) setLoad:(WFBikePowerConnection*)sensorConnection load:(int)load { [sensorConnection trainerSetErgMode:load]; }
|
||||
|
||||
//============================================================================
|
||||
// Sensor connection updates (delegate methods)
|
||||
//============================================================================
|
||||
|
||||
// state changed
|
||||
- (void)connection:(WFSensorConnection*)connectionInfo stateChanged:(WFSensorConnectionStatus_t)connState
|
||||
{ Q_UNUSED(connectionInfo);
|
||||
qtw->connectionState(connState);
|
||||
}
|
||||
// timed out
|
||||
- (void)connectionDidTimeout:(WFSensorConnection*)connectionInfo
|
||||
{ Q_UNUSED(connectionInfo);
|
||||
qtw->connectionTimeout();
|
||||
}
|
||||
// telemetry available
|
||||
- (BOOL) hasData:(WFSensorConnection*)sensorConnection { return [sensorConnection hasData]; }
|
||||
|
||||
// firmware available for this sensor
|
||||
-(void) hardwareConnector:(WFHardwareConnector*)hwConnector hasFirmwareUpdateAvailableForConnection:(WFSensorConnection*)connectionInfo required:(BOOL)required withWahooUtilityAppURL:(NSURL *)wahooUtilityAppURL
|
||||
{ Q_UNUSED(hwConnector);
|
||||
Q_UNUSED(connectionInfo);
|
||||
Q_UNUSED(required);
|
||||
Q_UNUSED(wahooUtilityAppURL);
|
||||
qtw->hasFirmwareUpdateAvalableForConnection(); //XXX do what?
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// Hardware connector updates (delegate methods)
|
||||
//============================================================================
|
||||
|
||||
// state changed on connector
|
||||
-(void)hardwareConnector:(WFHardwareConnector*)hwConnector stateChanged:(WFHardwareConnectorState_t)currentState
|
||||
{ Q_UNUSED(hwConnector);
|
||||
Q_UNUSED(currentState);
|
||||
qtw->stateChanged();
|
||||
}
|
||||
|
||||
// connection established
|
||||
-(void)hardwareConnector:(WFHardwareConnector*)hwConnector connectedSensor:(WFSensorConnection*)connectionInfo
|
||||
{ Q_UNUSED(hwConnector);
|
||||
qtw->connectedSensor(connectionInfo);
|
||||
}
|
||||
|
||||
// data has arrived on connector
|
||||
-(void)hardwareConnectorHasData { qtw->connectorHasData(); }
|
||||
|
||||
// a sensor was disconnected
|
||||
-(void)hardwareConnector:(WFHardwareConnector*)hwConnector disconnectedSensor:(WFSensorConnection*)connectionInfo
|
||||
{ Q_UNUSED(hwConnector);
|
||||
qtw->disconnectedSensor(connectionInfo);
|
||||
}
|
||||
|
||||
// devices discovered
|
||||
-(void)hardwareConnector:(WFHardwareConnector*)hwConnector didDiscoverDevices:(NSSet*)connectionParams searchCompleted:(BOOL)bCompleted
|
||||
{ Q_UNUSED(hwConnector);
|
||||
|
||||
if (!bCompleted) {
|
||||
// add discovered devices -- as they are discovered, not at the end.
|
||||
for (WFConnectionParams* connParams in connectionParams) {
|
||||
[discoveredSensors addObject:connParams];
|
||||
}
|
||||
}
|
||||
|
||||
qtw->didDiscoverDevices([discoveredSensors count], bCompleted);
|
||||
}
|
||||
|
||||
-(NSAutoreleasePool*) getPool { return [[NSAutoreleasePool alloc] init]; }
|
||||
-(void) freePool:(NSAutoreleasePool*)pool { [pool release]; }
|
||||
@end
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// C++ PUBLIC Interface
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
// Singleton API class
|
||||
WFApi *_gc_wfapi = NULL;
|
||||
WFApi::WFApi()
|
||||
{
|
||||
wf = [[WFBridge alloc] init];
|
||||
wf->qtw = this;
|
||||
}
|
||||
WFApi::~WFApi() { [wf release]; }
|
||||
|
||||
//============================================================================
|
||||
// wrappers for methods in private implementation above
|
||||
//============================================================================
|
||||
QString WFApi::apiVersion() { return toQString([wf apiVersion]); }
|
||||
bool WFApi::isBTLEEnabled() { return [wf isBTLEEnabled]; }
|
||||
bool WFApi::hasBTLESupport() { return [wf hasBTLESupport]; }
|
||||
bool WFApi::isCommunicationHWReady() { return [wf isCommunicationHWReady]; }
|
||||
bool WFApi::enableBTLE(bool enable, bool bondingmode) {
|
||||
return [wf enableBTLE:enable inBondingMode:bondingmode];
|
||||
}
|
||||
int WFApi::currentState() { return [wf currentState]; }
|
||||
|
||||
bool
|
||||
WFApi::discoverDevicesOfType(int eSensorType)
|
||||
{
|
||||
// ignore ehat was passed for now...
|
||||
return [wf discoverDevicesOfType:(WFSensorType_t)eSensorType onNetwork:WF_NETWORKTYPE_BTLE searchTimeout:15.00];
|
||||
}
|
||||
|
||||
QString WFApi::deviceUUID(int n)
|
||||
{
|
||||
if (n>=0 && n<deviceCount()) return toQString([wf deviceUUID:n]);
|
||||
else return "";
|
||||
}
|
||||
int WFApi::deviceType(int n)
|
||||
{
|
||||
if (n>=0 && n<deviceCount()) return (int)[wf deviceType:n];
|
||||
else return -1;
|
||||
}
|
||||
int WFApi::deviceSubType(int n)
|
||||
{
|
||||
if (n>=0 && n<deviceCount()) return (int)[wf deviceSubType:n];
|
||||
else return -1;
|
||||
}
|
||||
|
||||
int WFApi::connectionStatus(int sd) { return [wf connectionStatus:(WFSensorConnection*)connections.at(sd)]; }
|
||||
bool WFApi::isConnected(int sd) { return [wf isConnected:(WFSensorConnection*)connections.at(sd)]; }
|
||||
bool WFApi::hasData(int sd) { return [wf hasData:(WFSensorConnection*)connections.at(sd)]; }
|
||||
int WFApi::connectDevice(QString uuid) {
|
||||
void *conn = (void*)[wf connectDevice: fromQString(uuid)];
|
||||
connections.append(conn);
|
||||
return (connections.count()-1);
|
||||
}
|
||||
bool WFApi::disconnectDevice(int sd) { return [wf disconnectDevice:(WFSensorConnection*)connections.at(sd)]; }
|
||||
int WFApi::deviceCount() { return [wf deviceCount]; }
|
||||
|
||||
// set slope or ergo mode
|
||||
void WFApi::setSlopeMode(int sd) { [wf setSlopeMode:(WFBikePowerConnection*)connections.at(sd)]; }
|
||||
void WFApi::setErgoMode(int sd) { [wf setErgoMode:(WFBikePowerConnection*)connections.at(sd)]; }
|
||||
void WFApi::setSlope(int sd, double n) { [wf setSlope:(WFBikePowerConnection*)connections.at(sd) slope:n]; }
|
||||
void WFApi::setLoad(int sd, int n) { [wf setLoad:(WFBikePowerConnection*)connections.at(sd) load:n]; }
|
||||
|
||||
//============================================================================
|
||||
// methods called by delegate on updates
|
||||
//============================================================================
|
||||
void WFApi::connectedSensor(void*) { }
|
||||
void WFApi::didDiscoverDevices(int count, bool finished) { if (finished) emit discoverFinished();
|
||||
else emit discoveredDevices(count,finished); }
|
||||
void WFApi::disconnectedSensor(void*) { }
|
||||
void WFApi::hasFirmwareUpdateAvalableForConnection() { }
|
||||
void WFApi::stateChanged() { emit currentStateChanged(currentState()); }
|
||||
void WFApi::connectionState(int status) { emit connectionStateChanged(status); }
|
||||
void WFApi::connectionTimeout() { }
|
||||
void WFApi::connectorHasData() { emit connectionHasData(); }
|
||||
void WFApi::getRealtimeData(int sd, RealtimeData *rt) {
|
||||
WFBikePowerData *data = [wf getData:(WFSensorConnection*)connections.at(sd)];
|
||||
rt->setWatts((int)[data instantPower]);
|
||||
rt->setCadence((int)[data instantCadence]);
|
||||
rt->setWheelRpm((int)[data instantWheelRPM]);
|
||||
}
|
||||
void * WFApi::getPool() { return (void*)[wf getPool]; }
|
||||
void WFApi::freePool(void *pool) { [wf freePool:(NSAutoreleasePool*)pool]; }
|
||||
@@ -244,9 +244,6 @@ macx {
|
||||
# Uncomment this line if you have SDK 10.7 or higher
|
||||
#DEFINES += GC_HAVE_LION
|
||||
|
||||
# Uncomment this line if you have the Mac OSX Wahoo API installed (Kickr)
|
||||
#HAVE_WFAPI = true
|
||||
|
||||
#uncomment below if you are running on the 10.9 developer preview
|
||||
#INCLUDEPATH += /Library/Developer/CommandLineTools/SDKs/MacOSX10.9.sdk/usr/include/
|
||||
}
|
||||
|
||||
@@ -74,6 +74,11 @@ lessThan(QT_MAJOR_VERSION, 5) {
|
||||
} else {
|
||||
QT += multimedia multimediawidgets
|
||||
}
|
||||
greaterThan(QT_MINOR_VERSION, 3) {
|
||||
QT += bluetooth
|
||||
HEADERS += Train/BT40Controller.h
|
||||
SOURCES += Train/BT40Controller.cpp
|
||||
}
|
||||
}
|
||||
|
||||
###=======================================================================
|
||||
|
||||
Reference in New Issue
Block a user