diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index b6ed0946..811f0199 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -13,6 +13,7 @@ // Hyperion includes #include #include +#include // Effect engine includes #include @@ -125,6 +126,10 @@ public slots: /// Tell Hyperion that the transforms have changed and the leds need to be updated void transformsUpdated(); + /// Returns MessageForwarder Object + /// @return instance of message forwarder object + MessageForwarder * getForwarder(); + /// /// Clears the given priority channel. This will switch the led-colors to the colors of the next /// lower priority channel (or off if no more channels are set) @@ -168,6 +173,7 @@ public: static RgbChannelTransform * createRgbChannelTransform(const Json::Value& colorConfig); static LedDevice * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice); + static MessageForwarder * createMessageForwarder(const Json::Value & forwarderConfig); signals: /// Signal which is emitted when a priority channel is actively cleared @@ -201,6 +207,9 @@ private: /// Effect engine EffectEngine * _effectEngine; + // proto and json Message forwarder + MessageForwarder * _messageForwarder; + /// The timer for handling priority channel timeouts QTimer _timer; }; diff --git a/include/hyperion/MessageForwarder.h b/include/hyperion/MessageForwarder.h new file mode 100644 index 00000000..467d0d35 --- /dev/null +++ b/include/hyperion/MessageForwarder.h @@ -0,0 +1,31 @@ +#pragma once + +// STL includes +#include +#include +#include +#include + +// QT includes +#include +#include + +// Utils includes +#include +class MessageForwarder +{ +public: + MessageForwarder(); + ~MessageForwarder(); + + void addJsonSlave(std::string slave); + void addProtoSlave(std::string slave); + void sendMessage(); + + QStringList getProtoSlaves(); + +private: + bool _running; + + QStringList _protoSlaves; +}; diff --git a/include/protoserver/ProtoServer.h b/include/protoserver/ProtoServer.h index c18b38fb..1d530caa 100644 --- a/include/protoserver/ProtoServer.h +++ b/include/protoserver/ProtoServer.h @@ -6,12 +6,19 @@ // Qt includes #include #include +#include #include // Hyperion includes #include +// forward decl class ProtoClientConnection; +class ProtoConnection; + +namespace proto { +class HyperionRequest; +} /// /// This class creates a TCP server which accepts connections wich can then send @@ -28,7 +35,7 @@ public: /// @param hyperion Hyperion instance /// @param port port number on which to start listening for connections /// - ProtoServer(Hyperion * hyperion, uint16_t port = 19445, QStringList * forwardClientList = new QStringList() ); + ProtoServer(Hyperion * hyperion, uint16_t port = 19445); ~ProtoServer(); /// @@ -48,6 +55,8 @@ private slots: /// void closedConnection(ProtoClientConnection * connection); + void newMessage(const proto::HyperionRequest * message); + private: /// Hyperion instance Hyperion * _hyperion; @@ -58,4 +67,8 @@ private: /// List with open connections QSet _openConnections; QStringList _forwardClients; + + /// Hyperion proto connection object for forwarding + QList _proxy_connections; + }; diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index 1434f105..3a277a7e 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -1,66 +1,68 @@ - -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion) - -# Group the headers that go through the MOC compiler -SET(Hyperion_QT_HEADERS - ${CURRENT_HEADER_DIR}/Hyperion.h - - ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.h -) - -SET(Hyperion_HEADERS - ${CURRENT_HEADER_DIR}/ImageProcessor.h - ${CURRENT_HEADER_DIR}/ImageProcessorFactory.h - ${CURRENT_HEADER_DIR}/ImageToLedsMap.h - ${CURRENT_HEADER_DIR}/LedString.h - ${CURRENT_HEADER_DIR}/PriorityMuxer.h - - ${CURRENT_SOURCE_DIR}/MultiColorTransform.h -) - -SET(Hyperion_SOURCES - ${CURRENT_SOURCE_DIR}/Hyperion.cpp - ${CURRENT_SOURCE_DIR}/ImageProcessor.cpp - ${CURRENT_SOURCE_DIR}/ImageProcessorFactory.cpp - ${CURRENT_SOURCE_DIR}/LedString.cpp - ${CURRENT_SOURCE_DIR}/PriorityMuxer.cpp - - ${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp - ${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp - ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp -) - -set(Hyperion_RESOURCES - ${CURRENT_SOURCE_DIR}/resource.qrc -) - -if(ENABLE_QT5) -QT5_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS}) -QT5_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress") -else(ENABLE_QT5) -QT4_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS}) -QT4_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress") -endif(ENABLE_QT5) - -add_library(hyperion - ${Hyperion_HEADERS} - ${Hyperion_QT_HEADERS} - ${Hyperion_HEADERS_MOC} - ${Hyperion_SOURCES} - ${Hyperion_RESOURCES_RCC} -) - -if(ENABLE_QT5) -qt5_use_modules(hyperion Widgets) -endif(ENABLE_QT5) - -target_link_libraries(hyperion - blackborder - hyperion-utils - leddevice - effectengine - serialport - ${QT_LIBRARIES} -) + +# Define the current source locations +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion) + +# Group the headers that go through the MOC compiler +SET(Hyperion_QT_HEADERS + ${CURRENT_HEADER_DIR}/Hyperion.h + ${CURRENT_HEADER_DIR}/MessageForwarder.h + + ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.h +) + +SET(Hyperion_HEADERS + ${CURRENT_HEADER_DIR}/ImageProcessor.h + ${CURRENT_HEADER_DIR}/ImageProcessorFactory.h + ${CURRENT_HEADER_DIR}/ImageToLedsMap.h + ${CURRENT_HEADER_DIR}/LedString.h + ${CURRENT_HEADER_DIR}/PriorityMuxer.h + + ${CURRENT_SOURCE_DIR}/MultiColorTransform.h +) + +SET(Hyperion_SOURCES + ${CURRENT_SOURCE_DIR}/Hyperion.cpp + ${CURRENT_SOURCE_DIR}/ImageProcessor.cpp + ${CURRENT_SOURCE_DIR}/ImageProcessorFactory.cpp + ${CURRENT_SOURCE_DIR}/LedString.cpp + ${CURRENT_SOURCE_DIR}/PriorityMuxer.cpp + + ${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp + ${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp + ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp + ${CURRENT_SOURCE_DIR}/MessageForwarder.cpp +) + +set(Hyperion_RESOURCES + ${CURRENT_SOURCE_DIR}/resource.qrc +) + +if(ENABLE_QT5) +QT5_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS}) +QT5_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress") +else(ENABLE_QT5) +QT4_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS}) +QT4_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress") +endif(ENABLE_QT5) + +add_library(hyperion + ${Hyperion_HEADERS} + ${Hyperion_QT_HEADERS} + ${Hyperion_HEADERS_MOC} + ${Hyperion_SOURCES} + ${Hyperion_RESOURCES_RCC} +) + +if(ENABLE_QT5) +qt5_use_modules(hyperion Widgets) +endif(ENABLE_QT5) + +target_link_libraries(hyperion + blackborder + hyperion-utils + leddevice + effectengine + serialport + ${QT_LIBRARIES} +) diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 37c77927..ed23fb30 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -267,12 +267,39 @@ LedDevice * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig, } +MessageForwarder * Hyperion::createMessageForwarder(const Json::Value & forwarderConfig) +{ + MessageForwarder * forwarder = new MessageForwarder(); + if ( ! forwarderConfig.isNull() ) + { + if ( ! forwarderConfig["json"].isNull() && forwarderConfig["json"].isArray() ) + { + for (const Json::Value& addr : forwarderConfig["json"]) + forwarder->addJsonSlave(addr.asString()); + } + + if ( ! forwarderConfig["proto"].isNull() && forwarderConfig["proto"].isArray() ) + { + for (const Json::Value& addr : forwarderConfig["proto"]) + forwarder->addProtoSlave(addr.asString()); + } + } + + return forwarder; +} + +MessageForwarder * Hyperion::getForwarder() +{ + return _messageForwarder; +} + Hyperion::Hyperion(const Json::Value &jsonConfig) : _ledString(createLedString(jsonConfig["leds"], createColorOrder(jsonConfig["device"]))), _muxer(_ledString.leds().size()), _raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])), _device(LedDeviceFactory::construct(jsonConfig["device"])), _effectEngine(nullptr), + _messageForwarder(createMessageForwarder(jsonConfig["forwarder"])), _timer() { if (!_raw2ledTransform->verifyTransforms()) @@ -314,6 +341,9 @@ Hyperion::~Hyperion() // delete the color transform delete _raw2ledTransform; + + // delete the message forwarder + delete _messageForwarder; } unsigned Hyperion::getLedCount() const diff --git a/libsrc/hyperion/MessageForwarder.cpp b/libsrc/hyperion/MessageForwarder.cpp new file mode 100644 index 00000000..8da277e2 --- /dev/null +++ b/libsrc/hyperion/MessageForwarder.cpp @@ -0,0 +1,34 @@ +#include + + +MessageForwarder::MessageForwarder() : +_running(false) +{ +} + +MessageForwarder::~MessageForwarder() +{ +} + + +void MessageForwarder::addJsonSlave(std::string slave) +{ + std::cout << slave << std::endl; +} + +void MessageForwarder::addProtoSlave(std::string slave) +{ + _protoSlaves << QString(slave.c_str()); +} + +void MessageForwarder::sendMessage() +{ + if ( ! _running ) + return; + +} + +QStringList MessageForwarder::getProtoSlaves() +{ + return _protoSlaves; +} diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp index 87fb7c1f..1b700b25 100644 --- a/libsrc/jsonserver/JsonClientConnection.cpp +++ b/libsrc/jsonserver/JsonClientConnection.cpp @@ -250,8 +250,22 @@ void JsonClientConnection::handleMessage(const std::string &messageString) handleNotImplemented(); } + +void JsonClientConnection::forwardJsonMessage(const Json::Value & message) +{ + QTcpSocket client; + client.connectToHost(QHostAddress("127.0.0.1"), 19444); + if ( client.waitForConnected(500) ) + { + sendMessage(message,&client); + client.close(); + } +} + void JsonClientConnection::handleColorCommand(const Json::Value &message) { + forwardJsonMessage(message); + // extract parameters int priority = message["priority"].asInt(); int duration = message.get("duration", -1).asInt(); @@ -289,6 +303,8 @@ void JsonClientConnection::handleColorCommand(const Json::Value &message) void JsonClientConnection::handleImageCommand(const Json::Value &message) { + forwardJsonMessage(message); + // extract parameters int priority = message["priority"].asInt(); int duration = message.get("duration", -1).asInt(); @@ -320,6 +336,8 @@ void JsonClientConnection::handleImageCommand(const Json::Value &message) void JsonClientConnection::handleEffectCommand(const Json::Value &message) { + forwardJsonMessage(message); + // extract parameters int priority = message["priority"].asInt(); int duration = message.get("duration", -1).asInt(); @@ -418,6 +436,8 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &) void JsonClientConnection::handleClearCommand(const Json::Value &message) { + forwardJsonMessage(message); + // extract parameters int priority = message["priority"].asInt(); @@ -428,8 +448,10 @@ void JsonClientConnection::handleClearCommand(const Json::Value &message) sendSuccessReply(); } -void JsonClientConnection::handleClearallCommand(const Json::Value &) +void JsonClientConnection::handleClearallCommand(const Json::Value & message) { + forwardJsonMessage(message); + // clear priority _hyperion->clearall(); @@ -530,10 +552,51 @@ void JsonClientConnection::sendMessage(const Json::Value &message) response.append(serializedReply.c_str(), serializedReply.length()); - _socket->write(response.data(), response.length()); + _socket->write(response.data(), response.length()); } } + +void JsonClientConnection::sendMessage(const Json::Value & message, QTcpSocket * socket) +{ + // serialize message (FastWriter already appends a newline) + std::string serializedMessage = Json::FastWriter().write(message); + + // write message + socket->write(serializedMessage.c_str()); + if (!socket->waitForBytesWritten()) + { + //std::cout << "Error while writing data to host" << std::endl; + return; + } + + // read reply data + QByteArray serializedReply; + while (!serializedReply.contains('\n')) + { + // receive reply + if (!socket->waitForReadyRead()) + { + //std::cout << "Error while reading data from host" << std::endl; + return; + } + + serializedReply += socket->readAll(); + } + int bytes = serializedReply.indexOf('\n') + 1; // Find the end of message + + // parse reply data + Json::Reader jsonReader; + Json::Value reply; + if (!jsonReader.parse(serializedReply.constData(), serializedReply.constData() + bytes, reply)) + { + //std::cout << "Error while parsing reply: invalid json" << std::endl; + return; + } + +} + + void JsonClientConnection::sendSuccessReply() { // create reply diff --git a/libsrc/jsonserver/JsonClientConnection.h b/libsrc/jsonserver/JsonClientConnection.h index 6575388a..01db54b1 100644 --- a/libsrc/jsonserver/JsonClientConnection.h +++ b/libsrc/jsonserver/JsonClientConnection.h @@ -124,6 +124,7 @@ private: /// @param message The JSON message to send /// void sendMessage(const Json::Value & message); + void sendMessage(const Json::Value & message, QTcpSocket * socket); /// /// Send a standard reply indicating success @@ -147,6 +148,11 @@ private: /// void handleWebSocketFrame(); + /// + /// forward json message + /// + void forwardJsonMessage(const Json::Value & message); + private: /// /// Check if a JSON messag is valid according to a given JSON schema diff --git a/libsrc/protoserver/ProtoClientConnection.cpp b/libsrc/protoserver/ProtoClientConnection.cpp index 2f0cc8d3..d1f087cd 100644 --- a/libsrc/protoserver/ProtoClientConnection.cpp +++ b/libsrc/protoserver/ProtoClientConnection.cpp @@ -20,7 +20,7 @@ // project includes #include "ProtoClientConnection.h" -ProtoClientConnection::ProtoClientConnection(QTcpSocket *socket, Hyperion * hyperion, QStringList forwardClientList) : +ProtoClientConnection::ProtoClientConnection(QTcpSocket *socket, Hyperion * hyperion) : QObject(), _socket(socket), _imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()), @@ -30,22 +30,11 @@ ProtoClientConnection::ProtoClientConnection(QTcpSocket *socket, Hyperion * hype // connect internal signals and slots connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed())); connect(_socket, SIGNAL(readyRead()), this, SLOT(readData())); - - for (int i = 0; i < forwardClientList.size(); ++i) { - std::cout << "Proto forward to " << forwardClientList.at(i).toLocal8Bit().constData() << std::endl; - ProtoConnection* p = new ProtoConnection("127.0.0.1:19445"); - p->setSkipReply(true); - _proxy_connections << p; - } - } ProtoClientConnection::~ProtoClientConnection() { delete _socket; - - while (!_proxy_connections.isEmpty()) - delete _proxy_connections.takeFirst(); } void ProtoClientConnection::readData() @@ -93,8 +82,7 @@ void ProtoClientConnection::socketClosed() void ProtoClientConnection::handleMessage(const proto::HyperionRequest & message) { // forward messages - for (int i = 0; i < _proxy_connections.size(); ++i) - _proxy_connections.at(i)->sendMessage(message); + emit newMessage(&message); switch (message.command()) { diff --git a/libsrc/protoserver/ProtoClientConnection.h b/libsrc/protoserver/ProtoClientConnection.h index a0c1186e..c68f56e0 100644 --- a/libsrc/protoserver/ProtoClientConnection.h +++ b/libsrc/protoserver/ProtoClientConnection.h @@ -30,7 +30,7 @@ public: /// @param socket The Socket object for this connection /// @param hyperion The Hyperion server /// - ProtoClientConnection(QTcpSocket * socket, Hyperion * hyperion, QStringList forwardClientList); + ProtoClientConnection(QTcpSocket * socket, Hyperion * hyperion); /// /// Destructor @@ -43,6 +43,7 @@ signals: /// @param connection This connection object /// void connectionClosed(ProtoClientConnection * connection); + void newMessage(const proto::HyperionRequest * message); private slots: /// @@ -125,8 +126,4 @@ private: /// The buffer used for reading data from the socket QByteArray _receiveBuffer; - - /// Hyperion proto connection object for forwarding - QList _proxy_connections; - }; diff --git a/libsrc/protoserver/ProtoServer.cpp b/libsrc/protoserver/ProtoServer.cpp index 54d8611b..a7c51ca8 100644 --- a/libsrc/protoserver/ProtoServer.cpp +++ b/libsrc/protoserver/ProtoServer.cpp @@ -2,17 +2,27 @@ #include // project includes +#include #include +#include "protoserver/ProtoConnection.h" #include "ProtoClientConnection.h" -ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port, QStringList * forwardClientList) : +ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) : QObject(), _hyperion(hyperion), _server(), _openConnections() { - for (int i = 0; i < forwardClientList->size(); ++i) - _forwardClients << forwardClientList->at(i); + + MessageForwarder * forwarder = hyperion->getForwarder(); + QStringList slaves = forwarder->getProtoSlaves(); + + for (int i = 0; i < slaves.size(); ++i) { + std::cout << "Proto forward to " << slaves.at(i).toLocal8Bit().constData() << std::endl; + ProtoConnection* p = new ProtoConnection(slaves.at(i).toLocal8Bit().constData()); + p->setSkipReply(true); + _proxy_connections << p; + } if (!_server.listen(QHostAddress::Any, port)) { @@ -28,6 +38,9 @@ ProtoServer::~ProtoServer() foreach (ProtoClientConnection * connection, _openConnections) { delete connection; } + + while (!_proxy_connections.isEmpty()) + delete _proxy_connections.takeFirst(); } uint16_t ProtoServer::getPort() const @@ -42,14 +55,22 @@ void ProtoServer::newConnection() if (socket != nullptr) { std::cout << "New proto connection" << std::endl; - ProtoClientConnection * connection = new ProtoClientConnection(socket, _hyperion, _forwardClients); + ProtoClientConnection * connection = new ProtoClientConnection(socket, _hyperion); _openConnections.insert(connection); // register slot for cleaning up after the connection closed connect(connection, SIGNAL(connectionClosed(ProtoClientConnection*)), this, SLOT(closedConnection(ProtoClientConnection*))); + connect(connection, SIGNAL(newMessage(const proto::HyperionRequest*)), this, SLOT(newMessage(const proto::HyperionRequest*))); + } } +void ProtoServer::newMessage(const proto::HyperionRequest * message) +{ + for (int i = 0; i < _proxy_connections.size(); ++i) + _proxy_connections.at(i)->sendMessage(*message); +} + void ProtoServer::closedConnection(ProtoClientConnection *connection) { std::cout << "Proto connection closed" << std::endl; diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index d96b345a..7bb5e60e 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -369,18 +369,7 @@ int main(int argc, char** argv) if (config.isMember("protoServer")) { const Json::Value & protoServerConfig = config["protoServer"]; - QStringList forwardClientList; - - if ( ! protoServerConfig["forward"].isNull() && protoServerConfig["forward"].isArray() ) - { - for (const Json::Value& client : protoServerConfig["forward"]) - { - forwardClientList << client.asString().c_str(); - std::cout << client.asString() << std::endl; - } - } - - protoServer = new ProtoServer(&hyperion, protoServerConfig["port"].asUInt(), &forwardClientList ); + protoServer = new ProtoServer(&hyperion, protoServerConfig["port"].asUInt() ); std::cout << "Proto server created and started on port " << protoServer->getPort() << std::endl; } #endif