<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>
--- /dev/null
+<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 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
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
+//
+// 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
--- /dev/null
+//
+// 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