#include "headers/climbingrace.h" /* * manages: * - global state * - timers * - sounds * - next start action * - next start action delay progress * */ ClimbingRace::ClimbingRace(QObject *parent) : QObject(parent) { this->state = IDLE; this->mode = LOCAL; this->appSettings = new AppSettings; this->baseConn = new BaseConn; this->baseConn->setIP("ScStw"); connect(this->baseConn, &BaseConn::stateChanged, this, &ClimbingRace::baseStationStateChanged); this->speedTimers.append( new SpeedTimer ); this->player = new QMediaPlayer; this->date = new QDateTime; this->nextStartActionTimer = new QTimer(this); nextStartActionTimer->setSingleShot(true); this->baseStationSyncTimer = new QTimer(); this->baseStationSyncTimer->setInterval(100); this->baseStationSyncTimer->setSingleShot(true); this->baseStationSyncTimer->connect(this->baseStationSyncTimer, &QTimer::timeout, this, &ClimbingRace::syncWithBaseStation); this->baseStationSyncTimer->start(); this->timerTextRefreshTimer = new QTimer(); this->timerTextRefreshTimer->setInterval(1); this->timerTextRefreshTimer->setSingleShot(true); this->timerTextRefreshTimer->connect(this->timerTextRefreshTimer, &QTimer::timeout, this, &ClimbingRace::refreshTimerText); this->refreshTimerText(); } // -------------------------- // --- Main Functionality --- // -------------------------- int ClimbingRace::startRace() { if(this->state != IDLE) { return 904; } this->refreshMode(); qDebug() << "+ --- starting race"; int returnCode = 900; switch (this->mode) { case LOCAL: { this->setState(STARTING); this->nextStartAction = -1; this->playSoundsAndStartRace(); returnCode = 200; break; } case REMOTE: { QVariantMap reply = this->baseConn->sendCommand(1000); if(reply["status"] != 200){ //handle Error!! returnCode = reply["status"].toInt(); } else { returnCode = 200; } break; } } return returnCode; } int ClimbingRace::stopRace(int type) { if(this->state != RUNNING && this->state != STARTING) { return 904; } // type can be: // 0: stopp // 1: cancel // 2: fail (fase start) this->refreshMode(); qDebug() << "+ --- stopping race"; int returnCode = 900; switch (this->mode) { case LOCAL: { if(type == 1){ this->nextStartActionTimer->stop(); this->player->stop(); this->nextStartAction = -1; } returnCode = this->speedTimers[0]->stop(type) ? 200:904; if(returnCode == 200) { this->setState(STOPPED); } break; } case REMOTE: { QVariantMap reply = this->baseConn->sendCommand(1001); if(reply["status"] != 200){ //handle Error!! returnCode = reply["status"].toInt(); } else { returnCode = 200; } break; } } return returnCode; } int ClimbingRace::resetRace() { if(this->state != STOPPED) { return 904; } this->refreshMode(); qDebug() << "+ --- resetting race"; int returnCode = 900; switch (this->mode) { case LOCAL: { returnCode = this->speedTimers[0]->reset() ? 200:904; if(returnCode == 200){ this->setState(IDLE); } break; } case REMOTE: { QVariantMap reply = this->baseConn->sendCommand(1002); if(reply["status"] != 200){ //handle Error!! returnCode = reply["status"].toInt(); } else { returnCode = 200; } break; } } return returnCode; } // ------------------------- // --- Base Station sync --- // ------------------------- void ClimbingRace::syncWithBaseStation() { if(this->baseConn->state != "connected"){ this->baseStationSyncTimer->start(); return; } this->baseConn->refreshConnections(); emit this->baseStationConnectionsChanged(); QVariantMap tmpReply = this->baseConn->sendCommand(2000); if(tmpReply["status"] != 200){ this->baseStationSyncTimer->start(); return; } this->setState( raceState( tmpReply["data"].toInt() ) ); switch (this->state) { case 0: { // case IDLE if(speedTimers[0]->state != 0){ speedTimers[0]->setState(SpeedTimer::IDLE); } break; } case 1: { // case STARTING if(speedTimers[0]->state != 1){ speedTimers[0]->setState(SpeedTimer::STARTING); } // tmpReply = this->baseConn->sendCommand(2004); // if(tmpReply["status"] != 200){ // //handle Error!! // qDebug() << "+ --- getting next start action from basestation failed: " << tmpReply["status"]; // this->baseStationSyncTimer->start(); // return; // } // else { // if(this->nextStartAction != tmpReply["data"].toInt()){ // this->nextStartAction = tmpReply["data"].toInt(); // this->nextStartActionChanged(this->nextStartAction); // } // } tmpReply = this->baseConn->sendCommand(2005); if(tmpReply["status"] != 200){ //handle error!! qDebug() << "+ --- getting next start action progress from basestation failed"; this->baseStationSyncTimer->start(); return; } else { this->nextStartActionDelayProgress = tmpReply["data"].toDouble() > 0 ? tmpReply["data"].toDouble():0; this->nextStartActionDelayProgressChanged(); } break; } default: { // get current time tmpReply = this->baseConn->sendCommand(2007); if(tmpReply["status"] != 200){ //handle error!! qDebug() << "+ --- getting timers from basestation failed"; this->baseStationSyncTimer->start(); return; } else { QVariantList timers = tmpReply["data"].toList(); speedTimers[0]->startTime = this->date->currentMSecsSinceEpoch() - timers[0].toMap()["currTime"].toDouble(); speedTimers[0]->stoppedTime = timers[0].toMap()["currTime"].toDouble(); speedTimers[0]->reactionTime = timers[0].toMap()["reactTime"].toDouble(); speedTimers[0]->setState(SpeedTimer::timerState(timers[0].toMap()["state"].toInt())); } break; } } this->baseStationSyncTimer->start(); } // ------------------------ // --- helper functions --- // ------------------------ void ClimbingRace::playSoundsAndStartRace() { qDebug() << "next Action: " << nextStartAction; nextStartActionTimer->disconnect(nextStartActionTimer, SIGNAL(timeout()), this, SLOT(playSoundsAndStartRace())); switch (this->nextStartAction) { case 0: { if(!playSound("qrc:/sounds/at_marks_1.wav")){ return; } if(appSettings->loadSetting("ready_en") == "true"){ nextStartAction = 1; nextStartActionTimer->setInterval(appSettings->loadSetting("ready_delay").toInt() <= 0 ? 1:appSettings->loadSetting("ready_delay").toInt()); } else{ nextStartAction = 2; nextStartActionTimer->setInterval(1); } break; } case 1: { if(!playSound("qrc:/sounds/ready_1.wav")){ return; } nextStartAction = 2; nextStartActionTimer->setInterval(1); break; } case 2: { if(!playSound("qrc:/sounds/OFFICAL_IFSC_STARTIGNAL.wav")){ return; } nextStartAction = -1; nextStartActionTimer->disconnect(nextStartActionTimer, SIGNAL(timeout()), this, SLOT(playSoundsAndStartRace())); this->setState(RUNNING); speedTimers[0]->start(); return; } default: { this->speedTimers[0]->setState(SpeedTimer::STARTING); if(appSettings->loadSetting("at_marks_en") == "true"){ nextStartAction = 0; nextStartActionTimer->setInterval(appSettings->loadSetting("at_marks_delay").toInt() <= 0 ? 1:appSettings->loadSetting("at_marks_delay").toInt()); } else if(appSettings->loadSetting("ready_en") == "true"){ nextStartAction = 1; nextStartActionTimer->setInterval(appSettings->loadSetting("ready_delay").toInt() <= 0 ? 1:appSettings->loadSetting("ready_delay").toInt()); } else{ nextStartAction = 2; nextStartActionTimer->setInterval(1); } break; } } nextStartActionTimer->connect(nextStartActionTimer, SIGNAL(timeout()), this, SLOT(playSoundsAndStartRace())); nextStartActionTimer->start(); } bool ClimbingRace::playSound(QString path) { //connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64))); player->setMedia(QUrl(path)); player->setVolume(50); player->play(); QTimer timer; timer.setInterval(1); timer.setSingleShot(true); QEventLoop loop; loop.connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); while (player->mediaStatus() == QMediaPlayer::LoadingMedia || player->mediaStatus() == QMediaPlayer::BufferingMedia || player->mediaStatus() == QMediaPlayer::BufferedMedia) { timer.start(); loop.exec(); } if(player->mediaStatus() == QMediaPlayer::EndOfMedia){ return true; } else { return false; } } void ClimbingRace::setState(raceState newState) { if(newState != this->state) { this->state = newState; this->stateChanged(newState); } } void ClimbingRace::refreshMode() { raceMode newMode; if(this->baseConn->state == "connected"){ newMode = REMOTE; } else { newMode = LOCAL; } if(this->mode != newMode){ this->mode = newMode; emit this->modeChanged(); } } void ClimbingRace::refreshTimerText() { // --- refresh timer text --- QVariantList newTimerTextList; foreach(SpeedTimer * timer, this->speedTimers){ QVariantMap timerMap = {{"text",timer->getText()}, {"reacttime", timer->reactionTime}}; newTimerTextList.append(timerMap); } if(newTimerTextList != this->qmlTimers){ this->qmlTimers = newTimerTextList; emit timerTextChanged(); } // --- refresh next start action delay progress --- if(this->mode == LOCAL){ QString totalStr; if(nextStartAction == 0){ totalStr = appSettings->loadSetting("at_marks_delay"); } else if (nextStartAction == 1) { totalStr = appSettings->loadSetting("ready_delay"); } double remaining = this->nextStartActionTimer->remainingTime(); double total = totalStr.toDouble(); //qDebug() << "DELAY_PROG: " << "total: " << total << " remaining: " << remaining << " prog: " << remaining / total; if(remaining > 0){ this->nextStartActionDelayProgress = remaining / total; emit this->nextStartActionDelayProgressChanged(); } else { this->nextStartActionDelayProgress = 0; emit this->nextStartActionDelayProgressChanged(); } } else if (this->mode == REMOTE && this->state == IDLE) { this->nextStartActionDelayProgress = 0; emit this->nextStartActionDelayProgressChanged(); } this->timerTextRefreshTimer->start(); } // ------------------------- // --- functions for qml --- // ------------------------- int ClimbingRace::getState() { return this->state; } int ClimbingRace::getMode() { return this->mode; } QVariant ClimbingRace::getTimerTextList() { return this->qmlTimers; // QVariantList test; // QVariantMap test2 = {{"text", "1234"}, {"reacttime", 2.0}}; // test.append(test2); // return test; } double ClimbingRace::getNextStartActionDelayProgress() { return this->nextStartActionDelayProgress; } void ClimbingRace::writeSetting(QString key, QVariant value) { this->refreshMode(); switch (this->mode) { case LOCAL: this->appSettings->writeSetting(key, value); break; case REMOTE: this->baseConn->writeRemoteSetting(key, value.toString()); break; } } QString ClimbingRace::readSetting(QString key) { this->refreshMode(); switch (this->mode) { case LOCAL: return this->appSettings->loadSetting(key); case REMOTE: QVariantMap reply = this->baseConn->sendCommand(3001, key); if(reply["status"] != 200){ return "false"; } return reply["data"].toString(); } } bool ClimbingRace::connectBaseStation() { return this->baseConn->connectToHost(); } QString ClimbingRace::getBaseStationState() { return this->baseConn->getState(); } QVariant ClimbingRace::getBaseStationConnections() { return baseConn->getConnections(); }