diff --git a/source/Miniscope-DAQ-QT-Software.pro b/source/Miniscope-DAQ-QT-Software.pro index 0b70b34..1046831 100644 --- a/source/Miniscope-DAQ-QT-Software.pro +++ b/source/Miniscope-DAQ-QT-Software.pro @@ -1,4 +1,4 @@ -QT += qml quick widgets +QT += qml quick widgets network CONFIG += c++11 QT += 3dcore @@ -28,6 +28,7 @@ SOURCES += \ main.cpp \ miniscope.cpp \ newquickview.cpp \ + tcpserver.cpp \ tracedisplay.cpp \ videodevice.cpp \ videodisplay.cpp \ @@ -58,6 +59,7 @@ HEADERS += \ datasaver.h \ miniscope.h \ newquickview.h \ + tcpserver.h \ tracedisplay.h \ videodevice.h \ videodisplay.h \ diff --git a/source/backend.cpp b/source/backend.cpp index 75537c6..7275db1 100644 --- a/source/backend.cpp +++ b/source/backend.cpp @@ -30,6 +30,7 @@ #include "datasaver.h" #include "behaviortracker.h" #include "tracedisplay.h" +#include "tcpserver.h" #ifdef USE_USB #include @@ -920,6 +921,10 @@ void backEnd::constructUserConfigGUI() controlPanel = new ControlPanel(this, m_userConfig); QObject::connect(this, SIGNAL (sendMessage(QString) ), controlPanel, SLOT( receiveMessage(QString))); + // Create tcp server control GUI + TcpServer *tcpServer = new TcpServer(controlPanel, this); + tcpServer->on_socket_activate(true); + // Make trace display if (!ucTraceDisplay.isEmpty()) { if (ucTraceDisplay["enabled"].toBool(true)) diff --git a/source/controlpanel.h b/source/controlpanel.h index 68a76d6..6d077ab 100644 --- a/source/controlpanel.h +++ b/source/controlpanel.h @@ -16,6 +16,7 @@ class ControlPanel : public QObject void createView(); void connectSnS(); void fillUCEditText(); + QObject *rootObject; // void setUserConfig(QJsonObject userConfig) {m_userConfig = userConfig;} public slots: @@ -36,10 +37,9 @@ public slots: private: NewQuickView *view; - QObject *rootObject; + QQuickItem *messageTextArea; QQuickItem *recordTimeText; - QJsonObject m_userConfig; double currentRecordTime; int m_ucRecordLengthinSeconds; diff --git a/source/tcpserver.cpp b/source/tcpserver.cpp new file mode 100644 index 0000000..ed1685f --- /dev/null +++ b/source/tcpserver.cpp @@ -0,0 +1,186 @@ +#include +#include "tcpserver.h" + +using namespace TCPSERVER_PRIVATE; + +TcpServer::TcpServer(ControlPanel *ctl, QObject *parent): + QTcpServer(parent), controlPanel(ctl) +{ + connect(this, SIGNAL(recordButtonClick()), + controlPanel->rootObject->findChild("bRecord"), + SIGNAL(activated())); + connect(this, SIGNAL(stopButtonClick()), + controlPanel->rootObject->findChild("bStop"), + SIGNAL(activated())); + connect(this, SIGNAL(tell_socket_port(QString)), + controlPanel, + SLOT(receiveMessage(QString))); +} + + +void TcpServer::on_socket_activate(bool activate) +{ + if(activate){ + quint16 port = 20172; + if(listen(QHostAddress::Any, port) + || listen(QHostAddress::Any, port+1) + || listen(QHostAddress::Any, port+2) + || listen(QHostAddress::Any, port+3) + || listen(QHostAddress::Any, port+4) + || listen(QHostAddress::Any)) + { + qDebug() << "Server started!" << this->serverPort(); + emit tell_socket_port(QString("Connect socket to PORT [%1].").arg(this->serverPort())); + } + else + { + qDebug() << "Server could not start"; + emit tell_socket_port(QString("Warning: connect socket failed")); + } + } + else{ + this->close(); + } +} + +// This function is called by QTcpServer when a new connection is available. +void TcpServer::incomingConnection(qintptr socketDescriptor) +{ + // We have a new connection + qDebug() << socketDescriptor << " Connecting..."; + + // Every new connection will be run in a newly created thread + MyThread *thread = new MyThread(socketDescriptor, this); + + // connect signal/slot + // once a thread is not needed, it will be beleted later + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); +} + +bool TcpServer::tcpcmd_start_record(char *outmsg) +{ + bool currentstuts = controlPanel->rootObject->property("recording").toBool(); + if(currentstuts){ + strcpy(outmsg, "Error! Already started before."); + return false; + } + bool btn_enabled = true; + if(!btn_enabled){ + strcpy(outmsg, "Error! Device not connected."); + return false; + } + emit recordButtonClick(); + strcpy(outmsg, "OK! Task should be starting."); + return true; +} + +bool TcpServer::tcpcmd_stop_record(char *outmsg) +{ + bool currentstuts = controlPanel->rootObject->property("recording").toBool(); + if(!currentstuts){ + strcpy(outmsg, "Error! Already stopped before."); + return false; + } + bool btn_enabled = true; + if(!btn_enabled){ + strcpy(outmsg, "Error! Device not connected."); + return false; + } + emit stopButtonClick(); + strcpy(outmsg, "OK! Task should be stopping."); + return true; +} + +int TcpServer::tcpcmd_query_record(char *outmsg) +{ + bool btn_enabled = true; + if(!btn_enabled){ + strcpy(outmsg, "Device not connected."); + return 0; + } + bool currentstuts = controlPanel->rootObject->property("recording").toBool(); + if(currentstuts){ + strcpy(outmsg, "Running."); + return 2; + } + else{ + strcpy(outmsg, "Stopped."); + return 1; + } +} + +MyThread::MyThread(qintptr ID, TcpServer *tcpServer, QObject *parent) : + QThread(parent) +{ + this->socketDescriptor = ID; + this->tcpServer = tcpServer; +} + +void MyThread::run() +{ + // thread starts here + qDebug() << " Thread started"; + + socket = new QTcpSocket(); + + // set the ID + if(!socket->setSocketDescriptor(this->socketDescriptor)) + { + // something's wrong, we just emit a signal + emit error(socket->error()); + return; + } + + // connect socket and signal + // note - Qt::DirectConnection is used because it's multithreaded + // This makes the slot to be invoked immediately, when the signal is emitted. + + connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection); + connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected())); + + // We'll have multiple clients, we want to know which is which + qDebug() << socketDescriptor << " Client connected"; + + // make this thread a loop, + // thread will stay alive so that signal/slot to function properly + // not dropped out in the middle when thread dies + exec(); +} + +void MyThread::disconnected() +{ + qDebug() << socketDescriptor << " Disconnected"; + socket->deleteLater(); + exit(0); +} + +void MyThread::readyRead() +{ + const int MaxLength = 1024; + char buffer[MaxLength+1]; + qint64 byteCount = socket->read(buffer, MaxLength); + buffer[byteCount] = 0; + qDebug() << socket->bytesAvailable() << buffer; + + char response[MaxLength+1]; + if(strcmp(buffer, "start_record")==0) + { + tcpServer->tcpcmd_start_record(response); + } + else if(strcmp(buffer, "stop_record")==0) + { + tcpServer->tcpcmd_stop_record(response); + } + else if(strcmp(buffer, "query_record")==0) + { + tcpServer->tcpcmd_query_record(response); + } + else + { + strcpy(response, "Error! Not a valid command."); + } + socket->write(response); + socket->flush(); + socket->waitForBytesWritten(100); +} diff --git a/source/tcpserver.h b/source/tcpserver.h new file mode 100644 index 0000000..6d2f348 --- /dev/null +++ b/source/tcpserver.h @@ -0,0 +1,59 @@ +#ifndef TCPSERVER_H +#define TCPSERVER_H + +#include +#include +#include +#include +#include +#include "controlpanel.h" + + +class TcpServer : public QTcpServer +{ + Q_OBJECT +public: + explicit TcpServer(ControlPanel *,QObject *parent = 0); + bool tcpcmd_start_record(char *outmsg); + bool tcpcmd_stop_record(char *outmsg); + int tcpcmd_query_record(char *outmsg); + +signals: + void tell_socket_port(QString port); + void recordButtonClick(); + void stopButtonClick(); + +private: + ControlPanel *controlPanel; + +public slots: + void on_socket_activate(bool activate); + +protected: + void incomingConnection(qintptr socketDescriptor); +}; + + +namespace TCPSERVER_PRIVATE{ + class MyThread : public QThread + { + Q_OBJECT + public: + explicit MyThread(qintptr ID, TcpServer *tcpserver, QObject *parent = 0); + void run(); + + signals: + void error(QTcpSocket::SocketError socketerror); + + public slots: + void readyRead(); + void disconnected(); + + private: + TcpServer *tcpServer; + QTcpSocket *socket; + qintptr socketDescriptor; + }; +} + +#endif // TCPSERVER_H