2020-09-29 12:53:40 +02:00
|
|
|
/****************************************************************************
|
2020-06-07 14:43:47 +02:00
|
|
|
** ScStw Libraries
|
|
|
|
** Copyright (C) 2020 Itsblue development
|
|
|
|
**
|
|
|
|
** This program is free software: you can redistribute it and/or modify
|
|
|
|
** it under the terms of the GNU General Public License as published by
|
|
|
|
** the Free Software Foundation, either version 3 of the License, or
|
|
|
|
** (at your option) any later version.
|
|
|
|
**
|
|
|
|
** This program is distributed in the hope that it will be useful,
|
|
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
** GNU General Public License for more details.
|
|
|
|
**
|
|
|
|
** You should have received a copy of the GNU General Public License
|
|
|
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
****************************************************************************/
|
|
|
|
|
2020-04-11 23:41:34 +02:00
|
|
|
#include "../headers/scstwrace.h"
|
|
|
|
|
2020-10-09 15:18:56 +02:00
|
|
|
ScStwRace::ScStwRace(QObject* parent) : ScStwRace(nullptr, parent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-10-03 15:15:49 +02:00
|
|
|
ScStwRace::ScStwRace(ScStwSettings *settings, QObject *parent) : QObject(parent)
|
2020-04-11 23:41:34 +02:00
|
|
|
{
|
|
|
|
this->state = IDLE;
|
2020-10-03 17:45:54 +02:00
|
|
|
this->competitionMode = false;
|
2020-10-09 15:18:56 +02:00
|
|
|
this->autoRefreshTimerText = false;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
|
|
|
// configure the loop that waits for the sound effect to finish
|
|
|
|
this->soundPlayer = new ScStwSoundPlayer();
|
|
|
|
|
|
|
|
// configure timer that handles the delay between the start commands
|
2020-10-02 14:16:24 +02:00
|
|
|
this->startDelayTimer = new QTimer(this);
|
|
|
|
startDelayTimer->setSingleShot(true);
|
|
|
|
this->startWaitLoop = new QEventLoop(this);
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-10-02 14:16:24 +02:00
|
|
|
connect(this->startDelayTimer, &QTimer::timeout, this->startWaitLoop, &QEventLoop::quit);
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-10-02 19:53:07 +02:00
|
|
|
connect(this, &ScStwRace::currentStartDelayChanged, this, &ScStwRace::detailsChanged);
|
|
|
|
connect(this, &ScStwRace::timersChanged, this, &ScStwRace::detailsChanged);
|
2020-10-03 11:10:15 +02:00
|
|
|
connect(this, &ScStwRace::stateChanged, this, &ScStwRace::detailsChanged);
|
2020-10-02 19:53:07 +02:00
|
|
|
|
2020-10-03 15:15:49 +02:00
|
|
|
// init settings
|
2020-10-09 15:18:56 +02:00
|
|
|
this->setSettings(settings);
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------
|
|
|
|
// --- Main Functionality ---
|
|
|
|
// --------------------------
|
|
|
|
|
2020-10-02 15:23:30 +02:00
|
|
|
ScStw::StatusCode ScStwRace::start(bool asyncronous) {
|
2020-10-02 14:16:24 +02:00
|
|
|
if(this->state == WAITING) {
|
2020-10-03 11:10:15 +02:00
|
|
|
if(this->getIsReadyForNextState()) {
|
2020-10-02 14:16:24 +02:00
|
|
|
this->startWaitLoop->exit(LoopManualExit);
|
|
|
|
return ScStw::Success;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return ScStw::TimersNotReadyError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(this->state != IDLE) {
|
2020-05-10 17:39:16 +02:00
|
|
|
return ScStw::CurrentStateNotVaildForOperationError;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
2020-10-03 17:45:54 +02:00
|
|
|
this->refreshCompetitionMode();
|
|
|
|
|
2020-10-03 11:10:15 +02:00
|
|
|
if(!this->getIsReadyForNextState())
|
2020-10-02 14:16:24 +02:00
|
|
|
return ScStw::TimersNotReadyError;
|
2020-09-29 12:53:40 +02:00
|
|
|
|
|
|
|
this->setState(PREPAIRING);
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-04-19 13:09:24 +02:00
|
|
|
if(asyncronous) {
|
2020-09-29 12:53:40 +02:00
|
|
|
QTimer::singleShot(1, [=](){this->playSoundsAndStartTimers();});
|
2020-04-19 13:09:24 +02:00
|
|
|
}
|
|
|
|
else
|
2020-09-29 12:53:40 +02:00
|
|
|
this->playSoundsAndStartTimers();
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-04-19 22:39:33 +02:00
|
|
|
return ScStw::Success;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
2020-10-02 15:23:30 +02:00
|
|
|
ScStw::StatusCode ScStwRace::stop() {
|
2020-04-11 23:41:34 +02:00
|
|
|
if(this->state != RUNNING && this->state != STARTING) {
|
2020-05-10 17:39:16 +02:00
|
|
|
return ScStw::CurrentStateNotVaildForOperationError;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
2020-10-03 15:15:49 +02:00
|
|
|
if(this->competitionMode)
|
|
|
|
return this->cancel();
|
|
|
|
|
2020-10-03 17:01:05 +02:00
|
|
|
qDebug() << "[INFO][RACE] stopping race";
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-04-15 19:21:32 +02:00
|
|
|
double timeOfStop = QDateTime::currentMSecsSinceEpoch();
|
|
|
|
|
2020-10-02 15:23:30 +02:00
|
|
|
ScStw::StatusCode returnCode = ScStw::Success;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
|
|
|
foreach(ScStwTimer *speedTimer, this->timers){
|
2020-04-15 19:21:32 +02:00
|
|
|
if(!speedTimer->stop(timeOfStop) && speedTimer->getState() != ScStwTimer::DISABLED){
|
2020-04-19 22:39:33 +02:00
|
|
|
returnCode = ScStw::InternalErrorTimerOperationFailed;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-19 22:39:33 +02:00
|
|
|
if(returnCode == ScStw::Success) {
|
2020-04-11 23:41:34 +02:00
|
|
|
this->setState(STOPPED);
|
|
|
|
}
|
|
|
|
|
|
|
|
return returnCode;
|
|
|
|
}
|
|
|
|
|
2020-10-02 15:23:30 +02:00
|
|
|
ScStw::StatusCode ScStwRace::reset() {
|
2020-09-29 15:21:00 +02:00
|
|
|
if(this->state != STOPPED && this->state != INCIDENT) {
|
2020-05-10 17:39:16 +02:00
|
|
|
return ScStw::CurrentStateNotVaildForOperationError;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
2020-10-03 17:01:05 +02:00
|
|
|
qDebug() << "[INFO][RACE] resetting race";
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-10-07 13:02:03 +02:00
|
|
|
if(this->competitionMode) {
|
|
|
|
for(int i = 0; i < this->timers.length(); i++)
|
|
|
|
this->setTimerDisabled(i, false);
|
|
|
|
}
|
|
|
|
|
2020-10-02 15:23:30 +02:00
|
|
|
ScStw::StatusCode returnCode = ScStw::Success;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-10-06 13:58:42 +02:00
|
|
|
foreach(ScStwTimer *timer, this->timers){
|
|
|
|
if(!timer->reset() && timer->getState() != ScStwTimer::DISABLED && timer->getState() != ScStwTimer::IDLE) {
|
2020-04-19 22:39:33 +02:00
|
|
|
returnCode = ScStw::InternalErrorTimerOperationFailed;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-01 01:54:39 +02:00
|
|
|
if(returnCode == ScStw::Success)
|
2020-04-11 23:41:34 +02:00
|
|
|
this->setState(IDLE);
|
|
|
|
|
2020-10-02 17:05:52 +02:00
|
|
|
this->soundPlayer->cancel();
|
|
|
|
|
2020-04-11 23:41:34 +02:00
|
|
|
return returnCode;
|
|
|
|
}
|
|
|
|
|
2020-10-02 15:23:30 +02:00
|
|
|
ScStw::StatusCode ScStwRace::cancel() {
|
2020-09-29 15:21:00 +02:00
|
|
|
if(this->state != PREPAIRING && this->state != WAITING && this->state != STARTING && this->state != RUNNING)
|
2020-05-10 17:39:16 +02:00
|
|
|
return ScStw::CurrentStateNotVaildForOperationError;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-04-19 22:39:33 +02:00
|
|
|
qDebug() << "[INFO][RACE] cancelling race";
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-10-02 15:23:30 +02:00
|
|
|
ScStw::StatusCode returnCode = ScStw::Success;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-04-19 22:39:33 +02:00
|
|
|
foreach(ScStwTimer *timer, this->timers){
|
|
|
|
if(!timer->cancel() && timer->getState() != ScStwTimer::DISABLED)
|
|
|
|
returnCode = ScStw::InternalErrorTimerOperationFailed;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
2020-10-01 01:54:39 +02:00
|
|
|
if(returnCode != ScStw::Success)
|
|
|
|
return returnCode;
|
|
|
|
|
|
|
|
this->setState(STOPPED);
|
|
|
|
|
2020-10-02 14:16:24 +02:00
|
|
|
this->startWaitLoop->exit(LoopCancelExit);
|
2020-10-03 11:10:15 +02:00
|
|
|
this->soundPlayer->cancel();
|
2020-10-02 14:16:24 +02:00
|
|
|
this->startDelayTimer->stop();
|
2020-10-01 01:54:39 +02:00
|
|
|
|
2020-10-02 17:05:52 +02:00
|
|
|
emit this->currentStartDelayChanged();
|
|
|
|
|
2020-04-19 22:39:33 +02:00
|
|
|
return returnCode;
|
|
|
|
}
|
|
|
|
|
2020-10-05 22:21:33 +02:00
|
|
|
ScStw::StatusCode ScStwRace::setTimerDisabled(int timerId, bool disabled) {
|
2020-10-06 13:58:42 +02:00
|
|
|
if(timerId < 0 || timerId - 1 > this->timers.length())
|
|
|
|
return ScStw::ItemNotFoundError;
|
|
|
|
|
|
|
|
return this->setTimerDisabled(this->timers[timerId], disabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
ScStw::StatusCode ScStwRace::setTimerDisabled(ScStwTimer* timer, bool disabled) {
|
|
|
|
qDebug() << "[INFO][RACE] Setting timer "<< timer->getLetter() << " to disabled: " << disabled << " this state: " << this->state;
|
2020-10-05 22:21:33 +02:00
|
|
|
if(this->state != IDLE && this->state != WAITING)
|
|
|
|
return ScStw::CurrentStateNotVaildForOperationError;
|
|
|
|
|
2020-10-06 13:58:42 +02:00
|
|
|
if(!this->timers.contains(timer))
|
2020-10-05 22:21:33 +02:00
|
|
|
return ScStw::ItemNotFoundError;
|
|
|
|
|
2020-10-06 13:58:42 +02:00
|
|
|
int enabledTimerCount = 0;
|
|
|
|
|
|
|
|
foreach(ScStwTimer *timer, this->timers) {
|
|
|
|
if(timer->getState() != ScStwTimer::DISABLED)
|
|
|
|
enabledTimerCount ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(disabled && enabledTimerCount <= 1)
|
|
|
|
return ScStw::LastTimerCannotBeDisabledError;
|
|
|
|
|
|
|
|
qDebug() << "[INFO][RACE] Setting timer "<< timer->getLetter() << " to disabled: " << disabled;
|
|
|
|
|
|
|
|
timer->setDisabled(disabled);
|
2020-10-05 22:21:33 +02:00
|
|
|
|
2020-10-07 13:02:03 +02:00
|
|
|
emit this->timersChanged();
|
|
|
|
|
2020-10-05 22:21:33 +02:00
|
|
|
return ScStw::Success;
|
2020-10-06 13:58:42 +02:00
|
|
|
|
2020-10-05 22:21:33 +02:00
|
|
|
}
|
|
|
|
|
2020-10-04 01:47:50 +02:00
|
|
|
void ScStwRace::handleTimerReadyStateChange(ScStwTimer::ReadyState readyState) {
|
|
|
|
|
|
|
|
if(!this->competitionMode || this->state == IDLE || this->state == STOPPED || this->state == INCIDENT )
|
|
|
|
return;
|
|
|
|
|
2020-10-04 15:11:07 +02:00
|
|
|
qDebug() << "Some ready state changed: " << readyState;
|
|
|
|
|
2020-10-04 01:47:50 +02:00
|
|
|
// cancel as a technical incident if extensions are disconnected or run low on battery
|
2020-10-06 14:09:23 +02:00
|
|
|
if(readyState == ScStwTimer::ExtensionIsNotConnected || readyState == ScStwTimer::ExtensionBatteryIsCritical) {
|
2020-10-04 01:47:50 +02:00
|
|
|
this->technicalIncident();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// only continue if the current state is waiting
|
|
|
|
if(this->state == WAITING) {
|
|
|
|
this->startWaitLoop->exit(ScStwRace::LoopReadyStateChangeExit);
|
|
|
|
emit this->timersChanged();
|
|
|
|
}
|
2020-10-03 15:15:49 +02:00
|
|
|
}
|
|
|
|
|
2020-09-29 12:53:40 +02:00
|
|
|
bool ScStwRace::playSoundsAndStartTimers() {
|
|
|
|
if(this->state != PREPAIRING)
|
2020-04-11 23:41:34 +02:00
|
|
|
return true;
|
|
|
|
|
2020-09-29 12:53:40 +02:00
|
|
|
// The check if all timers are ready has already happened at this point
|
2020-10-03 18:33:17 +02:00
|
|
|
ScStwSoundPlayer::PlayResult res = this->doDelayAndSoundOfCurrentStartState();
|
|
|
|
|
|
|
|
if(res == ScStwSoundPlayer::Error) {
|
|
|
|
qDebug() << "[ERROR][RACE] error playing at your marks sound";
|
|
|
|
this->technicalIncident();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if(res == ScStwSoundPlayer::Cancelled) {
|
2020-09-29 12:53:40 +02:00
|
|
|
return false;
|
2020-10-03 18:33:17 +02:00
|
|
|
}
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-09-29 15:21:00 +02:00
|
|
|
// check if the start was cancelled
|
2020-10-01 01:54:39 +02:00
|
|
|
if(!this->isStarting())
|
2020-09-29 15:21:00 +02:00
|
|
|
return false;
|
|
|
|
|
2020-09-29 12:53:40 +02:00
|
|
|
this->setState(WAITING);
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-09-29 12:53:40 +02:00
|
|
|
// do climber readiness tests
|
2020-10-02 14:16:24 +02:00
|
|
|
|
2020-09-29 12:53:40 +02:00
|
|
|
// if the automatic ready tone is enabled, wait for the climbers to become ready
|
2020-10-03 17:45:54 +02:00
|
|
|
if(this->competitionMode && this->getSoundEnabledSetting(ScStwSoundPlayer::Ready)) {
|
2020-09-29 12:53:40 +02:00
|
|
|
|
2020-09-29 15:21:00 +02:00
|
|
|
qDebug() << "[RACE][INFO] Now waiting for climbers";
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-09-29 15:21:00 +02:00
|
|
|
// get delay
|
|
|
|
int minimumReadyDelay = 1000;
|
2020-10-03 17:45:54 +02:00
|
|
|
if(this->getSoundDelaySetting(ScStwSoundPlayer::Ready) > 1000)
|
|
|
|
minimumReadyDelay = this->getSoundDelaySetting(ScStwSoundPlayer::Ready);
|
2020-10-02 14:16:24 +02:00
|
|
|
|
|
|
|
this->startDelayTimer->setInterval(minimumReadyDelay);
|
2020-09-29 12:53:40 +02:00
|
|
|
|
2020-10-02 17:05:52 +02:00
|
|
|
// wait for all climbers to be ready for the ReadyActionDelay, but at least one second continuosly
|
|
|
|
// the climber ready wait loop will also quit, if the climber steps of the pad
|
|
|
|
// -> wait for both climbers to stand on the pad for at least one second
|
|
|
|
|
2020-10-02 14:16:24 +02:00
|
|
|
bool timerTriggered = true;
|
|
|
|
do {
|
|
|
|
|
2020-10-03 11:10:15 +02:00
|
|
|
if(!this->getIsReadyForNextState()) {
|
2020-10-02 14:16:24 +02:00
|
|
|
this->startDelayTimer->stop();
|
|
|
|
timerTriggered = false;
|
|
|
|
}
|
|
|
|
else if(this->startDelayTimer->isActive()) {
|
|
|
|
timerTriggered = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
this->startDelayTimer->stop();
|
2020-12-28 16:17:38 +01:00
|
|
|
this->startDelayTimer->setInterval(minimumReadyDelay);
|
2020-10-02 14:16:24 +02:00
|
|
|
this->startDelayTimer->start();
|
2020-10-02 15:20:11 +02:00
|
|
|
timerTriggered = true;
|
2020-09-29 15:21:00 +02:00
|
|
|
}
|
2020-09-29 12:53:40 +02:00
|
|
|
|
2020-10-02 14:16:24 +02:00
|
|
|
emit this->currentStartDelayChanged();
|
2020-09-29 12:53:40 +02:00
|
|
|
|
2020-10-02 14:16:24 +02:00
|
|
|
int loopExitCode = this->startWaitLoop->exec();
|
|
|
|
|
|
|
|
switch (loopExitCode) {
|
|
|
|
case LoopAutomaticExit:
|
|
|
|
break;
|
|
|
|
case LoopManualExit:
|
|
|
|
// prevent manual stop
|
|
|
|
timerTriggered = false;
|
|
|
|
break;
|
|
|
|
case LoopCancelExit:
|
2020-09-29 15:21:00 +02:00
|
|
|
return false;
|
2020-10-02 14:16:24 +02:00
|
|
|
}
|
|
|
|
|
2020-10-02 17:05:52 +02:00
|
|
|
//qDebug() << "At end of loop: remaining time: " << this->startDelayTimer->remainingTime() << " timer triggered: " << timerTriggered << " ready for next state: " << this->isReadyForNextState();
|
2020-10-02 15:20:11 +02:00
|
|
|
|
2020-10-03 11:10:15 +02:00
|
|
|
} while(this->startDelayTimer->remainingTime() > 0 || !timerTriggered || !this->getIsReadyForNextState());
|
2020-09-29 12:53:40 +02:00
|
|
|
|
2020-10-03 17:01:05 +02:00
|
|
|
qDebug() << "[DEBUG][RACE] Wait finished, starting now!";
|
2020-09-29 15:21:00 +02:00
|
|
|
|
|
|
|
// play ready tone
|
2020-10-03 18:33:17 +02:00
|
|
|
ScStwSoundPlayer::PlayResult res = this->soundPlayer->play(ScStwSoundPlayer::Ready, this->getSoundVolume());
|
|
|
|
|
|
|
|
if(res == ScStwSoundPlayer::Error) {
|
|
|
|
qDebug() << "[ERROR][RACE] error playing ready sound";
|
2020-10-03 15:15:49 +02:00
|
|
|
this->technicalIncident();
|
2020-09-29 15:21:00 +02:00
|
|
|
return false;
|
2020-10-02 17:05:52 +02:00
|
|
|
}
|
2020-10-03 18:33:17 +02:00
|
|
|
else if(res == ScStwSoundPlayer::Cancelled) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
2020-10-02 15:20:11 +02:00
|
|
|
else if(this->competitionMode) {
|
2020-10-02 14:16:24 +02:00
|
|
|
// wait for climbers and manual start
|
|
|
|
int loopExitCode;
|
|
|
|
do {
|
|
|
|
loopExitCode = this->startWaitLoop->exec();
|
|
|
|
|
|
|
|
if(loopExitCode == LoopCancelExit)
|
|
|
|
return false;
|
|
|
|
|
2020-10-03 11:10:15 +02:00
|
|
|
} while(loopExitCode != LoopManualExit || !this->getIsReadyForNextState());
|
2020-10-02 14:16:24 +02:00
|
|
|
}
|
2020-10-02 15:20:11 +02:00
|
|
|
else {
|
2020-10-03 18:33:17 +02:00
|
|
|
ScStwSoundPlayer::PlayResult res = this->doDelayAndSoundOfCurrentStartState();
|
|
|
|
|
|
|
|
if(res == ScStwSoundPlayer::Error) {
|
|
|
|
qDebug() << "[ERROR][RACE] error playing ready sound";
|
|
|
|
this->technicalIncident();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if(res == ScStwSoundPlayer::Cancelled) {
|
2020-10-02 15:20:11 +02:00
|
|
|
return false;
|
2020-10-02 17:05:52 +02:00
|
|
|
}
|
2020-10-02 15:20:11 +02:00
|
|
|
}
|
|
|
|
|
2020-09-29 12:53:40 +02:00
|
|
|
// enter starting state
|
|
|
|
this->setState(STARTING);
|
|
|
|
|
|
|
|
// play start tone
|
|
|
|
double timeOfSoundPlaybackStart;
|
2020-10-03 18:33:17 +02:00
|
|
|
res = this->doDelayAndSoundOfCurrentStartState(&timeOfSoundPlaybackStart);
|
|
|
|
|
|
|
|
if(res == ScStwSoundPlayer::Error) {
|
|
|
|
qDebug() << "[ERROR][RACE] error playing at your marks sound";
|
2020-10-03 15:15:49 +02:00
|
|
|
this->technicalIncident();
|
2020-10-02 17:05:52 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-10-03 18:33:17 +02:00
|
|
|
else if(res == ScStwSoundPlayer::Cancelled) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-09-29 12:53:40 +02:00
|
|
|
|
2020-04-11 23:41:34 +02:00
|
|
|
// perform start
|
|
|
|
|
|
|
|
// start all timers
|
|
|
|
bool startOk = true;
|
|
|
|
foreach(ScStwTimer *timer, this->timers){
|
|
|
|
if(!timer->start(timeOfSoundPlaybackStart + 3100) && timer->getState() != ScStwTimer::DISABLED){
|
|
|
|
startOk = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!startOk) {
|
2020-10-03 17:01:05 +02:00
|
|
|
qDebug() << "[ERROR][RACE] error staring all timers";
|
2020-10-03 15:15:49 +02:00
|
|
|
this->technicalIncident();
|
2020-04-11 23:41:34 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-10-03 18:33:17 +02:00
|
|
|
if(this->soundPlayer->waitForSoundFinish() == ScStwSoundPlayer::Error) {
|
2020-10-03 17:01:05 +02:00
|
|
|
qDebug() << "[ERROR][RACE] start sound wait error";
|
2020-10-03 15:15:49 +02:00
|
|
|
this->technicalIncident();
|
2020-04-11 23:41:34 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if a false start occured
|
2020-10-01 01:54:39 +02:00
|
|
|
if(!this->isStarting())
|
2020-10-03 18:33:17 +02:00
|
|
|
return false;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
|
|
|
this->setState(RUNNING);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-10-03 18:33:17 +02:00
|
|
|
ScStwSoundPlayer::PlayResult ScStwRace::doDelayAndSoundOfCurrentStartState(double *timeOfSoundPlaybackStart) {
|
2020-10-03 17:45:54 +02:00
|
|
|
ScStwSoundPlayer::StartSound sound = this->getSoundForState(this->state);
|
2020-10-01 01:54:39 +02:00
|
|
|
|
2020-10-03 17:45:54 +02:00
|
|
|
if(this->getSoundEnabledSetting(sound)) {
|
|
|
|
if(sound != ScStwSoundPlayer::Start && this->getSoundDelaySetting(sound) > 0) {
|
2020-09-29 12:53:40 +02:00
|
|
|
// perform the delay before the start
|
|
|
|
|
|
|
|
// get delay
|
2020-10-03 17:45:54 +02:00
|
|
|
int thisSoundDelay = this->getSoundDelaySetting(sound);
|
2020-09-29 12:53:40 +02:00
|
|
|
|
|
|
|
// perform next action
|
2020-10-02 14:16:24 +02:00
|
|
|
if(thisSoundDelay > 0) {
|
|
|
|
this->startDelayTimer->setInterval(thisSoundDelay);
|
|
|
|
this->startDelayTimer->start();
|
|
|
|
|
|
|
|
emit this->currentStartDelayChanged();
|
|
|
|
|
|
|
|
if(this->startWaitLoop->exec() == LoopCancelExit)
|
2020-10-03 18:33:17 +02:00
|
|
|
return ScStwSoundPlayer::Cancelled;
|
2020-09-29 12:53:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-01 01:54:39 +02:00
|
|
|
if(!this->isStarting())
|
2020-10-03 18:33:17 +02:00
|
|
|
return ScStwSoundPlayer::Error;
|
2020-10-01 01:54:39 +02:00
|
|
|
|
2020-10-03 18:33:17 +02:00
|
|
|
|
|
|
|
return this->soundPlayer->play(sound, this->getSoundVolume(), timeOfSoundPlaybackStart);
|
2020-09-29 12:53:40 +02:00
|
|
|
}
|
|
|
|
|
2020-10-03 18:33:17 +02:00
|
|
|
return ScStwSoundPlayer::Success;
|
2020-09-29 12:53:40 +02:00
|
|
|
}
|
|
|
|
|
2020-04-11 23:41:34 +02:00
|
|
|
void ScStwRace::setState(RaceState newState) {
|
|
|
|
if(newState != this->state) {
|
2020-04-19 13:09:24 +02:00
|
|
|
qDebug() << "[INFO][RACE] state changed: " << newState;
|
2020-04-11 23:41:34 +02:00
|
|
|
this->state = newState;
|
|
|
|
emit this->stateChanged(newState);
|
|
|
|
|
|
|
|
if(this->state == IDLE) {
|
2020-10-03 17:45:54 +02:00
|
|
|
this->refreshCompetitionMode();
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-04 16:31:49 +02:00
|
|
|
void ScStwRace::handleTimerStateChange(ScStwTimer::TimerState newState) {
|
|
|
|
if(
|
|
|
|
newState == ScStwTimer::WON ||
|
|
|
|
newState == ScStwTimer::LOST ||
|
|
|
|
newState == ScStwTimer::WILDCARD ||
|
2020-10-04 18:11:44 +02:00
|
|
|
newState == ScStwTimer::FAILED ||
|
|
|
|
this->state == INCIDENT
|
2020-10-04 16:31:49 +02:00
|
|
|
)
|
|
|
|
return;
|
|
|
|
|
2020-10-04 15:11:07 +02:00
|
|
|
qDebug() << "[INFO][MAIN] handling timer state change";
|
2020-04-11 23:41:34 +02:00
|
|
|
|
|
|
|
// check if the race is over
|
2020-04-15 19:21:32 +02:00
|
|
|
bool raceIsOver = true;
|
2020-10-04 18:11:44 +02:00
|
|
|
int incidentCount = 0;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
|
|
|
foreach(ScStwTimer * timer, this->timers){
|
2020-10-04 16:04:22 +02:00
|
|
|
if(timer->getState() < ScStwTimer::WAITING){
|
2020-04-11 23:41:34 +02:00
|
|
|
// if the timer is not in stoped state
|
2020-04-15 19:21:32 +02:00
|
|
|
raceIsOver = false;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
2020-10-04 15:11:07 +02:00
|
|
|
else if(timer->getState() == ScStwTimer::WAITING)
|
2020-04-15 19:21:32 +02:00
|
|
|
this->handleTimerStop();
|
2020-10-04 16:04:22 +02:00
|
|
|
else if (timer->getState() == ScStwTimer::FAILING)
|
2020-04-19 22:39:33 +02:00
|
|
|
this->handleFalseStart();
|
2020-10-04 15:11:07 +02:00
|
|
|
else if (timer->getState() == ScStwTimer::INCIDENT)
|
2020-10-04 18:11:44 +02:00
|
|
|
incidentCount ++;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
2020-10-04 18:11:44 +02:00
|
|
|
if(incidentCount == this->timers.length()) {
|
|
|
|
this->setState(INCIDENT);
|
|
|
|
this->soundPlayer->play(ScStwSoundPlayer::FalseStart, this->getSoundVolume());
|
|
|
|
}
|
|
|
|
else if(raceIsOver)
|
2020-04-11 23:41:34 +02:00
|
|
|
this->setState(STOPPED);
|
|
|
|
}
|
|
|
|
|
2020-10-04 16:04:22 +02:00
|
|
|
void ScStwRace::handleTimerStop() {
|
|
|
|
if(this->state != RUNNING)
|
|
|
|
return;
|
|
|
|
|
2020-10-04 17:25:30 +02:00
|
|
|
qDebug() << "[INFO][RACE] Got a TIMER STOP";
|
|
|
|
|
2020-10-04 16:04:22 +02:00
|
|
|
// find out which timer has won
|
|
|
|
double lowestStoppedTime = -1;
|
|
|
|
|
|
|
|
// iterate through all timers and find the lowest time that was stopped
|
|
|
|
foreach(ScStwTimer * timer, this->timers) {
|
2020-10-06 13:58:42 +02:00
|
|
|
qDebug() << "Current stopped time is: " << timer->getCurrentTime();
|
|
|
|
if(!timer->isRunning() && !timer->isDisabled() && (timer->getCurrentTime() <= lowestStoppedTime || lowestStoppedTime < 0)) {
|
2020-10-04 16:04:22 +02:00
|
|
|
// this is the timer with the lowest stopped time
|
|
|
|
lowestStoppedTime = timer->getCurrentTime();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-06 13:58:42 +02:00
|
|
|
qDebug() << "LOWEST Stop time is: " << lowestStoppedTime;
|
|
|
|
|
2020-10-04 16:04:22 +02:00
|
|
|
// append the timer(s) with the lowest stopped time to the winner list
|
|
|
|
foreach(ScStwTimer * timer, this->timers) {
|
2020-10-04 16:31:49 +02:00
|
|
|
if(timer->getCurrentTime() <= lowestStoppedTime)
|
2020-10-04 16:04:22 +02:00
|
|
|
// this is the timer with the lowest stopped time
|
|
|
|
timer->setResult(ScStwTimer::WON);
|
2020-10-04 16:31:49 +02:00
|
|
|
else
|
2020-10-04 16:04:22 +02:00
|
|
|
timer->setResult(ScStwTimer::LOST);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScStwRace::handleFalseStart() {
|
|
|
|
if(this->state != STARTING && this->state != RUNNING && this->state != STOPPED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
qDebug() << "[INFO][RACE] Got a FALSE START";
|
|
|
|
|
|
|
|
// set lowest to a value that is impossible to reach (start tone is only 3100ms long)
|
2020-10-04 16:31:49 +02:00
|
|
|
double lowestReactionTime = -4000;
|
2020-10-04 16:04:22 +02:00
|
|
|
|
|
|
|
// iterate through all timers and find the lowest reactiontime that was stopped
|
|
|
|
foreach(ScStwTimer *timer, this->timers) {
|
|
|
|
if(
|
|
|
|
timer->getState() >= ScStwTimer::FAILING &&
|
|
|
|
timer->getState() <= ScStwTimer::FAILED &&
|
2020-10-06 13:58:42 +02:00
|
|
|
!timer->isDisabled() &&
|
2020-10-04 16:04:22 +02:00
|
|
|
(
|
|
|
|
timer->getReactionTime() < lowestReactionTime ||
|
|
|
|
lowestReactionTime == -4000
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
lowestReactionTime = timer->getReactionTime();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(lowestReactionTime == -4000)
|
|
|
|
// no timer has failed
|
|
|
|
return;
|
|
|
|
|
2020-10-04 16:31:49 +02:00
|
|
|
qDebug() << "LOWEST reaction time is: " << lowestReactionTime;
|
|
|
|
|
2020-10-04 16:04:22 +02:00
|
|
|
// find out which timer(s) have lost and which get a wildcard
|
|
|
|
foreach(ScStwTimer * timer, this->timers) {
|
2020-10-04 18:11:44 +02:00
|
|
|
qDebug() << "THIS TIMERS reaction time is: " << timer->getReactionTime();
|
2020-10-04 16:04:22 +02:00
|
|
|
if(
|
|
|
|
timer->getState() >= ScStwTimer::FAILING &&
|
|
|
|
timer->getState() <= ScStwTimer::FAILED &&
|
2020-10-06 13:58:42 +02:00
|
|
|
!timer->isDisabled() &&
|
2020-10-04 18:11:44 +02:00
|
|
|
timer->getReactionTime() <= lowestReactionTime
|
2020-10-04 16:04:22 +02:00
|
|
|
) {
|
|
|
|
// this is the timer with the lowest stopped time
|
|
|
|
timer->setResult(ScStwTimer::FAILED);
|
2020-10-04 16:31:49 +02:00
|
|
|
qDebug() << "FOUND BAD TIMER";
|
2020-10-04 16:04:22 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
timer->setResult(ScStwTimer::WILDCARD);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-04 18:11:44 +02:00
|
|
|
if(this->state != STOPPED) {
|
|
|
|
this->setState(STOPPED);
|
|
|
|
this->soundPlayer->cancel();
|
|
|
|
this->soundPlayer->play(ScStwSoundPlayer::FalseStart, this->getSoundVolume());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScStwRace::technicalIncident() {
|
|
|
|
|
|
|
|
bool raceIsRunning = this->state == RUNNING;
|
|
|
|
|
|
|
|
qDebug() << "[ERROR][RACE] Got a technical incident, state is: " << this->state;
|
|
|
|
|
|
|
|
if(!raceIsRunning) {
|
|
|
|
this->startWaitLoop->exit(LoopCancelExit);
|
|
|
|
|
|
|
|
if(this->state == STARTING) {
|
|
|
|
this->soundPlayer->cancel();
|
|
|
|
this->soundPlayer->play(ScStwSoundPlayer::FalseStart, this->getSoundVolume());
|
|
|
|
}
|
|
|
|
|
|
|
|
this->setState(INCIDENT);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach(ScStwTimer *timer, this->timers){
|
2020-10-06 14:09:23 +02:00
|
|
|
if(raceIsRunning && timer->getReadyState() == ScStwTimer::ExtensionBatteryIsCritical)
|
2020-10-04 18:11:44 +02:00
|
|
|
continue;
|
2020-10-06 14:09:23 +02:00
|
|
|
else if(timer->getReadyState() == ScStwTimer::ExtensionIsNotConnected || timer->getReadyState() == ScStwTimer::ExtensionBatteryIsCritical)
|
2020-10-04 18:11:44 +02:00
|
|
|
timer->technicalIncident();
|
|
|
|
else if(!raceIsRunning)
|
|
|
|
timer->setState(ScStwTimer::CANCELLED);
|
|
|
|
}
|
2020-10-04 16:04:22 +02:00
|
|
|
}
|
|
|
|
|
2020-04-11 23:41:34 +02:00
|
|
|
// ------------------------
|
|
|
|
// --- helper functions ---
|
|
|
|
// ------------------------
|
|
|
|
|
2020-10-03 11:10:15 +02:00
|
|
|
bool ScStwRace::getIsReadyForNextState() {
|
2020-10-02 15:20:11 +02:00
|
|
|
if(!this->competitionMode) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-10-02 14:16:24 +02:00
|
|
|
switch (this->state) {
|
|
|
|
case IDLE: {
|
|
|
|
foreach (ScStwTimer *timer, this->timers) {
|
|
|
|
if(timer->getState() == ScStwTimer::DISABLED)
|
|
|
|
continue;
|
|
|
|
|
2020-10-06 14:09:23 +02:00
|
|
|
if(timer->getReadyState() == ScStwTimer::ExtensionIsNotConnected || timer->getReadyState() == ScStwTimer::ExtensionBatteryIsCritical) {
|
2020-10-02 14:16:24 +02:00
|
|
|
|
2020-10-03 15:15:49 +02:00
|
|
|
this->technicalIncident();
|
2020-10-02 14:16:24 +02:00
|
|
|
|
|
|
|
qDebug() << "[ERROR][RACE] Could not start due to not-ready timers";
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case WAITING: {
|
|
|
|
foreach (ScStwTimer *timer, this->timers) {
|
2020-10-06 13:58:42 +02:00
|
|
|
if(timer->getReadyState() != ScStwTimer::IsReady && timer->getReadyState() != ScStwTimer::IsDisabled)
|
2020-10-02 14:16:24 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-04-11 23:41:34 +02:00
|
|
|
/**
|
|
|
|
* @brief ScStwRace::handleTimerEnable function to enable timers at the right moment to prevent them from bricking the state machine
|
|
|
|
* @param {ScStwExtensionControlledTimer*} timer timer to be enabled
|
|
|
|
*/
|
2020-08-09 13:29:37 +02:00
|
|
|
void ScStwRace::handleTimerWantsToBeDisabledChange(ScStwTimer* timer, bool wantsToBeDisabled) {
|
2020-10-06 13:58:42 +02:00
|
|
|
qDebug() << "Handling timer wants to be disabled";
|
2020-10-02 14:16:24 +02:00
|
|
|
if(this->competitionMode)
|
2020-08-09 13:29:37 +02:00
|
|
|
return;
|
|
|
|
|
2020-04-11 23:41:34 +02:00
|
|
|
if(this->state == IDLE) {
|
2020-10-06 13:58:42 +02:00
|
|
|
|
|
|
|
qDebug() << "Handling timer wants to be disabled: " << wantsToBeDisabled;
|
|
|
|
this->setTimerDisabled(timer, wantsToBeDisabled);
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
2020-08-09 13:29:37 +02:00
|
|
|
}
|
|
|
|
|
2020-10-03 17:45:54 +02:00
|
|
|
void ScStwRace::refreshCompetitionMode() {
|
2020-08-09 13:29:37 +02:00
|
|
|
if(this->state != IDLE)
|
|
|
|
return;
|
|
|
|
|
2020-10-09 15:18:56 +02:00
|
|
|
bool currentCompetitionMode = false;
|
|
|
|
|
|
|
|
if(this->settings != nullptr)
|
|
|
|
currentCompetitionMode = this->settings->readSetting(ScStwSettings::CompetitionModeSetting).toBool();
|
2020-10-03 17:45:54 +02:00
|
|
|
|
|
|
|
if(this->competitionMode != currentCompetitionMode) {
|
|
|
|
|
|
|
|
qDebug() << "[INFO][RACE] Setting competition mode to " << currentCompetitionMode;
|
|
|
|
|
|
|
|
this->competitionMode = currentCompetitionMode;
|
2020-10-06 13:58:42 +02:00
|
|
|
}
|
2020-10-03 17:45:54 +02:00
|
|
|
|
2020-10-06 13:58:42 +02:00
|
|
|
if(this->competitionMode) {
|
|
|
|
foreach (ScStwTimer *timer, this->timers) {
|
|
|
|
timer->setDisabled(false);
|
2020-10-03 17:45:54 +02:00
|
|
|
}
|
2020-10-06 13:58:42 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
foreach(ScStwTimer* timer, this->timers) {
|
|
|
|
if(
|
|
|
|
(timer->getWantsToBeDisabled() && timer->getState() != ScStwTimer::DISABLED) ||
|
|
|
|
(!timer->getWantsToBeDisabled() && timer->getState() == ScStwTimer::DISABLED)
|
|
|
|
)
|
|
|
|
this->handleTimerWantsToBeDisabledChange(timer, timer->getWantsToBeDisabled());
|
2020-10-03 17:45:54 +02:00
|
|
|
}
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-03 11:44:56 +02:00
|
|
|
QVariantMap ScStwRace::getCurrentStartDelay() {
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-10-03 11:44:56 +02:00
|
|
|
QVariantMap currentStartDelay = {
|
|
|
|
{"total", -1.0},
|
|
|
|
{"progress", -1.0}
|
|
|
|
};
|
|
|
|
|
|
|
|
switch (this->state) {
|
|
|
|
|
|
|
|
case WAITING:
|
2020-10-03 17:45:54 +02:00
|
|
|
if(!this->getSoundEnabledSetting(ScStwSoundPlayer::Ready))
|
2020-10-03 11:44:56 +02:00
|
|
|
return currentStartDelay;
|
2020-10-03 12:45:44 +02:00
|
|
|
if(!this->getIsReadyForNextState()) {
|
2020-10-03 11:44:56 +02:00
|
|
|
// indicate that we are waiting for climbers and the progress shall be zero
|
2020-10-03 12:45:44 +02:00
|
|
|
currentStartDelay["progress"] = 0;
|
|
|
|
return currentStartDelay;
|
|
|
|
}
|
2020-10-03 13:49:56 +02:00
|
|
|
break;
|
2020-10-03 11:44:56 +02:00
|
|
|
case PREPAIRING: {
|
2020-10-03 17:45:54 +02:00
|
|
|
if(!this->getSoundEnabledSetting(ScStwSoundPlayer::AtYourMarks))
|
2020-10-03 11:44:56 +02:00
|
|
|
return currentStartDelay;
|
|
|
|
|
|
|
|
break;
|
2020-10-02 17:05:52 +02:00
|
|
|
}
|
|
|
|
|
2020-10-03 11:44:56 +02:00
|
|
|
default:
|
|
|
|
return currentStartDelay;
|
|
|
|
break;
|
|
|
|
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
2020-10-03 11:44:56 +02:00
|
|
|
// get the total delay and the delay progress of the next action timer
|
|
|
|
double remaining = this->startDelayTimer->remainingTime();
|
2020-10-03 13:49:56 +02:00
|
|
|
|
|
|
|
if(remaining < 0)
|
|
|
|
return currentStartDelay;
|
|
|
|
|
2020-10-03 11:44:56 +02:00
|
|
|
currentStartDelay["total"] = this->startDelayTimer->interval();
|
|
|
|
currentStartDelay["progress"] = 1 - (remaining / currentStartDelay["total"].toDouble());
|
|
|
|
|
|
|
|
if(currentStartDelay["progress"].toDouble() < 0)
|
|
|
|
currentStartDelay["progress"] = 0;
|
|
|
|
|
|
|
|
|
|
|
|
return currentStartDelay;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ScStwRace::addTimer(ScStwTimer *timer) {
|
|
|
|
if(this->state != IDLE)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
foreach(ScStwTimer *existingTimer, this->timers) {
|
|
|
|
if(existingTimer == timer)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->timers.append(timer);
|
|
|
|
|
2020-10-04 16:04:22 +02:00
|
|
|
connect(timer, &ScStwTimer::stateChanged, this, &ScStwRace::handleTimerStateChange);
|
2020-04-17 19:57:00 +02:00
|
|
|
connect(timer, &ScStwTimer::stateChanged, this, &ScStwRace::timersChanged);
|
2020-10-02 19:53:07 +02:00
|
|
|
connect(timer, &ScStwTimer::wantsToBeDisabledChanged, this, &ScStwRace::handleTimerWantsToBeDisabledChange);
|
2020-04-17 19:57:00 +02:00
|
|
|
connect(timer, &ScStwTimer::reactionTimeChanged, this, &ScStwRace::timersChanged);
|
2020-10-02 19:53:07 +02:00
|
|
|
connect(timer, &ScStwTimer::readyStateChanged, this, &ScStwRace::handleTimerReadyStateChange);
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-10-02 19:56:08 +02:00
|
|
|
connect(timer, &ScStwTimer::readyStateChanged, this, &ScStwRace::isReadyForNextStateChanged);
|
|
|
|
|
2020-10-02 14:16:24 +02:00
|
|
|
if(this->competitionMode && timer->getState() == ScStwTimer::DISABLED)
|
2020-08-09 13:29:37 +02:00
|
|
|
timer->setDisabled(false);
|
2020-10-06 13:58:42 +02:00
|
|
|
else if(!this->competitionMode)
|
|
|
|
this->handleTimerWantsToBeDisabledChange(timer, timer->getWantsToBeDisabled());
|
|
|
|
|
2020-08-09 13:29:37 +02:00
|
|
|
|
2020-04-11 23:41:34 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-10-03 17:45:54 +02:00
|
|
|
ScStwRace::RaceState ScStwRace::getState() {
|
|
|
|
return this->state;
|
|
|
|
}
|
2020-10-03 15:15:49 +02:00
|
|
|
|
2020-10-03 17:45:54 +02:00
|
|
|
QList<ScStwTimer*> ScStwRace::getTimers() {
|
|
|
|
return this->timers;
|
|
|
|
}
|
2020-10-03 15:15:49 +02:00
|
|
|
|
2020-10-03 17:45:54 +02:00
|
|
|
double ScStwRace::getSoundVolume() {
|
2020-10-09 15:18:56 +02:00
|
|
|
if(this->settings == nullptr)
|
|
|
|
return 1;
|
|
|
|
|
2020-10-03 17:45:54 +02:00
|
|
|
return this->settings->readSetting(ScStwSettings::SoundVolumeSetting).toDouble();
|
2020-10-03 15:15:49 +02:00
|
|
|
}
|
|
|
|
|
2020-10-03 17:45:54 +02:00
|
|
|
ScStwSoundPlayer::StartSound ScStwRace::getSoundForState(ScStwRace::RaceState state) {
|
|
|
|
switch (state) {
|
|
|
|
case PREPAIRING:
|
|
|
|
return ScStwSoundPlayer::AtYourMarks;
|
|
|
|
break;
|
|
|
|
case WAITING:
|
|
|
|
return ScStwSoundPlayer::Ready;
|
|
|
|
break;
|
|
|
|
case STARTING:
|
|
|
|
return ScStwSoundPlayer::Start;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return ScStwSoundPlayer::StartSound(-1);
|
2020-10-03 15:15:49 +02:00
|
|
|
}
|
2020-10-03 17:45:54 +02:00
|
|
|
}
|
2020-10-03 15:15:49 +02:00
|
|
|
|
2020-10-03 17:45:54 +02:00
|
|
|
bool ScStwRace::getSoundEnabledSetting(ScStwSoundPlayer::StartSound sound) {
|
|
|
|
ScStwSettings::BaseStationSetting soundEnabledSetting;
|
|
|
|
switch (sound) {
|
|
|
|
case ScStwSoundPlayer::AtYourMarks:
|
|
|
|
soundEnabledSetting = ScStwSettings::AtYourMarksSoundEnableSetting;
|
2020-10-03 15:15:49 +02:00
|
|
|
break;
|
2020-10-03 17:45:54 +02:00
|
|
|
case ScStwSoundPlayer::Ready:
|
|
|
|
soundEnabledSetting = ScStwSettings::ReadySoundEnableSetting;
|
|
|
|
break;
|
|
|
|
case ScStwSoundPlayer::Start:
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
2020-10-03 15:15:49 +02:00
|
|
|
}
|
|
|
|
|
2020-10-09 15:18:56 +02:00
|
|
|
if(this->settings == nullptr)
|
|
|
|
return false;
|
|
|
|
|
2020-10-03 17:45:54 +02:00
|
|
|
return this->settings->readSetting(soundEnabledSetting).toBool();
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
2020-10-03 17:45:54 +02:00
|
|
|
int ScStwRace::getSoundDelaySetting(ScStwSoundPlayer::StartSound sound) {
|
|
|
|
ScStwSettings::BaseStationSetting soundDelaySetting;
|
|
|
|
switch (sound) {
|
|
|
|
case ScStwSoundPlayer::AtYourMarks:
|
|
|
|
soundDelaySetting = ScStwSettings::AtYourMarksSoundDelaySetting;
|
|
|
|
break;
|
|
|
|
case ScStwSoundPlayer::Ready:
|
|
|
|
soundDelaySetting = ScStwSettings::ReadySoundDelaySetting;
|
|
|
|
break;
|
|
|
|
case ScStwSoundPlayer::Start:
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-10-09 15:18:56 +02:00
|
|
|
if(this->settings == nullptr)
|
|
|
|
return -1;
|
|
|
|
|
2020-10-03 17:45:54 +02:00
|
|
|
return this->settings->readSetting(soundDelaySetting).toInt();
|
2020-07-29 17:56:32 +02:00
|
|
|
}
|
|
|
|
|
2020-04-11 23:41:34 +02:00
|
|
|
QVariantList ScStwRace::getTimerDetailList() {
|
|
|
|
QVariantList tmpTimers;
|
|
|
|
|
|
|
|
foreach(ScStwTimer * timer, this->timers){
|
|
|
|
QVariantMap tmpTimer;
|
|
|
|
tmpTimer.insert("id", this->timers.indexOf(timer));
|
|
|
|
tmpTimer.insert("state", timer->getState());
|
|
|
|
tmpTimer.insert("currentTime", timer->getCurrentTime());
|
|
|
|
tmpTimer.insert("reactionTime", timer->getReactionTime());
|
2020-06-13 12:38:24 +02:00
|
|
|
tmpTimer.insert("letter", timer->getLetter());
|
2020-10-02 19:53:07 +02:00
|
|
|
tmpTimer.insert("readyState", timer->getReadyState());
|
2020-10-03 18:32:39 +02:00
|
|
|
tmpTimer.insert("text", timer->getText());
|
2020-04-11 23:41:34 +02:00
|
|
|
tmpTimers.append(tmpTimer);
|
|
|
|
}
|
|
|
|
|
|
|
|
return tmpTimers;
|
|
|
|
}
|
2020-10-01 01:54:39 +02:00
|
|
|
|
2020-10-02 19:53:07 +02:00
|
|
|
QVariantMap ScStwRace::getDetails() {
|
|
|
|
QVariantMap tmpDetails;
|
|
|
|
|
2020-10-03 11:10:15 +02:00
|
|
|
tmpDetails.insert("state", this->getState());
|
2020-10-02 19:53:07 +02:00
|
|
|
tmpDetails.insert("competitionMode", this->competitionMode);
|
2020-10-03 17:45:54 +02:00
|
|
|
tmpDetails.insert("readySoundEnabled", this->getSoundEnabledSetting(ScStwSoundPlayer::Ready));
|
2020-10-03 11:10:15 +02:00
|
|
|
tmpDetails.insert("currentStartDelay", this->getCurrentStartDelay());
|
|
|
|
tmpDetails.insert("timers", this->getTimerDetailList());
|
2020-10-02 19:53:07 +02:00
|
|
|
|
|
|
|
if(this->state == WAITING)
|
2020-10-03 11:10:15 +02:00
|
|
|
tmpDetails.insert("isReadyForNextState", this->getIsReadyForNextState());
|
2020-10-03 17:01:05 +02:00
|
|
|
else
|
|
|
|
tmpDetails.insert("isReadyForNextState", true);
|
2020-10-02 19:53:07 +02:00
|
|
|
|
|
|
|
return tmpDetails;
|
|
|
|
}
|
|
|
|
|
2020-10-01 01:54:39 +02:00
|
|
|
bool ScStwRace::isStarting() {
|
|
|
|
return this->state == PREPAIRING || this->state == WAITING || this->state == STARTING;
|
|
|
|
}
|
2020-10-03 11:44:56 +02:00
|
|
|
bool ScStwRace::getCompetitionMode() {
|
|
|
|
return this->competitionMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScStwRace::getReadySoundEnabled() {
|
2020-10-03 17:45:54 +02:00
|
|
|
return this->getSoundEnabledSetting(ScStwSoundPlayer::Ready);
|
2020-10-03 11:44:56 +02:00
|
|
|
}
|
2020-10-09 15:18:56 +02:00
|
|
|
|
|
|
|
ScStwSettings* ScStwRace::getSettings() {
|
|
|
|
return this->settings;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScStwRace::setSettings(ScStwSettings* settings) {
|
|
|
|
if(settings == this->settings)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this->settings = settings;
|
|
|
|
this->refreshCompetitionMode();
|
|
|
|
emit this->settingsChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScStwRace::getAutoRefreshTimerText() {
|
|
|
|
return this->autoRefreshTimerText;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScStwRace::setAutoRefreshTimerText(bool autoRefresh) {
|
|
|
|
if(autoRefresh == this->autoRefreshTimerText)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this->autoRefreshTimerText = autoRefresh;
|
|
|
|
|
|
|
|
if(this->autoRefreshTimerText) {
|
|
|
|
this->timerTextRefreshTimer = new QTimer(this);
|
|
|
|
this->timerTextRefreshTimer->setInterval(1);
|
|
|
|
this->timerTextRefreshTimer->connect(
|
|
|
|
this->timerTextRefreshTimer,
|
|
|
|
&QTimer::timeout,
|
|
|
|
[=]{
|
|
|
|
// refresh timer text
|
|
|
|
if(this->getState() == ScStwRace::RUNNING) {
|
|
|
|
emit this->timersChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
// refresh next start action delay progress
|
|
|
|
if(this->getState() == ScStwRace::WAITING || this->getState() == ScStwRace::PREPAIRING) {
|
|
|
|
emit this->currentStartDelayChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
this->timerTextRefreshTimer->start();
|
|
|
|
}
|
|
|
|
else if(this->timerTextRefreshTimer != nullptr) {
|
|
|
|
this->timerTextRefreshTimer->deleteLater();
|
|
|
|
}
|
|
|
|
|
|
|
|
emit this->autoRefreshTimerTextChanged();
|
|
|
|
}
|