further bugfixes, added documentation for timer and sound player

This commit is contained in:
Dorian Zedler 2020-04-15 19:21:32 +02:00
parent 6fbc556991
commit 654b05dec9
Signed by: dorian
GPG key ID: 989DE36109AFA354
8 changed files with 533 additions and 255 deletions

View file

@ -4,25 +4,25 @@
#include <QObject>
#include <QMap>
/**
* @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<QString, ScStw::BaseStationSetting> 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);

View file

@ -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<waitingRequest> 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 <major>.<minor>.<patch>
/*!
* \brief Function to get the current firmware version of the base station
* \see updateFirmware()
* \see isFirmwareUpToDate()
* \return Firmware version as string encoded as <major>.<minor>.<patch>
*/
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);
};

View file

@ -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<StartAction, QVariantMap> startActionSettings;

View file

@ -9,31 +9,94 @@
#include <QTimer>
#include <QDateTime>
/*!
* \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<int, QFile*> 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();
};

View file

@ -7,58 +7,263 @@
#include <QTimer>
#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);
};

View file

@ -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<int> timersWhichHaveWonIds;
QList<ScStwTimer *> 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;

View file

@ -80,6 +80,7 @@ bool ScStwSoundPlayer::cancel(double volume) {
// stop playback
this->audioOutput->stop();
this->waitLoop->quit();
if(this->currentlyPlayingAction != 2)
return true;

View file

@ -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;
}
}