/* 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 . */ #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 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 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;ioffset = 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 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 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 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); }