changed internal structure completely to be more flexible and reliable
This commit is contained in:
parent
88cf8a4328
commit
9260358e11
18 changed files with 854 additions and 1274 deletions
|
@ -20,12 +20,6 @@ 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)
|
||||
Q_PROPERTY(QStringList connections READ getConnections NOTIFY connectionsChanged)
|
||||
Q_PROPERTY(QString nextRemoteAction READ getNextRemoteAction NOTIFY nextRemoteActionChanged)
|
||||
Q_PROPERTY(float nextRemoteActionDelayProg READ getNextRemoteActionDelayProg NOTIFY nextRemoteActionDelayProgChanged)
|
||||
public:
|
||||
explicit BaseConn(QObject *parent = nullptr);
|
||||
|
||||
|
@ -43,7 +37,7 @@ public:
|
|||
// - 'connecting'
|
||||
// - 'connected'
|
||||
|
||||
QStringList connections;
|
||||
QVariant connections;
|
||||
|
||||
QString latestReadReply;
|
||||
|
||||
|
@ -57,9 +51,6 @@ private:
|
|||
QTcpSocket *socket;
|
||||
//socket for communication with the extention
|
||||
|
||||
QList<SpeedTimer*> speedTimers;
|
||||
|
||||
QTimer *refreshTimer;
|
||||
QSemaphore remoteSessions;
|
||||
|
||||
int nextConnectionId;
|
||||
|
@ -72,10 +63,6 @@ private:
|
|||
|
||||
QList<waitingRequest> waitingRequests;
|
||||
|
||||
QString nextRemoteAction;
|
||||
|
||||
float nextRemoteActionDelayProg;
|
||||
|
||||
signals:
|
||||
void stateChanged();
|
||||
//is emitted, when the connection state changes
|
||||
|
@ -104,20 +91,12 @@ public slots:
|
|||
|
||||
void gotError(QAbstractSocket::SocketError err);
|
||||
|
||||
Q_INVOKABLE QVariantMap sendCommand(int header, QJsonValue data);
|
||||
Q_INVOKABLE QVariantMap sendCommand(int header, QJsonValue data = "");
|
||||
|
||||
Q_INVOKABLE int writeRemoteSetting(QString key, QString value);
|
||||
|
||||
Q_INVOKABLE bool refreshConnections();
|
||||
|
||||
void refreshTimers();
|
||||
|
||||
bool startTimers();
|
||||
|
||||
bool stopTimers(QString type);
|
||||
|
||||
bool resetTimers();
|
||||
|
||||
// functions for the qml adapter
|
||||
QString getIP() const;
|
||||
void setIP(const QString &ipAdress);
|
||||
|
@ -127,14 +106,11 @@ public slots:
|
|||
|
||||
int getProgress() const;
|
||||
|
||||
QStringList getConnections();
|
||||
|
||||
QString getNextRemoteAction();
|
||||
|
||||
float getNextRemoteActionDelayProg();
|
||||
QVariant getConnections();
|
||||
|
||||
private slots:
|
||||
void readyRead();
|
||||
};
|
||||
extern BaseConn * pGlobalBaseConn;
|
||||
|
||||
#endif // BASECONN_H
|
||||
|
|
|
@ -1,131 +0,0 @@
|
|||
#ifndef BUZZERCONN_H
|
||||
#define BUZZERCONN_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QObject>
|
||||
#include <QDir>
|
||||
#include <QUrl>
|
||||
#include <QtNetwork>
|
||||
#include <QAuthenticator>
|
||||
#include <QDesktopServices>
|
||||
#include <QDateTime>
|
||||
#include <QtDebug>
|
||||
#include <QtConcurrent/qtconcurrentthreadengine.h>
|
||||
|
||||
#include "appsettings.h"
|
||||
|
||||
//typedef struct strReturnData{
|
||||
// int status_code;
|
||||
// QString text;
|
||||
//}ReturnData_t;
|
||||
|
||||
class BuzzerConn : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(double lastTriggered READ getLastTriggered NOTIFY triggered)
|
||||
Q_PROPERTY(QString ipAdress WRITE setIP READ getIP)
|
||||
Q_PROPERTY(QString state READ getState NOTIFY stateChanged)
|
||||
Q_PROPERTY(int progress READ getProgress NOTIFY progressChanged)
|
||||
Q_PROPERTY(double offset READ getOffset NOTIFY offsetChanged)
|
||||
|
||||
public:
|
||||
explicit BuzzerConn(QObject *parent = nullptr, QString ip = "http://192.168.4.1", int port = 80);
|
||||
|
||||
// values for the time calculation
|
||||
double offset;
|
||||
QList<double> latest_offsets;
|
||||
double latest_button_pressed;
|
||||
|
||||
// double starttime;
|
||||
// bool connected;
|
||||
|
||||
// values for the socket connection
|
||||
int connection_progress;
|
||||
QString ip;
|
||||
int port;
|
||||
int errors;
|
||||
int errors_until_disconnect = 4;
|
||||
|
||||
// the current state
|
||||
QString state;
|
||||
// can be:
|
||||
// - 'disconnected'
|
||||
// - 'connecting'
|
||||
// - 'connected'
|
||||
|
||||
|
||||
|
||||
private:
|
||||
QDateTime *date;
|
||||
//to get the current time
|
||||
|
||||
QTcpSocket *socket;
|
||||
//socket for communication with the extention
|
||||
|
||||
QStringList pending_commands;
|
||||
//commands to send to the extention
|
||||
//one command is being sent whenever refresh() is called
|
||||
signals:
|
||||
void triggered();
|
||||
//is emitted when the device is triggered
|
||||
|
||||
void stateChanged();
|
||||
//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);
|
||||
//function to send commands to the sensor
|
||||
//Can be:
|
||||
//command - return
|
||||
//GET_TIMESTAMP - timestamp of the sensor
|
||||
//GET_LASTPRESSED - timestamp of the sensor when it was triggered the last time
|
||||
//
|
||||
//error codes:
|
||||
//(-1) : timeout
|
||||
//(-2) : invalid data was recieved
|
||||
|
||||
Q_INVOKABLE QList<double> gettimes(int timeout, bool bothTimes = true);
|
||||
//function to get the times from the buzzer as a list
|
||||
//if bothTimes is true the current and the last-pressed timestamp will be returned
|
||||
//else only the current timestamp will be returned
|
||||
|
||||
Q_INVOKABLE bool connect();
|
||||
//function to connect to buzzer
|
||||
|
||||
Q_INVOKABLE bool calcoffset(QList<double> times);
|
||||
//function that calculates the average time offset between the buzzer and the device
|
||||
|
||||
Q_INVOKABLE double get(QString key);
|
||||
//can return some things (offset, lastpressed, currtime, connection_progress, connected)
|
||||
|
||||
Q_INVOKABLE bool refresh();
|
||||
//- refreshes the connection to the buzzer
|
||||
//- checks if it was triggered, if so 'triggered()' is emitted
|
||||
//- sends one command from the pending commans list
|
||||
|
||||
Q_INVOKABLE void appendCommand(QString command);
|
||||
//appends a command to the pending commnds list
|
||||
|
||||
|
||||
// functions for the qml adapter
|
||||
QString getIP() const;
|
||||
void setIP(const QString &ipAdress);
|
||||
|
||||
QString getState() const;
|
||||
void setState(QString newState);
|
||||
|
||||
int getProgress() const;
|
||||
|
||||
double getOffset() const;
|
||||
|
||||
double getLastTriggered() const;
|
||||
};
|
||||
|
||||
#endif // BUZZERCONN_H
|
90
headers/climbingrace.h
Normal file
90
headers/climbingrace.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
#ifndef CLIMBINGRACE_H
|
||||
#define CLIMBINGRACE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSound>
|
||||
#include <QSoundEffect>
|
||||
#include <QMediaPlayer>
|
||||
#include "headers/baseconn.h"
|
||||
#include "headers/appsettings.h"
|
||||
#include "headers/speedtimer.h"
|
||||
|
||||
class ClimbingRace : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int state READ getState NOTIFY stateChanged)
|
||||
Q_PROPERTY(QVariant timers READ getTimerTextList NOTIFY timerTextChanged)
|
||||
Q_PROPERTY(QString baseStationState READ getBaseStationState NOTIFY baseStationStateChanged)
|
||||
Q_PROPERTY(QVariant baseStationConnections READ getBaseStationConnections NOTIFY baseStationConnectionsChanged)
|
||||
Q_PROPERTY(double nextStartActionDelayProgress READ getNextStartActionDelayProgress NOTIFY nextStartActionDelayProgressChanged)
|
||||
|
||||
public:
|
||||
explicit ClimbingRace(QObject *parent = nullptr);
|
||||
|
||||
enum raceState { IDLE, STARTING, RUNNING, STOPPED };
|
||||
raceState state;
|
||||
|
||||
enum raceMode { LOCAL, REMOTE };
|
||||
raceMode mode;
|
||||
|
||||
private:
|
||||
AppSettings * appSettings;
|
||||
BaseConn * baseConn;
|
||||
|
||||
QMediaPlayer * player;
|
||||
|
||||
QTimer * baseStationSyncTimer;
|
||||
QTimer * timerTextRefreshTimer;
|
||||
QTimer * nextStartActionTimer;
|
||||
|
||||
QList<SpeedTimer *> speedTimers;
|
||||
|
||||
int nextStartAction;
|
||||
// 0 : 'at your marks'
|
||||
// 1 : 'ready'
|
||||
// 2 : 'start'
|
||||
|
||||
double nextStartActionDelayProgress;
|
||||
|
||||
// helper vars
|
||||
QVariantList qmlTimers;
|
||||
|
||||
private slots:
|
||||
// helper functions
|
||||
void playSoundsAndStartRace();
|
||||
bool playSound(QString path);
|
||||
void setState(raceState newState);
|
||||
void refreshMode();
|
||||
void refreshTimerText();
|
||||
|
||||
signals:
|
||||
void nextStartActionChanged(int nextStartAction);
|
||||
void nextStartActionDelayProgressChanged();
|
||||
|
||||
void stateChanged(int state);
|
||||
void timerTextChanged();
|
||||
void baseStationStateChanged();
|
||||
void baseStationConnectionsChanged();
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE int startRace();
|
||||
Q_INVOKABLE int stopRace(int type);
|
||||
Q_INVOKABLE int resetRace();
|
||||
|
||||
void syncWithBaseStation();
|
||||
|
||||
// functions for qml
|
||||
Q_INVOKABLE int getState();
|
||||
Q_INVOKABLE QVariant getTimerTextList();
|
||||
Q_INVOKABLE double getNextStartActionDelayProgress();
|
||||
|
||||
Q_INVOKABLE void writeSetting(QString key, QVariant value);
|
||||
Q_INVOKABLE QString readSetting(QString key);
|
||||
|
||||
Q_INVOKABLE bool connectBaseStation();
|
||||
Q_INVOKABLE QString getBaseStationState();
|
||||
Q_INVOKABLE QVariant getBaseStationConnections();
|
||||
};
|
||||
|
||||
#endif // CLIMBINGRACE_H
|
|
@ -13,7 +13,7 @@ class SpeedTimer : public QObject
|
|||
public:
|
||||
explicit SpeedTimer(QObject *parent = nullptr);
|
||||
|
||||
enum timerState { IDLE, STARTING, RUNNING, STOPPED };
|
||||
enum timerState { IDLE, STARTING, RUNNING, STOPPED, FAILED, CANCELLED };
|
||||
timerState state;
|
||||
|
||||
// variables for capturing the time
|
||||
|
@ -22,20 +22,19 @@ public:
|
|||
double stoppedTime;
|
||||
double reactionTime;
|
||||
|
||||
bool remoteControlled;
|
||||
|
||||
signals:
|
||||
void stateChanged(timerState newState);
|
||||
void startCanceled(bool falseStart);
|
||||
|
||||
public slots:
|
||||
void start();
|
||||
void stop(QString type);
|
||||
void reset();
|
||||
bool start(bool force = false);
|
||||
bool stop(int type, bool force = false);
|
||||
bool reset(bool force = false);
|
||||
|
||||
void setState(timerState newState);
|
||||
QString getState();
|
||||
double getCurrTime();
|
||||
QString getText();
|
||||
|
||||
//void handleStartpadTrigger();
|
||||
//void handleToppadTrigger();
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
#ifndef SPEEDTIMERQMLADAPTER_H
|
||||
#define SPEEDTIMERQMLADAPTER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include "speedtimer.h"
|
||||
|
||||
class SpeedTimerQmlAdapter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString state READ getState NOTIFY stateChanged)
|
||||
//Q_PROPERTY(int currtime READ getCurrTime)
|
||||
Q_PROPERTY(QString text READ getText NOTIFY textChanged)
|
||||
|
||||
public:
|
||||
explicit SpeedTimerQmlAdapter(QObject *parent = nullptr);
|
||||
|
||||
SpeedTimer::timerState state;
|
||||
|
||||
// variables for capturing the time
|
||||
double startTime;
|
||||
double stopTime;
|
||||
double stoppedTime;
|
||||
double reactionTime;
|
||||
|
||||
QString text;
|
||||
|
||||
signals:
|
||||
void stateChanged(SpeedTimer::timerState newState);
|
||||
Q_SIGNAL void startCanceled(bool falseStart);
|
||||
void textChanged();
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE bool setStarting();
|
||||
Q_INVOKABLE bool start();
|
||||
Q_INVOKABLE bool stop(QString type);
|
||||
Q_INVOKABLE bool reset();
|
||||
|
||||
void setState(SpeedTimer::timerState newState);
|
||||
Q_INVOKABLE QString getState();
|
||||
Q_INVOKABLE QString getText();
|
||||
|
||||
// double getCurrTime();
|
||||
|
||||
private:
|
||||
QTimer * refreshTimer;
|
||||
|
||||
private slots:
|
||||
void refreshValues();
|
||||
};
|
||||
|
||||
#endif // SPEEDTIMERQMLADAPTER_H
|
|
@ -169,7 +169,7 @@ Popup {
|
|||
/*----Connect to external devices----*/
|
||||
NextPageDelegate {
|
||||
id: connect_del
|
||||
text: qsTr("extentions")
|
||||
text: qsTr("Base Station")
|
||||
onClicked: {
|
||||
options_stack.push(connect)
|
||||
}
|
||||
|
@ -226,39 +226,29 @@ Popup {
|
|||
id: connect
|
||||
Column {
|
||||
id: connect_col
|
||||
property string title: qsTr("extentions")
|
||||
property string title: qsTr("Base Station")
|
||||
property int delegateHeight: height*0.18
|
||||
|
||||
ConnectionDelegate {
|
||||
id: connect_base_del
|
||||
text: "connect"
|
||||
|
||||
status: { "status": speedBackend.baseStationState }
|
||||
connect: speedBackend.connectBaseStation
|
||||
type: "baseStation"
|
||||
|
||||
width: parent.width
|
||||
font.pixelSize: options_stack.text_pixelSize
|
||||
}
|
||||
|
||||
NextPageDelegate {
|
||||
id: baseConn_del
|
||||
text: qsTr("Base Station")
|
||||
id: baseStationConnections_del
|
||||
text: qsTr("connected extentions")
|
||||
|
||||
onClicked: {
|
||||
options_stack.push(baseStation)
|
||||
options_stack.push(baseStationConnections)
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionDelegate {
|
||||
id: connect_buzz_del
|
||||
|
||||
status: root.connections.buzzer
|
||||
connect: root.connect
|
||||
type: "buzzer"
|
||||
|
||||
width: parent.width
|
||||
font.pixelSize: options_stack.text_pixelSize
|
||||
}
|
||||
|
||||
ConnectionDelegate {
|
||||
id: connect_stap_del
|
||||
|
||||
status: root.connections.startpad
|
||||
connect: root.connect
|
||||
type: "startpad"
|
||||
|
||||
width: parent.width
|
||||
font.pixelSize: options_stack.text_pixelSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,12 +263,7 @@ Popup {
|
|||
function updateSetting(key, val, del){
|
||||
del.enabled = false
|
||||
|
||||
if(baseConn.state === "connected"){
|
||||
baseConn.writeRemoteSetting(key, val)
|
||||
}
|
||||
else {
|
||||
_cppAppSettings.writeSetting(key, val)
|
||||
}
|
||||
speedBackend.writeSetting(key, val)
|
||||
|
||||
del.enabled = true
|
||||
}
|
||||
|
@ -286,12 +271,8 @@ Popup {
|
|||
function loadSetting(key, del){
|
||||
del.enabled = false
|
||||
var val
|
||||
if(baseConn.state === "connected"){
|
||||
val = baseConn.sendCommand(3001, key)["data"]
|
||||
}
|
||||
else {
|
||||
val = _cppAppSettings.loadSetting(key)
|
||||
}
|
||||
|
||||
val = speedBackend.readSetting(key)
|
||||
|
||||
del.enabled = true
|
||||
return val
|
||||
|
@ -407,70 +388,24 @@ 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"
|
||||
|
||||
status: root.connections.baseStation
|
||||
connect: root.connect
|
||||
type: "baseStation"
|
||||
|
||||
width: parent.width
|
||||
font.pixelSize: options_stack.text_pixelSize
|
||||
}
|
||||
|
||||
NextPageDelegate {
|
||||
id: baseStationConnections_del
|
||||
text: qsTr("connected extentions")
|
||||
|
||||
onClicked: {
|
||||
baseConn.refreshConnections()
|
||||
options_stack.push(baseStationConnections)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-----Page to view devices that core connected to the pase startion-----*/
|
||||
Component{
|
||||
id: baseStationConnections
|
||||
ListView {
|
||||
function getModel(){
|
||||
var keys = Object.keys(baseConn.connections);
|
||||
|
||||
var len = keys.length
|
||||
return(len)
|
||||
}
|
||||
|
||||
function getDetails(index){
|
||||
var ret = baseConn.connections[index]
|
||||
var details = ret.split("|")
|
||||
return(details)
|
||||
}
|
||||
|
||||
id: baseStationConnections_list
|
||||
property string title: qsTr("connections")
|
||||
property int delegateHeight: height*0.18
|
||||
model: getModel()
|
||||
model: speedBackend.baseStationConnections.length
|
||||
delegate: ConnectionDelegate {
|
||||
enabled: false
|
||||
font.pixelSize: options_stack.text_pixelSize
|
||||
width: parent.width
|
||||
height: baseStationConnections_list.delegateHeight
|
||||
|
||||
text: baseStationConnections_list.getDetails(index)[2]
|
||||
status: {'status': baseStationConnections_list.getDetails(index)[4], 'progress': 0}
|
||||
text: speedBackend.baseStationConnections[index]["name"]
|
||||
status: {'status': speedBackend.baseStationConnections[index]["state"], 'progress': speedBackend.baseStationConnections[index]["progress"]}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*-----Custom animations-----*/
|
||||
|
|
|
@ -48,7 +48,7 @@ Item {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
SpeedTimerBackend {
|
||||
id: timerBackend
|
||||
|
||||
|
@ -60,7 +60,7 @@ Item {
|
|||
console.log("start cnaceled")
|
||||
control.startCanceled(falseStart)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
function getState(){
|
||||
return(timerBackend.getState())
|
||||
|
|
|
@ -12,7 +12,7 @@ ItemDelegate {
|
|||
enabled: status.status === "disconnected"
|
||||
|
||||
onClicked: {
|
||||
connect(type)
|
||||
connect()
|
||||
if(status.status !== "connected"){
|
||||
statusIndicator.color_override = "red"
|
||||
shortDelay.start()
|
||||
|
|
334
qml/main.qml
334
qml/main.qml
|
@ -24,7 +24,7 @@ import "./components"
|
|||
import "./styles"
|
||||
//import QtQuick.Layouts 1.11
|
||||
|
||||
import com.itsblue.speedclimbingstopwatch 1.0
|
||||
import com.itsblue.speedclimbingstopwatch 2.0
|
||||
|
||||
Window {
|
||||
visible: true
|
||||
|
@ -46,20 +46,6 @@ Window {
|
|||
property double stoppedTime: 0
|
||||
property double currTime
|
||||
|
||||
property double buzzer_offset: buzzerConn.offset
|
||||
property double last_button_pressed: buzzerConn.lastTriggered
|
||||
|
||||
property var last_run : {
|
||||
'stop_type': "", 'time': 0, 'react_time': 0
|
||||
}
|
||||
|
||||
//array that contains all connections an their atatus
|
||||
property var connections: {
|
||||
'buzzer': buzzerConn.status,
|
||||
'startpad': startpadConn.status,
|
||||
'baseStation': baseConn.status
|
||||
}
|
||||
|
||||
//set default state to IDLE
|
||||
state: "IDLE"
|
||||
|
||||
|
@ -69,174 +55,28 @@ Window {
|
|||
color: StyleSettings.backgroundColor
|
||||
}
|
||||
|
||||
Item {
|
||||
id: connections
|
||||
SpeedBackend {
|
||||
id: speedBackend
|
||||
|
||||
BuzzerConn {
|
||||
id: buzzerConn
|
||||
ipAdress: "192.168.4.10"
|
||||
property var status: {'status': buzzerConn.state, 'progress': buzzerConn.progress}
|
||||
onLastTriggeredChanged: {
|
||||
timer_1.handleToppad()
|
||||
onStateChanged: {
|
||||
var stateString
|
||||
switch (state){
|
||||
case 0:
|
||||
stateString = "IDLE"
|
||||
break;
|
||||
case 1:
|
||||
stateString = "STARTING"
|
||||
break;
|
||||
case 2:
|
||||
stateString = "RUNNING"
|
||||
break;
|
||||
case 3:
|
||||
stateString = "STOPPED"
|
||||
}
|
||||
root.state = stateString
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: buzzerRefreshTimer
|
||||
running: buzzerConn.state === "connected"
|
||||
interval: root.state === "RUNNING" ? 1:1000
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
buzzerConn.refresh()
|
||||
this.start()
|
||||
}
|
||||
}
|
||||
|
||||
StartpadConn {
|
||||
id: startpadConn
|
||||
ipAdress: "192.168.4.11"
|
||||
property var status: {'status': startpadConn.state, 'progress': startpadConn.progress}
|
||||
property string color: root.state === "RUNNING" ? "SET_LED_RUNNING":"SET_LED_STARTING"
|
||||
onColorChanged: {
|
||||
appendCommand(color)
|
||||
}
|
||||
|
||||
onLastTriggeredChanged: {
|
||||
timer_1.handleStartpad()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: startpadRefreshTimer
|
||||
running: startpadConn.state === "connected"
|
||||
interval: root.state === "RUNNING" || root.state === "STARTING" ? 1:1000
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
startpadConn.refresh()
|
||||
this.start()
|
||||
}
|
||||
}
|
||||
|
||||
BaseStationConn {
|
||||
id: baseConn
|
||||
ipAdress: "192.168.4.1"
|
||||
|
||||
property var status: {'status': baseConn.state, 'progress': baseConn.progress, 'connections': baseConn.connections}
|
||||
|
||||
onNextRemoteActionChanged: {
|
||||
switch(nextRemoteAction){
|
||||
case "0":
|
||||
timer_1.text = "at your\nmarks"
|
||||
break
|
||||
case "1":
|
||||
timer_1.text = "ready"
|
||||
break
|
||||
case "2":
|
||||
timer_1.text = "0.000 sec"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
onNextRemoteActionDelayProgChanged: {
|
||||
prog.progress = baseConn.nextRemoteActionDelayProg * 100
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Timer {
|
||||
id: next_actionTimer
|
||||
|
||||
property string action
|
||||
property double started_at
|
||||
|
||||
running: false
|
||||
repeat: false
|
||||
onRunningChanged: {
|
||||
if(!running){
|
||||
started_at = 0
|
||||
if(action == "NONE"){
|
||||
timer_1.text = "0.000 sec"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if(action === "at_marks"){
|
||||
started_at = new Date().getTime()
|
||||
timer_1.text = "at your\nmarks"
|
||||
}
|
||||
else if(action === "ready"){
|
||||
started_at = new Date().getTime()
|
||||
timer_1.text = "ready"
|
||||
}
|
||||
}
|
||||
onTriggered: {
|
||||
if(action === "at_marks"){
|
||||
at_marksSound.play()
|
||||
}
|
||||
else if(action === "ready"){
|
||||
action = "NONE"
|
||||
readySound.play()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: sounds
|
||||
SoundEffect {
|
||||
id: at_marksSound
|
||||
source: "qrc:/sounds/at_marks_1.wav"
|
||||
|
||||
onPlayingChanged: {
|
||||
if(!playing && root.state==="STARTING"){
|
||||
if(_cppAppSettings.loadSetting("ready_en") === "true"){
|
||||
next_actionTimer.action = "ready"
|
||||
next_actionTimer.interval = _cppAppSettings.loadSetting("ready_delay")>0 ? _cppAppSettings.loadSetting("ready_delay"):1
|
||||
next_actionTimer.start()
|
||||
}
|
||||
else{
|
||||
startSound.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SoundEffect {
|
||||
id: readySound
|
||||
source: "qrc:/sounds/ready_1.wav"
|
||||
onPlayingChanged: {
|
||||
if(!playing && root.state==="STARTING"){
|
||||
|
||||
startSound.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SoundEffect {
|
||||
//start sound
|
||||
id: startSound
|
||||
source: "qrc:/sounds/OFFICAL_IFSC_STARTIGNAL.wav"
|
||||
|
||||
onPlayingChanged: {
|
||||
if(!playing && root.state==="STARTING"){
|
||||
root.state = "RUNNING"
|
||||
}
|
||||
else if(playing) {
|
||||
console.log("start sound started")
|
||||
|
||||
timer_1.start(3100)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SoundEffect {
|
||||
//false-start sound
|
||||
id: falseSound
|
||||
source: "qrc:/sounds/false.wav"
|
||||
}
|
||||
}
|
||||
/*------------------------
|
||||
Timer text an upper line
|
||||
------------------------*/
|
||||
|
@ -256,85 +96,37 @@ Window {
|
|||
color: StyleSettings.menuColor
|
||||
}
|
||||
|
||||
SpeedTimer {
|
||||
Label {
|
||||
id: timer_1
|
||||
anchors.centerIn: parent
|
||||
elide: "ElideRight"
|
||||
color: StyleSettings.textColor
|
||||
toppadConn: buzzerConn
|
||||
baseConn: baseConn
|
||||
startpadConn: startpadConn
|
||||
|
||||
text: "0.000 sec"
|
||||
font.pixelSize: 100
|
||||
|
||||
onStateChanged: {
|
||||
root.state = timer_1.getState()
|
||||
}
|
||||
text: speedBackend.timers[0]["text"]
|
||||
|
||||
onStopped: {
|
||||
root.state = "STOPPED"
|
||||
}
|
||||
|
||||
onStartCanceled: {
|
||||
root.state = "STOPPED"
|
||||
next_actionTimer.stop()
|
||||
at_marksSound.stop()
|
||||
readySound.stop()
|
||||
startSound.stop()
|
||||
if(falseStart && baseConn.state !== "connected"){
|
||||
falseSound.play()
|
||||
Behavior on text {
|
||||
enabled: root.state !== "RUNNING"
|
||||
FadeAnimation {
|
||||
target: timer_1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: react_time
|
||||
property int rtime: root.last_run.react_time
|
||||
property int rtime: speedBackend.timers[0]["reacttime"]
|
||||
text: qsTr("reaction time (ms): ") + Math.round(rtime)
|
||||
opacity: (root.state === "RUNNING" || root.sate === "STARTING" || root.state === "STOPPED") && rtime !== 0 ? 1:0
|
||||
opacity: (root.state === "RUNNING" || root.state === "STOPPED") && rtime !== 0 ? 1:0
|
||||
color: StyleSettings.textColor
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: timer_1.bottom
|
||||
topMargin: parent.height * 0.1
|
||||
}
|
||||
Timer {
|
||||
running: root.state === "RUNNING" || root.sate === "STARTING" || root.state === "STOPPED"
|
||||
repeat: true
|
||||
interval: 1
|
||||
onTriggered: {
|
||||
react_time.rtime = root.last_run.react_time
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ConnectionIcon {
|
||||
id: buzzerLogo
|
||||
source: "qrc:/graphics/icons/buzzer_black.png"
|
||||
status: root.connections["buzzer"].status
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 10
|
||||
left: parent.left
|
||||
leftMargin: 10
|
||||
}
|
||||
height: root.landscape()? root.height*0.1:root.width*0.1
|
||||
}
|
||||
|
||||
ConnectionIcon {
|
||||
id: startpadLogo
|
||||
source: "qrc:/graphics/icons/startpad_black.png"
|
||||
status: root.connections["startpad"].status
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 10
|
||||
left: parent.left
|
||||
leftMargin: 5 + buzzerLogo.width
|
||||
}
|
||||
height: root.landscape()? root.height*0.1:root.width*0.1
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: upper_line
|
||||
|
@ -395,7 +187,7 @@ Window {
|
|||
root.start()
|
||||
break
|
||||
case "RUNNING":
|
||||
root.stop("manual")
|
||||
root.stop(0)
|
||||
break
|
||||
case "STOPPED":
|
||||
root.reset()
|
||||
|
@ -407,31 +199,22 @@ Window {
|
|||
ProgressCircle {
|
||||
id: prog
|
||||
anchors.fill: startButt
|
||||
opacity: baseConn.state !== "connected" ?
|
||||
next_actionTimer.started_at > 0 ? 1:0
|
||||
:progress > 0 ? 1:0
|
||||
opacity: root.state === "STARTING" || root.state === "IDLE" ? 1:0
|
||||
|
||||
lineWidth: 5
|
||||
property int progress: 0
|
||||
|
||||
arcBegin: 0
|
||||
arcEnd: baseConn.state !== "connected" ? 360 * (( next_actionTimer.interval - ( new Date().getTime() - next_actionTimer.started_at ) ) / next_actionTimer.interval)
|
||||
:(360/100) * progress
|
||||
arcEnd: 360 * speedBackend.nextStartActionDelayProgress
|
||||
|
||||
colorCircle: "grey"
|
||||
onProgressChanged: {
|
||||
//console.log(progress)
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
|
||||
animationDuration: baseConn.state === "connected" ? 150:0
|
||||
|
||||
Timer {
|
||||
id: prog_refresh
|
||||
running: parent.opacity === 1 && baseConn.state !== "connected"
|
||||
interval: 1
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
prog.arcEnd = 360 * (( next_actionTimer.interval - ( new Date().getTime() - next_actionTimer.started_at ) ) / next_actionTimer.interval)
|
||||
prog_refresh.start()
|
||||
}
|
||||
}
|
||||
animationDuration: 0
|
||||
}
|
||||
|
||||
/*----------------------
|
||||
|
@ -455,7 +238,7 @@ Window {
|
|||
enabled: root.state === "STARTING"
|
||||
|
||||
onClicked: {
|
||||
root.stop("cancel")
|
||||
root.stop(1)
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
|
@ -634,7 +417,7 @@ Window {
|
|||
State {
|
||||
name: "IDLE"
|
||||
//state for the start page
|
||||
PropertyChanges { target: timer_1; pixelSize: root.landscape() ? parent.width * 0.1:parent.height * 0.3; scale: 1 }
|
||||
PropertyChanges { target: timer_1; font.pixelSize: root.landscape() ? parent.width * 0.1:parent.height * 0.3; scale: 1 }
|
||||
PropertyChanges {
|
||||
target: time_container;
|
||||
anchors.bottomMargin: root.landscape() ? undefined:parent.height * 0.1;
|
||||
|
@ -656,14 +439,14 @@ Window {
|
|||
anchors.rightMargin: root.landscape() ? parent.width * 0.05:parent.width * 0.5 - startButt.width * 0.5 //put the button more to the right to hide the menu (only in landscape mode)
|
||||
anchors.bottomMargin: root.landscape() ? parent.height * 0.5 - startButt.height * 0.5:parent.height * 0.1 //put the button lower to hide the menu (only in portrait mode)
|
||||
}
|
||||
PropertyChanges { target: timer_1; pixelSize: root.landscape() ? parent.width * 0.2:parent.height * 0.3; scale: 1 }
|
||||
PropertyChanges { target: timer_1; font.pixelSize: root.landscape() ? parent.width * 0.2:parent.height * 0.3; scale: 1 }
|
||||
PropertyChanges { target: cancelButt; scale: 1}
|
||||
PropertyChanges { target: menu_container; }
|
||||
},
|
||||
State {
|
||||
name: "RUNNING"
|
||||
//state when the timer is running
|
||||
PropertyChanges { target: timer_1; pixelSize: root.landscape() ? parent.width * 0.2:parent.height * 0.3; scale: 1 }
|
||||
PropertyChanges { target: timer_1; font.pixelSize: root.landscape() ? parent.width * 0.2:parent.height * 0.3; scale: 1 }
|
||||
PropertyChanges { target: startButt; enabled: true;
|
||||
text: "stop"
|
||||
anchors.rightMargin: root.landscape() ? parent.width * 0.05:parent.width * 0.5 - startButt.width * 0.5 //put the button more to the right to hide the menu (only in landscape mode)
|
||||
|
@ -677,7 +460,7 @@ Window {
|
|||
//state when the meassuring is over
|
||||
PropertyChanges {
|
||||
target: timer_1;
|
||||
pixelSize: root.landscape() ? parent.width * 0.15:parent.height * 0.1;
|
||||
font.pixelSize: root.landscape() ? parent.width * 0.15:parent.height * 0.1;
|
||||
scale: 1
|
||||
}
|
||||
PropertyChanges {
|
||||
|
@ -726,7 +509,12 @@ Window {
|
|||
|
||||
/*----Functions to control the stopwatch----*/
|
||||
function start(){
|
||||
if(baseConn.state === "connected"){
|
||||
var ret = speedBackend.startRace()
|
||||
if(ret !== 200){
|
||||
console.log("+ --- error starting race: "+ret)
|
||||
}
|
||||
|
||||
/*if(baseConn.state === "connected"){
|
||||
baseConn.startTimers()
|
||||
return;
|
||||
}
|
||||
|
@ -745,10 +533,18 @@ Window {
|
|||
return
|
||||
}
|
||||
|
||||
startSound.play()
|
||||
startSound.play()*/
|
||||
}
|
||||
|
||||
function stop(type){
|
||||
|
||||
var ret = speedBackend.stopRace(type)
|
||||
|
||||
if(ret !== 200){
|
||||
console.log("+ --- error stopping race: "+ret)
|
||||
}
|
||||
|
||||
/*
|
||||
if(baseConn.state === "connected"){
|
||||
baseConn.stopTimers(type)
|
||||
}
|
||||
|
@ -761,9 +557,18 @@ Window {
|
|||
}
|
||||
//root.state = "STOPPED"
|
||||
//timer_1.stop(type)
|
||||
*/
|
||||
}
|
||||
|
||||
function reset(){
|
||||
|
||||
var ret = speedBackend.resetRace()
|
||||
|
||||
if(ret !== 200){
|
||||
console.log("+ --- error resetting race: "+ret)
|
||||
}
|
||||
|
||||
/*
|
||||
if(baseConn.state === "connected"){
|
||||
baseConn.resetTimers()
|
||||
}
|
||||
|
@ -773,6 +578,7 @@ Window {
|
|||
|
||||
//
|
||||
//root.state = "IDLE"
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ AppSettings::AppSettings(QObject* parent)
|
|||
this->setDefaultSetting("at_marks_delay", 0);
|
||||
|
||||
this->setDefaultSetting("theme", "Default");
|
||||
|
||||
pGlobalAppSettings = this;
|
||||
}
|
||||
|
||||
QString AppSettings::loadSetting(const QString &key)
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#include "headers/baseconn.h"
|
||||
|
||||
BaseConn * pGlobalBaseConn = nullptr;
|
||||
|
||||
BaseConn::BaseConn(QObject *parent) : QObject(parent)
|
||||
{
|
||||
pGlobalBaseConn = this;
|
||||
socket = new QTcpSocket();
|
||||
this->setState("disconnected");
|
||||
|
||||
|
@ -9,14 +12,6 @@ BaseConn::BaseConn(QObject *parent) : QObject(parent)
|
|||
this, SLOT(gotError(QAbstractSocket::SocketError)));
|
||||
|
||||
this->nextConnectionId = 1;
|
||||
|
||||
this->speedTimers.append(pGlobalSpeedTimer);
|
||||
|
||||
this->refreshTimer = new QTimer();
|
||||
refreshTimer->setInterval(1);
|
||||
refreshTimer->setSingleShot(true);
|
||||
refreshTimer->connect(this->refreshTimer, &QTimer::timeout, this, &BaseConn::refreshTimers);
|
||||
refreshTimer->start();
|
||||
}
|
||||
|
||||
bool BaseConn::connectToHost() {
|
||||
|
@ -53,7 +48,6 @@ bool BaseConn::connectToHost() {
|
|||
connect(this->socket, &QTcpSocket::readyRead, this, &BaseConn::readyRead);
|
||||
this->connection_progress = 100;
|
||||
setState("connected");
|
||||
this->speedTimers[0]->remoteControlled = true;
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
@ -106,7 +100,7 @@ void BaseConn::gotError(QAbstractSocket::SocketError err)
|
|||
qDebug() << "got socket error: " << strError;
|
||||
}
|
||||
|
||||
QVariantMap BaseConn::sendCommand(int header, QJsonValue data = ""){
|
||||
QVariantMap BaseConn::sendCommand(int header, QJsonValue data){
|
||||
if(this->state != "connected"){
|
||||
return {{"status", 910}, {"data", "not connected"}};
|
||||
}
|
||||
|
@ -230,162 +224,6 @@ int BaseConn::writeRemoteSetting(QString key, QString value) {
|
|||
return this->sendCommand(3000, requestData)["status"].toInt();
|
||||
}
|
||||
|
||||
void BaseConn::refreshTimers(){
|
||||
if(this->state != "connected"){
|
||||
this->refreshTimer->start();
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap tmpReply = this->sendCommand(2000);
|
||||
|
||||
if(tmpReply["status"] != 200){
|
||||
return;
|
||||
}
|
||||
|
||||
int remoteRaceState = tmpReply["data"].toInt();
|
||||
|
||||
switch (remoteRaceState) {
|
||||
case 0:
|
||||
{
|
||||
// case IDLE
|
||||
if(speedTimers[0]->state != 0){
|
||||
speedTimers[0]->setState(SpeedTimer::IDLE);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// case STARTING
|
||||
if(speedTimers[0]->state != 1){
|
||||
speedTimers[0]->setState(SpeedTimer::STARTING);
|
||||
}
|
||||
|
||||
tmpReply = sendCommand(2004);
|
||||
if(tmpReply["status"] != 200){
|
||||
//handle Error!!
|
||||
qDebug() << "+--- getting next start action from basestation failed: " << tmpReply["status"];
|
||||
this->refreshTimer->start();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if(this->nextRemoteAction != tmpReply["data"].toString()){
|
||||
this->nextRemoteAction = tmpReply["data"].toString();
|
||||
this->nextRemoteActionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
tmpReply = sendCommand(2005);
|
||||
if(tmpReply["status"] != 200){
|
||||
//handle error!!
|
||||
qDebug() << "+--- getting next start action progress from basestation failed";
|
||||
this->refreshTimer->start();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if(this->nextRemoteActionDelayProg != tmpReply["data"].toFloat()){
|
||||
this->nextRemoteActionDelayProg = tmpReply["data"].toFloat();
|
||||
this->nextRemoteActionDelayProgChanged();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// case RUNNING
|
||||
if(speedTimers[0]->state != 2){
|
||||
speedTimers[0]->setState(SpeedTimer::RUNNING);
|
||||
}
|
||||
|
||||
// get current time
|
||||
tmpReply = sendCommand(2001, 0);
|
||||
if(tmpReply["status"] != 200){
|
||||
//handle error!!
|
||||
qDebug() << "+--- getting current time (timer 0) from basestation failed";
|
||||
this->refreshTimer->start();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
speedTimers[0]->stoppedTime = tmpReply["data"].toInt();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// case STOPPED
|
||||
if(speedTimers[0]->state != 3){
|
||||
speedTimers[0]->setState(SpeedTimer::STOPPED);
|
||||
}
|
||||
|
||||
// get current time
|
||||
tmpReply = sendCommand(2001, 0);
|
||||
if(tmpReply["status"] != 200){
|
||||
//handle error!!
|
||||
qDebug() << "+--- getting current time (timer 0) from basestation failed";
|
||||
return;
|
||||
}
|
||||
else {
|
||||
speedTimers[0]->stoppedTime = tmpReply["data"].toInt();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// some error
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->refreshTimer->start();
|
||||
}
|
||||
|
||||
bool BaseConn::startTimers(){
|
||||
qDebug() << "starting timers";
|
||||
|
||||
QVariantMap reply = this->sendCommand(1000);
|
||||
|
||||
if(reply["status"] != 200){
|
||||
//handle Error!!
|
||||
return false;
|
||||
}
|
||||
|
||||
this->speedTimers[0]->setState(SpeedTimer::STARTING);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseConn::stopTimers(QString type){
|
||||
qDebug() << "stopping timers";
|
||||
|
||||
QVariantMap reply = this->sendCommand(1001);
|
||||
|
||||
if(reply["status"] != 200){
|
||||
//handle Error!!
|
||||
return false;
|
||||
}
|
||||
|
||||
this->speedTimers[0]->stop(type);
|
||||
qDebug() << "stopped timers";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseConn::resetTimers(){
|
||||
qDebug() << "resetting timers";
|
||||
|
||||
QVariantMap reply = this->sendCommand(1002);
|
||||
|
||||
if(reply["status"] != 200){
|
||||
//handle Error!!
|
||||
return false;
|
||||
}
|
||||
|
||||
this->speedTimers[0]->reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseConn::setIP(const QString &ipAdress){
|
||||
this->ip = ipAdress;
|
||||
}
|
||||
|
@ -418,20 +256,15 @@ bool BaseConn::refreshConnections() {
|
|||
qDebug() << "+ --- error refreshing connections: " << reply["status"];
|
||||
return false;
|
||||
}
|
||||
connections = reply["data"].toString().split("|||");
|
||||
|
||||
QVariantList tmpConnections = reply["data"].toList();
|
||||
|
||||
this->connections = reply["data"].toList();
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
QStringList BaseConn::getConnections() {
|
||||
QVariant BaseConn::getConnections() {
|
||||
return(connections);
|
||||
}
|
||||
|
||||
QString BaseConn::getNextRemoteAction() {
|
||||
return this->nextRemoteAction;
|
||||
}
|
||||
|
||||
float BaseConn::getNextRemoteActionDelayProg(){
|
||||
return this->nextRemoteActionDelayProg;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,360 +0,0 @@
|
|||
/*
|
||||
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);
|
||||
}
|
549
sources/climbingrace.cpp
Normal file
549
sources/climbingrace.cpp
Normal file
|
@ -0,0 +1,549 @@
|
|||
#include "headers/climbingrace.h"
|
||||
|
||||
/*
|
||||
* manages:
|
||||
* - global state
|
||||
* - timers
|
||||
* - sounds
|
||||
* - next start action
|
||||
* - next start action delay progress
|
||||
*
|
||||
*/
|
||||
|
||||
ClimbingRace::ClimbingRace(QObject *parent) : QObject(parent)
|
||||
{
|
||||
this->state = IDLE;
|
||||
this->mode = LOCAL;
|
||||
|
||||
this->appSettings = new AppSettings;
|
||||
this->baseConn = new BaseConn;
|
||||
|
||||
this->baseConn->setIP("192.168.4.1");
|
||||
connect(this->baseConn, &BaseConn::stateChanged, this, &ClimbingRace::baseStationStateChanged);
|
||||
|
||||
this->speedTimers.append( new SpeedTimer );
|
||||
|
||||
this->player = new QMediaPlayer;
|
||||
|
||||
this->baseStationSyncTimer = new QTimer();
|
||||
this->baseStationSyncTimer->setInterval(10);
|
||||
this->baseStationSyncTimer->setSingleShot(true);
|
||||
this->baseStationSyncTimer->connect(this->baseStationSyncTimer, &QTimer::timeout, this, &ClimbingRace::syncWithBaseStation);
|
||||
this->baseStationSyncTimer->start();
|
||||
|
||||
this->timerTextRefreshTimer = new QTimer();
|
||||
this->timerTextRefreshTimer->setInterval(1);
|
||||
this->timerTextRefreshTimer->setSingleShot(true);
|
||||
this->timerTextRefreshTimer->connect(this->timerTextRefreshTimer, &QTimer::timeout, this, &ClimbingRace::refreshTimerText);
|
||||
this->refreshTimerText();
|
||||
|
||||
this->nextStartActionTimer = new QTimer(this);
|
||||
nextStartActionTimer->setSingleShot(true);
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
// --- Main Functionality ---
|
||||
// --------------------------
|
||||
|
||||
int ClimbingRace::startRace() {
|
||||
|
||||
if(this->state != IDLE) {
|
||||
return 904;
|
||||
}
|
||||
|
||||
this->refreshMode();
|
||||
|
||||
qDebug() << "+ --- starting race";
|
||||
|
||||
int returnCode = 900;
|
||||
|
||||
switch (this->mode) {
|
||||
case LOCAL:
|
||||
{
|
||||
|
||||
this->setState(STARTING);
|
||||
|
||||
this->nextStartAction = -1;
|
||||
this->playSoundsAndStartRace();
|
||||
|
||||
returnCode = 200;
|
||||
|
||||
break;
|
||||
}
|
||||
case REMOTE:
|
||||
{
|
||||
QVariantMap reply = this->baseConn->sendCommand(1000);
|
||||
|
||||
if(reply["status"] != 200){
|
||||
//handle Error!!
|
||||
returnCode = reply["status"].toInt();
|
||||
}
|
||||
else {
|
||||
|
||||
returnCode = 200;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
int ClimbingRace::stopRace(int type) {
|
||||
|
||||
if(this->state != RUNNING && this->state != STARTING) {
|
||||
return 904;
|
||||
}
|
||||
|
||||
// type can be:
|
||||
// 0: stopp
|
||||
// 1: cancel
|
||||
// 2: fail (fase start)
|
||||
|
||||
this->refreshMode();
|
||||
|
||||
qDebug() << "+ --- stopping race";
|
||||
|
||||
int returnCode = 900;
|
||||
|
||||
switch (this->mode) {
|
||||
case LOCAL:
|
||||
{
|
||||
|
||||
if(type == 1){
|
||||
this->nextStartActionTimer->stop();
|
||||
this->player->stop();
|
||||
this->nextStartAction = -1;
|
||||
}
|
||||
|
||||
returnCode = this->speedTimers[0]->stop(type) ? 200:904;
|
||||
|
||||
if(returnCode == 200) {
|
||||
this->setState(STOPPED);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case REMOTE:
|
||||
{
|
||||
QVariantMap reply = this->baseConn->sendCommand(1001);
|
||||
|
||||
if(reply["status"] != 200){
|
||||
//handle Error!!
|
||||
returnCode = reply["status"].toInt();
|
||||
}
|
||||
else {
|
||||
returnCode = 200;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
int ClimbingRace::resetRace() {
|
||||
|
||||
if(this->state != STOPPED) {
|
||||
return 904;
|
||||
}
|
||||
|
||||
this->refreshMode();
|
||||
|
||||
qDebug() << "+ --- resetting race";
|
||||
|
||||
int returnCode = 900;
|
||||
|
||||
|
||||
switch (this->mode) {
|
||||
case LOCAL:
|
||||
{
|
||||
returnCode = this->speedTimers[0]->reset() ? 200:904;
|
||||
|
||||
if(returnCode == 200){
|
||||
this->setState(IDLE);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case REMOTE:
|
||||
{
|
||||
|
||||
QVariantMap reply = this->baseConn->sendCommand(1002);
|
||||
|
||||
if(reply["status"] != 200){
|
||||
//handle Error!!
|
||||
returnCode = reply["status"].toInt();
|
||||
}
|
||||
else {
|
||||
returnCode = 200;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// --- Base Station sync ---
|
||||
// -------------------------
|
||||
|
||||
void ClimbingRace::syncWithBaseStation() {
|
||||
if(this->baseConn->state != "connected"){
|
||||
this->baseStationSyncTimer->start();
|
||||
return;
|
||||
}
|
||||
|
||||
this->baseConn->refreshConnections();
|
||||
emit this->baseStationConnectionsChanged();
|
||||
|
||||
QVariantMap tmpReply = this->baseConn->sendCommand(2000);
|
||||
|
||||
if(tmpReply["status"] != 200){
|
||||
this->baseStationSyncTimer->start();
|
||||
return;
|
||||
}
|
||||
|
||||
this->setState( raceState( tmpReply["data"].toInt() ) );
|
||||
|
||||
switch (this->state) {
|
||||
case 0:
|
||||
{
|
||||
// case IDLE
|
||||
if(speedTimers[0]->state != 0){
|
||||
speedTimers[0]->setState(SpeedTimer::IDLE);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// case STARTING
|
||||
if(speedTimers[0]->state != 1){
|
||||
speedTimers[0]->setState(SpeedTimer::STARTING);
|
||||
}
|
||||
|
||||
tmpReply = this->baseConn->sendCommand(2004);
|
||||
if(tmpReply["status"] != 200){
|
||||
//handle Error!!
|
||||
qDebug() << "+ --- getting next start action from basestation failed: " << tmpReply["status"];
|
||||
this->baseStationSyncTimer->start();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if(this->nextStartAction != tmpReply["data"].toInt()){
|
||||
this->nextStartAction = tmpReply["data"].toInt();
|
||||
this->nextStartActionChanged(this->nextStartAction);
|
||||
}
|
||||
}
|
||||
|
||||
tmpReply = this->baseConn->sendCommand(2005);
|
||||
if(tmpReply["status"] != 200){
|
||||
//handle error!!
|
||||
qDebug() << "+ --- getting next start action progress from basestation failed";
|
||||
this->baseStationSyncTimer->start();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this->nextStartActionDelayProgress = tmpReply["data"].toDouble() > 0 ? tmpReply["data"].toDouble():0;
|
||||
this->nextStartActionDelayProgressChanged();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// case RUNNING
|
||||
if(speedTimers[0]->state != 2){
|
||||
speedTimers[0]->setState(SpeedTimer::RUNNING);
|
||||
}
|
||||
|
||||
// get current time
|
||||
tmpReply = this->baseConn->sendCommand(2001, 0);
|
||||
if(tmpReply["status"] != 200){
|
||||
//handle error!!
|
||||
qDebug() << "+ --- getting current time (timer 0) from basestation failed";
|
||||
this->baseStationSyncTimer->start();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
speedTimers[0]->stoppedTime = tmpReply["data"].toInt();
|
||||
}
|
||||
|
||||
// get current time
|
||||
tmpReply = this->baseConn->sendCommand(2003, 0);
|
||||
if(tmpReply["status"] != 200){
|
||||
//handle error!!
|
||||
qDebug() << "+ --- getting reaction time (timer 0) from basestation failed";
|
||||
this->baseStationSyncTimer->start();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
speedTimers[0]->reactionTime = tmpReply["data"].toInt();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// case STOPPED
|
||||
if(speedTimers[0]->state != 3){
|
||||
speedTimers[0]->setState(SpeedTimer::STOPPED);
|
||||
}
|
||||
|
||||
// get current time
|
||||
tmpReply = this->baseConn->sendCommand(2001, 0);
|
||||
if(tmpReply["status"] != 200){
|
||||
//handle error!!
|
||||
qDebug() << "+ --- getting current time (timer 0) from basestation failed";
|
||||
return;
|
||||
}
|
||||
else {
|
||||
speedTimers[0]->stoppedTime = tmpReply["data"].toInt();
|
||||
}
|
||||
|
||||
// get current time
|
||||
tmpReply = this->baseConn->sendCommand(2003, 0);
|
||||
if(tmpReply["status"] != 200){
|
||||
//handle error!!
|
||||
qDebug() << "+ --- getting current time (timer 0) from basestation failed";
|
||||
this->baseStationSyncTimer->start();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
speedTimers[0]->reactionTime = tmpReply["data"].toInt();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->baseStationSyncTimer->start();
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
// --- helper functions ---
|
||||
// ------------------------
|
||||
|
||||
void ClimbingRace::playSoundsAndStartRace() {
|
||||
qDebug() << "next Action: " << nextStartAction;
|
||||
|
||||
nextStartActionTimer->disconnect(nextStartActionTimer, SIGNAL(timeout()), this, SLOT(playSoundsAndStartRace()));
|
||||
|
||||
switch (this->nextStartAction) {
|
||||
case 0:
|
||||
{
|
||||
if(!playSound("qrc:/sounds/at_marks_1.wav")){
|
||||
return;
|
||||
}
|
||||
if(appSettings->loadSetting("ready_en") == "true"){
|
||||
nextStartAction = 1;
|
||||
nextStartActionTimer->setInterval(appSettings->loadSetting("ready_delay").toInt() <= 0 ? 1:appSettings->loadSetting("ready_delay").toInt());
|
||||
}
|
||||
else{
|
||||
nextStartAction = 2;
|
||||
nextStartActionTimer->setInterval(1);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
if(!playSound("qrc:/sounds/ready_1.wav")){
|
||||
return;
|
||||
}
|
||||
nextStartAction = 2;
|
||||
nextStartActionTimer->setInterval(1);
|
||||
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if(!playSound("qrc:/sounds/OFFICAL_IFSC_STARTIGNAL.wav")){
|
||||
return;
|
||||
}
|
||||
nextStartAction = -1;
|
||||
nextStartActionTimer->disconnect(nextStartActionTimer, SIGNAL(timeout()), this, SLOT(playSoundsAndStartRace()));
|
||||
|
||||
this->setState(RUNNING);
|
||||
speedTimers[0]->start();
|
||||
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
this->speedTimers[0]->setState(SpeedTimer::STARTING);
|
||||
if(appSettings->loadSetting("at_marks_en") == "true"){
|
||||
nextStartAction = 0;
|
||||
nextStartActionTimer->setInterval(appSettings->loadSetting("at_marks_delay").toInt() <= 0 ? 1:appSettings->loadSetting("at_marks_delay").toInt());
|
||||
}
|
||||
else if(appSettings->loadSetting("ready_en") == "true"){
|
||||
nextStartAction = 1;
|
||||
nextStartActionTimer->setInterval(appSettings->loadSetting("ready_delay").toInt() <= 0 ? 1:appSettings->loadSetting("ready_delay").toInt());
|
||||
}
|
||||
else{
|
||||
nextStartAction = 2;
|
||||
nextStartActionTimer->setInterval(1);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nextStartActionTimer->connect(nextStartActionTimer, SIGNAL(timeout()), this, SLOT(playSoundsAndStartRace()));
|
||||
nextStartActionTimer->start();
|
||||
}
|
||||
|
||||
bool ClimbingRace::playSound(QString path) {
|
||||
|
||||
//connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
|
||||
player->setMedia(QUrl(path));
|
||||
player->setVolume(50);
|
||||
player->play();
|
||||
|
||||
QTimer timer;
|
||||
timer.setInterval(1);
|
||||
timer.setSingleShot(true);
|
||||
|
||||
QEventLoop loop;
|
||||
loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
|
||||
|
||||
while (player->mediaStatus() == QMediaPlayer::LoadingMedia || player->mediaStatus() == QMediaPlayer::BufferingMedia || player->mediaStatus() == QMediaPlayer::BufferedMedia) {
|
||||
timer.start();
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
if(player->mediaStatus() == QMediaPlayer::EndOfMedia){
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ClimbingRace::setState(raceState newState) {
|
||||
|
||||
if(newState != this->state) {
|
||||
this->state = newState;
|
||||
this->stateChanged(newState);
|
||||
}
|
||||
}
|
||||
|
||||
void ClimbingRace::refreshMode() {
|
||||
if(this->baseConn->state == "connected"){
|
||||
this->mode = REMOTE;
|
||||
}
|
||||
else {
|
||||
this->mode = LOCAL;
|
||||
}
|
||||
}
|
||||
|
||||
void ClimbingRace::refreshTimerText() {
|
||||
|
||||
// --- refresh timer text ---
|
||||
|
||||
QVariantList newTimerTextList;
|
||||
|
||||
foreach(SpeedTimer * timer, this->speedTimers){
|
||||
QVariantMap timerMap = {{"text",timer->getText()}, {"reacttime", timer->reactionTime}};
|
||||
newTimerTextList.append(timerMap);
|
||||
}
|
||||
|
||||
if(newTimerTextList != this->qmlTimers){
|
||||
this->qmlTimers = newTimerTextList;
|
||||
emit timerTextChanged();
|
||||
}
|
||||
|
||||
// --- refresh next start action delay progress ---
|
||||
|
||||
if(this->mode == LOCAL){
|
||||
QString totalStr;
|
||||
|
||||
if(nextStartAction == 0){
|
||||
totalStr = appSettings->loadSetting("at_marks_delay");
|
||||
}
|
||||
else if (nextStartAction == 1) {
|
||||
totalStr = appSettings->loadSetting("ready_delay");
|
||||
}
|
||||
|
||||
double remaining = this->nextStartActionTimer->remainingTime();
|
||||
double total = totalStr.toDouble();
|
||||
//qDebug() << "DELAY_PROG: " << "total: " << total << " remaining: " << remaining << " prog: " << remaining / total;
|
||||
if(remaining > 0){
|
||||
this->nextStartActionDelayProgress = remaining / total;
|
||||
emit this->nextStartActionDelayProgressChanged();
|
||||
}
|
||||
else {
|
||||
this->nextStartActionDelayProgress = 0;
|
||||
emit this->nextStartActionDelayProgressChanged();
|
||||
}
|
||||
}
|
||||
|
||||
this->timerTextRefreshTimer->start();
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// --- functions for qml ---
|
||||
// -------------------------
|
||||
|
||||
int ClimbingRace::getState() {
|
||||
return this->state;
|
||||
}
|
||||
|
||||
QVariant ClimbingRace::getTimerTextList() {
|
||||
return this->qmlTimers;
|
||||
// QVariantList test;
|
||||
// QVariantMap test2 = {{"text", "1234"}, {"reacttime", 2.0}};
|
||||
// test.append(test2);
|
||||
// return test;
|
||||
}
|
||||
|
||||
double ClimbingRace::getNextStartActionDelayProgress() {
|
||||
return this->nextStartActionDelayProgress;
|
||||
}
|
||||
|
||||
void ClimbingRace::writeSetting(QString key, QVariant value) {
|
||||
this->refreshMode();
|
||||
switch (this->mode) {
|
||||
case LOCAL:
|
||||
this->appSettings->writeSetting(key, value);
|
||||
break;
|
||||
case REMOTE:
|
||||
this->baseConn->writeRemoteSetting(key, value.toString());
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QString ClimbingRace::readSetting(QString key) {
|
||||
this->refreshMode();
|
||||
switch (this->mode) {
|
||||
case LOCAL:
|
||||
return this->appSettings->loadSetting(key);
|
||||
case REMOTE:
|
||||
QVariantMap reply = this->baseConn->sendCommand(3001, key);
|
||||
if(reply["status"] != 200){
|
||||
return "false";
|
||||
}
|
||||
return reply["data"].toString();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ClimbingRace::connectBaseStation() {
|
||||
return this->baseConn->connectToHost();
|
||||
}
|
||||
|
||||
QString ClimbingRace::getBaseStationState() {
|
||||
return this->baseConn->getState();
|
||||
}
|
||||
|
||||
QVariant ClimbingRace::getBaseStationConnections() {
|
||||
return baseConn->getConnections();
|
||||
}
|
|
@ -55,6 +55,7 @@
|
|||
#include "headers/baseconn.h"
|
||||
#include "headers/speedtimer.h"
|
||||
#include "headers/speedtimerqmladapter.h"
|
||||
#include "headers/climbingrace.h"
|
||||
#include <QTranslator>
|
||||
|
||||
static void connectToDatabase()
|
||||
|
@ -108,23 +109,20 @@ int main(int argc, char *argv[])
|
|||
#endif
|
||||
|
||||
connectToDatabase();
|
||||
//BuzzerConn * pBuzzerConn = new BuzzerConn(nullptr, "192.168.4.10", 80);
|
||||
//BuzzerConn * pStartpadConn = new BuzzerConn(nullptr, "192.168.4.11", 80);
|
||||
AppSettings * pAppSettings = new AppSettings();
|
||||
pGlobalAppSettings = pAppSettings;
|
||||
|
||||
SpeedTimer * pSpeedTimer = new SpeedTimer();
|
||||
pGlobalSpeedTimer = pSpeedTimer;
|
||||
AppSettings * pAppSettings = new AppSettings();
|
||||
|
||||
//setup the sql storage model as a qml model
|
||||
qmlRegisterType<SqlProfileModel>("com.itsblue.speedclimbingstopwatch", 1, 0, "SqlProfileModel");
|
||||
qmlRegisterType<SqlStorageModel>("com.itsblue.speedclimbingstopwatch", 1, 0, "SqlStorageModel");
|
||||
|
||||
//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");
|
||||
qmlRegisterType<SpeedTimerQmlAdapter>("com.itsblue.speedclimbingstopwatch", 1, 0, "SpeedTimerBackend");
|
||||
//qmlRegisterType<BuzzerConn>("com.itsblue.speedclimbingstopwatch", 1, 0, "BuzzerConn");
|
||||
//qmlRegisterType<BuzzerConn>("com.itsblue.speedclimbingstopwatch", 1, 0, "StartpadConn");
|
||||
//qmlRegisterType<BaseConn>("com.itsblue.speedclimbingstopwatch", 1, 0, "BaseStationConn");
|
||||
//qmlRegisterType<SpeedTimerQmlAdapter>("com.itsblue.speedclimbingstopwatch", 1, 0, "SpeedTimerBackend");
|
||||
|
||||
qmlRegisterType<ClimbingRace>("com.itsblue.speedclimbingstopwatch", 2, 0, "SpeedBackend");
|
||||
|
||||
//setup translation engine
|
||||
//to the language of the system
|
||||
|
@ -135,17 +133,15 @@ int main(int argc, char *argv[])
|
|||
|
||||
QQmlApplicationEngine engine;
|
||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||
|
||||
QQmlContext *context = engine.rootContext();
|
||||
context->setContextProperty("_cppAppSettings", pAppSettings);
|
||||
|
||||
if (engine.rootObjects().isEmpty())
|
||||
return -1;
|
||||
|
||||
engine.rootContext()->setContextProperty("_cppAppSettings", pAppSettings);
|
||||
engine.rootContext()->setContextProperty("_cppSpeedTimer", pSpeedTimer);
|
||||
|
||||
int iRet = 0;
|
||||
iRet = app.exec();
|
||||
|
||||
//call the destructors of all objects
|
||||
delete pAppSettings;
|
||||
|
||||
return iRet;
|
||||
}
|
||||
|
|
|
@ -12,15 +12,14 @@ SpeedTimer::SpeedTimer(QObject *parent) : QObject(parent)
|
|||
this->stoppedTime = 0;
|
||||
this->reactionTime = 0;
|
||||
this->state = IDLE;
|
||||
this->remoteControlled = false;
|
||||
}
|
||||
|
||||
void SpeedTimer::start() {
|
||||
if(this->state != STARTING){
|
||||
return;
|
||||
bool SpeedTimer::start(bool force) {
|
||||
if(this->state != STARTING && !force){
|
||||
return false;
|
||||
}
|
||||
qDebug() << "starting timer";
|
||||
if(!this->remoteControlled){
|
||||
if(!force){
|
||||
this->stopTime = 0;
|
||||
this->stoppedTime = 0;
|
||||
this->reactionTime = 0;
|
||||
|
@ -28,49 +27,55 @@ void SpeedTimer::start() {
|
|||
}
|
||||
|
||||
this->setState(RUNNING);
|
||||
|
||||
return true;
|
||||
//this->startPad->appendCommand("SET_LED_RUNNING");
|
||||
}
|
||||
|
||||
void SpeedTimer::stop(QString type) {
|
||||
if(this->state != SpeedTimer::STARTING && this->state != SpeedTimer::RUNNING){
|
||||
return;
|
||||
bool SpeedTimer::stop(int type, bool force) {
|
||||
|
||||
// type can be:
|
||||
// 0: stopped
|
||||
// 1: cancelled
|
||||
// 2: failed (fase start)
|
||||
|
||||
if( ( this->state != SpeedTimer::STARTING && this->state != SpeedTimer::RUNNING && this->state ) && !force ){
|
||||
return false;
|
||||
}
|
||||
|
||||
//qDebug() << "Stopping: " << "start Time: " << startTime << " stopTime: " << stopTime << " stoppedTime: " << stoppedTime << " reactionTime: " << reactionTime;
|
||||
|
||||
if(this->remoteControlled){
|
||||
if(type == "cancel"){
|
||||
emit startCanceled(false);
|
||||
}
|
||||
}
|
||||
else if(type == "cancel"){
|
||||
emit startCanceled(false);
|
||||
this->stoppedTime = 0;
|
||||
}
|
||||
else if(type == "manual"){
|
||||
if(this->state == STARTING){
|
||||
emit startCanceled(false);
|
||||
}
|
||||
switch (type) {
|
||||
case 0:
|
||||
{
|
||||
this->stopTime = this->date->currentMSecsSinceEpoch();
|
||||
this->stoppedTime = this->stopTime - this->startTime;
|
||||
this->setState(STOPPED);
|
||||
break;
|
||||
}
|
||||
else if(type == "topPad"){
|
||||
//this->stopTime = this->topPad->latest_button_pressed + this->topPad->offset;
|
||||
this->stoppedTime = this->stopTime - this->startTime;
|
||||
case 1:
|
||||
{
|
||||
this->stoppedTime = 0;
|
||||
this->setState(CANCELLED);
|
||||
break;
|
||||
}
|
||||
else if(type == "falseStart"){
|
||||
emit startCanceled(true);
|
||||
case 2:
|
||||
{
|
||||
this->stoppedTime = this->reactionTime;
|
||||
this->setState(FAILED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->setState(STOPPED);
|
||||
qDebug() << "Stopped: " << "start Time: " << startTime << " stopTime: " << stopTime << " stoppedTime: " << stoppedTime << " reactionTime: " << reactionTime;
|
||||
|
||||
return true;
|
||||
//this->startPad->appendCommand("SET_LED_STARTING");
|
||||
}
|
||||
|
||||
void SpeedTimer::reset(){
|
||||
if(this->state != STOPPED){
|
||||
return;
|
||||
bool SpeedTimer::reset(bool force){
|
||||
if( ( this->state != STOPPED && this->state != FAILED && this->state != CANCELLED ) && !force){
|
||||
return false;
|
||||
}
|
||||
|
||||
this->startTime = 0;
|
||||
|
@ -78,6 +83,8 @@ void SpeedTimer::reset(){
|
|||
this->stoppedTime = 0;
|
||||
this->reactionTime = 0;
|
||||
this->setState(IDLE);
|
||||
|
||||
return true;
|
||||
//this->startPad->appendCommand("SET_LED_STARTING");
|
||||
}
|
||||
|
||||
|
@ -102,7 +109,7 @@ QString SpeedTimer::getState(){
|
|||
|
||||
double SpeedTimer::getCurrTime() {
|
||||
double currTime;
|
||||
if(this->state == RUNNING && !this->remoteControlled){
|
||||
if(this->state == RUNNING && this->startTime > 0){
|
||||
currTime = this->date->currentMSecsSinceEpoch() - this->startTime;
|
||||
}
|
||||
else {
|
||||
|
@ -112,6 +119,33 @@ double SpeedTimer::getCurrTime() {
|
|||
return(currTime);
|
||||
}
|
||||
|
||||
QString SpeedTimer::getText() {
|
||||
//qDebug() << this->getState();
|
||||
QString newText;
|
||||
switch (this->state) {
|
||||
case SpeedTimer::IDLE:
|
||||
newText = "Click Start to start";
|
||||
break;
|
||||
case SpeedTimer::STARTING:
|
||||
newText = "0.000 sec";
|
||||
break;
|
||||
case SpeedTimer::RUNNING:
|
||||
newText = QString::number( this->getCurrTime() / 1000.0, 'f', 3 ) + " sec";
|
||||
break;
|
||||
case SpeedTimer::STOPPED:
|
||||
newText = QString::number( this->stoppedTime / 1000.0, 'f', 3 ) + " sec";
|
||||
break;
|
||||
case SpeedTimer::FAILED:
|
||||
newText = "False Start";
|
||||
break;
|
||||
case SpeedTimer::CANCELLED:
|
||||
newText = "Cancelled";
|
||||
break;
|
||||
}
|
||||
|
||||
return newText;
|
||||
}
|
||||
|
||||
void SpeedTimer::delay(int mSecs){
|
||||
QEventLoop loop;
|
||||
QTimer timer;
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
#include "headers/speedtimerqmladapter.h"
|
||||
|
||||
SpeedTimerQmlAdapter::SpeedTimerQmlAdapter(QObject *parent) : QObject(parent)
|
||||
{
|
||||
this->state = SpeedTimer::IDLE;
|
||||
connect(pGlobalSpeedTimer, &SpeedTimer::stateChanged, this, &SpeedTimerQmlAdapter::setState);
|
||||
connect(pGlobalSpeedTimer, &SpeedTimer::startCanceled, this, &SpeedTimerQmlAdapter::startCanceled);
|
||||
|
||||
this->refreshTimer = new QTimer();
|
||||
refreshTimer->setInterval(1);
|
||||
refreshTimer->setSingleShot(true);
|
||||
refreshTimer->connect(refreshTimer, &QTimer::timeout, this, &SpeedTimerQmlAdapter::refreshValues);
|
||||
refreshTimer->start();
|
||||
|
||||
}
|
||||
|
||||
QString SpeedTimerQmlAdapter::getState(){
|
||||
switch(state){
|
||||
case SpeedTimer::IDLE:
|
||||
return("IDLE");
|
||||
case SpeedTimer::STARTING:
|
||||
return("STARTING");
|
||||
case SpeedTimer::RUNNING:
|
||||
return("RUNNING");
|
||||
case SpeedTimer::STOPPED:
|
||||
return("STOPPED");
|
||||
}
|
||||
}
|
||||
|
||||
void SpeedTimerQmlAdapter::setState(SpeedTimer::timerState newState){
|
||||
|
||||
this->state = newState;
|
||||
emit this->stateChanged(newState);
|
||||
}
|
||||
|
||||
QString SpeedTimerQmlAdapter::getText(){
|
||||
return(this->text);
|
||||
}
|
||||
|
||||
void SpeedTimerQmlAdapter::refreshValues(){
|
||||
//qDebug() << this->getState();
|
||||
QString newText;
|
||||
switch (this->state) {
|
||||
case SpeedTimer::IDLE:
|
||||
newText = "Click Start to start";
|
||||
break;
|
||||
case SpeedTimer::STARTING:
|
||||
newText = "0.000 sec";
|
||||
break;
|
||||
case SpeedTimer::RUNNING:
|
||||
newText = QString::number( pGlobalSpeedTimer->getCurrTime() / 1000.0, 'f', 3 ) + " sec";
|
||||
break;
|
||||
case SpeedTimer::STOPPED:
|
||||
newText = QString::number( pGlobalSpeedTimer->stoppedTime / 1000.0, 'f', 3 ) + " sec";
|
||||
}
|
||||
|
||||
if(this->text != newText){
|
||||
this->text = newText;
|
||||
emit textChanged();
|
||||
}
|
||||
//qDebug() << this->text;
|
||||
refreshTimer->start();
|
||||
}
|
||||
|
||||
bool SpeedTimerQmlAdapter::setStarting(){
|
||||
if(pGlobalSpeedTimer->remoteControlled){
|
||||
return false;
|
||||
}
|
||||
pGlobalSpeedTimer->setState(SpeedTimer::STARTING);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpeedTimerQmlAdapter::start(){
|
||||
if(pGlobalSpeedTimer->remoteControlled){
|
||||
return false;
|
||||
}
|
||||
pGlobalSpeedTimer->start();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpeedTimerQmlAdapter::stop(QString type){
|
||||
if(pGlobalSpeedTimer->remoteControlled){
|
||||
return false;
|
||||
}
|
||||
pGlobalSpeedTimer->stop(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpeedTimerQmlAdapter::reset(){
|
||||
if(pGlobalSpeedTimer->remoteControlled){
|
||||
return false;
|
||||
}
|
||||
pGlobalSpeedTimer->reset();
|
||||
return true;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
QT += quick sql
|
||||
QT += quick sql multimedia
|
||||
|
||||
android {
|
||||
QT += androidextras
|
||||
|
@ -23,20 +23,18 @@ SOURCES += \
|
|||
sources/main.cpp \
|
||||
sources/sqlstoragemodel.cpp \
|
||||
sources/sqlprofilemodel.cpp \
|
||||
sources/buzzerconn.cpp \
|
||||
sources/appsettings.cpp \
|
||||
sources/baseconn.cpp \
|
||||
sources/speedtimer.cpp \
|
||||
sources/speedtimerqmladapter.cpp
|
||||
sources/climbingrace.cpp
|
||||
|
||||
HEADERS += \
|
||||
headers/sqlstoragemodel.h \
|
||||
headers/sqlprofilemodel.h \
|
||||
headers/buzzerconn.h \
|
||||
headers/appsettings.h \
|
||||
headers/baseconn.h \
|
||||
headers/speedtimer.h \
|
||||
headers/speedtimerqmladapter.h
|
||||
headers/climbingrace.h
|
||||
|
||||
RESOURCES += \
|
||||
shared.qrc \
|
||||
|
|
Reference in a new issue