From: APTX Date: Mon, 3 Aug 2009 00:38:34 +0000 (+0200) Subject: - Redesign of the state machine (incomplete). X-Git-Url: https://gitweb.tyo.aptx.org/?a=commitdiff_plain;h=eb802ad22c8937d01db5f2a9ab0f7dcddc6e4b1e;p=aniplayer-old.git - Redesign of the state machine (incomplete). - Added new method bool requiresSession() (returns true by default). - New UpdateCommand. - Polished AuthCommand. --- diff --git a/lib/anidbudpclient/abstractcommand.cpp b/lib/anidbudpclient/abstractcommand.cpp index a9cd0b5..ba529de 100644 --- a/lib/anidbudpclient/abstractcommand.cpp +++ b/lib/anidbudpclient/abstractcommand.cpp @@ -20,6 +20,11 @@ bool AbstractCommand::waitForResult() const return false; } +bool AbstractCommand::requiresSession() const +{ + return true; +} + void AbstractCommand::setRawReply(ReplyCode replyCode, const QString &reply, AniDBUdpClient *client) { Q_UNUSED(client); diff --git a/lib/anidbudpclient/abstractcommand.h b/lib/anidbudpclient/abstractcommand.h index 6f28e0f..5cbbad2 100644 --- a/lib/anidbudpclient/abstractcommand.h +++ b/lib/anidbudpclient/abstractcommand.h @@ -15,6 +15,8 @@ class ANIDBUDPCLIENTSHARED_EXPORT AbstractCommand : public QObject Q_OBJECT Q_ENUMS(ReplyCode); + Q_PROPERTY(ReplyCode replyCode READ replyCode); + public: enum ReplyCode @@ -159,6 +161,7 @@ public: virtual Command rawCommand() const; virtual bool waitForResult() const; + virtual bool requiresSession() const; virtual void setRawReply(ReplyCode replyCode, const QString &reply, AniDBUdpClient *client); virtual QString rawReply() const; diff --git a/lib/anidbudpclient/anidbudpclient.cpp b/lib/anidbudpclient/anidbudpclient.cpp index 1b1a65c..7666e99 100644 --- a/lib/anidbudpclient/anidbudpclient.cpp +++ b/lib/anidbudpclient/anidbudpclient.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -40,6 +41,8 @@ qDebug() << "Api instance init!"; authCommand = new AuthCommand(this); QObject::connect(authCommand, SIGNAL(replyReady(bool)), this, SLOT(doAuthenticate(bool))); + logoutCommand = new LogoutCommand(this); + setFloodInterval(5); stateMachine = new QtStateMachine(this); @@ -49,11 +52,11 @@ qDebug() << "Api instance init!"; connectingState = new QtState; connectedState = new QtState; authenticatingState = new QtState(connectedState); - authenticatedState = new QtState(connectedState); +// authenticatedState = new QtState(connectedState); idleState = new QtState(connectedState); idleTimeoutState = new QtState(connectedState); - logoutState = new QtState(connectedState); - loggedOutState = new QtState(connectedState); +// logoutState = new QtState(connectedState); +// loggedOutState = new QtState(connectedState); sendState = new QtState(connectedState); waitState = new QtState(connectedState); recieveState = new QtState; @@ -69,10 +72,10 @@ qDebug() << "Api instance init!"; stateMachine->setInitialState(disconnectedState); stateMachine->setErrorState(errorState); - connectedState->setInitialState(authenticatingState); - connectedHistoryState->setDefaultState(authenticatingState); - // ------------- Transitions --------------------- + connectedState->setInitialState(sendState); + connectedHistoryState->setDefaultState(sendState); + // ------------- Transitions --------------------- connectedState->addTransition(this, SIGNAL(startDisconnecting()), disconnectedState); connectedState->addTransition(socket, SIGNAL(readyRead()), recieveState); connectedState->addTransition(this, SIGNAL(sendFailed()), recieveFailState); @@ -91,17 +94,18 @@ qDebug() << "Api instance init!"; idleState->addTransition(this, SIGNAL(startSending()), sendState); idleState->addTransition(idleTimer, SIGNAL(timeout()), idleTimeoutState); +// idleState->addTransition(this, SIGNAL(startLogout()), logoutState); - idleTimeoutState->addTransition(this, SIGNAL(startLogout()), logoutState); - - logoutState->addTransition(this, SIGNAL(loggedOut()), loggedOutState); + idleTimeoutState->addTransition(this, SIGNAL(startSending()), sendState); +// +// logoutState->addTransition(this, SIGNAL(loggedOut()), loggedOutState); - recieveState->addTransition(this, SIGNAL(authenticated()), sendState); - recieveState->addTransition(this, SIGNAL(loggedOut()), loggedOutState); + recieveState->addTransition(this, SIGNAL(authenticated()), authenticatingState); +// recieveState->addTransition(this, SIGNAL(loggedOut()), loggedOutState); recieveState->addTransition(connectedHistoryState); - recieveFailState->addTransition(connectedHistoryState); + recieveFailState->addTransition(sendState); // ------------ END Transitions ------------------- // ------------- Methods --------------------- @@ -115,8 +119,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"); +// logoutState->invokeMethodOnEntry(this, "enterLogoutState"); +// loggedOutState->invokeMethodOnEntry(this, "enterLoggedOutState"); recieveState->invokeMethodOnExit(this, "exitRecieveState"); recieveFailState->invokeMethodOnEntry(this, "enterRecieveFailState"); @@ -129,6 +133,15 @@ AniDBUdpClient::~AniDBUdpClient() { disconnect(); clearCommandQueue(); + + if (!m_sessionId.isEmpty()) + { + while (commandTimer->isActive()) + QCoreApplication::processEvents(); + + sendCommand(new LogoutCommand); + socket->waitForBytesWritten(5); + } } QString AniDBUdpClient::host() const @@ -171,8 +184,7 @@ QString AniDBUdpClient::user() const void AniDBUdpClient::setUser(const QString &user) { - // All usernames are lowercaase - m_user = user.toLower(); + m_user = user; } QString AniDBUdpClient::pass() const @@ -225,6 +237,8 @@ QString AniDBUdpClient::errorString() const return m_errorString; } +// ------------------------------------------------------------------------------ + void AniDBUdpClient::enterErrorState() { qDebug() << "Entering Error State"; @@ -233,6 +247,8 @@ qDebug() << "Entering Error State"; void AniDBUdpClient::enterDisconnectedState() { qDebug() << "Entering Disconnected State"; + if (socket->state() == QAbstractSocket::BoundState) + socket->disconnectFromHost(); } void AniDBUdpClient::enterConnectingState() @@ -245,14 +261,13 @@ qDebug() << "Entering Connecting State"; { qDebug() << "Successful connection"; emit connected(); + return; } - else - { - m_error = BindError; - m_errorString = socket->errorString(); + + m_error = BindError; + m_errorString = socket->errorString(); qDebug() << QString("Bind on Address: %1 port: %2 failed").arg(m_hostAddress.toString()).arg(m_localPort); - emit connectionError(); - } + emit connectionError(); return; } QHostInfo::lookupHost(m_host, this, SLOT(lookedUp(QHostInfo))); @@ -271,7 +286,7 @@ qDebug() << "Host lookup finished"; } m_hostAddress = hostInfo.addresses()[0]; - // TODO + // TODO could it be nicer? enterConnectingState(); } @@ -286,8 +301,9 @@ void AniDBUdpClient::enterAuthenticatingState() qDebug() << "Entering Authenticating State"; authCommand->setUser(m_user); authCommand->setPass(m_pass); + authCommand->setCompression(m_compression); - enqueueCommand(authCommand, true); + enqueueControlCommand(authCommand, true); emit startSending(); } @@ -299,19 +315,35 @@ qDebug() << "doAuthenticate init"; qDebug() << "success!"; m_sessionId = authCommand->sessionId().toUtf8(); emit authenticated(); + return; } - else - { - m_error = AuthenticationError; - emit connectionError(); - } - authenticateOnConnect = false; + m_error = AuthenticationError; + m_errorString = authCommand->errorString(); + + emit connectionError(); } void AniDBUdpClient::enterSendState() { qDebug() << "Entering Send State"; + + // Do not send commands if wait time didn't end + // Happens if sendState is entered from recv* states. + if (commandTimer->isActive()) + { + emit commandSent(); + return; + } + + // Control commands (auth and such) have priority over any other commands. + if (!controlCommandQueue.isEmpty()) + { + sendCommand(controlCommandQueue.dequeue()); + emit commandSent(); + return; + } + if (commandQueue.isEmpty()) { emit queueEmpty(); @@ -324,19 +356,33 @@ qDebug() << "Entering Send State"; void AniDBUdpClient::enterWaitState() { qDebug() << "Entering Wait State"; - commandTimer->start(m_floodInterval); + + // Do not restart timer if it is active + if (!commandTimer->isActive()) + commandTimer->start(m_floodInterval); } void AniDBUdpClient::enterIdleState() { qDebug() << "Entering Idle State"; +qDebug() << m_idlePolicy; + + // If not loogged in, nothing has to be done in idle state. + if (m_sessionId.isEmpty()) + return; + switch (m_idlePolicy) { case KeepAliveIdlePolicy: + case LogoutIdlePolicy: idleTimer->start(UDP_API_INACTIVITY_LOGOUT * 1000); break; - case LogoutIdlePolicy: - emit startLogout(); + case KeepAliveIdlePolicy: + idleTimer->start(UDP_API_INACTIVITY_UPDATE * 1000); + break; + case ImmediateLogoutIdlePolicy: + enqueueControlCommand(logoutCommand); + emit startSending(); break; default: break; @@ -354,17 +400,21 @@ void AniDBUdpClient::enterIdleTiemoutState() qDebug() << "Entering IdleTiemout State"; switch (m_idlePolicy) { + case DoNothingIdlePolicy: + m_sessionId = ""; + break; case KeepAliveIdlePolicy: + enqueueControlCommand(updateCommand); + emit startSending(); default: break; } } - +/* void AniDBUdpClient::enterLogoutState() { qDebug() << "Entering Logout State"; - enqueueCommand(new LogoutCommand); - emit startSending(); + logout(); } void AniDBUdpClient::enterLoggedOutState() @@ -372,6 +422,7 @@ void AniDBUdpClient::enterLoggedOutState() qDebug() << "Entering LoggedOut State"; m_sessionId = ""; } +*/ void AniDBUdpClient::exitRecieveState() { @@ -410,13 +461,15 @@ qDebug() << "COMPRESSED DATAGRAM = " << tmp; QByteArray commandId = tmp.mid(0, 5); - // Do not parse reply for commands not waiting for a reply. + // Do not parse reply for unknown commands. if (!sentCommands.contains(commandId)) { -qDebug() << QString("Command with id: %1 is not waiting for a reply, discarding").arg(commandId.constData()); + qDebug() << QString("Unknown command with id: %1, discarding") + .arg(commandId.constData()); continue; } -qDebug() << QString("Sending reply to command with id: %1").arg(commandId.constData()); + qDebug() << QString("Sending reply to command with id: %1") + .arg(commandId.constData()); // tag + space = 5 + 1 QByteArray replyCodeText = tmp.mid(6, 3); @@ -429,7 +482,9 @@ qDebug() << QString("Sending reply to command with id: %1").arg(commandId.constD replyCode = AbstractCommand::ReplyCode(replyCodeInt); } - AbstractCommand *cmd = sentCommands.take(commandId); + CommandData *commandData = sentCommands.take(commandId); + AbstractCommand *cmd = commandData->command; + delete commandData; // Requeue command and reauthenticate if not logged in. switch (replyCode) @@ -514,7 +569,8 @@ qDebug() << QString("Sending RAW command: %1").arg(command.constData()); void AniDBUdpClient::logout() { - emit startLogout(); + enqueueCommand(new LogoutCommand); + emit startSending(); } void AniDBUdpClient::enqueueCommand(AbstractCommand *command, bool first) @@ -531,8 +587,32 @@ void AniDBUdpClient::enqueueCommand(AbstractCommand *command, bool first) emit startSending(); } -void AniDBUdpClient::sendCommand(AbstractCommand *command) +void AniDBUdpClient::enqueueControlCommand(AbstractCommand *command, bool first) +{ + if (first) + { + controlCommandQueue.push_front(command); + } + else + { + controlCommandQueue.enqueue(command); + } + + emit startSending(); +} + +void AniDBUdpClient::sendCommand(AbstractCommand *command, bool controlCommand) { + if (m_sessionId.isEmpty() && command->requiresSession()) + { + if (controlCommand) + enqueueControlCommand(command, true); + else + enqueueCommand(command, true); + emit startAuthentication(); + return; + } + Command cmdPair = command->rawCommand(); QByteArray datagram = buildCmd(cmdPair.first, cmdPair.second); @@ -546,7 +626,7 @@ void AniDBUdpClient::sendCommand(AbstractCommand *command) if (command->waitForResult()) { - sentCommands[commandId] = command; + sentCommands[commandId] = new CommandData(command, controlCommand); } else { @@ -604,3 +684,11 @@ QByteArray AniDBUdpClient::nextCommandId(int len) qDebug() << QString("Generated id %1").arg(result.constData()); return result; } + +AniDBUdpClient::CommandData::CommandData(AbstractCommand *command, bool controlCommand) +{ + this->command = command; + this->controlCommand = controlCommand; +} + + diff --git a/lib/anidbudpclient/anidbudpclient.h b/lib/anidbudpclient/anidbudpclient.h index 10e0e33..824060a 100644 --- a/lib/anidbudpclient/anidbudpclient.h +++ b/lib/anidbudpclient/anidbudpclient.h @@ -21,9 +21,20 @@ class QTimer; class AbstractCommand; class AuthCommand; +class LogoutCommand; +class UpdateCommand; class ANIDBUDPCLIENTSHARED_EXPORT AniDBUdpClient : public QObject { + struct CommandData + { + AbstractCommand *command; + bool controlCommand; + QTimer timeout; + + CommandData(AbstractCommand *command, bool controlCommand = false); + }; + Q_OBJECT Q_ENUMS(State Error IdlePolicy AbstractCommand::ReplyCode); @@ -62,6 +73,7 @@ public: { DoNothingIdlePolicy, LogoutIdlePolicy, + ImmediateLogoutIdlePolicy, KeepAliveIdlePolicy, }; @@ -159,7 +171,8 @@ private slots: void logout(); void enqueueCommand(AbstractCommand *command, bool first = false); - void sendCommand(AbstractCommand *command); + void enqueueControlCommand(AbstractCommand *command, bool first = false); + void sendCommand(AbstractCommand *command, bool controlCommand = false); private: QByteArray buildCmd(const QString &cmd, const QVariantMap &args); @@ -170,7 +183,8 @@ private: QTimer *replyTimeoutTimer; QQueue commandQueue; - QMap sentCommands; + QQueue controlCommandQueue; + QMap sentCommands; QUdpSocket *socket; @@ -199,11 +213,15 @@ private: bool disconnecting; AuthCommand *authCommand; - bool authenticateOnConnect; + LogoutCommand *logoutCommand; + UpdateCommand *updateCommand; static const int UDP_DATAGRAM_MAXIMUM_SIZE = 1400; - static const int UDP_API_INACTIVITY_LOGOUT = 30 * 60; + + // These are in seconds + static const int UDP_API_INACTIVITY_UPDATE = 30 * 60; + static const int UDP_API_INACTIVITY_LOGOUT = 35 * 60; QtStateMachine *stateMachine; QtState *errorState; diff --git a/lib/anidbudpclient/anidbudpclient.pro b/lib/anidbudpclient/anidbudpclient.pro index c3f0267..f6aeacc 100644 --- a/lib/anidbudpclient/anidbudpclient.pro +++ b/lib/anidbudpclient/anidbudpclient.pro @@ -22,12 +22,14 @@ SOURCES += anidbudpclient.cpp \ authcommand.cpp \ rawcommand.cpp \ mylistaddcommand.cpp \ - logoutcommand.cpp + logoutcommand.cpp \ + uptimecommand.cpp HEADERS += anidbudpclient.h \ anidbudpclient_global.h \ abstractcommand.h \ authcommand.h \ rawcommand.h \ mylistaddcommand.h \ - logoutcommand.h + logoutcommand.h \ + uptimecommand.h include(../../lib/qtstatemachine/src/qtstatemachine.pri) diff --git a/lib/anidbudpclient/authcommand.cpp b/lib/anidbudpclient/authcommand.cpp index 871aba6..3b942c6 100644 --- a/lib/anidbudpclient/authcommand.cpp +++ b/lib/anidbudpclient/authcommand.cpp @@ -7,39 +7,74 @@ AuthCommand::AuthCommand(QObject *parent) : AbstractCommand(parent) m_compression = false; } -AuthCommand::AuthCommand(QString user, QString pass, QObject *parent) : AbstractCommand(parent) +AuthCommand::AuthCommand(const QString &user, const QString &pass, QObject *parent) : AbstractCommand(parent) { m_user = user; m_pass = pass; } +QString AuthCommand::user() const +{ + return m_user; +} + void AuthCommand::setUser(const QString &user) { m_user = user; } +QString AuthCommand::pass() const +{ + return m_pass; +} + void AuthCommand::setPass(const QString &pass) { m_pass = pass; } +bool AuthCommand::compression() const +{ + return m_compression; +} + void AuthCommand::setCompression(bool compress) { m_compression = compress; } +QString AuthCommand::sessionId() const +{ + return m_sessionId; +} + +QString AuthCommand::errorString() const +{ + return m_errorString; +} + +void AuthCommand::clearError() +{ + m_errorString = ""; +} + bool AuthCommand::waitForResult() const { return true; } +bool AuthCommand::requiresSession() const +{ + return false; +} + Command AuthCommand::rawCommand() const { Command command; command.first = "AUTH"; - command.second["user"] = m_user; + command.second["user"] = m_user.toLower(); // All usernames are lower case command.second["pass"] = m_pass; command.second["protover"] = AniDBUdpClient::protocolVersion; command.second["client"] = AniDBUdpClient::clientName.constData(); @@ -49,11 +84,6 @@ Command AuthCommand::rawCommand() const return command; } -QString AuthCommand::sessionId() const -{ - return m_sessionId; -} - void AuthCommand::setRawReply(ReplyCode replyCode, const QString &reply, AniDBUdpClient *client) { qDebug() << replyCode; @@ -61,13 +91,25 @@ qDebug() << replyCode; switch(replyCode) { + case LOGIN_ACCEPTED: case LOGIN_ACCEPTED_NEW_VER: + m_errorString = ""; m_sessionId = m_rawReply.mid(0, m_rawReply.indexOf(" ")); emit replyReady(true); break; + case LOGIN_FAILED: + m_errorString = tr("Username and/or password incorrect"); + break; + case CLIENT_VERSION_OUTDATED: + m_errorString = tr("Client version outdated"); + break; + case CLIENT_BANNED: + m_errorString = tr("Banned: %1").arg(m_rawReply.mid(m_rawReply.indexOf("BANNED") + 1)); + break; default: qDebug() << "ERROR CODE: " << replyCode; + m_errorString = tr("Unknown errorString (CODE: %1)").arg(m_replyCode); emit replyReady(false); } } diff --git a/lib/anidbudpclient/authcommand.h b/lib/anidbudpclient/authcommand.h index ab88016..5f855de 100644 --- a/lib/anidbudpclient/authcommand.h +++ b/lib/anidbudpclient/authcommand.h @@ -7,20 +7,36 @@ class AuthCommand : public AbstractCommand { Q_OBJECT + 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(QString sessionId READ sessionId); + Q_PROPERTY(QString errorString READ errorString RESET clearError); + public: AuthCommand(QObject *parent = 0); - AuthCommand(QString user, QString pass, QObject *parent = 0); + AuthCommand(const QString &user, const QString &pass, QObject *parent = 0); + QString user() const; void setUser(const QString &user); + + QString pass() const; void setPass(const QString &pass); + bool compression() const; void setCompression(bool compress); - bool waitForResult() const; - Command rawCommand() const; QString sessionId() const; + QString errorString() const; + void clearError(); + + bool waitForResult() const; + bool requiresSession() const; + + Command rawCommand() const; void setRawReply(ReplyCode replyCode, const QString &reply, AniDBUdpClient *client); @@ -29,6 +45,7 @@ private: QString m_user; QString m_pass; QString m_sessionId; + QString m_errorString; bool m_compression; }; diff --git a/lib/anidbudpclient/uptimecommand.cpp b/lib/anidbudpclient/uptimecommand.cpp new file mode 100644 index 0000000..e5f5358 --- /dev/null +++ b/lib/anidbudpclient/uptimecommand.cpp @@ -0,0 +1,39 @@ +#include "uptimecommand.h" + +UptimeCommand::UptimeCommand() +{ + m_uptime = 0; +} + +int UptimeCommand::uptime() +{ + return m_uptime; +} + +Command UptimeCommand::rawCommand() const +{ + Command command; + command.first = "UPTIME"; + return command; +} + +void UptimeCommand::setRawReply(ReplyCode replyCode, const QString &reply, AniDBUdpClient *client) +{ + switch (replyCode) + { + case UPTIME: + { + QString uptimeText = reply.mid(reply.indexOf(' ')); + bool ok = false; + m_uptime = uptimeText.toInt(&ok, 10); + if (!ok) + m_uptime = 0; + + emit replyReady(ok); + } + break; + default: + emit replyReady(false); + break; + } +} diff --git a/lib/anidbudpclient/uptimecommand.h b/lib/anidbudpclient/uptimecommand.h new file mode 100644 index 0000000..0d8ef89 --- /dev/null +++ b/lib/anidbudpclient/uptimecommand.h @@ -0,0 +1,23 @@ +#ifndef UPTIMECOMMAND_H +#define UPTIMECOMMAND_H + +#include "abstractcommand.h" + +class ANIDBUDPCLIENTSHARED_EXPORT UptimeCommand : public AbstractCommand +{ + Q_OBJECT + + Q_PROPERTY(int uptime READ uptime); +public: + UptimeCommand(); + + int uptime(); + + Command rawCommand() const; + void setRawReply(ReplyCode replyCode, const QString &reply, AniDBUdpClient *client); + +private: + int m_uptime; +}; + +#endif // UPTIMECOMMAND_H diff --git a/src/videowindow.cpp b/src/videowindow.cpp index ef7f50d..f406665 100644 --- a/src/videowindow.cpp +++ b/src/videowindow.cpp @@ -46,7 +46,6 @@ VideoWindow::VideoWindow(QWidget *parent) : QMainWindow(parent) #ifndef NO_ANIDBUDPCLIENT anidb = new AniDBUdpClient(this); - anidb->setIdlePolicy(AniDBUdpClient::LogoutIdlePolicy); addCommand = 0; m_marked = true; m_automark = 0; @@ -160,16 +159,15 @@ VideoWindow::VideoWindow(QWidget *parent) : QMainWindow(parent) videoSceneMenu->volumeSlider()->setAudioOutput(videoPlayer->audioOutput()); #endif -#ifndef BROWSERPLUGIN_BUILD - move(50, 50); -#endif setWindowTitle(qApp->applicationName() + " v" + qApp->applicationVersion()); +#ifndef BROWSERPLUGIN_BUILD loadSettings(); +#endif #ifndef NO_ANIDBUDPCLIENT anidb->setCompression(true); - anidb->setIdlePolicy(AniDBUdpClient::LogoutIdlePolicy); + anidb->setIdlePolicy(AniDBUdpClient::ImmediateLogoutIdlePolicy); #endif }