/**************************************************************************** ** 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 . ****************************************************************************/ #include "../headers/scstwtimer.h" ScStwTimer::ScStwTimer(QObject* parent) : ScStwTimer("", parent) { } ScStwTimer::ScStwTimer(QString letter, QObject *parent) : QObject(parent) { if(letter.length() > 1) this->letter = letter[0]; else this->letter = letter; //qDebug() << "Timer created with letter: " << letter; this->startTime = 0; this->stopTime = 0; this->reactionTime = 0; this->wantsToBeDisabled = false; this->state = IDLE; } bool ScStwTimer::start() { return this->start(QDateTime::currentMSecsSinceEpoch()); } bool ScStwTimer::start(double timeOfStart) { switch (this->state) { case IDLE: { // in case of IDLE, start the race! this->startTime = timeOfStart; this->stopTime = 0; if(timeOfStart - QDateTime::currentMSecsSinceEpoch() > 0) { this->setState(STARTING); QTimer::singleShot(timeOfStart - QDateTime::currentMSecsSinceEpoch(), [=]() { if(this->state == STARTING) this->setState(RUNNING); }); } else this->setState(RUNNING); return true; } default: { // otherwise the timer is not supposed to be started! return false; } } } void ScStwTimer::technicalIncident() { qDebug() << "[INFO][TIMER] Timer got a technical incident"; this->setState(INCIDENT); } bool ScStwTimer::wildcard() { if(this->state != STARTING) return false; this->setState(WILDCARD); return true; } void ScStwTimer::handleClimberStart(double timeOfStart) { this->reactionTime = timeOfStart - this->startTime; qDebug() << "+ [INFO][TIMER] reaction time: " << this->reactionTime; if(this->reactionTime <= 100 && this->reactionTime) { qDebug() << "[INFO][TIMER] False Start detected: " << "start Time: " << startTime << " reactionTime: " << reactionTime; this->setState(FAILING); } emit this->reactionTimeChanged(); } bool ScStwTimer::cancel() { if(!(this->state == IDLE || this->state == STARTING || this->state == RUNNING)) return false; qDebug() << "[INFO][TIMER] Timer was cancelled"; this->reactionTime = 0; this->startTime = 0; this->stopTime = 0; this->setState(CANCELLED); return true; } bool ScStwTimer::stop() { return this->stop(QDateTime::currentMSecsSinceEpoch()); } bool ScStwTimer::stop(double timeOfStop) { if(this->state != RUNNING) return false; this->stopTime = timeOfStop; // 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); qDebug() << "[INFO][TIMER] Stopped: " << "start Time: " << startTime << " stopTime: " << stopTime << " stoppedTime: " << this->getCurrentTime() << " reactionTime: " << reactionTime; return true; } bool ScStwTimer::setResult(TimerState result) { QList allowedStates = { STARTING, RUNNING, WAITING, WON, FAILING, FAILED }; if(!allowedStates.contains(this->state)) return false; /* The reasons why it has to accept all these states: * * STARTING: To set a timer to WILDCARD when its opponent has done a false start * RUNNING: To set a timer to WILDCARD when its opponent has done a false start that was received with a delay * WAITING: To set a timer to either WON or LOST * WON: To set a timer to LOST when its opponent has won the race but their trigger was delayed * FAILING: To set a timer to either FAILED or WILDCARD * FAILED: To set a timer to WILDCARD when its opponent did an earlier false start but their tirgger was delayed */ switch (result) { case LOST: case WON: if(this->state == WAITING || this->state == WON) { this->setState(result); return true; } break; case WILDCARD: case FAILED: if(this->state == STARTING || this->state == RUNNING || this->state == FAILED || this->state == FAILING) { this->setState(result); return true; } break; default: break; } return false; } bool ScStwTimer::reset() { if( this->state < WON || this->state == DISABLED ) { return false; } this->startTime = 0; this->stopTime = 0; this->reactionTime = 0; this->setState(IDLE); return true; } ScStwTimer::ReadyState ScStwTimer::getReadyState() { return this->state == IDLE ? ScStwTimer::IsReady : ScStwTimer::NotInIdleState; } // ------------------------ // --- helper functions --- // ------------------------ void ScStwTimer::setState(TimerState newState) { if(this->state == DISABLED && newState != IDLE) return; if(this->state == INCIDENT && newState != IDLE) return; if(this->state != newState) { this->state = newState; qDebug() << "[INFO][TIMER] timer state changed: " << newState; emit this->stateChanged(newState); } } ScStwTimer::TimerState ScStwTimer::getState() { return this->state; } double ScStwTimer::getCurrentTime() { switch (this->state) { case RUNNING: return QDateTime::currentMSecsSinceEpoch() - this->startTime; default: { if(this->state == WAITING || this->state == WON || this->state == LOST) return abs(this->stopTime - this->startTime); else return -1; } } } double ScStwTimer::getReactionTime() { return this->reactionTime; } QString ScStwTimer::getLetter() { return this->letter; } QString ScStwTimer::getText() { QString newText = ""; int newTime = 0; switch (this->state) { case ScStwTimer::IDLE: newTime = 0; break; case ScStwTimer::STARTING: newTime = 0; break; case ScStwTimer::RUNNING: newTime = this->getCurrentTime(); break; case ScStwTimer::WAITING: newText = "please wait..."; break; case ScStwTimer::WON: newTime = this->getCurrentTime(); break; case ScStwTimer::LOST: newTime = this->getCurrentTime(); break; case ScStwTimer::FAILING: newText = "please wait..."; break; case ScStwTimer::FAILED: newText = "false start"; break; case ScStwTimer::WILDCARD: newText = "wildcard"; break; case ScStwTimer::CANCELLED: newText = "cancelled"; break; case ScStwTimer::INCIDENT: newText = "Technical incident!"; break; case ScStwTimer::DISABLED: newText = "---"; break; } if(newText == "") newText = QString::number( newTime / 1000.0, 'f', 3 ).rightJustified(6, '0'); return newText; } void ScStwTimer::setDisabled(bool disabled) { if(this->isDisabled() == disabled) return; if(disabled) this->setState(DISABLED); else this->setState(IDLE); emit this->readyStateChanged(this->getReadyState()); } void ScStwTimer::setWantsToBeDisabled(bool wantsToBeDisabled) { if(this->wantsToBeDisabled == wantsToBeDisabled) return; qDebug() << "Wants to be disabled changed: " << wantsToBeDisabled; this->wantsToBeDisabled = wantsToBeDisabled; emit this->wantsToBeDisabledChanged(this, wantsToBeDisabled); } bool ScStwTimer::getWantsToBeDisabled() { return this->wantsToBeDisabled; } bool ScStwTimer::isRunning() { return this->state == RUNNING; } bool ScStwTimer::isDisabled() { return this->state == DISABLED; }