BoblightServer added

Former-commit-id: deb3479ee673d763ad2e5451fcd35a0febedb4f3
This commit is contained in:
johan 2013-11-08 22:18:10 +01:00
parent 85c8ba0100
commit 7300413015
12 changed files with 528 additions and 2 deletions

View File

@ -0,0 +1,57 @@
#pragma once
// system includes
#include <cstdint>
// Qt includes
#include <QTcpServer>
#include <QSet>
// Hyperion includes
#include <hyperion/Hyperion.h>
class BoblightClientConnection;
///
/// This class creates a TCP server which accepts connections from boblight clients.
///
class BoblightServer : public QObject
{
Q_OBJECT
public:
///
/// BoblightServer constructor
/// @param hyperion Hyperion instance
/// @param port port number on which to start listening for connections
///
BoblightServer(Hyperion * hyperion, uint16_t port = 19333);
~BoblightServer();
///
/// @return the port number on which this TCP listens for incoming connections
///
uint16_t getPort() const;
private slots:
///
/// Slot which is called when a client tries to create a new connection
///
void newConnection();
///
/// Slot which is called when a client closes a connection
/// @param connection The Connection object which is being closed
///
void closedConnection(BoblightClientConnection * connection);
private:
/// Hyperion instance
Hyperion * _hyperion;
/// The TCP server object
QTcpServer _server;
/// List with open connections
QSet<BoblightClientConnection *> _openConnections;
};

View File

@ -52,6 +52,17 @@ public:
/// ///
void process(const RgbImage& image, std::vector<RgbColor>& ledColors); void process(const RgbImage& image, std::vector<RgbColor>& ledColors);
///
/// Get the hscan and vscan parameters for a single led
///
/// @param[in] led Index of the led
/// @param[out] hscanBegin begin of the hscan
/// @param[out] hscanEnd end of the hscan
/// @param[out] vscanBegin begin of the hscan
/// @param[out] vscanEnd end of the hscan
/// @return true if the parameters could be retrieved
bool getScanParameters(size_t led, double & hscanBegin, double & hscanEnd, double & vscanBegin, double & vscanEnd) const;
private: private:
/// Friend declaration of the factory for creating ImageProcessor's /// Friend declaration of the factory for creating ImageProcessor's
friend class ImageProcessorFactory; friend class ImageProcessorFactory;

View File

@ -8,5 +8,6 @@ add_subdirectory(dispmanx-grabber)
add_subdirectory(hyperion) add_subdirectory(hyperion)
add_subdirectory(jsonserver) add_subdirectory(jsonserver)
add_subdirectory(protoserver) add_subdirectory(protoserver)
add_subdirectory(boblightserver)
add_subdirectory(utils) add_subdirectory(utils)
add_subdirectory(xbmcvideochecker) add_subdirectory(xbmcvideochecker)

View File

