360 lines
10 KiB
C++
360 lines
10 KiB
C++
/*
|
|
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
|
|
Copyright (C) 2018 Itsblue Development - Dorian Zeder
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published
|
|
by the Free Software Foundation, version 3 of the License.
|
|
|
|
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 Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "headers/buzzerconn.h"
|
|
|
|
BuzzerConn::BuzzerConn(QObject *parent, QString ip, int port) : QObject(parent)
|
|
{
|
|
// initialize TcpSocket for communication with the extentions
|
|
this->socket = new QTcpSocket();
|
|
|
|
// initialize Qdate
|
|
this->date = new QDateTime;
|
|
this->latest_button_pressed = 0;
|
|
|
|
// set ip and port
|
|
this->ip = ip;
|
|
this->port = port;
|
|
|
|
//standard state: disconnected
|
|
this->setState("disconnected");
|
|
}
|
|
|
|
bool BuzzerConn::connect()
|
|
{
|
|
// function to connect to buzzer
|
|
|
|
qDebug() << "connecting...";
|
|
|
|
setState("connecting");
|
|
|
|
//setup loop to wait until the connection has finished
|
|
QEventLoop loop;
|
|
QTimer timer;
|
|
|
|
timer.setSingleShot(true);
|
|
// quit the loop when the timer times out
|
|
loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
|
//quit the loop when the connection was established
|
|
loop.connect(this->socket, SIGNAL(connected()), &loop, SLOT(quit()));
|
|
// start the timer before starting to connect
|
|
timer.start(3000);
|
|
//connect
|
|
this->socket->connectToHost(this->ip, this->port);
|
|
|
|
//wait for the connection to finish (programm gets stuck in here)
|
|
loop.exec();
|
|
|
|
//loop finished
|
|
if(timer.remainingTime() == -1){
|
|
//the time has been triggered -> timeout
|
|
setState("disconnected");
|
|
return(false);
|
|
}
|
|
|
|
// stop the timer as the connection has been established
|
|
timer.stop();
|
|
|
|
// get the timestamps ( last_pressed and current_timestamp ) from the extention
|
|
QList<double> times = gettimes(2000, true);
|
|
|
|
if(times[0] == 200.0){
|
|
//if the request was successfull, set last_triggered and
|
|
this->latest_button_pressed = times[2];
|
|
for(int i=0;i<=100;i++){
|
|
// middle the offset 100 times
|
|
this->connection_progress = i;
|
|
emit this->progressChanged();
|
|
if(!calcoffset(this->gettimes(1000, false))){
|
|
// if a request fails, cancle the connection process
|
|
this->connection_progress = 100;
|
|
setState("disconnected");
|
|
return(false);
|
|
}
|
|
}
|
|
// after middeling the offset for 100 times set the state to connected and quit
|
|
setState("connected");
|
|
return(true);
|
|
}
|
|
else{
|
|
//if not, cancel
|
|
setState("disconnected");
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
bool BuzzerConn::calcoffset(QList<double> times)
|
|
{
|
|
//function that recieves the current time of the extention,
|
|
//puts it into the latest offsets list and
|
|
//calculates the avarage offset
|
|
|
|
//if there are not enoug items in the list (0=status of the request (200 = success ...); 1 = timestamp that has to be added to the list)
|
|
if(times.length() != 2){
|
|
return(false);
|
|
}
|
|
|
|
if(times[0] == 200.0){
|
|
// if the given request was a successfull request, calculate the offset
|
|
double offset = date->currentMSecsSinceEpoch() - times[1];
|
|
//check if the list contains more than or 100 values
|
|
if(this->latest_offsets.length()>=100){
|
|
//if so, delete the first (oldest) one
|
|
this->latest_offsets.removeFirst();
|
|
}
|
|
//append the new offset to the list
|
|
this->latest_offsets.append(offset);
|
|
//variable to store the avarage
|
|
double mem = 0;
|
|
for(int i=0;i<latest_offsets.length();i++){
|
|
//go through the list and add all offsets
|
|
mem += latest_offsets[i];
|
|
}
|
|
//calculate the avarage
|
|
this->offset = mem / double(latest_offsets.length());
|
|
|
|
//emit that the offset has changed
|
|
emit offsetChanged();
|
|
//qDebug("%20f", this->offset);
|
|
return(true);
|
|
}
|
|
else {
|
|
//if the given request was not valid, return false
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
QList<double> BuzzerConn::gettimes(int timeout, bool bothTimes)
|
|
{
|
|
// function to recieve the timestamps (last_triggered, current_timestamp) from the extentions
|
|
|
|
//list to store the return code of the request and the timestamps
|
|
QList<double> times;
|
|
//variable to store answer of the extention
|
|
signed long ret;
|
|
//send request to extention
|
|
ret = this->sendCommand("GET_TIMESTAMP", timeout);
|
|
if(ret >= 0){
|
|
// if it is higer than 0 (=success) append the return code
|
|
times.append(double(200));
|
|
//and the timestamp
|
|
times.append(double(ret));
|
|
|
|
if(bothTimes){
|
|
//if both timstamps were requested do the same thing again for the other one (last_triggered)
|
|
ret = this->sendCommand("GET_LASTPRESSED", timeout);
|
|
|
|
if(ret >= 0){
|
|
//if the reuest was successfull, append the value to the list
|
|
times.append(double(ret));
|
|
}
|
|
else {
|
|
// if not, change the return code
|
|
times[0] = ret;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// if not, append the return code
|
|
times.append(ret);
|
|
}
|
|
|
|
//return the list
|
|
return(times);
|
|
|
|
}
|
|
|
|
double BuzzerConn::get(QString key)
|
|
{
|
|
// function to get all kinds of data
|
|
|
|
if(key == "offset"){
|
|
// get the offset of the extention
|
|
return(this->offset);
|
|
}
|
|
else if (key == "lastpressed") {
|
|
// get the last_triggered timestamp of the extention
|
|
return(this->latest_button_pressed);
|
|
}
|
|
else if( key == "currtime") {
|
|
// get the current time
|
|
return(this->date->currentMSecsSinceEpoch());
|
|
}
|
|
else if( key == "connection_progress") {
|
|
//get the connection progress
|
|
return(this->connection_progress);
|
|
}
|
|
else if( key == "connected") {
|
|
// get the state of the connection
|
|
if(this->state == "connected"){
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
bool BuzzerConn::refresh()
|
|
{
|
|
// function that has to be called frequently to refresh the connection
|
|
// check if the extention has been triggered
|
|
// sends one command the pending command list
|
|
// and calculates the offset once
|
|
|
|
if(this->state != "connected"){
|
|
// if the extention is not connected return
|
|
return(false);
|
|
}
|
|
|
|
//send one of the pending commands
|
|
if(pending_commands.length() > 0){
|
|
//get the irst command in the list
|
|
QString command = this->pending_commands.first();
|
|
//send the command
|
|
signed long retval = this->sendCommand(command, 2000);
|
|
|
|
if(retval > 0){
|
|
//if the request has been successfull, remove the command from the list
|
|
this->pending_commands.removeFirst();
|
|
}
|
|
}
|
|
|
|
// get the timestamps from the extention
|
|
QList<double> ret = this->gettimes(2000);
|
|
|
|
if(ret[0] >= 0){
|
|
//if the request has been successfull check if the last_triggered timestamp has changed
|
|
if(ret[2] > this->latest_button_pressed){
|
|
// if it has, set it
|
|
this->latest_button_pressed = ret[2];
|
|
//and emit the trggered signal
|
|
emit triggered();
|
|
}
|
|
// as the requst as been sucessfull, reset the error counter
|
|
this->errors = 0;
|
|
// calculate the offset on time and return
|
|
return(this->calcoffset(ret));
|
|
}
|
|
else {
|
|
// if not add one to the error conter
|
|
this->errors ++;
|
|
|
|
if(this->errors > errors_until_disconnect){
|
|
// if the error counter is too high, disconnect
|
|
this->socket->disconnectFromHost();
|
|
this->setState("disconnected");
|
|
}
|
|
|
|
// return false
|
|
return(false);
|
|
}
|
|
|
|
}
|
|
|
|
signed long BuzzerConn::sendCommand(QString command, int timeout){
|
|
//function to send a commnd to the extention
|
|
|
|
//if there is any data in the storage, clear it
|
|
if(this->socket->bytesAvailable() > 0){
|
|
this->socket->readAll();
|
|
}
|
|
|
|
//send request to the socket server
|
|
QByteArray arrBlock;
|
|
QDataStream out(&arrBlock, QIODevice::WriteOnly);
|
|
|
|
out << quint16(0) << command;
|
|
|
|
out.device()->seek(0);
|
|
out << quint16(arrBlock.size() - sizeof(quint16));
|
|
|
|
this->socket->write(arrBlock);
|
|
|
|
//now wait for the extention to answer
|
|
QEventLoop loop;
|
|
QTimer timer;
|
|
|
|
timer.setSingleShot(true);
|
|
//quit the loop if the timer times out
|
|
loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
|
//or if the request is anwered
|
|
loop.connect(socket, SIGNAL(readyRead()), &loop, SLOT(quit()));
|
|
|
|
//start the timer
|
|
timer.start(timeout);
|
|
//start the loop
|
|
loop.exec();
|
|
|
|
//loop finished
|
|
if(timer.remainingTime() == -1){
|
|
//the time has been triggered -> timeout
|
|
return(-1);
|
|
}
|
|
// stop the timer
|
|
timer.stop();
|
|
|
|
|
|
//if the data is not 4 bytes long it is invalid -> clear and terminate
|
|
if(this->socket->bytesAvailable() != 4){
|
|
this->socket->readAll();
|
|
return(-2);
|
|
}
|
|
long data = 0;
|
|
// read four bytes and cast them as a double
|
|
this->socket->read((char*)&data,4);
|
|
|
|
//return the data
|
|
return data;
|
|
}
|
|
|
|
void BuzzerConn::appendCommand(QString command){
|
|
this->pending_commands.append(command);
|
|
}
|
|
|
|
void BuzzerConn::setIP(const QString &ipAdress){
|
|
this->ip = ipAdress;
|
|
}
|
|
|
|
QString BuzzerConn::getIP() const
|
|
{
|
|
return(this->ip);
|
|
}
|
|
|
|
QString BuzzerConn::getState() const
|
|
{
|
|
return(this->state);
|
|
}
|
|
|
|
void BuzzerConn::setState(QString newState){
|
|
this->state = newState;
|
|
emit stateChanged();
|
|
}
|
|
|
|
int BuzzerConn::getProgress() const
|
|
{
|
|
return(connection_progress);
|
|
}
|
|
|
|
double BuzzerConn::getOffset() const
|
|
{
|
|
return(this->offset);
|
|
}
|
|
|
|
double BuzzerConn::getLastTriggered() const
|
|
{
|
|
return(this->latest_button_pressed);
|
|
}
|