553 lines
14 KiB
C++
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();
|
|
}
|