@ -0,0 +1,217 @@
// system includes
#include <stdexcept>
#include <cassert>
#include <iomanip>
#include <cstdio>
// stl includes
#include <iostream>
#include <sstream>
#include <iterator>
// Qt includes
#include <QResource>
#include <QDateTime>
// hyperion util includes
#include "hyperion/ImageProcessorFactory.h"
#include "hyperion/ImageProcessor.h"
#include "utils/RgbColor.h"
// project includes
#include "BoblightClientConnection.h"
BoblightClientConnection::BoblightClientConnection(QTcpSocket *socket, Hyperion * hyperion) :
QObject(),
_locale(QLocale::C),
_socket(socket),
_imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()),
_hyperion(hyperion),
_receiveBuffer(),
_priority(255),
_ledColors(hyperion->getLedCount(), RgbColor::BLACK)
{
// initalize the locale. Start with the default C-locale
_locale.setNumberOptions(QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
// connect internal signals and slots
connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
connect(_socket, SIGNAL(readyRead()), this, SLOT(readData()));
}
BoblightClientConnection::~BoblightClientConnection()
{
if (_priority < 255)
{
// clear the current channel
_hyperion->clear(_priority);
_priority = 255;
}
delete _socket;
}
void BoblightClientConnection::readData()
{
_receiveBuffer += _socket->readAll();
int bytes = _receiveBuffer.indexOf('\n') + 1;
while(bytes > 0)
{
// create message string (strip the newline)
QString message = QString::fromAscii(_receiveBuffer.data(), bytes-1);
// remove message data from buffer
_receiveBuffer = _receiveBuffer.mid(bytes);
// handle message
handleMessage(message);
// try too look up '\n' again
bytes = _receiveBuffer.indexOf('\n') + 1;
}
}
void BoblightClientConnection::socketClosed()
{
if (_priority < 255)
{
// clear the current channel
_hyperion->clear(_priority);
_priority = 255;
}
emit connectionClosed(this);
}
void BoblightClientConnection::handleMessage(const QString & message)
{
//std::cout << "boblight message: " << message.toStdString() << std::endl;
QStringList messageParts = message.split(" ", QString::SkipEmptyParts);
if (messageParts.size() > 0)
{
if (messageParts[0] == "hello")
{
sendMessage("hello\n");
return;
}
else if (messageParts[0] == "ping")
{
sendMessage("ping 1\n");
return;
}
else if (messageParts[0] == "get" && messageParts.size() > 1)
{
if (messageParts[1] == "version")
{
sendMessage("version 5\n");
return;
}
else if (messageParts[1] == "lights")
{
sendLightMessage();
return;
}
}
else if (messageParts[0] == "set" && messageParts.size() > 2)
{
if (messageParts.size() > 3 && messageParts[1] == "light")
{
bool rc;
unsigned ledIndex = messageParts[2].toUInt(&rc);
if (rc && ledIndex < _ledColors.size())
{
if (messageParts[3] == "rgb" && messageParts.size() == 7)
{
bool rc1, rc2, rc3;
uint8_t red = 255 * messageParts[4].toFloat(&rc1);
if (!rc1)
{
// maybe a locale issue. switch to a locale with a comma instead of a dot as decimal seperator (or vice versa)
_locale = QLocale((_locale.decimalPoint() == QChar('.')) ? QLocale::Dutch : QLocale::C);
_locale.setNumberOptions(QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
// try again
red = 255 * messageParts[4].toFloat(&rc1);
}
uint8_t green = 255 * messageParts[5].toFloat(&rc2);
uint8_t blue = 255 * messageParts[6].toFloat(&rc3);
if (rc1 && rc2 && rc3)
{
RgbColor & rgb = _ledColors[ledIndex];
rgb.red = red;
rgb.green = green;
rgb.blue = blue;
return;
}
}
else if(messageParts[3] == "speed" ||
messageParts[3] == "interpolation" ||
messageParts[3] == "use" ||
messageParts[3] == "singlechange")
{
// these message are ignored by Hyperion
return;
}
}
}
else if (messageParts.size() == 3 && messageParts[1] == "priority")
{
bool rc;
int prio = messageParts[2].toInt(&rc);
if (rc && prio != _priority)
{
if (_priority < 255)
{
// clear the current channel
_hyperion->clear(_priority);
}
_priority = prio;
return;
}
}
}
else if (messageParts[0] == "sync")
{
// send current color values to hyperion
if (_priority < 255)
{
_hyperion->setColors(_priority, _ledColors, -1);
}
}
}
std::cout << "unknown boblight message: " << message.toStdString() << std::endl;
}
void BoblightClientConnection::sendMessage(const std::string & message)
{
//std::cout << "send boblight message: " << message;
_socket->write(message.c_str(), message.size());
}
void BoblightClientConnection::sendMessage(const char * message, int size)
{
//std::cout << "send boblight message: " << std::string(message, size);
_socket->write(message, size);
}
void BoblightClientConnection::sendLightMessage()
{
char buffer[256];
int n = snprintf(buffer, sizeof(buffer), "lights %d\n", _hyperion->getLedCount());
sendMessage(buffer, n);
for (unsigned i = 0; i < _hyperion->getLedCount(); ++i)
{
double h0, h1, v0, v1;
_imageProcessor->getScanParameters(i, h0, h1, v0, v1);
n = snprintf(buffer, sizeof(buffer), "light %03d scan %f %f %f %f\n", i, 100*v0, 100*v1, 100*h0, 100*h1);
sendMessage(buffer, n);
}
}

View File

@ -0,0 +1,103 @@
#pragma once
// stl includes
#include <string>
// Qt includes
#include <QByteArray>
#include <QTcpSocket>
#include <QLocale>
// Hyperion includes
#include <hyperion/Hyperion.h>
class ImageProcessor;
///
/// The Connection object created by \a BoblightServer when a new connection is establshed
///
class BoblightClientConnection : public QObject
{
Q_OBJECT
public:
///
/// Constructor
/// @param socket The Socket object for this connection
/// @param hyperion The Hyperion server
///
BoblightClientConnection(QTcpSocket * socket, Hyperion * hyperion);
///
/// Destructor
///
~BoblightClientConnection();
signals:
///
/// Signal which is emitted when the connection is being closed
/// @param connection This connection object
///
void connectionClosed(BoblightClientConnection * connection);
private slots:
///
/// Slot called when new data has arrived
///
void readData();
///
/// Slot called when this connection is being closed
///
void socketClosed();
private:
///
/// Handle an incoming boblight message
///
/// @param message the incoming message as string
///
void handleMessage(const QString &message);
///
/// Send a message to the connected client
///
/// @param message The boblight message to send
///
void sendMessage(const std::string &message);
///
/// Send a message to the connected client
///
/// @param message The boblight message to send
/// @param size The size of the message
///
void sendMessage(const char * message, int size);
///
/// Send a lights message the to connected client
///
void sendLightMessage();
private:
/// Locale used for parsing floating point values
QLocale _locale;
/// The TCP-Socket that is connected tot the boblight-client
QTcpSocket * _socket;
/// The processor for translating images to led-values
ImageProcessor * _imageProcessor;
/// Link to Hyperion for writing led-values to a priority channel
Hyperion * _hyperion;
/// The buffer used for reading data from the socket
QByteArray _receiveBuffer;
/// The priority used by this connection
int _priority;
/// The latest led color data
std::vector<RgbColor> _ledColors;
};

View File

@ -0,0 +1,57 @@
// system includes
#include <stdexcept>
// project includes
#include <boblightserver/BoblightServer.h>
#include "BoblightClientConnection.h"
BoblightServer::BoblightServer(Hyperion *hyperion, uint16_t port) :
QObject(),
_hyperion(hyperion),
_server(),
_openConnections()
{
if (!_server.listen(QHostAddress::Any, port))
{
throw std::runtime_error("Boblight server could not bind to port");
}
// Set trigger for incoming connections
connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection()));
}
BoblightServer::~BoblightServer()
{
foreach (BoblightClientConnection * connection, _openConnections) {
delete connection;
}
}
uint16_t BoblightServer::getPort() const
{
return _server.serverPort();
}
void BoblightServer::newConnection()
{
QTcpSocket * socket = _server.nextPendingConnection();
if (socket != nullptr)
{
std::cout << "New boblight connection" << std::endl;
BoblightClientConnection * connection = new BoblightClientConnection(socket, _hyperion);
_openConnections.insert(connection);
// register slot for cleaning up after the connection closed
connect(connection, SIGNAL(connectionClosed(BoblightClientConnection*)), this, SLOT(closedConnection(BoblightClientConnection*)));
}
}
void BoblightServer::closedConnection(BoblightClientConnection *connection)
{
std::cout << "Boblight connection closed" << std::endl;
_openConnections.remove(connection);
// schedule to delete the connection object
connection->deleteLater();
}

