mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 16:39:57 +00:00
Fixed a number of issues with data from quarqd inf and nan values where inserted as valid data points and thus destoying all plotting in the realtime window and in later analysis. The unit was used to distinguish between the entities, thus rpm was erroneously used as a cadence, rpm is used as the unit for wheel rotation and for cadence. This made the cadence useless together with a PowerTap hub which reports both cadence and wheel rotation. No error checking was performed on the received data, bad data is ignored now.
258 lines
7.4 KiB
C++
258 lines
7.4 KiB
C++
/*
|
|
* Copyright (c) 2009 Steve Gribble (gribble [at] cs.washington.edu) and
|
|
* 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 "SimpleNetworkClient.h"
|
|
#include "DeviceTypes.h"
|
|
#include "DeviceConfiguration.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <QMutexLocker>
|
|
|
|
SimpleNetworkClient::SimpleNetworkClient(QObject *parent,
|
|
DeviceConfiguration *dc)
|
|
: parent (parent), running(false), connected(false), kill_signal(false)
|
|
{
|
|
server_hostname = dc->portSpec.section(':',0,0).toAscii(); // after the colon
|
|
server_port = dc->portSpec.section(':',1,1).toInt(); // after the colon
|
|
qDebug()<<"client constructed for" << server_hostname << server_port;
|
|
}
|
|
|
|
SimpleNetworkClient::~SimpleNetworkClient() {
|
|
closeAndExit();
|
|
}
|
|
|
|
bool SimpleNetworkClient::start() {
|
|
QMutexLocker locker(&client_lock);
|
|
|
|
if (running) {
|
|
// already running; fail.
|
|
return false;
|
|
}
|
|
|
|
// Fire up socket connector and reader thread
|
|
QThread::start();
|
|
|
|
// Wait for child to indicate that it connected to the server
|
|
client_cond.wait(&client_lock);
|
|
if (!connected) {
|
|
QThread::wait(); // second of two ways a thread can exit
|
|
return false;
|
|
}
|
|
|
|
printf("Connected to network...\n");
|
|
return true;
|
|
}
|
|
|
|
void SimpleNetworkClient::closeAndExit() {
|
|
QMutexLocker locker(&client_lock);
|
|
|
|
if (!running)
|
|
return;
|
|
|
|
kill_signal = true;
|
|
client_cond.wait(&client_lock);
|
|
kill_signal = false;
|
|
QThread::wait(); // second of two ways the thread can exit
|
|
}
|
|
|
|
bool SimpleNetworkClient::getRealtimeData(RealtimeData &rtData) {
|
|
QMutexLocker locker(&client_lock);
|
|
|
|
if (!connected) {
|
|
return false;
|
|
}
|
|
rtData = read_data_cache;
|
|
return true;
|
|
}
|
|
|
|
bool SimpleNetworkClient::pushRealtimeData(RealtimeData &rtData) {
|
|
QMutexLocker locker(&client_lock);
|
|
|
|
if (!connected) {
|
|
qDebug() << "SimpleNetworkClient Not connected!";
|
|
return false;
|
|
}
|
|
|
|
qDebug() << "SimpleNetworkClient is about to push...";
|
|
|
|
// need second pair of eyes here: can somebody confirm this pushes a
|
|
// copy of rtData into the queue, not the reference? (I think
|
|
// it pushes a copy because the type of write_queue is
|
|
// queue<RealtimeData>, not queue<RealtimeData&>. )
|
|
|
|
// push the realtime data into a queue to be written by the child
|
|
write_queue.enqueue(rtData);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
#define MAX_BYTES_PER_LINE 128
|
|
|
|
void SimpleNetworkClient::run() {
|
|
QTcpSocket server;
|
|
QMutexLocker locker(&client_lock);
|
|
|
|
// signal that I'm running
|
|
running = true;
|
|
|
|
/////////////// try to connect to the remote host
|
|
server.connectToHost(server_hostname, server_port);
|
|
// wait up to 5 seconds to connect
|
|
if (server.waitForConnected(5000)) {
|
|
connected = true;
|
|
}
|
|
// let start() invoker know the outcome of connection attempt
|
|
client_cond.wakeOne();
|
|
if (connected == false) {
|
|
server.close();
|
|
running = connected = false;
|
|
while (!write_queue.empty()) write_queue.dequeue();
|
|
return;
|
|
}
|
|
|
|
/////////// Loop, reading lines in from the network and writing from the queue.
|
|
while(1) {
|
|
char network_data[MAX_BYTES_PER_LINE];
|
|
|
|
// Try up to 1 second to read next line from network.
|
|
if (!read_next_line(locker, server, read_data_cache, network_data)) {
|
|
// read failed, close up shop.
|
|
server.close();
|
|
running = connected = false;
|
|
while (!write_queue.empty()) write_queue.dequeue();
|
|
if (kill_signal)
|
|
client_cond.wakeOne();
|
|
return;
|
|
}
|
|
|
|
// If anything in our write queue, push it into the network.
|
|
while(!write_queue.empty()) {
|
|
if (!write_next_line(locker,
|
|
server,
|
|
write_queue.dequeue(),
|
|
network_data)) {
|
|
// write failed, close up shop.
|
|
server.close();
|
|
running = connected = false;
|
|
while (!write_queue.empty()) write_queue.dequeue();
|
|
if (kill_signal)
|
|
client_cond.wakeOne();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SimpleNetworkClient::read_next_line(QMutexLocker &locker,
|
|
QTcpSocket &server,
|
|
RealtimeData &read_into_me,
|
|
char *next_line) {
|
|
qint64 read_result;
|
|
qint64 num_read_so_far;
|
|
bool done;
|
|
|
|
next_line[0] = '\0';
|
|
next_line[MAX_BYTES_PER_LINE-1] = '\0'; // to be safe
|
|
num_read_so_far = 0;
|
|
done = false;
|
|
|
|
while(!done) {
|
|
// wait up to a second for socket to be ready to read
|
|
locker.unlock();
|
|
server.waitForReadyRead(100);
|
|
locker.relock();
|
|
|
|
// re-entered lock; make sure I'm not told to kill myself.
|
|
if (kill_signal) {
|
|
printf("Got kill signal\n");
|
|
return false;
|
|
}
|
|
|
|
read_result =
|
|
server.readLine(next_line + num_read_so_far,
|
|
MAX_BYTES_PER_LINE - num_read_so_far - 1);
|
|
if (read_result == -1) {
|
|
return false;
|
|
}
|
|
|
|
if ((read_result == 0) && (num_read_so_far == 0)) {
|
|
// didn't get anything yet, but need to give writing a
|
|
// chance. so-- return to main loop.
|
|
return true;
|
|
}
|
|
|
|
num_read_so_far += read_result;
|
|
if ((num_read_so_far == MAX_BYTES_PER_LINE - 1) ||
|
|
((num_read_so_far > 0) &&
|
|
next_line[strlen(next_line)-1] == '\n'))
|
|
done = true;
|
|
}
|
|
|
|
// Make sure there is a trailing newline.
|
|
if (next_line[strlen(next_line) - 1] != '\n') {
|
|
// nope; quit out.
|
|
return false;
|
|
}
|
|
|
|
// Yup; try to parse it.
|
|
{
|
|
float watts, hr, speed, rpm, load;
|
|
long time;
|
|
|
|
if (sscanf(next_line, "%f %f %ld %f %f %f\n",
|
|
&watts, &hr, &time, &speed, &rpm, &load) != 6) {
|
|
// couldn't parse, so quit.
|
|
return false;
|
|
}
|
|
read_into_me.setWatts(watts);
|
|
read_into_me.setHr(hr);
|
|
read_into_me.setTime(time);
|
|
read_into_me.setSpeed(speed);
|
|
read_into_me.setCadence(rpm);
|
|
read_into_me.setLoad(load);
|
|
|
|
printf("Read from network: %f %f %ld %f %f %f\n",
|
|
read_into_me.getWatts(), read_into_me.getHr(),
|
|
read_into_me.getTime(), read_into_me.getSpeed(),
|
|
read_into_me.getCadence(), read_into_me.getLoad());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SimpleNetworkClient::write_next_line(QMutexLocker &locker,
|
|
QTcpSocket &server,
|
|
RealtimeData record,
|
|
char *buffer) {
|
|
int num_written;
|
|
snprintf(buffer, MAX_BYTES_PER_LINE-1,
|
|
"%.2f %.2f %ld %.2f %.2f %.3f\n",
|
|
record.getWatts(), record.getHr(), record.getTime(),
|
|
record.getSpeed(), record.getCadence(), record.getLoad());
|
|
locker.unlock();
|
|
num_written = server.write(buffer, strlen(buffer));
|
|
locker.relock();
|
|
if (num_written != (int) strlen(buffer)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|