This repository has been archived on 2024-06-03. You can view files and clone it, but cannot push or open issues or pull requests.
app/sources/climbingrace.cpp
2019-03-07 22:31:23 +01:00

553 lines
14 KiB
C++

#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("localhost");
connect(this->baseConn, &BaseConn::stateChanged, this, &ClimbingRace::baseStationStateChanged);
this->speedTimers.append( new SpeedTimer );
this->player = new QMediaPlayer;
this->nextStartActionTimer = new QTimer(this);
nextStartActionTimer->setSingleShot(true);
this->baseStationSyncTimer = new QTimer();
this->baseStationSyncTimer->setInterval(10);
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;
}
case 2:
{
// case RUNNING
if(speedTimers[0]->state != 2){
speedTimers[0]->setState(SpeedTimer::RUNNING);
}
// get current time
tmpReply = this->baseConn->sendCommand(2001, 0);
if(tmpReply["status"] != 200){
//handle error!!
qDebug() << "+ --- getting current time (timer 0) from basestation failed";
this->baseStationSyncTimer->start();
return;
}
else {
speedTimers[0]->stoppedTime = tmpReply["data"].toInt();
}
// get current time
tmpReply = this->baseConn->sendCommand(2003, 0);
if(tmpReply["status"] != 200){
//handle error!!
qDebug() << "+ --- getting reaction time (timer 0) from basestation failed";
this->baseStationSyncTimer->start();
return;
}
else {
speedTimers[0]->reactionTime = tmpReply["data"].toInt();
}
break;
}
case 3:
{
// case STOPPED
if(speedTimers[0]->state != 3){
speedTimers[0]->setState(SpeedTimer::STOPPED);
}
// get current time
tmpReply = this->baseConn->sendCommand(2001, 0);
if(tmpReply["status"] != 200){
//handle error!!
qDebug() << "+ --- getting current time (timer 0) from basestation failed";
return;
}
else {
speedTimers[0]->stoppedTime = tmpReply["data"].toInt();
}
// get current time
tmpReply = this->baseConn->sendCommand(2003, 0);
if(tmpReply["status"] != 200){
//handle error!!
qDebug() << "+ --- getting current time (timer 0) from basestation failed";
this->baseStationSyncTimer->start();
return;
}
else {
speedTimers[0]->reactionTime = tmpReply["data"].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() {
if(this->baseConn->state == "connected"){
this->mode = REMOTE;
}
else {
this->mode = LOCAL;
}
}
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;
}
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();
}