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
|
path = dependencies/external/rpi_ws281x
|
||||||
url = https://github.com/hyperion-project/rpi_ws281x.git
|
url = https://github.com/hyperion-project/rpi_ws281x.git
|
||||||
branch = master
|
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