From: APTX Date: Mon, 3 Aug 2009 15:42:59 +0000 (+0200) Subject: - Finished redesign of state machine X-Git-Url: https://gitweb.aptx.org/?a=commitdiff_plain;h=b23b69968d14d97b81412299b5ee84f0532a9e57;p=aniplayer-old.git - Finished redesign of state machine - Commands which didn't receive a reply in a given time frame will be resent. --- diff --git a/lib/anidbudpclient/anidbudpclient.cpp b/lib/anidbudpclient/anidbudpclient.cpp index 7666e99..5b9d207 100644 --- a/lib/anidbudpclient/anidbudpclient.cpp +++ b/lib/anidbudpclient/anidbudpclient.cpp @@ -11,6 +11,7 @@ #include #include +#include #include @@ -27,12 +28,15 @@ qDebug() << "Api instance init!"; m_idlePolicy = DoNothingIdlePolicy; disconnecting = false; - authCommand = 0; + commandsTimedOut = 0; socket = new QUdpSocket(this); commandTimer = new QTimer(this); + commandTimer->setSingleShot(true); + idleTimer = new QTimer(this); + idleTimer->setSingleShot(true); m_localPort = 9001; m_host = "api.anidb.info"; @@ -42,6 +46,7 @@ qDebug() << "Api instance init!"; QObject::connect(authCommand, SIGNAL(replyReady(bool)), this, SLOT(doAuthenticate(bool))); logoutCommand = new LogoutCommand(this); + uptimeCommand = new UptimeCommand(this); setFloodInterval(5); @@ -52,11 +57,8 @@ qDebug() << "Api instance init!"; connectingState = new QtState; connectedState = new QtState; authenticatingState = new QtState(connectedState); -// authenticatedState = new QtState(connectedState); idleState = new QtState(connectedState); idleTimeoutState = new QtState(connectedState); -// logoutState = new QtState(connectedState); -// loggedOutState = new QtState(connectedState); sendState = new QtState(connectedState); waitState = new QtState(connectedState); recieveState = new QtState; @@ -89,19 +91,16 @@ qDebug() << "Api instance init!"; sendState->addTransition(this, SIGNAL(queueEmpty()), idleState); sendState->addTransition(this, SIGNAL(commandSent()), waitState); + sendState->addTransition(this, SIGNAL(startAuthentication()), authenticatingState); waitState->addTransition(commandTimer, SIGNAL(timeout()), sendState); idleState->addTransition(this, SIGNAL(startSending()), sendState); idleState->addTransition(idleTimer, SIGNAL(timeout()), idleTimeoutState); -// idleState->addTransition(this, SIGNAL(startLogout()), logoutState); idleTimeoutState->addTransition(this, SIGNAL(startSending()), sendState); -// -// logoutState->addTransition(this, SIGNAL(loggedOut()), loggedOutState); recieveState->addTransition(this, SIGNAL(authenticated()), authenticatingState); -// recieveState->addTransition(this, SIGNAL(loggedOut()), loggedOutState); recieveState->addTransition(connectedHistoryState); @@ -119,10 +118,8 @@ qDebug() << "Api instance init!"; idleState->invokeMethodOnEntry(this, "enterIdleState"); idleState->invokeMethodOnExit(this, "exitIdleState"); idleTimeoutState->invokeMethodOnEntry(this, "enterIdleTiemoutState"); -// logoutState->invokeMethodOnEntry(this, "enterLogoutState"); -// loggedOutState->invokeMethodOnEntry(this, "enterLoggedOutState"); - recieveState->invokeMethodOnExit(this, "exitRecieveState"); + recieveState->invokeMethodOnEntry(this, "enterRecieveState"); recieveFailState->invokeMethodOnEntry(this, "enterRecieveFailState"); // ------------ END Methods ------------------- @@ -134,12 +131,26 @@ AniDBUdpClient::~AniDBUdpClient() disconnect(); clearCommandQueue(); + foreach (CommandData *cmd, sentCommands) + { + if (cmd->command->waitForResult()) + { + delete cmd->command; + } + else + { + // Send CLIENT_DESTROYED to indicate that no real reply will come. + cmd->command->setRawReply(AbstractCommand::CLIENT_DESTROYED, "", this); + } + } + qDeleteAll(sentCommands); + if (!m_sessionId.isEmpty()) { while (commandTimer->isActive()) QCoreApplication::processEvents(); - sendCommand(new LogoutCommand); + sendCommand(new LogoutCommand, true); socket->waitForBytesWritten(5); } } @@ -299,12 +310,17 @@ qDebug() << "Entering Connected State"; void AniDBUdpClient::enterAuthenticatingState() { qDebug() << "Entering Authenticating State"; - authCommand->setUser(m_user); - authCommand->setPass(m_pass); - authCommand->setCompression(m_compression); - enqueueControlCommand(authCommand, true); - emit startSending(); + if (m_sessionId.isEmpty()) + { + authCommand->setUser(m_user); + authCommand->setPass(m_pass); + authCommand->setCompression(m_compression); + + enqueueControlCommand(authCommand, true); + return; + } + emit authenticated(); } void AniDBUdpClient::doAuthenticate(bool success) @@ -332,6 +348,7 @@ qDebug() << "Entering Send State"; // Happens if sendState is entered from recv* states. if (commandTimer->isActive()) { +qDebug() << "commandTimer activein sendState"; emit commandSent(); return; } @@ -339,7 +356,8 @@ qDebug() << "Entering Send State"; // Control commands (auth and such) have priority over any other commands. if (!controlCommandQueue.isEmpty()) { - sendCommand(controlCommandQueue.dequeue()); +qDebug() << "Sending Control Command"; + sendCommand(controlCommandQueue.dequeue(), true); emit commandSent(); return; } @@ -373,7 +391,6 @@ qDebug() << m_idlePolicy; switch (m_idlePolicy) { - case KeepAliveIdlePolicy: case LogoutIdlePolicy: idleTimer->start(UDP_API_INACTIVITY_LOGOUT * 1000); break; @@ -382,7 +399,6 @@ qDebug() << m_idlePolicy; break; case ImmediateLogoutIdlePolicy: enqueueControlCommand(logoutCommand); - emit startSending(); break; default: break; @@ -404,27 +420,13 @@ qDebug() << "Entering IdleTiemout State"; m_sessionId = ""; break; case KeepAliveIdlePolicy: - enqueueControlCommand(updateCommand); - emit startSending(); + enqueueControlCommand(uptimeCommand); default: break; } } -/* -void AniDBUdpClient::enterLogoutState() -{ -qDebug() << "Entering Logout State"; - logout(); -} - -void AniDBUdpClient::enterLoggedOutState() -{ -qDebug() << "Entering LoggedOut State"; - m_sessionId = ""; -} -*/ -void AniDBUdpClient::exitRecieveState() +void AniDBUdpClient::enterRecieveState() { qDebug() << "Entering Recieve State"; while (socket->hasPendingDatagrams()) @@ -461,6 +463,8 @@ qDebug() << "COMPRESSED DATAGRAM = " << tmp; QByteArray commandId = tmp.mid(0, 5); + commandsTimedOut = 0; + // Do not parse reply for unknown commands. if (!sentCommands.contains(commandId)) { @@ -484,6 +488,7 @@ qDebug() << "COMPRESSED DATAGRAM = " << tmp; CommandData *commandData = sentCommands.take(commandId); AbstractCommand *cmd = commandData->command; + bool controlCommand = commandData->controlCommand; delete commandData; // Requeue command and reauthenticate if not logged in. @@ -492,14 +497,45 @@ qDebug() << "COMPRESSED DATAGRAM = " << tmp; case AbstractCommand::LOGIN_FIRST: case AbstractCommand::INVALID_SESSION: qDebug() << "LOGIN FIRST required, authing"; - enqueueCommand(cmd); + m_sessionId = ""; + if (controlCommand) + enqueueControlCommand(cmd); + else + enqueueCommand(cmd); emit startAuthentication(); goto continueLoop; break; case AbstractCommand::LOGGED_OUT: - emit loggedOut(); + m_sessionId = ""; + break; + case AbstractCommand::BANNED: + m_error = BannedError; + m_errorString = reply.mid(10); + emit connectionError(); + goto endLoop; break; + case AbstractCommand::INTERNAL_SERVER_ERROR: + m_error = ServerError; + m_errorString = tr("Internal Server Error"); + emit connectionError(); + goto endLoop; + break; + case AbstractCommand::ANIDB_OUT_OF_SERVICE: + m_error = ServerError; + m_errorString = tr("AniDB out of service"); + emit connectionError(); + goto endLoop; + break; + case AbstractCommand::SERVER_BUSY: + m_error = ServerError; + m_errorString = tr("Server busy. Try again later. Wait at least 30 minutes."); + emit connectionError(); + goto endLoop; default: + if (replyCode > 601 && replyCode < 700) + { + qDebug() << QString("SERVER ERROR %1").arg(replyCode); + } break; } // tag + space + replyCode + space = 5 + 1 + 3 + 1 @@ -509,6 +545,8 @@ qDebug() << "LOGIN FIRST required, authing"; continueLoop: ; } +endLoop: + ; } void AniDBUdpClient::enterRecieveFailState() @@ -521,7 +559,22 @@ qDebug() << "Entering RecieveFail State"; void AniDBUdpClient::clearCommandQueue() { // Delete all unsent commands that are managed by the client. - while (!commandQueue.empty()) + while (!controlCommandQueue.isEmpty()) + { + AbstractCommand *cmd = commandQueue.dequeue(); + if (!cmd->waitForResult()) + { + // These would be deleted anyway + delete cmd; + } + else + { + // Send CLIENT_DESTROYED to indicate that no real reply will come. + cmd->setRawReply(AbstractCommand::CLIENT_DESTROYED, "", this); + } + } + + while (!commandQueue.isEmpty()) { AbstractCommand *cmd = commandQueue.dequeue(); if (!cmd->waitForResult()) @@ -569,8 +622,28 @@ qDebug() << QString("Sending RAW command: %1").arg(command.constData()); void AniDBUdpClient::logout() { - enqueueCommand(new LogoutCommand); - emit startSending(); + if (!m_sessionId.isEmpty()) + enqueueControlCommand(logoutCommand); +} + +void AniDBUdpClient::commandTimeout(const QByteArray &commandId) +{ +qDebug() << commandId << "timed out"; + if (!sentCommands.contains(commandId)) + return; + + CommandData *cmd = sentCommands.take(commandId); + + if (cmd->controlCommand) + enqueueControlCommand(cmd->command); + else + enqueueCommand(cmd->command); + + delete cmd; + + ++commandsTimedOut; + + emit sendFailed(); } void AniDBUdpClient::enqueueCommand(AbstractCommand *command, bool first) @@ -624,14 +697,10 @@ void AniDBUdpClient::sendCommand(AbstractCommand *command, bool controlCommand) if (m_sessionId.length()) datagram += "&s=" + m_sessionId; - if (command->waitForResult()) - { - sentCommands[commandId] = new CommandData(command, controlCommand); - } - else - { - command->deleteLater(); - } + CommandData *cmd = new CommandData(command, commandId, controlCommand); + QObject::connect(cmd, SIGNAL(timeout(QByteArray)), this, SLOT(commandTimeout(QByteArray))); + + sentCommands[commandId] = cmd; qDebug() << QString("SENDING datagram:\n\t%1\nto: %2 ([%3]:%4)") .arg(datagram.constData()) @@ -685,10 +754,17 @@ qDebug() << QString("Generated id %1").arg(result.constData()); return result; } -AniDBUdpClient::CommandData::CommandData(AbstractCommand *command, bool controlCommand) +CommandData::CommandData(AbstractCommand *command, const QByteArray &commandId, bool controlCommand) : QObject() { this->command = command; this->controlCommand = controlCommand; -} + this->commandId = commandId; + connect(&timer, SIGNAL(timeout()), this, SLOT(timerTimeout())); + timer.start(AniDBUdpClient::UDP_API_COMMAND_TIMEOUT * 1000); +} +void CommandData::timerTimeout() +{ + emit timeout(commandId); +} diff --git a/lib/anidbudpclient/anidbudpclient.h b/lib/anidbudpclient/anidbudpclient.h index 824060a..98ed3e1 100644 --- a/lib/anidbudpclient/anidbudpclient.h +++ b/lib/anidbudpclient/anidbudpclient.h @@ -22,18 +22,13 @@ class QTimer; class AbstractCommand; class AuthCommand; class LogoutCommand; -class UpdateCommand; +class UptimeCommand; + +class CommandData; class ANIDBUDPCLIENTSHARED_EXPORT AniDBUdpClient : public QObject { - struct CommandData - { - AbstractCommand *command; - bool controlCommand; - QTimer timeout; - - CommandData(AbstractCommand *command, bool controlCommand = false); - }; + friend class CommandData; Q_OBJECT Q_ENUMS(State Error IdlePolicy AbstractCommand::ReplyCode); @@ -66,6 +61,7 @@ public: HostUnreachableError, AuthenticationError, BannedError, + ServerError, UnknownError, }; @@ -140,10 +136,6 @@ signals: void sendFailed(); void connectionError(); - void notLoggedInError(); - - void startLogout(); - void loggedOut(); private slots: void enterErrorState(); @@ -162,19 +154,20 @@ private slots: void enterIdleState(); void exitIdleState(); void enterIdleTiemoutState(); - void enterLogoutState(); - void enterLoggedOutState(); - void exitRecieveState(); + void enterRecieveState(); void enterRecieveFailState(); void logout(); + void commandTimeout(const QByteArray &commandId); + +private: void enqueueCommand(AbstractCommand *command, bool first = false); void enqueueControlCommand(AbstractCommand *command, bool first = false); void sendCommand(AbstractCommand *command, bool controlCommand = false); -private: + QByteArray buildCmd(const QString &cmd, const QVariantMap &args); QByteArray nextCommandId(int len = 5); @@ -212,9 +205,11 @@ private: bool disconnecting; + int commandsTimedOut; + AuthCommand *authCommand; LogoutCommand *logoutCommand; - UpdateCommand *updateCommand; + UptimeCommand *uptimeCommand; static const int UDP_DATAGRAM_MAXIMUM_SIZE = 1400; @@ -223,13 +218,14 @@ private: static const int UDP_API_INACTIVITY_UPDATE = 30 * 60; static const int UDP_API_INACTIVITY_LOGOUT = 35 * 60; + static const int UDP_API_COMMAND_TIMEOUT = 10; + QtStateMachine *stateMachine; QtState *errorState; QtState *disconnectedState; QtState *connectingState; QtState *connectedState; QtState *authenticatingState; - QtState *authenticatedState; QtState *logoutState; QtState *loggedOutState; @@ -244,4 +240,22 @@ private: QtHistoryState *connectedHistoryState; }; +class CommandData : QObject +{ + friend class AniDBUdpClient; + + Q_OBJECT +public: + AbstractCommand *command; + bool controlCommand; + QByteArray commandId; + QTimer timer; + + CommandData(AbstractCommand *command, const QByteArray &commandId, bool controlCommand = false); +signals: + void timeout(QByteArray commandId); +private slots: + void timerTimeout(); +}; + #endif // ANIDBUDPCLIENT_H diff --git a/lib/anidbudpclient/authcommand.h b/lib/anidbudpclient/authcommand.h index 5f855de..b22d10d 100644 --- a/lib/anidbudpclient/authcommand.h +++ b/lib/anidbudpclient/authcommand.h @@ -9,7 +9,7 @@ class AuthCommand : public AbstractCommand Q_PROPERTY(QString user READ user WRITE setUser); Q_PROPERTY(QString pass READ pass WRITE setPass); - Q_PROPERTY(bool compression READ compression WRITE setCOmpression); + Q_PROPERTY(bool compression READ compression WRITE setCompression); Q_PROPERTY(QString sessionId READ sessionId); Q_PROPERTY(QString errorString READ errorString RESET clearError); diff --git a/lib/anidbudpclient/logoutcommand.cpp b/lib/anidbudpclient/logoutcommand.cpp index 10039b6..a6671e8 100644 --- a/lib/anidbudpclient/logoutcommand.cpp +++ b/lib/anidbudpclient/logoutcommand.cpp @@ -1,6 +1,6 @@ #include "logoutcommand.h" -LogoutCommand::LogoutCommand() +LogoutCommand::LogoutCommand(QObject *parent) : AbstractCommand(parent) { } diff --git a/lib/anidbudpclient/logoutcommand.h b/lib/anidbudpclient/logoutcommand.h index 250a1af..bb4350b 100644 --- a/lib/anidbudpclient/logoutcommand.h +++ b/lib/anidbudpclient/logoutcommand.h @@ -7,7 +7,7 @@ class ANIDBUDPCLIENTSHARED_EXPORT LogoutCommand : public AbstractCommand { Q_OBJECT public: - LogoutCommand(); + LogoutCommand(QObject *parent = 0); Command rawCommand() const; }; diff --git a/lib/anidbudpclient/uptimecommand.cpp b/lib/anidbudpclient/uptimecommand.cpp index e5f5358..096aa34 100644 --- a/lib/anidbudpclient/uptimecommand.cpp +++ b/lib/anidbudpclient/uptimecommand.cpp @@ -1,6 +1,6 @@ #include "uptimecommand.h" -UptimeCommand::UptimeCommand() +UptimeCommand::UptimeCommand(QObject *parent) : AbstractCommand(parent) { m_uptime = 0; } @@ -19,6 +19,8 @@ Command UptimeCommand::rawCommand() const void UptimeCommand::setRawReply(ReplyCode replyCode, const QString &reply, AniDBUdpClient *client) { + Q_UNUSED(client); + switch (replyCode) { case UPTIME: diff --git a/lib/anidbudpclient/uptimecommand.h b/lib/anidbudpclient/uptimecommand.h index 0d8ef89..98fbd2b 100644 --- a/lib/anidbudpclient/uptimecommand.h +++ b/lib/anidbudpclient/uptimecommand.h @@ -9,7 +9,7 @@ class ANIDBUDPCLIENTSHARED_EXPORT UptimeCommand : public AbstractCommand Q_PROPERTY(int uptime READ uptime); public: - UptimeCommand(); + UptimeCommand(QObject *parent = 0); int uptime();