mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
add flatbuffer dependencies
This commit is contained in:
parent
d762aa2f3e
commit
3700566d10
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
|
||||
|
1
dependencies/external/flatbuffers
vendored
Submodule
1
dependencies/external/flatbuffers
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 0eb7b3beb037748bf5b469e4df9db862c4833e35
|
127
include/flatbufserver/FlatBufferConnection.h
Normal file
127
include/flatbufserver/FlatBufferConnection.h
Normal file
@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
// Qt includes
|
||||
#include <QString>
|
||||
#include <QColor>
|
||||
#include <QImage>
|
||||
#include <QTcpSocket>
|
||||
#include <QTimer>
|
||||
#include <QMap>
|
||||
|
||||
// hyperion util
|
||||
#include <utils/Image.h>
|
||||
#include <utils/ColorRgb.h>
|
||||
#include <utils/VideoMode.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
// 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<ColorRgb> & 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;
|
||||
};
|
67
include/flatbufserver/FlatBufferServer.h
Normal file
67
include/flatbufserver/FlatBufferServer.h
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
// util
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/settings.h>
|
||||
|
||||
// qt
|
||||
#include <QVector>
|
||||
|
||||
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<FlatBufferClient*> _openConnections;
|
||||
};
|
42
libsrc/flatbufserver/CMakeLists.txt
Normal file
42
libsrc/flatbufserver/CMakeLists.txt
Normal file
@ -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
|
||||
)
|
195
libsrc/flatbufserver/FlatBufferClient.cpp
Normal file
195
libsrc/flatbufserver/FlatBufferClient.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
#include "FlatBufferClient.h"
|
||||
|
||||
// qt
|
||||
#include <QTcpSocket>
|
||||
#include <QHostAddress>
|
||||
#include <QTimer>
|
||||
|
||||
#include <hyperion/Hyperion.h>
|
||||
#include <QDebug>
|
||||
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<const uint8_t*>(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<ColorRgb> 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();
|
||||
}
|
122
libsrc/flatbufserver/FlatBufferClient.h
Normal file
122
libsrc/flatbufserver/FlatBufferClient.h
Normal file
@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
// util
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Image.h>
|
||||
#include <utils/ColorRgb.h>
|
||||
#include <utils/Components.h>
|
||||
|
||||
// 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<ColorRgb>& 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;
|
||||
};
|
223
libsrc/flatbufserver/FlatBufferConnection.cpp
Normal file
223
libsrc/flatbufserver/FlatBufferConnection.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
// stl includes
|
||||
#include <stdexcept>
|
||||
|
||||
// Qt includes
|
||||
#include <QRgb>
|
||||
|
||||
// protoserver includes
|
||||
#include <flatbufserver/FlatBufferConnection.h>
|
||||
|
||||
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<const uint8_t*>(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<ColorRgb> &image, int priority, int duration)
|
||||
{
|
||||
/* #TODO #BROKEN auto imgData = _builder.CreateVector<flatbuffers::Offset<uint8_t>>(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<const char *>(header), 4);
|
||||
count += _socket.write(reinterpret_cast<const char *>(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;
|
||||
}
|
113
libsrc/flatbufserver/FlatBufferServer.cpp
Normal file
113
libsrc/flatbufserver/FlatBufferServer.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
#include <flatbufserver/FlatBufferServer.h>
|
||||
#include "FlatBufferClient.h"
|
||||
|
||||
// qt
|
||||
#include <QJsonObject>
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
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"<<this->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"<<this->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"<<this->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<FlatBufferClient*>(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");
|
||||
}
|
||||
}
|
15
libsrc/flatbufserver/hyperion_reply.fbs
Normal file
15
libsrc/flatbufserver/hyperion_reply.fbs
Normal file
@ -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;
|
35
libsrc/flatbufserver/hyperion_request.fbs
Normal file
35
libsrc/flatbufserver/hyperion_request.fbs
Normal file
@ -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;
|
37
libsrc/hyperion/schema/schema-flatbufServer.json
Normal file
37
libsrc/hyperion/schema/schema-flatbufServer.json
Normal file
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user