Added functionality to connect to base station

This commit is contained in:
Dorian Zedler 2018-09-23 17:54:20 +02:00
parent 19acffbe11
commit b93b598c80
9 changed files with 520 additions and 146 deletions

74
headers/baseconn.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef BASECONN_H
#define BASECONN_H
#include <QObject>
#include <QTcpSocket>
#include <QDataStream>
#include <QDateTime>
#include <QTimer>
#include <QEventLoop>
class BaseConn : public QObject
{
Q_OBJECT
Q_PROPERTY(QString ipAdress WRITE setIP READ getIP)
Q_PROPERTY(QString state READ getState NOTIFY stateChanged)
Q_PROPERTY(int progress READ getProgress NOTIFY progressChanged)
public:
explicit BaseConn(QObject *parent = nullptr);
// values for the socket connection
int connection_progress;
QString ip;
qint16 port = 3563;
int errors;
int errors_until_disconnect = 4;
// the current state
QString state;
// can be:
// - 'disconnected'
// - 'connecting'
// - 'connected'
QString latestReadReply;
private:
QDateTime *date;
//to get the current time
QTcpSocket *socket;
//socket for communication with the extention
signals:
void stateChanged();
//is emitted, when the connection state changes
void progressChanged();
//is emmited during the connection process when the progress changes
void gotReply();
public slots:
Q_INVOKABLE bool connectToHost();
//function to connect to the base station
Q_INVOKABLE QString sendCommand(QString command);
// functions for the qml adapter
QString getIP() const;
void setIP(const QString &ipAdress);
QString getState() const;
void setState(QString newState);
int getProgress() const;
private slots:
void readyRead();
};
#endif // BASECONN_H

View file

@ -70,9 +70,11 @@ signals:
//is emitted, when the connection state changes
void progressChanged();
//is emmited during the connection process when the progress changes
void offsetChanged();
public slots:
Q_INVOKABLE signed long sendCommand(QString command, int timeout);
@ -102,7 +104,7 @@ public slots:
Q_INVOKABLE bool refresh();
//- refreshes the connection to the buzzer
//- checks if it as triggered if it was 'triggered()' is emitted
//- checks if it was triggered, if so 'triggered()' is emitted
//- sends one command from the pending commans list
Q_INVOKABLE void appendCommand(QString command);

View file

