From 9260358e117d44644a2b37ffb633ad78da34ba63 Mon Sep 17 00:00:00 2001 From: dorian Date: Thu, 7 Mar 2019 17:18:24 +0100 Subject: [PATCH] changed internal structure completely to be more flexible and reliable --- headers/baseconn.h | 32 +- headers/buzzerconn.h | 131 ------ headers/climbingrace.h | 90 +++++ headers/speedtimer.h | 11 +- headers/speedtimerqmladapter.h | 52 --- qml/SettingsDialog.qml | 133 ++----- qml/SpeedTimer.qml | 4 +- qml/components/ConnectionDelegate.qml | 2 +- qml/main.qml | 330 ++++------------ qml/styles/StyleSettings.qml | 16 +- sources/appsettings.cpp | 2 + sources/baseconn.cpp | 189 +-------- sources/buzzerconn.cpp | 360 ----------------- sources/climbingrace.cpp | 549 ++++++++++++++++++++++++++ sources/main.cpp | 28 +- sources/speedtimer.cpp | 96 +++-- sources/speedtimerqmladapter.cpp | 95 ----- speedclimbing_stopwatch.pro | 8 +- 18 files changed, 854 insertions(+), 1274 deletions(-) delete mode 100644 headers/buzzerconn.h create mode 100644 headers/climbingrace.h delete mode 100644 headers/speedtimerqmladapter.h delete mode 100644 sources/buzzerconn.cpp create mode 100644 sources/climbingrace.cpp delete mode 100644 sources/speedtimerqmladapter.cpp diff --git a/headers/baseconn.h b/headers/baseconn.h index c2650a3..ee0bbdd 100644 --- a/headers/baseconn.h +++ b/headers/baseconn.h @@ -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 speedTimers; - - QTimer *refreshTimer; QSemaphore remoteSessions; int nextConnectionId; @@ -72,10 +63,6 @@ private: QList 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 diff --git a/headers/buzzerconn.h b/headers/buzzerconn.h deleted file mode 100644 index 13eeece..0000000 --- a/headers/buzzerconn.h +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef BUZZERCONN_H -#define BUZZERCONN_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 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 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 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 diff --git a/headers/climbingrace.h b/headers/climbingrace.h new file mode 100644 index 0000000..e10e568 --- /dev/null +++ b/headers/climbingrace.h @@ -0,0 +1,90 @@ +#ifndef CLIMBINGRACE_H +#define CLIMBINGRACE_H + +#include +#include +#include +#include +#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 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 diff --git a/headers/speedtimer.h b/headers/speedtimer.h index 8e2e339..766b34a 100644 --- a/headers/speedtimer.h +++ b/headers/speedtimer.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(); diff --git a/headers/speedtimerqmladapter.h b/headers/speedtimerqmladapter.h deleted file mode 100644 index c216fb7..0000000 --- a/headers/speedtimerqmladapter.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef SPEEDTIMERQMLADAPTER_H -#define SPEEDTIMERQMLADAPTER_H - -#include -#include -#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 diff --git a/qml/SettingsDialog.qml b/qml/SettingsDialog.qml index 3ee914f..b9ceb40 100644 --- a/qml/SettingsDialog.qml +++ b/qml/SettingsDialog.qml @@ -124,7 +124,7 @@ Popup { } onClicked: { - options_stack.depth > 1 ? options_stack.pop():root.close() + options_stack.depth > 1 ? options_stack.pop():root.close() } Behavior on opacity { @@ -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); + ListView { + id: baseStationConnections_list + property string title: qsTr("connections") + property int delegateHeight: height*0.18 + model: speedBackend.baseStationConnections.length + delegate: ConnectionDelegate { + enabled: false + font.pixelSize: options_stack.text_pixelSize + width: parent.width + height: baseStationConnections_list.delegateHeight - 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() - 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-----*/ diff --git a/qml/SpeedTimer.qml b/qml/SpeedTimer.qml index 84c1ba9..a9d0025 100644 --- a/qml/SpeedTimer.qml +++ b/qml/SpeedTimer.qml @@ -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()) diff --git a/qml/components/ConnectionDelegate.qml b/qml/components/ConnectionDelegate.qml index 76b8448..3d63ed8 100644 --- a/qml/components/ConnectionDelegate.qml +++ b/qml/components/ConnectionDelegate.qml @@ -12,7 +12,7 @@ ItemDelegate { enabled: status.status === "disconnected" onClicked: { - connect(type) + connect() if(status.status !== "connected"){ statusIndicator.color_override = "red" shortDelay.start() diff --git a/qml/main.qml b/qml/main.qml index e0f4350..c33c812 100644 --- a/qml/main.qml +++ b/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() - } - } - - 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 + 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: 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,84 +96,36 @@ 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 { @@ -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) - } - 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() + Behavior on opacity { + NumberAnimation { + duration: 200 } } + + 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" + */ } } } diff --git a/qml/styles/StyleSettings.qml b/qml/styles/StyleSettings.qml index bc9308b..27a9806 100644 --- a/qml/styles/StyleSettings.qml +++ b/qml/styles/StyleSettings.qml @@ -113,10 +113,10 @@ Item { case "Default": theme = Dark break - case "Dark": + case "Dark": theme = Light break - case "Light": + case "Light": theme = Default break } @@ -142,13 +142,13 @@ Item { _cppAppSettings.writeSetting("theme", "Dark") theme = Dark break - case "Dark": - _cppAppSettings.writeSetting("theme", "Light") - theme = Light + case "Dark": + _cppAppSettings.writeSetting("theme", "Light") + theme = Light break - case "Light": - _cppAppSettings.writeSetting("theme", "Default") - theme = Default + case "Light": + _cppAppSettings.writeSetting("theme", "Default") + theme = Default break } } diff --git a/sources/appsettings.cpp b/sources/appsettings.cpp index 46b3ce5..04a77b4 100644 --- a/sources/appsettings.cpp +++ b/sources/appsettings.cpp @@ -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) diff --git a/sources/baseconn.cpp b/sources/baseconn.cpp index a0180b3..cd77c9b 100644 --- a/sources/baseconn.cpp +++ b/sources/baseconn.cpp @@ -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; } @@ -415,23 +253,18 @@ bool BaseConn::refreshConnections() { if(reply["status"] != 200){ //handle Error!! - qDebug() << "+--- error refreshing connections: " << reply["status"]; + 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; -} - diff --git a/sources/buzzerconn.cpp b/sources/buzzerconn.cpp deleted file mode 100644 index 1669375..0000000 --- a/sources/buzzerconn.cpp +++ /dev/null @@ -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 . -*/ - -#include "headers/buzzerconn.h" - -BuzzerConn::BuzzerConn(QObject *parent, QString ip, int port) : QObject(parent) -{ - // initialize TcpSocket for communication with the extentions - this->socket = new QTcpSocket(); - - // initialize Qdate - this->date = new QDateTime; - this->latest_button_pressed = 0; - - // set ip and port - this->ip = ip; - this->port = port; - - //standard state: disconnected - this->setState("disconnected"); -} - -bool BuzzerConn::connect() -{ - // function to connect to buzzer - - qDebug() << "connecting..."; - - setState("connecting"); - - //setup loop to wait until the connection has finished - QEventLoop loop; - QTimer timer; - - timer.setSingleShot(true); - // quit the loop when the timer times out - loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); - //quit the loop when the connection was established - loop.connect(this->socket, SIGNAL(connected()), &loop, SLOT(quit())); - // start the timer before starting to connect - timer.start(3000); - //connect - this->socket->connectToHost(this->ip, this->port); - - //wait for the connection to finish (programm gets stuck in here) - loop.exec(); - - //loop finished - if(timer.remainingTime() == -1){ - //the time has been triggered -> timeout - setState("disconnected"); - return(false); - } - - // stop the timer as the connection has been established - timer.stop(); - - // get the timestamps ( last_pressed and current_timestamp ) from the extention - QList times = gettimes(2000, true); - - if(times[0] == 200.0){ - //if the request was successfull, set last_triggered and - this->latest_button_pressed = times[2]; - for(int i=0;i<=100;i++){ - // middle the offset 100 times - this->connection_progress = i; - emit this->progressChanged(); - if(!calcoffset(this->gettimes(1000, false))){ - // if a request fails, cancle the connection process - this->connection_progress = 100; - setState("disconnected"); - return(false); - } - } - // after middeling the offset for 100 times set the state to connected and quit - setState("connected"); - return(true); - } - else{ - //if not, cancel - setState("disconnected"); - return(false); - } -} - -bool BuzzerConn::calcoffset(QList times) -{ - //function that recieves the current time of the extention, - //puts it into the latest offsets list and - //calculates the avarage offset - - //if there are not enoug items in the list (0=status of the request (200 = success ...); 1 = timestamp that has to be added to the list) - if(times.length() != 2){ - return(false); - } - - if(times[0] == 200.0){ - // if the given request was a successfull request, calculate the offset - double offset = date->currentMSecsSinceEpoch() - times[1]; - //check if the list contains more than or 100 values - if(this->latest_offsets.length()>=100){ - //if so, delete the first (oldest) one - this->latest_offsets.removeFirst(); - } - //append the new offset to the list - this->latest_offsets.append(offset); - //variable to store the avarage - double mem = 0; - for(int i=0;ioffset = mem / double(latest_offsets.length()); - - //emit that the offset has changed - emit offsetChanged(); - //qDebug("%20f", this->offset); - return(true); - } - else { - //if the given request was not valid, return false - return(false); - } -} - -QList BuzzerConn::gettimes(int timeout, bool bothTimes) -{ - // function to recieve the timestamps (last_triggered, current_timestamp) from the extentions - - //list to store the return code of the request and the timestamps - QList times; - //variable to store answer of the extention - signed long ret; - //send request to extention - ret = this->sendCommand("GET_TIMESTAMP", timeout); - if(ret >= 0){ - // if it is higer than 0 (=success) append the return code - times.append(double(200)); - //and the timestamp - times.append(double(ret)); - - if(bothTimes){ - //if both timstamps were requested do the same thing again for the other one (last_triggered) - ret = this->sendCommand("GET_LASTPRESSED", timeout); - - if(ret >= 0){ - //if the reuest was successfull, append the value to the list - times.append(double(ret)); - } - else { - // if not, change the return code - times[0] = ret; - } - } - } - else { - // if not, append the return code - times.append(ret); - } - - //return the list - return(times); - -} - -double BuzzerConn::get(QString key) -{ - // function to get all kinds of data - - if(key == "offset"){ - // get the offset of the extention - return(this->offset); - } - else if (key == "lastpressed") { - // get the last_triggered timestamp of the extention - return(this->latest_button_pressed); - } - else if( key == "currtime") { - // get the current time - return(this->date->currentMSecsSinceEpoch()); - } - else if( key == "connection_progress") { - //get the connection progress - return(this->connection_progress); - } - else if( key == "connected") { - // get the state of the connection - if(this->state == "connected"){ - return(1); - } - return(0); - } - return(0); -} - -bool BuzzerConn::refresh() -{ - // function that has to be called frequently to refresh the connection - // check if the extention has been triggered - // sends one command the pending command list - // and calculates the offset once - - if(this->state != "connected"){ - // if the extention is not connected return - return(false); - } - - //send one of the pending commands - if(pending_commands.length() > 0){ - //get the irst command in the list - QString command = this->pending_commands.first(); - //send the command - signed long retval = this->sendCommand(command, 2000); - - if(retval > 0){ - //if the request has been successfull, remove the command from the list - this->pending_commands.removeFirst(); - } - } - - // get the timestamps from the extention - QList ret = this->gettimes(2000); - - if(ret[0] >= 0){ - //if the request has been successfull check if the last_triggered timestamp has changed - if(ret[2] > this->latest_button_pressed){ - // if it has, set it - this->latest_button_pressed = ret[2]; - //and emit the trggered signal - emit triggered(); - } - // as the requst as been sucessfull, reset the error counter - this->errors = 0; - // calculate the offset on time and return - return(this->calcoffset(ret)); - } - else { - // if not add one to the error conter - this->errors ++; - - if(this->errors > errors_until_disconnect){ - // if the error counter is too high, disconnect - this->socket->disconnectFromHost(); - this->setState("disconnected"); - } - - // return false - return(false); - } - -} - -signed long BuzzerConn::sendCommand(QString command, int timeout){ - //function to send a commnd to the extention - - //if there is any data in the storage, clear it - if(this->socket->bytesAvailable() > 0){ - this->socket->readAll(); - } - - //send request to the socket server - QByteArray arrBlock; - QDataStream out(&arrBlock, QIODevice::WriteOnly); - - out << quint16(0) << command; - - out.device()->seek(0); - out << quint16(arrBlock.size() - sizeof(quint16)); - - this->socket->write(arrBlock); - - //now wait for the extention to answer - QEventLoop loop; - QTimer timer; - - timer.setSingleShot(true); - //quit the loop if the timer times out - loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); - //or if the request is anwered - loop.connect(socket, SIGNAL(readyRead()), &loop, SLOT(quit())); - - //start the timer - timer.start(timeout); - //start the loop - loop.exec(); - - //loop finished - if(timer.remainingTime() == -1){ - //the time has been triggered -> timeout - return(-1); - } - // stop the timer - timer.stop(); - - - //if the data is not 4 bytes long it is invalid -> clear and terminate - if(this->socket->bytesAvailable() != 4){ - this->socket->readAll(); - return(-2); - } - long data = 0; - // read four bytes and cast them as a double - this->socket->read((char*)&data,4); - - //return the data - return data; -} - -void BuzzerConn::appendCommand(QString command){ - this->pending_commands.append(command); -} - -void BuzzerConn::setIP(const QString &ipAdress){ - this->ip = ipAdress; -} - -QString BuzzerConn::getIP() const -{ - return(this->ip); -} - -QString BuzzerConn::getState() const -{ - return(this->state); -} - -void BuzzerConn::setState(QString newState){ - this->state = newState; - emit stateChanged(); -} - -int BuzzerConn::getProgress() const -{ - return(connection_progress); -} - -double BuzzerConn::getOffset() const -{ - return(this->offset); -} - -double BuzzerConn::getLastTriggered() const -{ - return(this->latest_button_pressed); -} diff --git a/sources/climbingrace.cpp b/sources/climbingrace.cpp new file mode 100644 index 0000000..db85a89 --- /dev/null +++ b/sources/climbingrace.cpp @@ -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(); +} diff --git a/sources/main.cpp b/sources/main.cpp index 5849289..f308d87 100644 --- a/sources/main.cpp +++ b/sources/main.cpp @@ -55,6 +55,7 @@ #include "headers/baseconn.h" #include "headers/speedtimer.h" #include "headers/speedtimerqmladapter.h" +#include "headers/climbingrace.h" #include 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("com.itsblue.speedclimbingstopwatch", 1, 0, "SqlProfileModel"); qmlRegisterType("com.itsblue.speedclimbingstopwatch", 1, 0, "SqlStorageModel"); //setup the startpad and buzzer conn qml objects - qmlRegisterType("com.itsblue.speedclimbingstopwatch", 1, 0, "BuzzerConn"); - qmlRegisterType("com.itsblue.speedclimbingstopwatch", 1, 0, "StartpadConn"); - qmlRegisterType("com.itsblue.speedclimbingstopwatch", 1, 0, "BaseStationConn"); - qmlRegisterType("com.itsblue.speedclimbingstopwatch", 1, 0, "SpeedTimerBackend"); + //qmlRegisterType("com.itsblue.speedclimbingstopwatch", 1, 0, "BuzzerConn"); + //qmlRegisterType("com.itsblue.speedclimbingstopwatch", 1, 0, "StartpadConn"); + //qmlRegisterType("com.itsblue.speedclimbingstopwatch", 1, 0, "BaseStationConn"); + //qmlRegisterType("com.itsblue.speedclimbingstopwatch", 1, 0, "SpeedTimerBackend"); + + qmlRegisterType("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; } diff --git a/sources/speedtimer.cpp b/sources/speedtimer.cpp index fe1d7a0..e9b237b 100644 --- a/sources/speedtimer.cpp +++ b/sources/speedtimer.cpp @@ -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; diff --git a/sources/speedtimerqmladapter.cpp b/sources/speedtimerqmladapter.cpp deleted file mode 100644 index 18f9759..0000000 --- a/sources/speedtimerqmladapter.cpp +++ /dev/null @@ -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; -} diff --git a/speedclimbing_stopwatch.pro b/speedclimbing_stopwatch.pro index ab9240a..fe90887 100644 --- a/speedclimbing_stopwatch.pro +++ b/speedclimbing_stopwatch.pro @@ -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 \