implement SIT and document it
authorKonrad Rosenbaum <konrad@silmor.de>
Mon, 26 May 2014 19:55:54 +0000 (21:55 +0200)
committerKonrad Rosenbaum <konrad@silmor.de>
Tue, 27 May 2014 19:04:15 +0000 (21:04 +0200)
doc/index.html
doc/sitex-protocol.html [new file with mode: 0644]
examples/system-in-test/sit.pro
examples/system-in-test/sitmain.cpp
examples/system-in-test/sitmain.h [new file with mode: 0644]

index e770369..acfc8ae 100644 (file)
@@ -31,6 +31,7 @@ development.</p>
 <li>SKID Internals:<ol>
   <li>The <a href="protocol.html">Runner-Controller-Protocol</a></li>
   <li>TODO: C++ signalling mechanism</li>
+  <li>The protocol of the <a href="sitex-protocol.html">System-In-Test</a> example</li>
   </ol></li>
 </ol>
   
diff --git a/doc/sitex-protocol.html b/doc/sitex-protocol.html
new file mode 100644 (file)
index 0000000..42ec08e
--- /dev/null
@@ -0,0 +1,76 @@
+<html>
+<title>Protocol of the System-In-Test Example</title>
+<body>
+<h1>Protocol of the System-In-Test Example</h1>
+
+<p>The protocol of the System-In-Test example is a simple line-by-line ASCII protocol. The client (runner) sends a request line and the server (System-In-Test) responds with an ASCII line. Then the protocol repeats.</p>
+
+<p>Each line is terminated with a simple newline (carriage-return plus newline will be tolerated from the client, but will never be sent). Command and parameter are separated by a single space (tab will not work).</p>
+
+<p>Each connection keeps an integer state that is initialized at zero.</p>
+
+<h2>Requests</h2>
+
+<p>Requests are formatted as <tt>command</tt> without parameters or as <tt>command&nbsp;parameter</tt>.</p>
+
+<table frame="1" border="1">
+<tr>
+ <td><b>Command</b></td>
+ <td><b>Description</b></td>
+</tr>
+<tr>
+ <td><tt>add <i>number</i></tt></td>
+ <td>Adds the <i>number</i> to the internal state. The number must be a decimal integer. The successful response will contain the new state.</td>
+</tr>
+<tr>
+ <td><tt>reset</tt></td>
+ <td>Resets the connection state to zero.</td>
+</tr>
+<tr>
+ <td><tt>count</tt></td>
+ <td>The successful response returns the number of open connections instead of the state.</td>
+</tr>
+<tr>
+ <td><tt>close</tt></td>
+ <td>Closes the connection. There is no response on success.</td>
+</tr>
+<tr>
+ <td><tt>quit</tt></td>
+ <td>Exits the entire system. There is no response on success.</td>
+</tr>
+</table>
+
+<h2>Responses</h2>
+
+<p>Responses always start with <tt>ok</tt> for successful commands or <tt>error</tt> for indicating problems.</p>
+
+<table frame="1" border="1">
+<tr>
+ <td><b>Response</b></td>
+ <td><b>Description</b></td>
+</tr>
+<tr>
+ <td><tt>ok <i>number</i></tt></td>
+ <td>The command was successful. The number contains the current state or (after <tt>count</tt>) the number of connections.</td>
+</tr>
+<tr>
+ <td><tt>error nan</tt></td>
+ <td>The parameter was not a number.</td>
+</tr>
+<tr>
+ <td><tt>error param</tt></td>
+ <td>The number of parameter was not correct.</td>
+</tr>
+<tr>
+ <td><tt>error command</tt></td>
+ <td>The command was not understood.</td>
+</tr>
+<tr>
+ <td><tt>error nocommand</tt></td>
+ <td>There was no command.</td>
+</tr>
+</table>
+
+
+</body>
+</html>
\ No newline at end of file
index a2a75c7..cbab16f 100644 (file)
@@ -2,9 +2,11 @@
 TEMPLATE = app
 TARGET = sit
 QT -= gui
+QT += network
 
 include(../../common.pri)
 DESTDIR = $$BINDIR
 
 #our actual sources:
 SOURCES += sitmain.cpp
+HEADERS += sitmain.h
\ No newline at end of file
index 2717afb..f4dbd9d 100644 (file)
@@ -1,8 +1,177 @@
+//
+// SKID Examples: System In Test
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2014
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
 #include <QCoreApplication>
+#include <QDebug>
+#include <QTcpServer>
+#include <QLocalServer>
+#include <QTcpSocket>
+#include <QLocalSocket>
+
+#include "sitmain.h"
+
+void ServerBase::newConnection()
+{
+        qDebug()<<"Ooops. This should not have happened.";
+}
+
+int Connection::cctr=0;
+
+Connection::Connection ( QIODevice *s ) : sock(s)
+{
+        s->setParent(this);
+        connect(s,SIGNAL(readyRead()),this,SLOT(read()));
+        connect(s,SIGNAL(disconnected()),this,SLOT(close()));
+        cctr++;
+}
+
+Connection::~Connection()
+{
+        if(sock)sock->close();
+        sock=nullptr;
+        cctr--;
+}
 
