From 654b05dec9ab8f5940fe2c7d338c6fa50704c80f Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Wed, 15 Apr 2020 19:21:32 +0200 Subject: [PATCH] further bugfixes, added documentation for timer and sound player --- ScStwLibraries/headers/ScStw.hpp | 108 ++++---- ScStwLibraries/headers/scstwclient.h | 260 ++++++++++---------- ScStwLibraries/headers/scstwrace.h | 6 +- ScStwLibraries/headers/scstwsoundplayer.h | 69 +++++- ScStwLibraries/headers/scstwtimer.h | 231 ++++++++++++++++- ScStwLibraries/sources/scstwrace.cpp | 49 ++-- ScStwLibraries/sources/scstwsoundplayer.cpp | 1 + ScStwLibraries/sources/scstwtimer.cpp | 64 ++--- 8 files changed, 533 insertions(+), 255 deletions(-) diff --git a/ScStwLibraries/headers/ScStw.hpp b/ScStwLibraries/headers/ScStw.hpp index 345b44f..f8bf9e4 100644 --- a/ScStwLibraries/headers/ScStw.hpp +++ b/ScStwLibraries/headers/ScStw.hpp @@ -4,25 +4,25 @@ #include #include -/** - * @mainpage ScStw Libraries documentation +/*! + * \mainpage ScStw Libraries documentation * - * @section intro_sec Introduction + * \section intro_sec Introduction * * This library is meant for usage with the Speed climbing stopwatch project. * It contains some helper classes to build a client application for the ScStw basestation with Qt. * - * @section section Installation - * @code{.sh} + * \section section Installation + * \code{.sh} * cd yourRepo * git submodule add https://git.itsblue.de/ScStw/shared-libraries/ * git submodule update --init --recursive - * @endcode + * \endcode * * Add to the list of libraries for the Qt-Secret assembly. For an example you can create Main.Pro in which connect Qt-Secret and your project.pro files as subprojects. * * Main.pro: - * @code{.pro} + * \code{.pro} * TEMPLATE = subdirs * CONFIG += ordered * @@ -31,25 +31,25 @@ * MyProject * * ScStwLibraries.file = shared-libraries/ScStwLibraries/ScStwLibraries.pro - * @endcode + * \endcode * * And in your MyProject.pro include the .pri file: - * @code{.pro} + * \code{.pro} * include($$PWD/../shared-libraries/ScStwLibraries/ScStwLibraries.pri) - * @endcode + * \endcode */ -/** - * @brief The ScStw class provides some shared functions and enums for use in the ScStw project. +/*! + * \brief The ScStw class provides some shared functions and enums for use in the ScStw project. */ class ScStw : public QObject { Q_OBJECT public: - /** - * @brief The SignalKey enum contains all signal keys a client can subscribe to + /*! + * \brief The SignalKey enum contains all signal keys a client can subscribe to * - * @see ScStw::signalKeyFromInt() + * \see ScStw::signalKeyFromInt() */ enum SignalKey { InvalidSignal = -1, @@ -60,13 +60,13 @@ public: }; Q_ENUM(SignalKey) - /** - * @brief The BaseStationSetting enum contains all settings of the base station that can be changed by a client + /*! + * \brief The BaseStationSetting enum contains all settings of the base station that can be changed by a client * - * @see ScStw::baseStationSettingFromInt() - * @see ScStw::baseStationSettingToString() - * @see ScStw::baseStationSettingFromString() - * @see ScStw::baseStationSettings + * \see ScStw::baseStationSettingFromInt() + * \see ScStw::baseStationSettingToString() + * \see ScStw::baseStationSettingFromString() + * \see ScStw::baseStationSettings */ enum BaseStationSetting { InvalidSetting = -1, @@ -78,8 +78,8 @@ public: }; Q_ENUM(BaseStationSetting) - /** - * @brief The ErrorCode enum contains all error codes that can occur when sending a command to the basestation + /*! + * \brief The ErrorCode enum contains all error codes that can occur when sending a command to the basestation */ enum ErrorCode { Success = 200, @@ -91,60 +91,60 @@ public: }; Q_ENUM(ErrorCode) - /** - * @brief SOCKET_MESSAGE_START_KEY contains the key, a message is supposed to start with + /*! + * \brief SOCKET_MESSAGE_START_KEY contains the key, a message is supposed to start with */ static const char* SOCKET_MESSAGE_START_KEY; - /** - * @brief SOCKET_MESSAGE_END_KEY contains the key, a message is supposed to end with + /*! + * \brief SOCKET_MESSAGE_END_KEY contains the key, a message is supposed to end with */ static const char* SOCKET_MESSAGE_END_KEY; - /** - * @brief baseStationSettings contains a string with reference to all BaseStationSetting values + /*! + * \brief baseStationSettings contains a string with reference to all BaseStationSetting values * - * @see ScStw::BaseStationSetting - * @see ScStw::baseStationSettingToString() - * @see ScStw::baseStationSettingFromString() + * \see ScStw::BaseStationSetting + * \see ScStw::baseStationSettingToString() + * \see ScStw::baseStationSettingFromString() */ static const QMap baseStationSettings; - /** - * @brief Function to convert an int to a BaseStationSetting - * @param i the int to convert - * @return a BaseStationSetting + /*! + * \brief Function to convert an int to a BaseStationSetting + * \param i the int to convert + * \return a BaseStationSetting * - * @see ScStw::BaseStationSetting + * \see ScStw::BaseStationSetting */ static BaseStationSetting baseStationSettingfromInt(int i); - /** - * @brief Function to convert a QString to a BaseStationSetting - * @param s the string to convert - * @return a BaseStationSetting + /*! + * \brief Function to convert a QString to a BaseStationSetting + * \param s the string to convert + * \return a BaseStationSetting * - * @see ScStw::BaseStationSetting - * @see ScStw::baseStationSettingToString() + * \see ScStw::BaseStationSetting + * \see ScStw::baseStationSettingToString() */ static BaseStationSetting baseStationSettingFromString(QString s); - /** - * @brief Function to convert BaseStationSetting to a QString - * @param s the BaseStationSetting to convert - * @return a QString + /*! + * \brief Function to convert BaseStationSetting to a QString + * \param s the BaseStationSetting to convert + * \return a QString * - * @see ScStw::BaseStationSetting - * @see ScStw::baseStationSettingFromString() + * \see ScStw::BaseStationSetting + * \see ScStw::baseStationSettingFromString() */ static QString baseStationSettingToString(BaseStationSetting s); - /** - * @brief Function to convert an int to a SignalKey - * @param i the int to convert - * @return a SignalKey + /*! + * \brief Function to convert an int to a SignalKey + * \param i the int to convert + * \return a SignalKey * - * @see ScStw::SignalKey + * \see ScStw::SignalKey */ static SignalKey signalKeyFromInt(int i); diff --git a/ScStwLibraries/headers/scstwclient.h b/ScStwLibraries/headers/scstwclient.h index 7200c66..f2e2cf3 100644 --- a/ScStwLibraries/headers/scstwclient.h +++ b/ScStwLibraries/headers/scstwclient.h @@ -16,27 +16,27 @@ #include "ScStw.hpp" -/** +/*! * This class is used to connect and talk to the ScStw basestation. * - * @code{.cpp} + * \code{.cpp} * ScStwClient * client = new ScStwClient(); * client->setIp("192.168.4.1"); * client->connectToHost(); - * @endcode + * \endcode * - * @brief The ScStwClient class - * @author Dorian Zedler + * \brief The ScStwClient class + * \author Dorian Zedler */ class ScStwClient : public QObject { Q_OBJECT public: - /** + /*! * The constructor * - * @brief ScStwClient + * \brief ScStwClient */ explicit ScStwClient(); @@ -81,232 +81,232 @@ private: QList waitingRequests; signals: - /** - * @brief Is emitted, when the connection state changes + /*! + * \brief Is emitted, when the connection state changes */ void stateChanged(); - /** - * @brief Is emitted, whenever a reply is recieved which does not match any requests + /*! + * \brief Is emitted, whenever a reply is recieved which does not match any requests * - * @param reply contains the reply + * \param reply contains the reply */ void gotUnexpectedMessage(QString message); - /** - * @brief Is emitted, when an update signal from the basestation is recieved + /*! + * \brief Is emitted, when an update signal from the basestation is recieved * - * @param data + * \param data */ void gotSignal(ScStw::SignalKey key, QVariant data); - /** - * @brief Is emitted, when there is any network error - * @param error + /*! + * \brief Is emitted, when there is any network error + * \param error */ void gotError(QAbstractSocket::SocketError error); public slots: - /** - * @brief Function to connect to the base station + /*! + * \brief Function to connect to the base station */ void connectToHost(); - /** - * @brief Function to disconnect from the basestation + /*! + * \brief Function to disconnect from the basestation */ void closeConnection(); - /** socket communication handling */ + /*! socket communication handling */ - /** - * @brief Funtion to send a command to the server (for internal use) - * @param header the command to send - * @param data the data to send - * @param timeout the timeout - * @param useTerminationKeys wether to use the termination keys defined in + /*! + * \brief Funtion to send a command to the server (for internal use) + * \param header the command to send + * \param data the data to send + * \param timeout the timeout + * \param useTerminationKeys wether to use the termination keys defined in * - * @return a variant map containing the Keys "status" and "data" + * \return a variant map containing the Keys "status" and "data" */ QVariantMap sendCommand(int header, QJsonValue data = "", int timeout = 3000); - /** updater functions */ + /*! updater functions */ - /** - * @brief Function to set the timestamp of the base station to match the client - * @see getTimeOffset() - * @return true or false + /*! + * \brief Function to set the timestamp of the base station to match the client + * \see getTimeOffset() + * \return true or false */ bool updateTime(); - /** - * @brief Function to update the firmware of the basestation to the version stored in the client - * @details will not do anything if the remote firmware is newer or the same as the clients one - * @see isFirmwareUpToDate() - * @see getFirmwareVersion() - * @return true: firmware was updated or is already up-to-date; false: there was an error during the update + /*! + * \brief Function to update the firmware of the basestation to the version stored in the client + * \details will not do anything if the remote firmware is newer or the same as the clients one + * \see isFirmwareUpToDate() + * \see getFirmwareVersion() + * \return true: firmware was updated or is already up-to-date; false: there was an error during the update */ bool updateFirmware(); - /** - * @brief Function to check wether the firmware of the base station is up-to-date - * @see getFirmwareVersion() - * @see updateFirmware() - * @return true or false + /*! + * \brief Function to check wether the firmware of the base station is up-to-date + * \see getFirmwareVersion() + * \see updateFirmware() + * \return true or false */ bool isFirmwareUpToDate(); - /** helper functions */ + /*! helper functions */ - /** - * @brief Function to write a setting on the base station - * @param key the key to write to - * @param value the value to write to - * @return the status code returned by the command + /*! + * \brief Function to write a setting on the base station + * \param key the key to write to + * \param value the value to write to + * \return the status code returned by the command */ ScStw::ErrorCode writeRemoteSetting(ScStw::BaseStationSetting key, QString value); - /** - * @brief Function to read a setting on the base station - * @param key the key to read from - * @return the value of the key or "false" if the key is not found or an error occured + /*! + * \brief Function to read a setting on the base station + * \param key the key to read from + * \return the value of the key or "false" if the key is not found or an error occured */ QString readRemoteSetting(ScStw::BaseStationSetting key); - /** Getter fuctions */ + /*! Getter fuctions */ - /** - * @brief Function to get the ip the client will try to connect to. - * @see setIP() - * @return the ip + /*! + * \brief Function to get the ip the client will try to connect to. + * \see setIP() + * \return the ip */ QString getIP(); - /** - * @brief Function to get the current state of the client - * @return the current state + /*! + * \brief Function to get the current state of the client + * \return the current state */ ScStwClient::State getState(); - /** - * @brief Functio to get the extensions and their state from the base station - * @return a list with all configured extensions and their state + /*! + * \brief Functio to get the extensions and their state from the base station + * \return a list with all configured extensions and their state */ QVariantList getConnections(); - /** - * @brief Function to get the time offset of the base station relative to the clients time - * @see updateTime() - * @return the time offset in milliseconds + /*! + * \brief Function to get the time offset of the base station relative to the clients time + * \see updateTime() + * \return the time offset in milliseconds */ int getTimeOffset(); - /** - * @brief Function to get the current firmware version of the base station - * @see updateFirmware() - * @see isFirmwareUpToDate() - * @return Firmware version as string encoded as .. + /*! + * \brief Function to get the current firmware version of the base station + * \see updateFirmware() + * \see isFirmwareUpToDate() + * \return Firmware version as string encoded as .. */ QString getFirmwareVersion(); - /** setter functions */ + /*! setter functions */ - /** - * @brief Function to set the ip to connect to - * @see getIP() - * @param ipAdress + /*! + * \brief Function to set the ip to connect to + * \see getIP() + * \param ipAdress */ void setIP(QString ipAdress); private slots: - /** - * @brief called when timeout timer times out + /*! + * \brief called when timeout timer times out */ void connectionTimeout(); - /** - * @brief Function that is connected to the QAbstractSocket::error slot and used to handle upcoming errors - * @param err the error that occurred + /*! + * \brief Function that is connected to the QAbstractSocket::error slot and used to handle upcoming errors + * \param err the error that occurred */ void handleError(QAbstractSocket::SocketError err); - /** - * @brief Function to init a session at the base station - * @return true or false + /*! + * \brief Function to init a session at the base station + * \return true or false */ bool init(); - /** - * @brief Function to end a session on the base station + /*! + * \brief Function to end a session on the base station */ void deInit(); - /** - * @brief Funtion to send a command to the server (for internal use) - * @param header the command to send - * @param data the data to send - * @param timeout the timeout - * @param useTerminationKeys wether to use the termination keys defined in + /*! + * \brief Funtion to send a command to the server (for internal use) + * \param header the command to send + * \param data the data to send + * \param timeout the timeout + * \param useTerminationKeys wether to use the termination keys defined in * ScStw::SOCKET_MESSAGE_START_KEY and ScStw::SOCKET_MESSAGE_END_KEY - * @return a variant map containing the Keys "status" and "data" + * \return a variant map containing the Keys "status" and "data" */ QVariantMap sendCommand(int header, QJsonValue data, int timeout, bool useTerminationKeys); - /** - * @brief Function connected to the QAbstractSocket::readyRead signal + /*! + * \brief Function connected to the QAbstractSocket::readyRead signal */ void handleReadyRead(); - /** - * @brief Function to process an incoming string and parse the messages contained in it. - * @param message the message sting to parse + /*! + * \brief Function to process an incoming string and parse the messages contained in it. + * \param message the message sting to parse */ void processSocketMessage(QString message); - /** - * @brief Function that handles a parsed message - * @details This fuction looks up the id of the incoming message and tries to find the according waiting request. + /*! + * \brief Function that handles a parsed message + * \details This fuction looks up the id of the incoming message and tries to find the according waiting request. * If it is unable to find an accordin request, the signal ScStwClient::gotUnexpectedMessage(QString message) is called. * If the recieved message is a signal, the ScStwClient::handleSignal() function is called. - * @see gotUnexpectedMessage() - * @see handleSignal() - * @param reply the massage that needs to be handles + * \see gotUnexpectedMessage() + * \see handleSignal() + * \param reply the massage that needs to be handles */ void handleSocketMessage(QString reply); - /** - * @brief Function to handle a change of the state of the tcp socket - * @details it is connected to the QAbstractSocket::stateChanged signal - * @see stateChanged() - * @param socketState + /*! + * \brief Function to handle a change of the state of the tcp socket + * \details it is connected to the QAbstractSocket::stateChanged signal + * \see stateChanged() + * \param socketState */ void handleSocketStateChange(QAbstractSocket::SocketState socketState); - /** - * @brief Function to handle a signal from the base station. - * @details Called by the ScStwClient::handleSocketMessage function - * @see handleSocketMessage() - * @param data + /*! + * \brief Function to handle a signal from the base station. + * \details Called by the ScStwClient::handleSocketMessage function + * \see handleSocketMessage() + * \param data */ void handleSignal(QVariantMap data); - /** Helper Functions */ + /*! Helper Functions */ - /** - * @brief Function used to set the local cache of the baseStation connections. - * @details emits ScStwClient::gotSignal() with a ScStw::ExtensionsChanged signal. - * @see gotSignal() - * @param connections the list to set the chache to + /*! + * \brief Function used to set the local cache of the baseStation connections. + * \details emits ScStwClient::gotSignal() with a ScStw::ExtensionsChanged signal. + * \see gotSignal() + * \param connections the list to set the chache to */ void setConnections(QVariantList connections); - /** - * @brief Function to set the local state. - * @details emits ScStwClient::stateChanged() when the new state does not match the old one. - * @see stateChanged() - * @param newState the state to change to + /*! + * \brief Function to set the local state. + * \details emits ScStwClient::stateChanged() when the new state does not match the old one. + * \see stateChanged() + * \param newState the state to change to */ void setState(ScStwClient::State newState); }; diff --git a/ScStwLibraries/headers/scstwrace.h b/ScStwLibraries/headers/scstwrace.h index e0b2788..d928408 100644 --- a/ScStwLibraries/headers/scstwrace.h +++ b/ScStwLibraries/headers/scstwrace.h @@ -37,10 +37,10 @@ private: // some settings double soundVolume; - /** - * @brief stores the start action settings + /*! + * \brief stores the start action settings * - * @details Stores the settings for the action ScStwRace::AtYourMarks and ScStwRace::Ready. The settings keys are: "Enabled" and "Delay" + * \details Stores the settings for the action ScStwRace::AtYourMarks and ScStwRace::Ready. The settings keys are: "Enabled" and "Delay" */ QMap startActionSettings; diff --git a/ScStwLibraries/headers/scstwsoundplayer.h b/ScStwLibraries/headers/scstwsoundplayer.h index 8ba1f8f..bfb7447 100644 --- a/ScStwLibraries/headers/scstwsoundplayer.h +++ b/ScStwLibraries/headers/scstwsoundplayer.h @@ -9,31 +9,94 @@ #include #include +/*! + * \brief The ScStwSoundPlayer class is used for ultra low latency sound playback of the speed clibing start tones and commands + */ class ScStwSoundPlayer : public QObject { Q_OBJECT public: + /*! + * \brief ScStwSoundPlayer constructor + * \param parent + */ explicit ScStwSoundPlayer(QObject *parent = nullptr); private: + /*! + * \brief A map containing all sound files + * 0: AtYourMarksSound + * 1: ReadySound + * 2: StartSound + */ QMap soundFiles; + + /*! + * \brief Containng the false start sound file + */ QFile *falseStartSoundFile; + /*! + * \brief The audio output object + */ QAudioOutput *audioOutput; + + /*! + * \brief The QEventLoop used to wait for the sound to finish + */ QEventLoop *waitLoop; + + /*! + * \brief The QTimer to wait for the sound to finish + */ QTimer *waitTimer; + + /*! + * \brief The action that is currently played + */ int currentlyPlayingAction; public slots: - bool play(int action, double volume, double *timeOfStop = nullptr); - bool cancel(double volume); + + /*! + * \brief Function to begin playing the sound of a certain state + * \param action The action to play (0: AtYourMarks, 1:Ready, 2:Start) + * \param volume The volume to play at + * \param timeOfStop The time the playback actually started (msecs since epoch) + * \return true if the playback was successfully started, false otherwise + */ + bool play(int action, double volume, double *timeOfStart = nullptr); + + /*! + * \brief Function to wait for the playback to finish + * \param timeOfStop the point in time when the plyback actually stopped (msecs since epoch) + * \return false if there was any error (eg. there was no playback currently), true otherwise + */ bool waitForSoundFinish(double *timeOfStop = nullptr); - //int interrupt(); + + /*! + * \brief Function to cancel the current playback + * + * Note that this function will automatically play the false start tone if the currently playing action is 2 + * + * \param volume the volume to play the false start sound at + * \return true if the playback was successfully stopped, false otherwise + */ + bool cancel(double volume = 0); private slots: + + /*! + * \brief Handle a state change of the audio output + * \param newState the new state of the audio output + */ void handleStateChanged(QAudio::State newState); signals: + + /*! + * \brief Emitted whenever a playback started + */ void playbackStarted(); }; diff --git a/ScStwLibraries/headers/scstwtimer.h b/ScStwLibraries/headers/scstwtimer.h index 12a4e01..6b2abc8 100644 --- a/ScStwLibraries/headers/scstwtimer.h +++ b/ScStwLibraries/headers/scstwtimer.h @@ -7,58 +7,263 @@ #include #include "ScStw.hpp" +/*! + * \brief The ScStwTimer class is used for advanced time measurement. + * + * It does not work on its own though. + * It is recommended to use it in combination with the ScStwRace class. + * + * ## When using standalone: + * \code{.cpp} + * ScStwTimer timer; + * // start the timer + * timer.start(); + * // stop the timer + * timer.stop(); + * \endcode + * + * The timer will now go into ScStw::WAITING state. + * That indicates that the timer has stopped and the final result has to be assigned by an external handler. + * + * \code{.cpp} + * // assign result 'won' + * timer.setResult(ScStwTimer::WON); + * \endcode + * + * The timer is now in ScStwTimer::WON state. + * + * \code{.cpp} + * // reset the timer + * timer.reset(); + * \endcode + * + * The timer is not in ScStwTimer::IDLE state again. + */ class ScStwTimer : public QObject { Q_OBJECT public: + /*! + * \brief ScStwTimer constructor + * \param parent the parent object + */ explicit ScStwTimer(QObject *parent = nullptr); - enum TimerState { IDLE, STARTING, WAITING, RUNNING, WON, LOST, FAILED, CANCELLED, DISABLED }; - enum StopReason { ManualStop, CancelStop, FailStop, TopPadStop }; + /*! + * \brief The TimerState enum contains all state the timer can be in + */ + enum TimerState { + IDLE, /*!< Timer is waiting to be started */ + STARTING, /*!< Timer is starting and will react with a false start if the climber starts */ + RUNNING, /*!< Timer is running */ + WAITING, /*!< Timer was stopped and is waiting for the result */ + WON, /*!< Timer has won */ + LOST, /*!< Timer has lost */ + FAILED, /*!< A false start occured */ + CANCELLED, /*!< Timer was cancelled */ + DISABLED /*!< Timer is disabled */ + }; + + /*! + * \brief The StopReason enum contains all possible reasons for a stop + */ + enum StopReason { + ManualStop, /*!< Timer was stopped manually */ + CancelStop, /*!< Timer was cancelled */ + FailStop, /*!< A false start occured */ + TopPadStop /*!< Timer was stopped by a physical trigger (eg. a ScStwExtension) */ + }; protected: + /*! + * \brief The current state of the timer + */ TimerState state; - // variables for capturing the time + /*! + * \brief The time the timer was started at + */ double startTime; + + /*! + * \brief The time the timer was stopped at + */ double stopTime; - double stoppedTime; + + /*! + * \brief the reaction time of the climber + */ double reactionTime; - // values for the startpad - double startPadTriggerTime; - - QDateTime *date; - public slots: - // --- main functionality --- + /*! + * \brief Function to start the timer + * + * To do this, the timer has to be in ScStwTimer::STARTING state! + * + * \return false if the timer was not in the required state and therefore not started, true otherwise + */ bool start(); + + /*! + * \brief Function to start the timer at a given point in time (present or future) + * + * To do this, the timer has to be in ScStwTimer::STARTING state! + * + * \param timeOfStart the point in time (msecs since epoch) when the timer is supposted to be started + * \return false if the timer was not in the required state and therefore not started, true otherwise + */ bool start(double timeOfStart); + + /*! + * \brief Function to cancel the timer + * + * To do this, the timer has to be in ScStwTimer::IDLE, ScStwTimer::STARTING or ScStwTimer::RUNNING state! + * + * \return false if the timer was not in the required state and therefore not cancelled, true otherwise + */ bool cancel(); + + /*! + * \brief Function to stop the timer + * + * To do this, the timer has to be in ScStwTimer::RUNNING state! + * + * \return false if the timer was not in the required state and therefore not stopped, true otherwise + */ bool stop(); - bool stop(TimerState); + + /*! + * \brief Function to stop the timer at a given point in time (past or future) + * + * To do this, the timer has to be in ScStwTimer::RUNNING state! + * + * \param timeOfStop the point in time (msecs since epoch) when the timer is supposted to be stopped + * + * \return false if the timer was not in the required state and therefore not stopped, true otherwise + */ + bool stop(double timeOfStop); + + /*! + * \brief Function to assing the result of the race to the timer + * + * To do this, the timer has to be in ScStwTimer::WAITING state! + * + * \return false if the timer was not in the required state and the result therefore not set, true otherwise + */ + bool setResult(TimerState); + + /*! + * \brief Function to reset the timer + * + * To do this, the timer has to be in ScStwTimer::WON or ScSTw::LOST state! + * + * \return false if the timer was not in the required state and therefore not reset, true otherwise + */ bool reset(); - // --- helper functions --- + // -- helper functions -- + /*! + * \brief Function to get the current state of the timer + * \return current state of the timer + * \see ScStwTimer::TimerState + */ TimerState getState(); + + /*! + * \brief Function to get the current time of the timer + * + * To do this, the timer has to be in ScStwTimer::RUNNING, ScStwTimer::WAITING, ScStwTimer::WON or ScSTw::LOST state! + * + * \return The current / final time of the timer or -1 if it is not in the required state + */ double getCurrentTime(); + + /*! + * \brief Function to get the reaction time of the climber. + * \return The climbers reaction time + */ double getReactionTime(); + + /*! + * \brief Function to set if the timer is supposed to be disabled + * + * !!! CAUTION use this function with care, it immidiately changes the state of the timer !!! + * It is recommended to only use this function to change the timers state after the + * ScStwTimer::requestTimerEnableChange() signal was called, during the race, + * the timer is used in, is in IDLE state. + * + * \param disabled if the timer is supposed to be diabled + */ void setDisabled(bool disabled); protected slots: + /*! + * \brief slot to call when the climber has started + * \param timeOfStart time (msecs since epoch) when the climber started + */ void handleClimberStart(double timeOfStart); + + /*! + * \brief Function to stop the timer at a given point in time (past or future) + * + * To do this, the timer has to be in ScStwTimer::RUNNING state! + * + * \param reason reason for the timer stop + * \param timeOfStop the point in time (msecs since epoch) when the timer is supposted to be stopped + * + * \return false if the timer was not in the required state and therefore not stopped, true otherwise + * + * \see ScStwTimer::StopReason + */ + bool stop(StopReason reason, double timeOfStop); + + /*! + * \brief Function to stop the timer + * + * To do this, the timer has to be in ScStwTimer::RUNNING state! + * + * \param reason reason for the timer stop + * + * \return false if the timer was not in the required state and therefore not stopped, true otherwise + * + * \see ScStwTimer::StopReason + */ bool stop(StopReason reason); // --- helper functions --- + /*! + * \brief Function to change the state of the timer + * + * Doing this will emit the ScStwTimer::stateChanged() signal (only if the new state differs from the current one) + * + * \param newState The new state + */ void setState(TimerState newState); signals: + /*! + * \brief Emitted when the state of the timer changed + */ void stateChanged(); + + /*! + * \brief Emitted when the start was canceled + * \param falseStart Wheter a false start occured + */ void startCanceled(bool falseStart); + + /*! + * \brief Emitted when the reaction time changed + */ void reactionTimeChanged(); - void stopRequested(); + + /*! + * \brief Emitted when the timer wants its state to be changed by the external handler + * \param timer the timer object + */ void requestEnableChange(ScStwTimer* timer); }; diff --git a/ScStwLibraries/sources/scstwrace.cpp b/ScStwLibraries/sources/scstwrace.cpp index 7c611d1..27e50ed 100644 --- a/ScStwLibraries/sources/scstwrace.cpp +++ b/ScStwLibraries/sources/scstwrace.cpp @@ -10,7 +10,7 @@ ScStwRace::ScStwRace(QObject *parent) : QObject(parent) // configure timer that handles the delay between the start commands this->nextActionTimer = new QTimer(this); nextActionTimer->setSingleShot(true); - this->nextActionLoop = new QEventLoop(this); + this->nextActionLoop = new QEventLoop(); connect(this->nextActionTimer, &QTimer::timeout, this->nextActionLoop, &QEventLoop::quit); @@ -59,12 +59,14 @@ int ScStwRace::stop() { qDebug() << "+ [INFO] stopping race"; + double timeOfStop = QDateTime::currentMSecsSinceEpoch(); + int returnCode = 900; bool stopOk = true; foreach(ScStwTimer *speedTimer, this->timers){ - if(!speedTimer->stop() && speedTimer->getState() != ScStwTimer::DISABLED){ + if(!speedTimer->stop(timeOfStop) && speedTimer->getState() != ScStwTimer::DISABLED){ stopOk = false; } } @@ -85,11 +87,10 @@ void ScStwRace::handleTimerStop() { if(this->state == RUNNING) { // find out which timer has won double lowestStoppedTime = -1; - QList timersWhichHaveWonIds; + QList timersWhichHaveWonIds; // iterate through all timers and find the lowest time taht was stopped - for(int i = 0; i < this->timers.length(); i++) { - ScStwTimer * timer = this->timers[i]; + foreach(ScStwTimer * timer, this->timers) { if(timer->getCurrentTime() > 0 && (timer->getCurrentTime() <= lowestStoppedTime || lowestStoppedTime < 0)) { // this is the timer with the lowest stopped time lowestStoppedTime = timer->getCurrentTime(); @@ -97,27 +98,28 @@ void ScStwRace::handleTimerStop() { } // append the timer(s) with the lowest stopped time to the winner list - for(int i = 0; i < this->timers.length(); i++) { - ScStwTimer * timer = this->timers[i]; - if(timer->getCurrentTime() > 0 && (timer->getCurrentTime() <= lowestStoppedTime || lowestStoppedTime < 0)) { + foreach(ScStwTimer * timer, this->timers) { + if(timer->getCurrentTime() > 0 + && (timer->getCurrentTime() <= lowestStoppedTime || lowestStoppedTime < 0) + && timer->getState() != ScStwTimer::RUNNING + ) { // this is the timer with the lowest stopped time - timersWhichHaveWonIds.append(i); + timersWhichHaveWonIds.append(timer); } } // update the states of all timers - for(int i = 0; i < this->timers.length(); i++) { - ScStwTimer * timer = this->timers[i]; + foreach(ScStwTimer * timer, this->timers) { + if(timer->getState() == ScStwTimer::RUNNING) + continue; - if(!timersWhichHaveWonIds.contains(i) && timer->getCurrentTime() > 0) { - timer->stop(ScStwTimer::LOST); + if(timersWhichHaveWonIds.contains(timer)) { + timer->setResult(ScStwTimer::WON); } - else if (timersWhichHaveWonIds.contains(i)) { - qDebug() << "timer " << i << " has won"; - timer->stop(ScStwTimer::WON); + else { + timer->setResult(ScStwTimer::LOST); } } - } } @@ -181,6 +183,8 @@ bool ScStwRace::playSoundsAndStartTimers(StartAction thisAction) { if(this->startActionSettings.contains(thisAction) && this->startActionSettings[thisAction]["Enabled"].toBool()) { if(!this->soundPlayer->play(thisAction, this->soundVolume, &timeOfSoundPlaybackStart)) return false; + if(this->state != STARTING) + return true; } this->nextStartAction = StartAction(thisAction + 1); @@ -262,20 +266,20 @@ void ScStwRace::refreshTimerStates() { qDebug() << "[INFO][MAIN] refreshing timer states"; // check if the race is over - int stoppedState = 1; + bool raceIsOver = true; foreach(ScStwTimer * timer, this->timers){ if(timer->getState() < ScStwTimer::WON){ // if the timer is not in stoped state - stoppedState = 0; + raceIsOver = false; break; } - else if(this->state != RUNNING) { - // TODO athleteProfiles->addResult(timer->getCurrTime(), timer->getReactionTime(), this->speedTimers.indexOf(timer)); + else if(timer->getState() == ScStwTimer::WAITING) { + this->handleTimerStop(); } } - if(stoppedState == 1){ + if(raceIsOver){ this->setState(STOPPED); return; } @@ -357,7 +361,6 @@ bool ScStwRace::addTimer(ScStwTimer *timer) { connect(timer, &ScStwTimer::startCanceled, this, &ScStwRace::cancelStart); connect(timer, &ScStwTimer::stateChanged, this, &ScStwRace::refreshTimerStates); - connect(timer, &ScStwTimer::stopRequested, this, &ScStwRace::handleTimerStop); connect(timer, &ScStwTimer::requestEnableChange, this, &ScStwRace::handleTimerEnable); return true; diff --git a/ScStwLibraries/sources/scstwsoundplayer.cpp b/ScStwLibraries/sources/scstwsoundplayer.cpp index 21ae82e..0041248 100644 --- a/ScStwLibraries/sources/scstwsoundplayer.cpp +++ b/ScStwLibraries/sources/scstwsoundplayer.cpp @@ -80,6 +80,7 @@ bool ScStwSoundPlayer::cancel(double volume) { // stop playback this->audioOutput->stop(); + this->waitLoop->quit(); if(this->currentlyPlayingAction != 2) return true; diff --git a/ScStwLibraries/sources/scstwtimer.cpp b/ScStwLibraries/sources/scstwtimer.cpp index 9ea2588..9efc8ca 100644 --- a/ScStwLibraries/sources/scstwtimer.cpp +++ b/ScStwLibraries/sources/scstwtimer.cpp @@ -5,7 +5,6 @@ ScStwTimer::ScStwTimer(QObject *parent) : QObject(parent) this->startTime = 0; this->stopTime = 0; - this->stoppedTime = 0; this->reactionTime = 0; this->state = IDLE; @@ -22,7 +21,6 @@ bool ScStwTimer::start(double timeOfStart) { this->startTime = timeOfStart; this->stopTime = 0; - this->stoppedTime = 0; if(timeOfStart - QDateTime::currentMSecsSinceEpoch() > 0) { this->setState(STARTING); @@ -67,23 +65,18 @@ bool ScStwTimer::cancel() { } bool ScStwTimer::stop() { - return this->stop(ManualStop); + return this->stop(QDateTime::currentMSecsSinceEpoch()); } -bool ScStwTimer::stop(TimerState result) { - switch (result) { - case WON: - this->setState(WON); - return true; - case LOST: - this->setState(LOST); - return false; - default: - return false; - } +bool ScStwTimer::stop(double timeOfStop) { + return this->stop(ManualStop, timeOfStop); } bool ScStwTimer::stop(StopReason reason) { + return this->stop(reason, QDateTime::currentMSecsSinceEpoch()); +} + +bool ScStwTimer::stop(StopReason reason, double timeOfStop) { if(this->state != STARTING && this->state != RUNNING && this->state != WAITING){ return false; } @@ -95,18 +88,14 @@ bool ScStwTimer::stop(StopReason reason) { this->setState(CANCELLED); } else { + this->stopTime = timeOfStop; - this->stopTime = this->date->currentMSecsSinceEpoch(); - this->stoppedTime = this->stopTime - this->startTime; - - // trigger an external state refresh to set the state to either WON or LOST depending on the other timers values (see MainActivity::refreshTimerStates()) - emit this->stopRequested(); + // trigger an external state refresh to set the state to either WON or LOST depending on the other timers values (see ScStwRace::refreshTimerStates()) + this->setState(WAITING); } break; } case FailStop: { - this->stoppedTime = 0; - qDebug() << "+ [INFO][TIMER] False Start detected: " << "start Time: " << startTime << " reactionTime: " << reactionTime; this->setState(FAILED); @@ -119,10 +108,26 @@ bool ScStwTimer::stop(StopReason reason) { } } - qDebug() << "+ [INFO][TIMER] Stopped: " << "start Time: " << startTime << " stopTime: " << stopTime << " stoppedTime: " << stoppedTime << " reactionTime: " << reactionTime; + qDebug() << "+ [INFO][TIMER] Stopped: " << "start Time: " << startTime << " stopTime: " << stopTime << " stoppedTime: " << this->getCurrentTime() << " reactionTime: " << reactionTime; return true; } +bool ScStwTimer::setResult(TimerState result) { + if(this->state != WAITING) + return false; + + switch (result) { + case WON: + this->setState(WON); + return true; + case LOST: + this->setState(LOST); + return true; + default: + return false; + } +} + bool ScStwTimer::reset(){ if( this->state < WON || this->state == DISABLED ){ return false; @@ -130,11 +135,8 @@ bool ScStwTimer::reset(){ this->startTime = 0; this->stopTime = 0; - this->stoppedTime = 0; this->reactionTime = 0; - this->startPadTriggerTime = 0; - this->setState(IDLE); return true; } @@ -159,11 +161,15 @@ ScStwTimer::TimerState ScStwTimer::getState() { } double ScStwTimer::getCurrentTime() { - if(this->state == RUNNING){ - return this->date->currentMSecsSinceEpoch() - this->startTime; + switch (this->state) { + case RUNNING: + return QDateTime::currentMSecsSinceEpoch() - this->startTime; + default: { + if(this->state == WAITING || this->state == WON || this->state == LOST) + return this->stopTime - this->startTime; + else + return -1; } - else { - return this->stoppedTime; } }