@ -261,6 +261,36 @@ Popup {
id: connect_col
property string title: qsTr("connections")
property int delegateHeight: height*0.18
ItemDelegate {
id: baseConn_del
text: qsTr("Base Station")
contentItem: Text {
text: parent.text
color: StyleSettings.textColor
font.pixelSize: options_stack.text_pixelSize
}
width: parent.width
Image {
id: connect_del_image
source: StyleSettings.backIcon
rotation: 180
height: options_stack.text_pixelSize
width: height
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 10
}
}
onClicked: {
options_stack.push(baseStation)
}
}
ConnectionDelegate {
id: connect_buzz_del
@ -414,6 +444,34 @@ Popup {
}
}
/*-----Page to connect to and manage the Base Station----*/
Component {
id: baseStation
Column {
id: baseStation_col
property string title: qsTr("Base Station")
property int delegateHeight: height*0.18
ConnectionDelegate {
id: connect_base_del
text: "connect"
contentItem: Text {
text: parent.text
color: StyleSettings.textColor
font.pixelSize: options_stack.text_pixelSize
}
status: root.connections.baseStation
connect: root.connect
type: "baseStation"
width: parent.width
font.pixelSize: options_stack.text_pixelSize
}
}
}
/*-----Custom animations-----*/
pushEnter: Transition {

View file

@ -10,7 +10,6 @@ ItemDelegate {
enabled: status.status === "disconnected"
onClicked: {
connect(type)
if(status.status !== "connected"){

View file

@ -56,7 +56,8 @@ Window {
//array that contains all connections an their atatus
property var connections: {
'buzzer': buzzerConn.status,
'startpad': startpadConn.status
'startpad': startpadConn.status,
'baseStation': baseConn.status
}
//set default state to IDLE
@ -121,37 +122,46 @@ Window {
}
}
BaseStationConn {
id: baseConn
ipAdress: "localhost"
property var status: {'status': baseConn.state, 'progress': baseConn.progress}
/*
BuzzerConn {
id: buzzerConn
onPushed: {
// the buzzer was pushed
root.stop("buzzer")
}
}
StartpadConn {
id: startpadConn
active: true
onActiveChanged: {
console.log("active changed: "+active)
}
onPushed: {
active = false
console.log("startpad triggered")
var offset = _cppStartpadConn.get("offset")
var last_pressed = _cppStartpadConn.get("lastpressed")
var trigger_time = (last_pressed + offset)
root.last_run.react_time = trigger_time - root.startTime
if(trigger_time - root.startTime <= 0){
root.stop("false")
function getTime(type){
var time = parseInt(sendCommand("GET_CURRTIME"))
if(type === "readable"){
return(time / 1000).toFixed(3)
}
else if(type === "raw"){
return(time)
}
}
}
*/
Timer {
id: baseRefreshTimer
running: baseConn.state === "connected"
repeat: false
interval: 1
onTriggered: {
if(root.state === "RUNNING"){
time.text = baseConn.getTime("readable") + " sec"
if(baseConn.sendCommand("GET_TIMER_STATE") === "STOPPED"){
root.stop("manual")
}
}
if(root.state === "STARTING"){
if(baseConn.sendCommand("GET_TIMER_STATE") === "RUNNING"){
root.state = "RUNNING";
}
}
start()
}
}
Timer {
//timer that updates the currTime variable
running: true
@ -251,7 +261,7 @@ Window {
}
SoundEffect {
//start sound
//false-start sound
id: falseSound
source: "qrc:/sounds/false.wav"
}
@ -326,10 +336,6 @@ Window {
leftMargin: 10
}
height: root.landscape()? root.height*0.1:root.width*0.1
Component.onCompleted: {
console.log(root.connections.buzzer)
}
}
ConnectionIcon {
@ -343,10 +349,6 @@ Window {
leftMargin: 5 + buzzerLogo.width
}
height: root.landscape()? root.height*0.1:root.width*0.1
Component.onCompleted: {
console.log(root.connections.buzzer)
}
}
Rectangle {
@ -500,6 +502,9 @@ Window {
case "startpad":
startpadConn.connect()
break
case "baseStation":
baseConn.connectToHost()
break
}
}
}
@ -640,7 +645,6 @@ Window {
anchors.bottomMargin: root.landscape() ? undefined:parent.height * 0.1;
anchors.rightMargin: root.landscape() ? parent.height * 0.05:0
}
PropertyChanges {
target: startButt;
enabled: true; text: qsTr("start");
@ -728,6 +732,15 @@ Window {
/*----Functions to control the stopwatch----*/
function start(){
if(baseConn.state === "connected"){
var ret = baseConn.sendCommand("CMD_START_TIMER")
if(ret === "OK"){
root.state = "STARTING"
return
}
}
root.state = "STARTING"
if(_cppAppSettings.loadSetting("at_marks_en") === "true"){
next_actionTimer.action = "at_marks"
@ -759,6 +772,15 @@ Window {
break
case "manual":
//the stop button was pressed
if(baseConn.state === "connected"){
root.stoppedTime = baseConn.getTime("raw")
time.text = (root.stoppedTime / 1000).toFixed(3) + " sec"
root.state = "STOPPED"
return
}
root.stoppedTime = new Date().getTime() - root.startTime
time.text = ( root.stoppedTime / 1000 ).toFixed(3) + " sec"
root.state = "STOPPED"
@ -775,10 +797,16 @@ Window {
falseSound.play()
break
}
//tartpadConn.active = true
}
function reset(){
if(baseConn.state === "connected"){
var ret = baseConn.sendCommand("CMD_RESET_TIMER")
if(ret !== "OK"){
return
}
}
root.state = "IDLE"
root.last_run.react_time = 0
}

138
sources/baseconn.cpp Normal file
View file

@ -0,0 +1,138 @@
#include "headers/baseconn.h"
BaseConn::BaseConn(QObject *parent) : QObject(parent)
{
socket = new QTcpSocket();
this->setState("disconnected");
}
bool BaseConn::connectToHost() {
setState("connecting");
this->connection_progress = 0;
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
this->socket->abort();
setState("disconnected");
return(false);
}
// stop the timer as the connection has been established
timer.stop();
connect(this->socket, &QTcpSocket::readyRead, this, &BaseConn::readyRead);
this->connection_progress = 100;
setState("connected");
return(true);
}
QString BaseConn::sendCommand(QString command){
QByteArray arrBlock;
QDataStream out(&arrBlock, QIODevice::WriteOnly);
//out.setVersion(QDataStream::Qt_5_10);
out << quint16(0) << command;
out.device()->seek(0);
out << quint16(arrBlock.size() - sizeof(quint16));
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, &BaseConn::gotReply, &loop, &QEventLoop::quit);
// start the timer before starting to connect
timer.start(3000);
//write data
socket->write(arrBlock);
//wait for an answer to finish (programm gets stuck in here)
loop.exec();
//loop finished
if(timer.remainingTime() == -1){
//the time has been triggered -> timeout
return("timeout");
}
// stop the timer as the connection has been established
timer.stop();
return(this->latestReadReply);
}
void BaseConn::readyRead() {
qDebug() << "readyRead";
QDataStream in(socket);
//in.setVersion(QDataStream::Qt_5_10);
qint16 nextBlockSize = 0;
for (;;)
{
if (!nextBlockSize)
{
if (socket->bytesAvailable() < sizeof(quint16)) { break; }
in >> nextBlockSize;
}
if (socket->bytesAvailable() < nextBlockSize) { break; }
QString str; in >> str;
// if (str == "0")
// {
// str = "Connection closed";
// closeConnection();
// }
nextBlockSize = 0;
latestReadReply = str;
emit gotReply();
}
}
void BaseConn::setIP(const QString &ipAdress){
this->ip = ipAdress;
}
QString BaseConn::getIP() const
{
return(this->ip);
}
QString BaseConn::getState() const
{
return(this->state);
}
void BaseConn::setState(QString newState){
this->state = newState;
emit stateChanged();
}
int BaseConn::getProgress() const
{
return(connection_progress);
}

View file

@ -19,20 +19,25 @@
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");
// "http://192.168.4.1"
}
bool BuzzerConn::connect()
{
// function to connect to buzzer
qDebug() << "connecting...";
setState("connecting");
@ -42,14 +47,16 @@ bool BuzzerConn::connect()
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
//wait for the connection to finish (programm gets stuck in here)
loop.exec();
//loop finished
@ -58,26 +65,33 @@ bool BuzzerConn::connect()
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);
qDebug() << times;
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);
}
@ -85,75 +99,107 @@ bool BuzzerConn::connect()
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(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());
offsetChanged();
//emit that the offset has changed
emit offsetChanged();
//qDebug("%20f", this->offset);
return(true);
}
else {
//this->connected = false;
//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);
}
@ -162,6 +208,123 @@ double BuzzerConn::get(QString key)
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;
}
@ -195,95 +358,3 @@ double BuzzerConn::getLastTriggered() const
{
return(this->latest_button_pressed);
}
bool BuzzerConn::refresh()
{
if(this->state != "connected"){
return(false);
}
//send one of the pending commands
if(pending_commands.length() > 0){
QString command = this->pending_commands.first();
signed long retval = this->sendCommand(command, 2000);
if(retval > 0){
this->pending_commands.removeFirst();
}
}
//refresh the times
QList<double> ret = this->gettimes(2000);
if(ret[0] >= 0){
if(ret[2] > this->latest_button_pressed){
this->latest_button_pressed = ret[2];
emit triggered();
}
this->errors = 0;
return(this->calcoffset(ret));
}
else {
this->errors ++;
if(this->errors > errors_until_disconnect){
this->socket->disconnectFromHost();
this->setState("disconnected");
}
return(false);
}
}
signed long BuzzerConn::sendCommand(QString command, int timeout){
//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.setVersion(QDataStream::Qt_5_10);
out << quint16(0) << command;
out.device()->seek(0);
out << quint16(arrBlock.size() - sizeof(quint16));
this->socket->write(arrBlock);
//now wait for the server of the sensor to answer
QEventLoop loop;
QTimer timer;
timer.setSingleShot(true);
loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
loop.connect(socket, SIGNAL(readyRead()), &loop, SLOT(quit()));
timer.start(timeout);
loop.exec();
//loop finished
if(timer.remainingTime() == -1){
//the time has been triggered -> timeout
return(-1);
}
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;
this->socket->read((char*)&data,4);
//qDebug() << data;
//qDebug() << this->socket->bytesAvailable();
return data;
}
void BuzzerConn::appendCommand(QString command){
this->pending_commands.append(command);
}

View file

@ -52,6 +52,7 @@
#include "headers/sqlprofilemodel.h"
#include "headers/buzzerconn.h"
#include "headers/appsettings.h"
#include "headers/baseconn.h"
#include <QTranslator>
static void connectToDatabase()
@ -64,7 +65,7 @@ static void connectToDatabase()
}
const QDir writeDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
qDebug() << writeDir;
if (!writeDir.mkpath("."))
qFatal("Failed to create writable directory at %s", qPrintable(writeDir.absolutePath()));
@ -116,6 +117,7 @@ int main(int argc, char *argv[])
//setup the startpad and buzzer conn qml objects
qmlRegisterType<BuzzerConn>("com.itsblue.speedclimbingstopwatch", 1, 0, "BuzzerConn");
qmlRegisterType<BuzzerConn>("com.itsblue.speedclimbingstopwatch", 1, 0, "StartpadConn");
qmlRegisterType<BaseConn>("com.itsblue.speedclimbingstopwatch", 1, 0, "BaseStationConn");
//setup translation engine
//to the language of the system

View file

@ -24,13 +24,15 @@ SOURCES += \
sources/sqlstoragemodel.cpp \
sources/sqlprofilemodel.cpp \
sources/buzzerconn.cpp \
sources/appsettings.cpp
sources/appsettings.cpp \
sources/baseconn.cpp
HEADERS += \
headers/sqlstoragemodel.h \
headers/sqlprofilemodel.h \
headers/buzzerconn.h \
headers/appsettings.h
headers/appsettings.h \
headers/baseconn.h
RESOURCES += \
shared.qrc \