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"
|
|
|
|
|
|
|
|
ScStwRace::ScStwRace(QObject *parent) : QObject(parent)
|
|
|
|
{
|
|
|
|
this->state = IDLE;
|
|
|
|
|
|
|
|
// 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
|
|
|
|
this->nextActionTimer = new QTimer(this);
|
|
|
|
nextActionTimer->setSingleShot(true);
|
2020-09-29 12:53:40 +02:00
|
|
|
this->nextActionLoop = new QEventLoop(this);
|
2020-04-17 19:57:00 +02:00
|
|
|
this->nextStartAction = None;
|
2020-09-29 12:53:40 +02:00
|
|
|
this->climberReadyWaitLoop = new QEventLoop(this);
|
2020-04-11 23:41:34 +02:00
|
|
|
|
|
|
|
connect(this->nextActionTimer, &QTimer::timeout, this->nextActionLoop, &QEventLoop::quit);
|
2020-09-29 12:53:40 +02:00
|
|
|
connect(this->nextActionTimer, &QTimer::timeout, this->climberReadyWaitLoop, &QEventLoop::quit);
|
2020-04-17 19:57:00 +02:00
|
|
|
connect(this, &ScStwRace::nextStartActionChanged, this, &ScStwRace::nextStartActionDetailsChanged);
|
2020-04-11 23:41:34 +02:00
|
|
|
|
|
|
|
// write default settings
|
|
|
|
this->startActionSettings.insert(Start, {{"Enabled", true}, {"Delay", 1}});
|
|
|
|
this->writeStartActionSetting(AtYourMarks, false, 0);
|
|
|
|
this->writeStartActionSetting(Ready, false, 0);
|
|
|
|
this->setSoundVolume(1.0);
|
2020-08-09 13:29:37 +02:00
|
|
|
this->allowAutomaticTimerDisable = false;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------
|
|
|
|
// --- Main Functionality ---
|
|
|
|
// --------------------------
|
|
|
|
|
2020-04-19 13:09:24 +02:00
|
|
|
int ScStwRace::start(bool asyncronous) {
|
2020-04-11 23:41:34 +02:00
|
|
|
if(this->state != IDLE) {
|
2020-05-10 17:39:16 +02:00
|
|
|
return ScStw::CurrentStateNotVaildForOperationError;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
2020-09-29 12:53:40 +02:00
|
|
|
qDebug() << "[INFO][RACE] checking timers";
|
|
|
|
foreach (ScStwTimer *timer, this->timers) {
|
|
|
|
if(timer->getState() == ScStwTimer::DISABLED)
|
|
|
|
continue;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-09-29 15:21:00 +02:00
|
|
|
if(timer->getReadyState() == ScStwTimer::ExtensionIsNotConnected || timer->getReadyState() == ScStwTimer::ExtensionBatteryNotFine) {
|
2020-09-29 12:53:40 +02:00
|
|
|
|
|
|
|
if(this->allowAutomaticTimerDisable) {
|
|
|
|
timer->setDisabled(true);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
qDebug() << "Timer ready state is: " << timer->getReadyState();
|
|
|
|
|
|
|
|
timer->technicalIncident();
|
|
|
|
|
|
|
|
foreach (ScStwTimer *subTimer, this->timers) {
|
2020-09-29 15:21:00 +02:00
|
|
|
if(timer != subTimer && (timer->getReadyState() == ScStwTimer::ExtensionIsNotConnected || timer->getReadyState() == ScStwTimer::ExtensionBatteryNotFine))
|
2020-09-29 12:53:40 +02:00
|
|
|
subTimer->technicalIncident();
|
|
|
|
else if(timer != subTimer)
|
|
|
|
subTimer->setState(ScStwTimer::CANCELLED);
|
|
|
|
}
|
|
|
|
|
2020-09-29 15:21:00 +02:00
|
|
|
this->setState(INCIDENT);
|
2020-09-29 12:53:40 +02:00
|
|
|
|
|
|
|
qDebug() << "[ERROR][RACE] Could not start due to not-ready timers";
|
|
|
|
|
|
|
|
return ScStw::TimersNotReadyError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qDebug() << "[INFO][RACE] starting race";
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
int ScStwRace::stop() {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
qDebug() << "+ [INFO] stopping race";
|
|
|
|
|
2020-04-15 19:21:32 +02:00
|
|
|
double timeOfStop = QDateTime::currentMSecsSinceEpoch();
|
|
|
|
|
2020-04-19 22:39:33 +02:00
|
|
|
int 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScStwRace::handleTimerStop() {
|
|
|
|
if(this->state == RUNNING) {
|
|
|
|
// find out which timer has won
|
|
|
|
double lowestStoppedTime = -1;
|
2020-04-15 19:21:32 +02:00
|
|
|
QList<ScStwTimer *> timersWhichHaveWonIds;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
|
|
|
// iterate through all timers and find the lowest time taht was stopped
|
2020-04-15 19:21:32 +02:00
|
|
|
foreach(ScStwTimer * timer, this->timers) {
|
2020-04-11 23:41:34 +02:00
|
|
|
if(timer->getCurrentTime() > 0 && (timer->getCurrentTime() <= lowestStoppedTime || lowestStoppedTime < 0)) {
|
|
|
|
// this is the timer with the lowest stopped time
|
|
|
|
lowestStoppedTime = timer->getCurrentTime();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// append the timer(s) with the lowest stopped time to the winner list
|
2020-04-15 19:21:32 +02:00
|
|
|
foreach(ScStwTimer * timer, this->timers) {
|
|
|
|
if(timer->getCurrentTime() > 0
|
|
|
|
&& (timer->getCurrentTime() <= lowestStoppedTime || lowestStoppedTime < 0)
|
|
|
|
&& timer->getState() != ScStwTimer::RUNNING
|
|
|
|
) {
|
2020-04-11 23:41:34 +02:00
|
|
|
// this is the timer with the lowest stopped time
|
2020-04-15 19:21:32 +02:00
|
|
|
timersWhichHaveWonIds.append(timer);
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// update the states of all timers
|
2020-04-15 19:21:32 +02:00
|
|
|
foreach(ScStwTimer * timer, this->timers) {
|
|
|
|
if(timer->getState() == ScStwTimer::RUNNING)
|
|
|
|
continue;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-04-15 19:21:32 +02:00
|
|
|
if(timersWhichHaveWonIds.contains(timer)) {
|
|
|
|
timer->setResult(ScStwTimer::WON);
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
2020-04-15 19:21:32 +02:00
|
|
|
else {
|
|
|
|
timer->setResult(ScStwTimer::LOST);
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int 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
|
|
|
}
|
|
|
|
|
|
|
|
qDebug() << "+ [INFO] resetting race";
|
|
|
|
|
2020-04-19 22:39:33 +02:00
|
|
|
int returnCode = ScStw::Success;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
|
|
|
foreach(ScStwTimer *speedTimer, this->timers){
|
|
|
|
if(!speedTimer->reset() && speedTimer->getState() != ScStwTimer::DISABLED) {
|
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);
|
|
|
|
|
|
|
|
return returnCode;
|
|
|
|
}
|
|
|
|
|
2020-04-19 22:39:33 +02:00
|
|
|
int 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-04-19 22:39:33 +02:00
|
|
|
int 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);
|
|
|
|
|
|
|
|
this->soundPlayer->cancel(this->soundVolume);
|
|
|
|
this->nextActionTimer->stop();
|
|
|
|
this->nextActionLoop->quit();
|
|
|
|
this->climberReadyWaitLoop->quit();
|
|
|
|
this->nextStartAction = None;
|
|
|
|
|
2020-04-19 22:39:33 +02:00
|
|
|
return returnCode;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ScStwRace::handleFalseStart() {
|
|
|
|
if(this->getState() != STARTING && this->getState() != RUNNING)
|
2020-05-10 17:39:16 +02:00
|
|
|
return ScStw::CurrentStateNotVaildForOperationError;
|
2020-04-19 22:39:33 +02:00
|
|
|
|
|
|
|
int returnCode = ScStw::Success;
|
|
|
|
// cancel all running timers
|
|
|
|
foreach(ScStwTimer *timer, this->timers) {
|
|
|
|
if(!timer->cancel() && timer->getState() != ScStwTimer::DISABLED && timer->getState() != ScStwTimer::FAILED)
|
|
|
|
returnCode = ScStw::InternalErrorTimerOperationFailed;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
2020-04-19 22:39:33 +02:00
|
|
|
|
|
|
|
this->setState(STOPPED);
|
|
|
|
this->soundPlayer->cancel(this->soundVolume);
|
|
|
|
|
|
|
|
return returnCode;
|
2020-04-11 23:41:34 +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
|
|
|
|
if(!this->doDelayAndSoundOfStartAction(AtYourMarks)) {
|
|
|
|
return false;
|
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
|
|
|
|
this->nextStartAction = Ready;
|
|
|
|
emit this->nextStartActionChanged();
|
|
|
|
|
|
|
|
// if the automatic ready tone is enabled, wait for the climbers to become ready
|
|
|
|
if(this->startActionSettings.contains(Ready) && this->startActionSettings[Ready]["Enabled"].toBool()) {
|
|
|
|
|
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;
|
|
|
|
if(this->startActionSettings[Ready]["Delay"].toInt() > 1000 || !this->allowAutomaticTimerDisable)
|
|
|
|
minimumReadyDelay = this->startActionSettings[Ready]["Delay"].toInt();
|
2020-09-29 12:53:40 +02:00
|
|
|
|
2020-09-29 15:21:00 +02:00
|
|
|
// wait for climbers to become ready initially
|
|
|
|
bool allClimbersReady = false;
|
|
|
|
while (!allClimbersReady) {
|
|
|
|
allClimbersReady = true;
|
|
|
|
foreach (ScStwTimer *timer, this->timers) {
|
|
|
|
if(timer->getReadyState() != ScStwTimer::IsReady)
|
|
|
|
allClimbersReady = false;
|
|
|
|
}
|
2020-09-29 12:53:40 +02:00
|
|
|
|
2020-09-29 15:21:00 +02:00
|
|
|
if(!allClimbersReady)
|
2020-09-29 12:53:40 +02:00
|
|
|
this->climberReadyWaitLoop->exec();
|
|
|
|
|
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
|
|
|
|
2020-09-29 15:21:00 +02:00
|
|
|
qDebug() << "[RACE][DEBUG] Initial wait finished";
|
2020-09-29 12:53:40 +02:00
|
|
|
|
2020-09-29 15:21:00 +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
|
|
|
|
do {
|
|
|
|
this->nextActionTimer->stop();
|
|
|
|
this->nextActionTimer->start(minimumReadyDelay);
|
|
|
|
this->climberReadyWaitLoop->exec();
|
2020-09-29 12:53:40 +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 12:53:40 +02:00
|
|
|
return false;
|
2020-09-29 15:21:00 +02:00
|
|
|
|
|
|
|
} while(this->nextActionTimer->remainingTime() > 0);
|
|
|
|
|
|
|
|
qDebug() << "[RACE][DEBUG] Wait finished, starting now!";
|
|
|
|
|
|
|
|
// play ready tone
|
|
|
|
if(!this->soundPlayer->play(Ready, this->soundVolume))
|
|
|
|
return false;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
2020-09-29 12:53:40 +02:00
|
|
|
// enter starting state
|
|
|
|
this->setState(STARTING);
|
|
|
|
|
|
|
|
// play start tone
|
|
|
|
double timeOfSoundPlaybackStart;
|
|
|
|
this->doDelayAndSoundOfStartAction(Start, &timeOfSoundPlaybackStart);
|
|
|
|
|
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) {
|
|
|
|
qDebug() << "[ERROR][START] error staring all timers";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!this->soundPlayer->waitForSoundFinish()) {
|
|
|
|
qDebug() << "[ERROR][START] start sound wait error";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if a false start occured
|
2020-10-01 01:54:39 +02:00
|
|
|
if(!this->isStarting())
|
2020-04-11 23:41:34 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
nextStartAction = None;
|
|
|
|
emit this->nextStartActionChanged();
|
|
|
|
|
|
|
|
this->setState(RUNNING);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-09-29 12:53:40 +02:00
|
|
|
bool ScStwRace::doDelayAndSoundOfStartAction(ScStwRace::StartAction action, double *timeOfSoundPlaybackStart) {
|
2020-10-01 01:54:39 +02:00
|
|
|
if(!this->isStarting())
|
|
|
|
return false;
|
|
|
|
|
2020-09-29 12:53:40 +02:00
|
|
|
if(this->startActionSettings.contains(action) && this->startActionSettings[action]["Enabled"].toBool()) {
|
|
|
|
|
|
|
|
this->nextStartAction = action;
|
|
|
|
emit this->nextStartActionChanged();
|
|
|
|
|
|
|
|
if(action != Start && this->startActionSettings[action]["Delay"].toInt() > 0) {
|
|
|
|
// perform the delay before the start
|
|
|
|
|
|
|
|
// get delay
|
|
|
|
int thisActionDelay = this->startActionSettings[action]["Delay"].toInt();
|
|
|
|
|
|
|
|
// perform next action
|
|
|
|
if(thisActionDelay > 0) {
|
|
|
|
this->nextActionTimer->start(thisActionDelay);
|
|
|
|
this->nextActionLoop->exec();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-01 01:54:39 +02:00
|
|
|
if(!this->isStarting())
|
|
|
|
return false;
|
|
|
|
|
2020-09-29 12:53:40 +02:00
|
|
|
if(!this->soundPlayer->play(action, this->soundVolume, timeOfSoundPlaybackStart))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
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-08-09 13:29:37 +02:00
|
|
|
// if we changed to IDLE -> handle timer enable / disable
|
|
|
|
if(this->allowAutomaticTimerDisableChanged && !this->allowAutomaticTimerDisable) {
|
|
|
|
this->enableAllTimers();
|
|
|
|
this->allowAutomaticTimerDisableChanged = false;
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
2020-08-09 13:29:37 +02:00
|
|
|
if(this->allowAutomaticTimerDisable) {
|
|
|
|
foreach(ScStwTimer* timer, this->timers) {
|
|
|
|
if(timer->getWantsToBeDisabled() && timer->getState() != ScStwTimer::DISABLED)
|
|
|
|
this->handleTimerWantsToBeDisabledChange(timer, timer->getWantsToBeDisabled());
|
|
|
|
}
|
|
|
|
}
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScStwRace::refreshTimerStates() {
|
|
|
|
|
|
|
|
qDebug() << "[INFO][MAIN] refreshing timer states";
|
|
|
|
|
|
|
|
// check if the race is over
|
2020-04-15 19:21:32 +02:00
|
|
|
bool raceIsOver = true;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
|
|
|
foreach(ScStwTimer * timer, this->timers){
|
2020-04-17 19:57:00 +02:00
|
|
|
if(timer->getState() < ScStwTimer::WON && 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
|
|
|
break;
|
|
|
|
}
|
2020-04-15 19:21:32 +02:00
|
|
|
else if(timer->getState() == ScStwTimer::WAITING) {
|
|
|
|
this->handleTimerStop();
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
2020-04-19 22:39:33 +02:00
|
|
|
else if (timer->getState() == ScStwTimer::FAILED) {
|
|
|
|
this->handleFalseStart();
|
|
|
|
}
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
2020-04-19 22:39:33 +02:00
|
|
|
if(raceIsOver)
|
2020-04-11 23:41:34 +02:00
|
|
|
this->setState(STOPPED);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------
|
|
|
|
// --- helper functions ---
|
|
|
|
// ------------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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) {
|
|
|
|
if(!this->allowAutomaticTimerDisable)
|
|
|
|
return;
|
|
|
|
|
2020-04-11 23:41:34 +02:00
|
|
|
if(this->state == IDLE) {
|
2020-08-09 13:29:37 +02:00
|
|
|
timer->setDisabled(wantsToBeDisabled);
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
2020-08-09 13:29:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ScStwRace::setAllowAutomaticTimerDisable(bool allow) {
|
|
|
|
if(this->allowAutomaticTimerDisable == allow)
|
|
|
|
return;
|
|
|
|
|
|
|
|
qDebug() << "Setting allow automatic timer disable to " << allow;
|
|
|
|
|
|
|
|
this->allowAutomaticTimerDisable = allow;
|
|
|
|
|
|
|
|
if(this->state != IDLE)
|
|
|
|
this->allowAutomaticTimerDisableChanged = true;
|
|
|
|
else if(!this->allowAutomaticTimerDisable)
|
|
|
|
this->enableAllTimers();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScStwRace::enableAllTimers() {
|
|
|
|
if(this->state != IDLE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
qDebug() << "ENABLING ALL TIMERS";
|
|
|
|
|
|
|
|
foreach (ScStwTimer *timer, this->timers) {
|
|
|
|
timer->setDisabled(false);
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-17 19:57:00 +02:00
|
|
|
QVariantList ScStwRace::getNextStartActionDetails() {
|
2020-04-11 23:41:34 +02:00
|
|
|
int nextActionDelay = 0;
|
2020-04-17 19:57:00 +02:00
|
|
|
double nextActionDelayProg = -1;
|
2020-04-11 23:41:34 +02:00
|
|
|
|
|
|
|
if(this->nextStartAction == AtYourMarks || this->nextStartAction == Ready) {
|
|
|
|
// get the total delay and the delay progress of the next action timer
|
|
|
|
double remaining = this->nextActionTimer->remainingTime();
|
|
|
|
nextActionDelay = this->startActionSettings[this->nextStartAction]["Delay"].toInt();
|
|
|
|
if(remaining < 0) {
|
|
|
|
remaining = nextActionDelay;
|
|
|
|
}
|
|
|
|
nextActionDelayProg = 1 - (remaining / nextActionDelay);
|
|
|
|
}
|
|
|
|
|
2020-04-17 19:57:00 +02:00
|
|
|
return {
|
|
|
|
this->nextStartAction,
|
2020-04-19 22:39:33 +02:00
|
|
|
nextActionDelay,
|
|
|
|
nextActionDelayProg
|
2020-04-17 19:57:00 +02:00
|
|
|
};
|
2020-04-11 23:41:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ScStwRace::writeStartActionSetting(StartAction action, bool enabled, int delay) {
|
|
|
|
if(action != AtYourMarks && action != Ready)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
QVariantMap setting = {{"Enabled", enabled}, {"Delay", delay}};
|
|
|
|
|
|
|
|
if(!this->startActionSettings.contains(action))
|
|
|
|
this->startActionSettings.insert(action, setting);
|
|
|
|
else
|
|
|
|
this->startActionSettings[action] = setting;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScStwRace::setSoundVolume(double volume) {
|
|
|
|
if(volume >= 0 && volume <= 1) {
|
|
|
|
this->soundVolume = volume;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
connect(timer, &ScStwTimer::stateChanged, this, &ScStwRace::refreshTimerStates);
|
2020-08-09 13:29:37 +02:00
|
|
|
connect(timer, &ScStwTimer::wantsToBeDisabledChanged, this, &ScStwRace::handleTimerWantsToBeDisabledChange);
|
2020-04-17 19:57:00 +02:00
|
|
|
connect(timer, &ScStwTimer::stateChanged, this, &ScStwRace::timersChanged);
|
|
|
|
connect(timer, &ScStwTimer::reactionTimeChanged, this, &ScStwRace::timersChanged);
|
2020-09-29 12:53:40 +02:00
|
|
|
connect(timer, &ScStwTimer::readyStateChanged, this->climberReadyWaitLoop, &QEventLoop::quit);
|
2020-04-11 23:41:34 +02:00
|
|
|
|
2020-08-09 13:29:37 +02:00
|
|
|
if(!this->allowAutomaticTimerDisable && timer->getState() == ScStwTimer::DISABLED)
|
|
|
|
timer->setDisabled(false);
|
|
|
|
|
2020-04-11 23:41:34 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ScStwRace::RaceState ScStwRace::getState() {
|
|
|
|
return this->state;
|
|
|
|
}
|
|
|
|
|
|
|
|
ScStwRace::StartAction ScStwRace::getNextStartAction()
|
|
|
|
{
|
|
|
|
return this->nextStartAction;
|
|
|
|
}
|
|
|
|
|
2020-07-29 17:56:32 +02:00
|
|
|
QList<ScStwTimer*> ScStwRace::getTimers() {
|
|
|
|
return this->timers;
|
|
|
|
}
|
|
|
|
|
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-04-17 19:57:00 +02:00
|
|
|
tmpTimer.insert("text", timer->getText());
|
2020-06-13 12:38:24 +02:00
|
|
|
tmpTimer.insert("letter", timer->getLetter());
|
2020-04-11 23:41:34 +02:00
|
|
|
tmpTimers.append(tmpTimer);
|
|
|
|
}
|
|
|
|
|
|
|
|
return tmpTimers;
|
|
|
|
}
|
2020-10-01 01:54:39 +02:00
|
|
|
|
|
|
|
bool ScStwRace::isStarting() {
|
|
|
|
return this->state == PREPAIRING || this->state == WAITING || this->state == STARTING;
|
|
|
|
}
|