/**************************************************************************** ** 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 "scstwremotemonitorrace.h" ScStwRemoteMonitorRace::ScStwRemoteMonitorRace(ScStwClient *monitorClient, QObject *parent) : ScStwRace(parent) { this->currentlyWaitingForClimbers = false; this->scStwClient = monitorClient; this->scStwClient->addSignalSubscription(ScStw::RaceStateChanged); this->scStwClient->addSignalSubscription(ScStw::TimersChanged); this->scStwClient->addSignalSubscription(ScStw::CurrentStartDelayChanged); connect(this->scStwClient, &ScStwClient::stateChanged, this, &ScStwRemoteMonitorRace::handleClientStateChanged); connect(this->scStwClient, &ScStwClient::gotSignal, this, &ScStwRemoteMonitorRace::handleBaseStationSignal); } // -------------------------- // --- Main Functionality --- // -------------------------- ScStw::StatusCode ScStwRemoteMonitorRace::start() { if(this->getState() != ScStwRace::IDLE && this->getState() != ScStwRace::WAITING) return ScStw::CurrentStateNotVaildForOperationError; qDebug() << "+ --- starting race"; QVariantMap reply = this->scStwClient->sendCommand(ScStw::StartRaceCommand); return ScStw::StatusCode(reply["status"].toInt()); } ScStw::StatusCode ScStwRemoteMonitorRace::cancel() { if(this->getState() != PREPAIRING && this->getState() != WAITING && this->getState() != STARTING && this->getState() != RUNNING) return ScStw::CurrentStateNotVaildForOperationError; qDebug() << "+ --- stopping race"; QVariantMap reply = this->scStwClient->sendCommand(ScStw::CancelRaceCommand); return ScStw::StatusCode(reply["status"].toInt()); } ScStw::StatusCode ScStwRemoteMonitorRace::stop() { if(this->getState() != ScStwRace::RUNNING && this->getState() != ScStwRace::STARTING) return ScStw::CurrentStateNotVaildForOperationError; qDebug() << "+ --- stopping race"; QVariantMap reply = this->scStwClient->sendCommand(ScStw::StopRaceCommand); return ScStw::StatusCode(reply["status"].toInt()); } ScStw::StatusCode ScStwRemoteMonitorRace::reset() { if(this->getState() != ScStwRace::STOPPED && this->getState() != ScStwRace::INCIDENT) return ScStw::CurrentStateNotVaildForOperationError; qDebug() << "+ --- resetting race"; QVariantMap reply = this->scStwClient->sendCommand(ScStw::ResetRaceCommand); return ScStw::StatusCode(reply["status"].toInt()); } // ------------------------- // --- Base Station sync --- // ------------------------- void ScStwRemoteMonitorRace::handleClientStateChanged() { // TODO switch (this->scStwClient->getState()) { case ScStwClient::CONNECTED: break; default: this->timers.clear(); this->setState(IDLE); break; } } /** * @brief ScStwAppBackend::handleBaseStationUpdate * * Function to handle an update, sent by the base station, which indicates * that some remote value (like a state) has changed * * @param data */ void ScStwRemoteMonitorRace::handleBaseStationSignal(ScStw::SignalKey key, QVariant data) { //qDebug() << "got signal: " << data; switch (key) { case ScStw::RaceStateChanged: { // the remote race state has changed this->setState( ScStwRace::RaceState( data.toInt() ) ); break; } case ScStw::TimersChanged: { // the remote timers have changed this->refreshRemoteTimers(data.toList()); break; } case ScStw::CurrentStartDelayChanged: { // the next start action has changed this->currentStartTotalDelay = data.toList()[ScStwRace::CurrentStartStateTotalDelay].toInt(); this->currentStartDelayStartedAt = QDateTime::currentMSecsSinceEpoch() - (this->currentStartTotalDelay * data.toList()[ScStwRace::CurrentStartStateDelayProgress].toDouble()); this->currentlyWaitingForClimbers = data.toList()[ScStwRace::CurrentStartStateDelayProgress].toDouble() == 0 && this->currentStartTotalDelay == -1; emit this->currentStartDelayChanged(); qDebug() << "Current start delay changed: total:" << this->currentStartTotalDelay << " progress: " << data.toList()[ScStwRace::CurrentStartStateDelayProgress].toDouble(); break; } case ScStw::InvalidSignal: return; default: return; } } bool ScStwRemoteMonitorRace::refreshRemoteTimers(QVariantList remoteTimers) { if(remoteTimers.length() != this->timers.length()){ // local timers are out of sync // delete all current timers foreach(ScStwTimer * locTimer, this->timers){ delete locTimer; } this->timers.clear(); foreach(QVariant remoteTimer, remoteTimers){ // create a local timer for each remote timer ScStwTimer * timer = new ScStwTimer(this, true); this->timers.append(timer); connect(timer, &ScStwTimer::stateChanged, this, &ScStwRace::timersChanged); connect(timer, &ScStwTimer::reactionTimeChanged, this, &ScStwRace::timersChanged); } } foreach(QVariant remoteTimer, remoteTimers){ int currId = remoteTimer.toMap()["id"].toInt(); ScStwTimer::TimerState newState = ScStwTimer::TimerState(remoteTimer.toMap()["state"].toInt()); double currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); this->timers[currId]->setStartTime(currentMSecsSinceEpoch - remoteTimer.toMap()["currentTime"].toDouble()); if(newState >= ScStwTimer::WAITING) this->timers[currId]->setStopTime(currentMSecsSinceEpoch); this->timers[currId]->setReactionTime(remoteTimer.toMap()["reactionTime"].toDouble()); this->timers[currId]->setState(newState, true); this->timers[currId]->setLetter(remoteTimer.toMap()["letter"].toString()); } return true; } bool ScStwRemoteMonitorRace::addTimer(ScStwTimer* timer) { Q_UNUSED(timer) return false; } QVariantList ScStwRemoteMonitorRace::getCurrentStartDelay() { int nextActionDelay = -1; double nextActionDelayProg = -1; if(this->getState() == WAITING && this->currentlyWaitingForClimbers) { nextActionDelayProg = 0; } else if(this->getState() == PREPAIRING || this->getState() == WAITING) { // get the total delay and the delay progress of the next action timer double elapsed = QDateTime::currentMSecsSinceEpoch() - this->currentStartDelayStartedAt; nextActionDelay = this->currentStartTotalDelay; if(elapsed < 0 || elapsed > nextActionDelay) { elapsed = nextActionDelay; } nextActionDelayProg = elapsed / nextActionDelay; } return { nextActionDelay, nextActionDelayProg }; }