View File

@ -0,0 +1,36 @@
# Define the current source locations
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/boblightserver)
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/boblightserver)
# Group the headers that go through the MOC compiler
set(BoblightServer_QT_HEADERS
${CURRENT_HEADER_DIR}/BoblightServer.h
${CURRENT_SOURCE_DIR}/BoblightClientConnection.h
)
set(BoblightServer_HEADERS
)
set(BoblightServer_SOURCES
${CURRENT_SOURCE_DIR}/BoblightServer.cpp
${CURRENT_SOURCE_DIR}/BoblightClientConnection.cpp
)
qt4_wrap_cpp(BoblightServer_HEADERS_MOC ${BoblightServer_QT_HEADERS})
add_library(boblightserver
${BoblightServer_HEADERS}
${BoblightServer_QT_HEADERS}
${BoblightServer_SOURCES}
${BoblightServer_HEADERS_MOC}
)
target_link_libraries(boblightserver
hyperion
hyperion-utils)
qt4_use_modules(boblightserver
Core
Gui
Network)

View File

@ -67,6 +67,20 @@ void ImageProcessor::process(const RgbImage& image, std::vector<RgbColor>& ledCo
mImageToLeds->getMeanLedColor(image, ledColors); mImageToLeds->getMeanLedColor(image, ledColors);
} }
bool ImageProcessor::getScanParameters(size_t led, double &hscanBegin, double &hscanEnd, double &vscanBegin, double &vscanEnd) const
{
if (led < mLedString.leds().size())
{
const Led & l = mLedString.leds()[led];
hscanBegin = l.minX_frac;
hscanEnd = l.maxX_frac;
vscanBegin = l.minY_frac;
vscanEnd = l.maxY_frac;
}
return false;
}
void ImageProcessor::verifyBorder(const RgbImage& image) void ImageProcessor::verifyBorder(const RgbImage& image)
{ {
if(_enableBlackBorderRemoval && _borderProcessor->process(image)) if(_enableBlackBorderRemoval && _borderProcessor->process(image))

View File

@ -295,6 +295,20 @@
"additionalProperties" : false "additionalProperties" : false
}, },
"protoServer" : "protoServer" :
{
"type" : "object",
"required" : false,
"properties" : {
"port" : {
"type" : "integer",
"required" : true,
"minimum" : 0,
"maximum" : 65535
}
},
"additionalProperties" : false
},
"boblightServer" :
{ {
"type" : "object", "type" : "object",
"required" : false, "required" : false,

View File

@ -42,7 +42,7 @@ void JsonClientConnection::readData()
_receiveBuffer += _socket->readAll(); _receiveBuffer += _socket->readAll();
int bytes = _receiveBuffer.indexOf('\n') + 1; int bytes = _receiveBuffer.indexOf('\n') + 1;
if (bytes != 0) while(bytes > 0)
{ {
// create message string // create message string
std::string message(_receiveBuffer.data(), bytes); std::string message(_receiveBuffer.data(), bytes);
@ -52,6 +52,9 @@ void JsonClientConnection::readData()
// handle message // handle message
handleMessage(message); handleMessage(message);
// try too look up '\n' again
bytes = _receiveBuffer.indexOf('\n') + 1;
} }
} }

