further bugfixes, added documentation for timer and sound player
This commit is contained in:
parent
6fbc556991
commit
654b05dec9
8 changed files with 533 additions and 255 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -80,6 +80,7 @@ bool ScStwSoundPlayer::cancel(double volume) {
|
|||
|
||||
// stop playback
|
||||
this->audioOutput->stop();
|
||||
this->waitLoop->quit();
|
||||
|
||||
if(this->currentlyPlayingAction != 2)
|
||||
return true;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Reference in a new issue