diff --git a/include/jsonserver/JsonServer.h b/include/jsonserver/JsonServer.h new file mode 100644 index 00000000..c14a6176 --- /dev/null +++ b/include/jsonserver/JsonServer.h @@ -0,0 +1,36 @@ +#pragma once + +// system includes +#include + +// Qt includes +#include +#include + +// Hyperion includes +#include + +class JsonClientConnection; + +class JsonServer : public QObject +{ + Q_OBJECT + +public: + JsonServer(Hyperion * hyperion, uint16_t port = 19444); + ~JsonServer(); + + uint16_t getPort() const; + +private slots: + void newConnection(); + + void closedConnection(JsonClientConnection * connection); + +private: + Hyperion * _hyperion; + + QTcpServer _server; + + QSet _openConnections; +}; diff --git a/libsrc/CMakeLists.txt b/libsrc/CMakeLists.txt index 6fd6c7a0..56455f7b 100644 --- a/libsrc/CMakeLists.txt +++ b/libsrc/CMakeLists.txt @@ -4,4 +4,5 @@ SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc) add_subdirectory(hyperion) +add_subdirectory(jsonserver) add_subdirectory(utils) diff --git a/libsrc/jsonserver/CMakeLists.txt b/libsrc/jsonserver/CMakeLists.txt new file mode 100644 index 00000000..80233de4 --- /dev/null +++ b/libsrc/jsonserver/CMakeLists.txt @@ -0,0 +1,36 @@ + +# Define the current source locations +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/jsonserver) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/jsonserver) + +# Group the headers that go through the MOC compiler +SET(JsonServer_QT_HEADERS + ${CURRENT_HEADER_DIR}/JsonServer.h + ${CURRENT_SOURCE_DIR}/JsonClientConnection.h +) + +SET(JsonServer_HEADERS +) + +SET(JsonServer_SOURCES + ${CURRENT_SOURCE_DIR}/JsonServer.cpp + ${CURRENT_SOURCE_DIR}/JsonClientConnection.cpp +) + +qt4_wrap_cpp(JsonServer_HEADERS_MOC ${JsonServer_QT_HEADERS}) + +add_library(jsonserver + ${JsonServer_HEADERS} + ${JsonServer_QT_HEADERS} + ${JsonServer_HEADERS_MOC} + ${JsonServer_SOURCES} +) + +target_link_libraries(jsonserver + hyperion + jsoncpp) + +qt4_use_modules(jsonserver + Core + Gui + Network) diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp new file mode 100644 index 00000000..ceb4cd3a --- /dev/null +++ b/libsrc/jsonserver/JsonClientConnection.cpp @@ -0,0 +1,77 @@ +// stl includes +#include + +#include "JsonClientConnection.h" + +JsonClientConnection::JsonClientConnection(QTcpSocket *socket) : + QObject(), + _socket(socket) +{ + connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed())); + connect(_socket, SIGNAL(readyRead()), this, SLOT(readData())); +} + + +JsonClientConnection::~JsonClientConnection() +{ + delete _socket; +} + +void JsonClientConnection::readData() +{ + _receiveBuffer += _socket->readAll(); + + int bytes = _receiveBuffer.indexOf('\n') + 1; + if (bytes != 0) + { + // create message string + std::string message(_receiveBuffer.data(), bytes); + + // remove message data from buffer + _receiveBuffer = _receiveBuffer.mid(bytes); + + // handle message + handleMessage(message); + } +} + +void JsonClientConnection::socketClosed() +{ + emit connectionClosed(this); +} + +void JsonClientConnection::handleMessage(const std::string &message) +{ + Json::Reader reader; + Json::Value messageRoot; + if (!reader.parse(message, messageRoot, false)) + { + sendErrorReply("Error while parsing json: " + reader.getFormattedErrorMessages()); + return; + } + + handleNotImplemented(messageRoot); +} + +void JsonClientConnection::handleNotImplemented(const Json::Value & message) +{ + sendErrorReply("Command not implemented"); +} + +void JsonClientConnection::sendMessage(const Json::Value &message) +{ + Json::FastWriter writer; + std::string serializedReply = writer.write(message); + _socket->write(serializedReply.data(), serializedReply.length()); +} + +void JsonClientConnection::sendErrorReply(const std::string &error) +{ + // create reply + Json::Value reply; + reply["success"] = false; + reply["error"] = error; + + // send reply + sendMessage(reply); +} diff --git a/libsrc/jsonserver/JsonClientConnection.h b/libsrc/jsonserver/JsonClientConnection.h new file mode 100644 index 00000000..b8c6f9a9 --- /dev/null +++ b/libsrc/jsonserver/JsonClientConnection.h @@ -0,0 +1,39 @@ +#pragma once + +// stl includes +#include + +// Qt includes +#include +#include + +// jsoncpp includes +#include + +class JsonClientConnection : public QObject +{ + Q_OBJECT + +public: + JsonClientConnection(QTcpSocket * socket); + ~JsonClientConnection(); + +signals: + void connectionClosed(JsonClientConnection * connection); + +private slots: + void readData(); + void socketClosed(); + +private: + void handleMessage(const std::string & message); + void handleNotImplemented(const Json::Value & message); + + void sendMessage(const Json::Value & message); + void sendErrorReply(const std::string & error); + +private: + QTcpSocket * _socket; + + QByteArray _receiveBuffer; +}; diff --git a/libsrc/jsonserver/JsonServer.cpp b/libsrc/jsonserver/JsonServer.cpp new file mode 100644 index 00000000..9155499a --- /dev/null +++ b/libsrc/jsonserver/JsonServer.cpp @@ -0,0 +1,57 @@ +// system includes +#include + +// project includes +#include +#include "JsonClientConnection.h" + +JsonServer::JsonServer(Hyperion *hyperion, uint16_t port) : + QObject(), + _hyperion(hyperion), + _server(), + _openConnections() +{ + if (!_server.listen(QHostAddress::Any, port)) + { + throw std::runtime_error("Json server could not bind to port"); + } + + // Set trigger for incoming connections + connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection())); +} + +JsonServer::~JsonServer() +{ + foreach (JsonClientConnection * connection, _openConnections) { + delete connection; + } +} + +uint16_t JsonServer::getPort() const +{ + return _server.serverPort(); +} + +void JsonServer::newConnection() +{ + std::cout << "New incoming json connection" << std::endl; + QTcpSocket * socket = _server.nextPendingConnection(); + + if (socket != nullptr) + { + JsonClientConnection * connection = new JsonClientConnection(socket); + _openConnections.insert(connection); + + // register slot for cleaning up after the connection closed + connect(connection, SIGNAL(connectionClosed(JsonClientConnection*)), this, SLOT(closedConnection(JsonClientConnection*))); + } +} + +void JsonServer::closedConnection(JsonClientConnection *connection) +{ + std::cout << "Json connection closed" << std::endl; + _openConnections.remove(connection); + + // schedule to delete the connection object + connection->deleteLater(); +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7e015215..4f47cc50 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,7 +6,8 @@ add_executable(hyperiond hyperion-d.cpp) target_link_libraries(hyperiond - hyperion) + hyperion + jsonserver) # Find the libPNG find_package(PNG QUIET) diff --git a/src/hyperion-d.cpp b/src/hyperion-d.cpp index 34a2b391..a657e319 100644 --- a/src/hyperion-d.cpp +++ b/src/hyperion-d.cpp @@ -9,6 +9,9 @@ #include #include +// JsonServer includes +#include + int main(int argc, char** argv) { // Initialising QCoreApplication @@ -16,9 +19,9 @@ int main(int argc, char** argv) std::cout << "QCoreApplication initialised" << std::endl; // Select config and schema file - const std::string homeDir = getenv("RASPILIGHT_HOME"); - const std::string schemaFile = homeDir + "/hyperion.schema.json"; - const std::string configFile = homeDir + "/hyperion.config.json"; + //const std::string homeDir = getenv("RASPILIGHT_HOME"); + const std::string schemaFile = "hyperion.schema.json"; + const std::string configFile = "hyperion.config.json"; // Load configuration and check against the schema at the same time Json::Value config; @@ -36,6 +39,9 @@ int main(int argc, char** argv) dispmanx.start(); std::cout << "Frame grabber created and started" << std::endl; + JsonServer jsonServer(&hyperion); + std::cout << "Json server created and started on port " << jsonServer.getPort() << std::endl; + app.exec(); std::cout << "Application closed" << std::endl; } diff --git a/src/hyperion-remote/JsonConnection.cpp b/src/hyperion-remote/JsonConnection.cpp index 17c7ffdb..3f4b887d 100644 --- a/src/hyperion-remote/JsonConnection.cpp +++ b/src/hyperion-remote/JsonConnection.cpp @@ -210,16 +210,15 @@ void JsonConnection::setTransform(ColorTransformValues *threshold, ColorTransfor Json::Value JsonConnection::sendMessage(const Json::Value & message) { + // serialize message (FastWriter already appends a newline) + std::string serializedMessage = Json::FastWriter().write(message); + // print command if requested if (_printJson) { - std::cout << "Command: " << message << std::endl; + std::cout << "Command: " << serializedMessage; } - // serialize message (FastWriter already appends a newline - Json::FastWriter jsonWriter; - std::string serializedMessage = jsonWriter.write(message); - // write message _socket.write(serializedMessage.c_str()); if (!_socket.waitForBytesWritten()) @@ -241,6 +240,12 @@ Json::Value JsonConnection::sendMessage(const Json::Value & message) } int bytes = serializedReply.indexOf('\n') + 1; // Find the end of message + // print reply if requested + if (_printJson) + { + std::cout << "Reply: " << std::string(serializedReply.data(), bytes); + } + // parse reply data Json::Reader jsonReader; Json::Value reply; @@ -249,12 +254,6 @@ Json::Value JsonConnection::sendMessage(const Json::Value & message) throw std::runtime_error("Error while parsing reply: invalid json"); } - // print reply if requested - if (_printJson) - { - std::cout << "Reply:" << reply << std::endl; - } - return reply; } @@ -271,12 +270,12 @@ bool JsonConnection::parseReply(const Json::Value &reply) } catch (const std::runtime_error &) { - // Some json paring error: ignore and set parsing error + // Some json parsing error: ignore and set parsing error } if (!success) { - throw std::runtime_error("Error while paring reply: " + reason); + throw std::runtime_error("Error while executing command: " + reason); } return success;