+void Connection::read()
+{
+        if(sock==nullptr)return;
+        const QStringList line=QString::fromLatin1(sock->readLine()).trimmed().split(' ');
+        if(line.size()<1){
+                sock->write("error nocommand\n");
+                return;
+        }
+        if(line[0].isEmpty()){
+                sock->write("error nocommand\n");
+                return;
+        }
+        if(line[0]=="quit"){
+                if(line.size()!=1){
+                        sock->write("error param\n");
+                        return;
+                }
+                qApp->quit();
+                return;
+        }
+        if(line[0]=="close"){
+                if(line.size()!=1){
+                        sock->write("error param\n");
+                        return;
+                }
+                close();
+                return;
+        }
+        if(line[0]=="count"){
+                if(line.size()!=1){
+                        sock->write("error param\n");
+                        return;
+                }
+                sock->write(QString("ok %1\n").arg(cctr).toLatin1());
+                return;
+        }
+        if(line[0]=="add"){
+                if(line.size()!=2){
+                        sock->write("error param\n");
+                        return;
+                }
+                bool ok;
+                int add=line[1].toInt(&ok);
+                if(!ok){
+                        sock->write("error nan\n");
+                        return;
+                }
+                state+=add;
+                sock->write(QString("ok %1\n").arg(state).toLatin1());
+                return;
+        }
+        if(line[0]=="reset"){
+                if(line.size()!=1){
+                        sock->write("error param\n");
+                        return;
+                }
+                state=0;
+                sock->write(QString("ok 0\n").toLatin1());
+                return;
+        }
+        //no match
+        sock->write("error command\n");
+}
+
+void Connection::close()
+{
+        deleteLater();
+}
+
+Server<QTcpServer>* tcpServer(quint16 port)
+{
+        QTcpServer*sock=new QTcpServer;
+        if(sock->listen(QHostAddress::Any,port)){
+                return new Server<QTcpServer>(sock);
+        }else{
+                sock->deleteLater();
+                return nullptr;
+        }
+}
+
+Server<QLocalServer>* localServer(const QString&path)
+{
+        QLocalServer*sock=new QLocalServer;
+        if(sock->listen(path)){
+                return new Server<QLocalServer>(sock);
+        }else{
+                sock->deleteLater();
+                return nullptr;
+        }
+}
+
+void usage()
+{
+        qDebug()<<"Usage: sit [options...]";
+        qDebug()<<"\t-port=##  -- create a TCP server, ## must be an integer between 1 and 65535";
+        qDebug()<<"\t-sock=path -- create a local server, path must be a valid path name for it";
+}
 
 int main(int ac,char**av)
 {
         QCoreApplication app(ac,av);
+        
+        bool havesock=false;
+        for(const QString&arg:app.arguments().mid(1)){
+                if(arg.startsWith("-port=")){
+                        bool ok;
+                        const int port=arg.mid(6).toInt(&ok);
+                        if(!ok || port<1 || port>65535){
+                                qDebug()<<"Not a valid port number:"<<arg;
+                                return 1;
+                        }
+                        if(tcpServer(port))havesock=true;
+                        else{
+                                qDebug()<<"Unable to create TCP server on port"<<port;
+                                return 1;
+                        }
+                }else
+                if(arg.startsWith("-sock=")){
+                        if(localServer(arg.mid(6)))havesock=true;
+                        else{
+                                qDebug()<<"Unable to create local socket on"<<arg.mid(6);
+                                return 1;
+                        }
+                }else{
+                        qDebug()<<"Invalid argument:"<<arg;
+                        usage();
+                        return 1;
+                }
+        }
+        if(!havesock){
+                usage();
+                return 1;
+        }
+        
         return app.exec();
 }
\ No newline at end of file
diff --git a/examples/system-in-test/sitmain.h b/examples/system-in-test/sitmain.h
new file mode 100644 (file)
index 0000000..8e80525
--- /dev/null
@@ -0,0 +1,61 @@
+//
+// SKID Examples: System In Test
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2014
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#ifndef SKID_EXAMPLE_SITMAIN_H
+#define SKID_EXAMPLE_SITMAIN_H
+
+#include <QIODevice>
+
+class Connection:public QObject
+{
+        Q_OBJECT
+        private:
+                QIODevice*sock;
+                int state=0;
+                static int cctr;
+        public:
+                explicit Connection (QIODevice*s);
+                virtual ~Connection();
+        private slots:
+                void read();
+                void close();
+};
+
+class ServerBase:public QObject
+{
+        Q_OBJECT
+        public:
+                ServerBase(){}
+        protected slots:
+                virtual void newConnection();
+};
+
+template<typename ServerSocket>
+class Server:public ServerBase
+{
+        private:
+                ServerSocket*server;
+        public:
+                Server(ServerSocket*s)
+                {
+                        server=s;
+                        s->setParent(this);
+                        connect(s,SIGNAL(newConnection()),this,SLOT(newConnection()));
+                }
+                
+        protected:
+                virtual void newConnection()override
+                {
+                        while(server->hasPendingConnections())
+                                new Connection(server->nextPendingConnection());
+                }
+};
+
+#endif