]> Some of my projects - anidbudpclient.git/commitdiff
- Finished redesign of state machine
authorAPTX <mail@example.com>
Mon, 3 Aug 2009 15:42:59 +0000 (17:42 +0200)
committerAPTX <mail@example.com>
Mon, 3 Aug 2009 15:42:59 +0000 (17:42 +0200)
- Commands which didn't receive a reply in a given time frame will be resent.

anidbudpclient.cpp
anidbudpclient.h
authcommand.h
logoutcommand.cpp
logoutcommand.h
uptimecommand.cpp
uptimecommand.h

index 7666e99816f1691f19c7f57058361f3846500278..5b9d207ec015a2be3f4f7120f1d293024df691a8 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <rawcommand.h>
 #include <logoutcommand.h>
+#include <uptimecommand.h>
 
 #include <QtDebug>
 
@@ -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);
+}
index 824060a51f84d16e964a4187c7ff1ae8a39d7379..98ed3e11ee0efff3841ac824604a8f506c7684e3 100644 (file)
@@ -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
index 5f855dede6a19db5fe7a3ac3fb4092bc1c1da66e..b22d10d6b4c4ea119ab58df1930629f21db655ee 100644 (file)
@@ -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);
index 10039b6fed1f295bb6961047ea061ebf9a39106c..a6671e8364172bdc8455ebb312aa70feae1e37b6 100644 (file)
@@ -1,6 +1,6 @@
 #include "logoutcommand.h"
 
-LogoutCommand::LogoutCommand()
+LogoutCommand::LogoutCommand(QObject *parent) : AbstractCommand(parent)
 {
 }
 
index 250a1af8e99db4a1ca2f5fafea5937afae35d4d8..bb4350bf194b844aa9fd0e5f981461ef6e1a6cbf 100644 (file)
@@ -7,7 +7,7 @@ class ANIDBUDPCLIENTSHARED_EXPORT LogoutCommand : public AbstractCommand
 {
        Q_OBJECT
 public:
-       LogoutCommand();
+       LogoutCommand(QObject *parent = 0);
        Command rawCommand() const;
 };
 
index e5f5358ec751c9b602393b192afc29850a8241ae..096aa34e1b868737fc3463a6c7b651825095987b 100644 (file)
@@ -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:
index 0d8ef89e2a5d7767d2f68e9a1fe51631f97f1cb2..98fbd2bf5aa69e41f1a37832d16efd378087efc4 100644 (file)
@@ -9,7 +9,7 @@ class ANIDBUDPCLIENTSHARED_EXPORT UptimeCommand : public AbstractCommand
 
        Q_PROPERTY(int uptime READ uptime);
 public:
-       UptimeCommand();
+       UptimeCommand(QObject *parent = 0);
 
        int uptime();