View File

@ -8,4 +8,5 @@ target_link_libraries(hyperiond
dispmanx-grabber dispmanx-grabber
xbmcvideochecker xbmcvideochecker
jsonserver jsonserver
protoserver) protoserver
boblightserver)

View File

@ -26,6 +26,9 @@
// ProtoServer includes // ProtoServer includes
#include <protoserver/ProtoServer.h> #include <protoserver/ProtoServer.h>
// BoblightServer includes
#include <boblightserver/BoblightServer.h>
void signal_handler(const int signum) void signal_handler(const int signum)
{ {
QCoreApplication::quit(); QCoreApplication::quit();
@ -153,6 +156,15 @@ int main(int argc, char** argv)
std::cout << "Proto server created and started on port " << protoServer->getPort() << std::endl; std::cout << "Proto server created and started on port " << protoServer->getPort() << std::endl;
} }
// Create Boblight server if configuration is present
BoblightServer * boblightServer = nullptr;
if (config.isMember("boblightServer"))
{
const Json::Value & boblightServerConfig = config["boblightServer"];
boblightServer = new BoblightServer(&hyperion, boblightServerConfig["port"].asUInt());
std::cout << "Boblight server created and started on port " << boblightServer->getPort() << std::endl;
}
// run the application // run the application
int rc = app.exec(); int rc = app.exec();
std::cout << "Application closed with code " << rc << std::endl; std::cout << "Application closed with code " << rc << std::endl;