diff --git a/.gitmodules b/.gitmodules index a9b2e1ba..68102e8d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,6 @@ path = dependencies/external/rpi_ws281x url = https://github.com/hyperion-project/rpi_ws281x.git branch = master +[submodule "dependencies/external/flatbuffers"] + path = dependencies/external/flatbuffers + url = git://github.com/google/flatbuffers.git diff --git a/dependencies/external/flatbuffers b/dependencies/external/flatbuffers new file mode 160000 index 00000000..0eb7b3be --- /dev/null +++ b/dependencies/external/flatbuffers @@ -0,0 +1 @@ +Subproject commit 0eb7b3beb037748bf5b469e4df9db862c4833e35 diff --git a/include/flatbufserver/FlatBufferConnection.h b/include/flatbufserver/FlatBufferConnection.h new file mode 100644 index 00000000..a4c06a59 --- /dev/null +++ b/include/flatbufserver/FlatBufferConnection.h @@ -0,0 +1,127 @@ +#pragma once + +// Qt includes +#include +#include +#include +#include +#include +#include + +// hyperion util +#include +#include +#include +#include + +// flatbuffer FBS +#include "hyperion_reply_generated.h" +#include "hyperion_request_generated.h" + +/// +/// Connection class to setup an connection to the hyperion server and execute commands. Used from standalone capture binaries (x11/dispamnx/...) +/// +class FlatBufferConnection : public QObject +{ + + Q_OBJECT + +public: + /// + /// Constructor + /// + /// @param address The address of the Hyperion server (for example "192.168.0.32:19444) + /// + FlatBufferConnection(const QString & address); + + /// + /// Destructor + /// + ~FlatBufferConnection(); + + /// Do not read reply messages from Hyperion if set to true + void setSkipReply(bool skip); + + /// + /// Set all leds to the specified color + /// + /// @param color The color + /// @param priority The priority + /// @param duration The duration in milliseconds + /// + void setColor(const ColorRgb & color, int priority, int duration = 1); + + /// + /// Set the leds according to the given image (assume the image is stretched to the display size) + /// + /// @param image The image + /// @param priority The priority + /// @param duration The duration in milliseconds + /// + void setImage(const Image & image, int priority, int duration = -1); + + /// + /// Clear the given priority channel + /// + /// @param priority The priority + /// + void clear(int priority); + + /// + /// Clear all priority channels + /// + void clearAll(); + + /// + /// Send a command message and receive its reply + /// + /// @param message The message to send + /// + void sendMessage(const uint8_t* buffer, uint32_t size); + +private slots: + /// Try to connect to the Hyperion host + void connectToHost(); + + /// + /// Slot called when new data has arrived + /// + void readData(); + +signals: + + /// + /// emits when a new videoMode was requested from flatbuf client + /// + void setVideoMode(const VideoMode videoMode); + +private: + + /// + /// Parse a reply message + /// + /// @param reply The received reply + /// + /// @return true if the reply indicates success + /// + bool parseReply(const flatbuf::HyperionReply * reply); + +private: + /// The TCP-Socket with the connection to the server + QTcpSocket _socket; + + /// Host address + QString _host; + + /// Host port + uint16_t _port; + + /// Skip receiving reply messages from Hyperion if set + bool _skipReply; + + QTimer _timer; + QAbstractSocket::SocketState _prevSocketState; + + Logger * _log; + flatbuffers::FlatBufferBuilder _builder; +}; diff --git a/include/flatbufserver/FlatBufferServer.h b/include/flatbufserver/FlatBufferServer.h new file mode 100644 index 00000000..799e3d92 --- /dev/null +++ b/include/flatbufserver/FlatBufferServer.h @@ -0,0 +1,67 @@ +#pragma once + +// util +#include +#include + +// qt +#include + +class QTcpServer; +class FlatBufferClient; +class NetOrigin; + +/// +/// @brief A TcpServer to receive images of different formats with Google Flatbuffer +/// Images will be forwarded to all Hyperion instances +/// +class FlatBufferServer : public QObject +{ + Q_OBJECT +public: + FlatBufferServer(const QJsonDocument& config, QObject* parent = nullptr); + ~FlatBufferServer(); + +public slots: + /// + /// @brief Handle settings update + /// @param type The type from enum + /// @param config The configuration + /// + void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config); + + void initServer(); + +private slots: + /// + /// @brief Is called whenever a new socket wants to connect + /// + void newConnection(); + + /// + /// @brief is called whenever a client disconnected + /// + void clientDisconnected(); + +private: + /// + /// @brief Start the server with current _port + /// + void startServer(); + + /// + /// @brief Stop server + /// + void stopServer(); + + +private: + QTcpServer* _server; + NetOrigin* _netOrigin; + Logger* _log; + int _timeout; + quint16 _port; + const QJsonDocument _config; + + QVector _openConnections; +}; diff --git a/libsrc/flatbufserver/CMakeLists.txt b/libsrc/flatbufserver/CMakeLists.txt new file mode 100644 index 00000000..48a31dd8 --- /dev/null +++ b/libsrc/flatbufserver/CMakeLists.txt @@ -0,0 +1,42 @@ + +# Define the current source locations +set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/flatbufserver) +set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/flatbufserver) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${FLATBUFFERS_INCLUDE_DIRS} +) + +FILE ( GLOB FLATBUFSERVER_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) + +set(Flatbuffer_GENERATED_FBS + hyperion_reply_generated.h + hyperion_request_generated.h +) + +set(Flatbuffer_FBS + ${CURRENT_SOURCE_DIR}/hyperion_reply.fbs + ${CURRENT_SOURCE_DIR}/hyperion_request.fbs +) + +FOREACH(FBS_FILE ${Flatbuffer_FBS}) + compile_flattbuffer_schema(${FBS_FILE} ${CMAKE_CURRENT_BINARY_DIR}) + ENDFOREACH(FBS_FILE) + +# let cmake know about new generated source files +set_source_files_properties( + ${Flatbuffer_GENERATED_FBS} PROPERTIES GENERATED TRUE +) + +add_library(flatbufserver + ${FLATBUFSERVER_SOURCES} + ${Flatbuffer_GENERATED_FBS} +) + +target_link_libraries(flatbufserver + hyperion-utils + flatbuffers + Qt5::Network + Qt5::Core +) diff --git a/libsrc/flatbufserver/FlatBufferClient.cpp b/libsrc/flatbufserver/FlatBufferClient.cpp new file mode 100644 index 00000000..335bf829 --- /dev/null +++ b/libsrc/flatbufserver/FlatBufferClient.cpp @@ -0,0 +1,195 @@ +#include "FlatBufferClient.h" + +// qt +#include +#include +#include + +#include +#include +FlatBufferClient::FlatBufferClient(QTcpSocket* socket, const int &timeout, QObject *parent) + : QObject(parent) + , _log(Logger::getInstance("FLATBUFSERVER")) + , _socket(socket) + , _clientAddress(socket->peerAddress().toString()) + , _timeoutTimer(new QTimer(this)) + , _timeout(timeout * 1000) + , _priority() + , _hyperion(Hyperion::getInstance()) +{ + // timer setup + _timeoutTimer->setSingleShot(true); + _timeoutTimer->setInterval(_timeout); + connect(_timeoutTimer, &QTimer::timeout, this, &FlatBufferClient::forceClose); + + // connect socket signals + connect(_socket, &QTcpSocket::readyRead, this, &FlatBufferClient::readyRead); + connect(_socket, &QTcpSocket::disconnected, this, &FlatBufferClient::disconnected); +} + +void FlatBufferClient::readyRead() +{ + qDebug()<<"readyRead"; + _timeoutTimer->start(); + + _receiveBuffer += _socket->readAll(); + + // check if we can read a header + while(_receiveBuffer.size() >= 4) + { + + uint32_t messageSize = + ((_receiveBuffer[0]<<24) & 0xFF000000) | + ((_receiveBuffer[1]<<16) & 0x00FF0000) | + ((_receiveBuffer[2]<< 8) & 0x0000FF00) | + ((_receiveBuffer[3] ) & 0x000000FF); + + // check if we can read a complete message + if((uint32_t) _receiveBuffer.size() < messageSize + 4) return; + + // remove header + msg from buffer + const QByteArray& msg = _receiveBuffer.remove(0, messageSize + 4); + + const uint8_t* msgData = reinterpret_cast(msg.mid(3, messageSize).constData()); + flatbuffers::Verifier verifier(msgData, messageSize); + + if (flatbuf::VerifyHyperionRequestBuffer(verifier)) + { + auto message = flatbuf::GetHyperionRequest(msgData); + handleMessage(message); + continue; + } + qDebug()<<"Unable to pasrse msg"; + sendErrorReply("Unable to parse message"); + } + //emit newMessage(msgData,messageSize); + + + // Emit this to send a new priority register event to all Hyperion instances, + // emit registerGlobalInput(_priority, hyperion::COMP_FLATBUFSERVER, QString("%1@%2").arg("PLACE_ORIGIN_STRING_FROM_SENDER_HERE",_socket->peerAddress())); + + // Emit this to send the image data event to all Hyperion instances + // emit setGlobalInput(_priority, _image, _timeout); +} + +void FlatBufferClient::forceClose() +{ + _socket->close(); +} + +void FlatBufferClient::disconnected() +{ + qDebug()<<"Socket Closed"; + //emit clearGlobalPriority(_priority, hyperion::COMP_FLATBUFSERVER); + _socket->deleteLater(); + emit clientDisconnected(); +} + +void FlatBufferClient::handleMessage(const flatbuf::HyperionRequest * message) +{ + switch (message->command()) + { + case flatbuf::Command_COLOR: + qDebug()<<"handle colorReuest"; + if (!flatbuffers::IsFieldPresent(message, flatbuf::HyperionRequest::VT_COLORREQUEST)) + { + sendErrorReply("Received COLOR command without ColorRequest"); + break; + } + //handleColorCommand(message->colorRequest()); + break; + case flatbuf::Command_IMAGE: + qDebug()<<"handle imageReuest"; + if (!flatbuffers::IsFieldPresent(message, flatbuf::HyperionRequest::VT_IMAGEREQUEST)) + { + sendErrorReply("Received IMAGE command without ImageRequest"); + break; + } + handleImageCommand(message->imageRequest()); + break; + case flatbuf::Command_CLEAR: + if (!flatbuffers::IsFieldPresent(message, flatbuf::HyperionRequest::VT_CLEARREQUEST)) + { + sendErrorReply("Received CLEAR command without ClearRequest"); + break; + } + //handleClearCommand(message->clearRequest()); + break; + case flatbuf::Command_CLEARALL: + //handleClearallCommand(); + break; + default: + qDebug()<<"handleNotImplemented"; + handleNotImplemented(); + } +} + +void FlatBufferClient::handleImageCommand(const flatbuf::ImageRequest *message) +{ + // extract parameters + int priority = message->priority(); + int duration = message->duration(); + int width = message->imagewidth(); + int height = message->imageheight(); + const auto & imageData = message->imagedata(); + + // make sure the prio is registered before setInput() + if(priority != _priority) + { + _hyperion->clear(_priority); + _hyperion->registerInput(priority, hyperion::COMP_FLATBUFSERVER, "proto@"+_clientAddress); + _priority = priority; + } + + // check consistency of the size of the received data + if ((int) imageData->size() != width*height*3) + { + sendErrorReply("Size of image data does not match with the width and height"); + return; + } + + // create ImageRgb + Image image(width, height); + memcpy(image.memptr(), imageData->data(), imageData->size()); + + _hyperion->setInputImage(_priority, image, duration); + + // send reply + sendSuccessReply(); +} + + +void FlatBufferClient::handleNotImplemented() +{ + sendErrorReply("Command not implemented"); +} + +void FlatBufferClient::sendMessage() +{ + auto size = _builder.GetSize(); + const uint8_t* buffer = _builder.GetBufferPointer(); + uint8_t sizeData[] = {uint8_t(size >> 24), uint8_t(size >> 16), uint8_t(size >> 8), uint8_t(size)}; + _socket->write((const char *) sizeData, sizeof(sizeData)); + _socket->write((const char *)buffer, size); + _socket->flush(); + _builder.Clear(); +} + +void FlatBufferClient::sendSuccessReply() +{ + auto reply = flatbuf::CreateHyperionReplyDirect(_builder, flatbuf::Type_REPLY, true); + _builder.Finish(reply); + + // send reply + sendMessage(); +} + +void FlatBufferClient::sendErrorReply(const std::string &error) +{ + // create reply + auto reply = flatbuf::CreateHyperionReplyDirect(_builder, flatbuf::Type_REPLY, false, error.c_str()); + _builder.Finish(reply); + + // send reply + sendMessage(); +} diff --git a/libsrc/flatbufserver/FlatBufferClient.h b/libsrc/flatbufserver/FlatBufferClient.h new file mode 100644 index 00000000..cbddfdac --- /dev/null +++ b/libsrc/flatbufserver/FlatBufferClient.h @@ -0,0 +1,122 @@ +#pragma once + +// util +#include +#include +#include +#include + +// flatbuffer FBS +#include "hyperion_reply_generated.h" +#include "hyperion_request_generated.h" + +class QTcpSocket; +class QTimer; +class Hyperion; + +namespace flatbuf { +class HyperionRequest; +} + +/// +/// @brief Socket (client) of FlatBufferServer +/// +class FlatBufferClient : public QObject +{ + Q_OBJECT +public: + /// + /// @brief Construct the client + /// @param socket The socket + /// @param timeout The timeout when a client is automatically disconnected and the priority unregistered + /// @param parent The parent + /// + explicit FlatBufferClient(QTcpSocket* socket, const int &timeout, QObject *parent = nullptr); + +signals: + /// + /// @brief forward register data to HyperionDaemon + /// + void registerGlobalInput(const int priority, const hyperion::Components& component, const QString& origin = "System", const QString& owner = "", unsigned smooth_cfg = 0); + + /// + /// @brief forward prepared image to HyperionDaemon + /// + const bool setGlobalInputImage(const int priority, const Image& image, const int timeout_ms = -1); + + /// + /// @brief forward clear to HyperionDaemon + /// + void clearGlobalPriority(const int& priority, const hyperion::Components& component); + + /// + /// @brief Emits whenever the client disconnected + /// + void clientDisconnected(); + +public slots: + /// + /// @brief close the socket and call disconnected() + /// + void forceClose(); + +private slots: + /// + /// @brief Is called whenever the socket got new data to read + /// + void readyRead(); + + /// + /// @brief Is called when the socket closed the connection, also requests thread exit + /// + void disconnected(); + +private: + /// + /// @brief Handle the received message + /// + void handleMessage(const flatbuf::HyperionRequest *message); + + /// + /// Handle an incoming Image message + /// + /// @param message the incoming message + /// + void handleImageCommand(const flatbuf::ImageRequest * message); + + /// + /// Send handle not implemented + /// + void handleNotImplemented(); + + /// + /// Send a message to the connected client + /// + void sendMessage(); + + /// + /// Send a standard reply indicating success + /// + void sendSuccessReply(); + + /// + /// Send an error message back to the client + /// + /// @param error String describing the error + /// + void sendErrorReply(const std::string & error); + +private: + Logger *_log; + QTcpSocket *_socket; + const QString _clientAddress; + QTimer *_timeoutTimer; + int _timeout; + int _priority; + Hyperion* _hyperion; + + QByteArray _receiveBuffer; + + // Flatbuffers builder + flatbuffers::FlatBufferBuilder _builder; +}; diff --git a/libsrc/flatbufserver/FlatBufferConnection.cpp b/libsrc/flatbufserver/FlatBufferConnection.cpp new file mode 100644 index 00000000..064b8d56 --- /dev/null +++ b/libsrc/flatbufserver/FlatBufferConnection.cpp @@ -0,0 +1,223 @@ +// stl includes +#include + +// Qt includes +#include + +// protoserver includes +#include + +FlatBufferConnection::FlatBufferConnection(const QString & address) : + _socket(), + _skipReply(false), + _prevSocketState(QAbstractSocket::UnconnectedState), + _log(Logger::getInstance("FLATBUFCONNECTION")) + { + QStringList parts = address.split(":"); + if (parts.size() != 2) + { + throw std::runtime_error(QString("FLATBUFCONNECTION ERROR: Wrong address: Unable to parse address (%1)").arg(address).toStdString()); + } + _host = parts[0]; + + bool ok; + _port = parts[1].toUShort(&ok); + if (!ok) + { + throw std::runtime_error(QString("FLATBUFCONNECTION ERROR: Wrong port: Unable to parse the port number (%1)").arg(parts[1]).toStdString()); + } + + // try to connect to host + Info(_log, "Connecting to Hyperion: %s:%d", _host.toStdString().c_str(), _port); + connectToHost(); + + // start the connection timer + _timer.setInterval(5000); + _timer.setSingleShot(false); + + connect(&_timer,SIGNAL(timeout()), this, SLOT(connectToHost())); + connect(&_socket, SIGNAL(readyRead()), this, SLOT(readData())); + _timer.start(); +} + +FlatBufferConnection::~FlatBufferConnection() +{ + _timer.stop(); + _socket.close(); +} + +void FlatBufferConnection::readData() +{ + qint64 bytesAvail; + while((bytesAvail = _socket.bytesAvailable())) + { + // ignore until we get 4 bytes. + if (bytesAvail < 4) { + continue; + } + + char sizeBuf[4]; + _socket.read(sizeBuf, sizeof(sizeBuf)); + + uint32_t messageSize = + ((sizeBuf[0]<<24) & 0xFF000000) | + ((sizeBuf[1]<<16) & 0x00FF0000) | + ((sizeBuf[2]<< 8) & 0x0000FF00) | + ((sizeBuf[3] ) & 0x000000FF); + + QByteArray buffer; + while((uint32_t)buffer.size() < messageSize) + { + _socket.waitForReadyRead(); + buffer.append(_socket.read(messageSize - buffer.size())); + } + + const uint8_t* replyData = reinterpret_cast(buffer.constData()); + flatbuffers::Verifier verifier(replyData, messageSize); + + if (!flatbuf::VerifyHyperionReplyBuffer(verifier)) + { + Error(_log, "Error while reading data from host"); + return; + } + + auto reply = flatbuf::GetHyperionReply(replyData); + + parseReply(reply); + } +} + +void FlatBufferConnection::setSkipReply(bool skip) +{ + _skipReply = skip; +} + +void FlatBufferConnection::setColor(const ColorRgb & color, int priority, int duration) +{ + auto colorReq = flatbuf::CreateColorRequest(_builder, priority, (color.red << 16) | (color.green << 8) | color.blue, duration); + auto req = flatbuf::CreateHyperionRequest(_builder,flatbuf::Command_COLOR, colorReq); + + _builder.Finish(req); + sendMessage(_builder.GetBufferPointer(), _builder.GetSize()); +} + +void FlatBufferConnection::setImage(const Image &image, int priority, int duration) +{ + /* #TODO #BROKEN auto imgData = _builder.CreateVector>(image.memptr(), image.width() * image.height() * 3); + auto imgReq = flatbuf::CreateImageRequest(_builder, priority, image.width(), image.height(), imgData, duration); + auto req = flatbuf::CreateHyperionRequest(_builder,flatbuf::Command_IMAGE,0,imgReq); + _builder.Finish(req); + sendMessage(_builder.GetBufferPointer(), _builder.GetSize());*/ +} + +void FlatBufferConnection::clear(int priority) +{ + auto clearReq = flatbuf::CreateClearRequest(_builder, priority); + auto req = flatbuf::CreateHyperionRequest(_builder,flatbuf::Command_CLEAR,0,0,clearReq); + + _builder.Finish(req); + sendMessage(_builder.GetBufferPointer(), _builder.GetSize()); +} + +void FlatBufferConnection::clearAll() +{ + auto req = flatbuf::CreateHyperionRequest(_builder,flatbuf::Command_CLEARALL); + + // send command message + _builder.Finish(req); + sendMessage(_builder.GetBufferPointer(), _builder.GetSize()); +} + +void FlatBufferConnection::connectToHost() +{ + // try connection only when + if (_socket.state() == QAbstractSocket::UnconnectedState) + { + _socket.connectToHost(_host, _port); + //_socket.waitForConnected(1000); + } +} + +void FlatBufferConnection::sendMessage(const uint8_t* buffer, uint32_t size) +{ + // print out connection message only when state is changed + if (_socket.state() != _prevSocketState ) + { + switch (_socket.state() ) + { + case QAbstractSocket::UnconnectedState: + Info(_log, "No connection to Hyperion: %s:%d", _host.toStdString().c_str(), _port); + break; + + case QAbstractSocket::ConnectedState: + Info(_log, "Connected to Hyperion: %s:%d", _host.toStdString().c_str(), _port); + break; + + default: + Debug(_log, "Connecting to Hyperion: %s:%d", _host.toStdString().c_str(), _port); + break; + } + _prevSocketState = _socket.state(); + } + + + if (_socket.state() != QAbstractSocket::ConnectedState) + { + return; + } + + const uint8_t header[] = { + uint8_t((size >> 24) & 0xFF), + uint8_t((size >> 16) & 0xFF), + uint8_t((size >> 8) & 0xFF), + uint8_t((size ) & 0xFF)}; + + // write message + int count = 0; + count += _socket.write(reinterpret_cast(header), 4); + count += _socket.write(reinterpret_cast(buffer), size); + if (!_socket.waitForBytesWritten()) + { + Error(_log, "Error while writing data to host"); + return; + } +} + +bool FlatBufferConnection::parseReply(const flatbuf::HyperionReply *reply) +{ + bool success = false; + + switch (reply->type()) + { + case flatbuf::Type_REPLY: + { + if (!_skipReply) + { + if (!reply->success()) + { + if (flatbuffers::IsFieldPresent(reply, flatbuf::HyperionReply::VT_ERROR)) + { + throw std::runtime_error("PROTOCONNECTION ERROR: " + reply->error()->str()); + } + else + { + throw std::runtime_error("PROTOCONNECTION ERROR: No error info"); + } + } + else + { + success = true; + } + } + break; + } + case flatbuf::Type_VIDEO: + { + VideoMode vMode = (VideoMode)reply->video(); + emit setVideoMode(vMode); + break; + } + } + + return success; +} diff --git a/libsrc/flatbufserver/FlatBufferServer.cpp b/libsrc/flatbufserver/FlatBufferServer.cpp new file mode 100644 index 00000000..ffef50f1 --- /dev/null +++ b/libsrc/flatbufserver/FlatBufferServer.cpp @@ -0,0 +1,113 @@ +#include +#include "FlatBufferClient.h" + +// qt +#include +#include +#include + + +#include + + +FlatBufferServer::FlatBufferServer(const QJsonDocument& config, QObject* parent) + : QObject(parent) + , _server(new QTcpServer(this)) + , _log(Logger::getInstance("FLATBUFSERVER")) + , _timeout(5000) + , _config(config) +{ + +} + +FlatBufferServer::~FlatBufferServer() +{ + stopServer(); + delete _server; +} + +void FlatBufferServer::initServer() +{ + qDebug()<<"Thread in InitServer is"<thread(); + connect(_server, &QTcpServer::newConnection, this, &FlatBufferServer::newConnection); + + // apply config + handleSettingsUpdate(settings::FLATBUFSERVER, _config); +} + +void FlatBufferServer::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config) +{ + qDebug()<<"Thread in handleSettingsUpdate is"<thread(); + if(type == settings::FLATBUFSERVER) + { + const QJsonObject& obj = config.object(); + + quint16 port = obj["port"].toInt(19400); + + // port check + if(_server->serverPort() != port) + { + stopServer(); + _port = port; + } + + // new timeout just for new connections + _timeout = obj["timeout"].toInt(5000); + // enable check + obj["enable"].toBool(true) ? startServer() : stopServer(); + } +} + +void FlatBufferServer::newConnection() +{ + qDebug()<<"Thread in newConnection is"<thread(); + while(_server->hasPendingConnections()) + { + if(QTcpSocket* socket = _server->nextPendingConnection()) + { + Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString())); + FlatBufferClient *client = new FlatBufferClient(socket, _timeout, this); + // internal + connect(client, &FlatBufferClient::clientDisconnected, this, &FlatBufferServer::clientDisconnected); + // forward data + //connect(clientThread, &FlatBufferClient::); + _openConnections.append(client); + } + } +} + +void FlatBufferServer::clientDisconnected() +{ + FlatBufferClient* client = qobject_cast(sender()); + client->deleteLater(); + _openConnections.removeAll(client); +} + +void FlatBufferServer::startServer() +{ + if(!_server->isListening()) + { + if(!_server->listen(QHostAddress::Any, _port)) + { + Error(_log,"Failed to bind port %d", _port); + } + else + { + Info(_log,"Started on port %d", _port); + } + } +} + +void FlatBufferServer::stopServer() +{ + if(_server->isListening()) + { + // close client connections + for(auto client : _openConnections) + { + client->forceClose(); + } + _server->close(); + Info(_log, "Stopped"); + } +} diff --git a/libsrc/flatbufserver/hyperion_reply.fbs b/libsrc/flatbufserver/hyperion_reply.fbs new file mode 100644 index 00000000..878f2612 --- /dev/null +++ b/libsrc/flatbufserver/hyperion_reply.fbs @@ -0,0 +1,15 @@ +namespace flatbuf; + +enum Type : int { + REPLY = 0, + VIDEO = 1, +} + +table HyperionReply { + type:Type; + success:bool; + error:string; + video:int; +} + +root_type HyperionReply; diff --git a/libsrc/flatbufserver/hyperion_request.fbs b/libsrc/flatbufserver/hyperion_request.fbs new file mode 100644 index 00000000..3ce9f423 --- /dev/null +++ b/libsrc/flatbufserver/hyperion_request.fbs @@ -0,0 +1,35 @@ +namespace flatbuf; + +enum Command : int { + COLOR = 0, + IMAGE = 1, + CLEAR = 2, + CLEARALL = 3, +} + +table HyperionRequest { + command:Command; + colorRequest:flatbuf.ColorRequest; + imageRequest:flatbuf.ImageRequest; + clearRequest:flatbuf.ClearRequest; +} + +table ColorRequest { + priority:int; + RgbColor:int; + duration:int; +} + +table ImageRequest { + priority:int; + imagewidth:int; + imageheight:int; + imagedata:[ubyte]; + duration:int; +} + +table ClearRequest { + priority:int; +} + +root_type HyperionRequest; diff --git a/libsrc/hyperion/schema/schema-flatbufServer.json b/libsrc/hyperion/schema/schema-flatbufServer.json new file mode 100644 index 00000000..e86633d9 --- /dev/null +++ b/libsrc/hyperion/schema/schema-flatbufServer.json @@ -0,0 +1,37 @@ +{ + "type" : "object", + "required" : true, + "title" : "edt_conf_fbs_heading_title", + "properties" : + { + "enable" : + { + "type" : "boolean", + "required" : true, + "title" : "edt_conf_general_enable_title", + "default" : true, + "propertyOrder" : 1 + }, + "port" : + { + "type" : "integer", + "required" : true, + "title" : "edt_conf_general_port_title", + "minimum" : 1024, + "maximum" : 65535, + "default" : 19400, + "propertyOrder" : 2 + }, + "timeout" : + { + "type" : "integer", + "required" : true, + "title" : "edt_conf_fbs_timeout_title", + "append" : "edt_append_s", + "minimum" : 1, + "default" : 5, + "propertyOrder" : 3 + } + }, + "additionalProperties" : false +}