hyperion.ng/libsrc/protoserver/ProtoClientConnection.cpp
LordGrey efc2046ab5
Various Cleanups (#1075)
* LedDevice - Address clang findings

* Fix Windows Warnings

* Ensure newInput is initialised

* Clean-up unused elements for Plaform Capture

* Fix initialization problem and spellings

* Address clang findings and spelling corrections

* LedDevice clean-ups

* Cleanups

* Align that getLedCount is int

* Have "display" as default for Grabbers

* Fix config during start-up for missing elements

* Framegrabber Clean-up - Remove non supported grabbers from selection, filter valid options

* Typo

* Framegrabber.json - Fix property numbering

* Preselect active Grabbertype

* Sort Grabbernames

* Align options with selected element

* Fix deletion of pointer to incomplete type 'BonjourBrowserWrapper'

* Address macOS compile warnings

* Have default layout = 1 LED only to avoid errors as in #673

* Address lgtm findings

* Address finding that params passed to LedDevice discovery were not considered

* Cleanups after merging with latest master

* Update Changelog

* Address lgtm findings

* Fix comment

* Test Fix

* Fix Python Warning

* Handle Dummy Device assignment correctly

* Address delete called on non-final 'commandline::Option' that has virtual functions but non-virtual destructor

* Correct that QTimer.start accepts only int

* Have Release Python GIL & reset threat state chnage downward compatible

* Correct format specifier

* LedDevice - add assertions

* Readonly DB - Fix merge issue

* Smoothing - Fix wrong defaults

* LedDevice - correct assertion

* Show smoothing config set# in debug and related values.

* Suppress error on windows, if default file is "/dev/null"

* CMAKE - Allow to define QT_BASE_DIR dynamically via environment-variable

* Ignore Visual Studio specific files

Co-authored-by: Paulchen Panther <16664240+Paulchen-Panther@users.noreply.github.com>
2020-11-14 17:58:56 +01:00

246 lines
6.6 KiB
C++

// project includes
#include "ProtoClientConnection.h"
// qt
#include <QTcpSocket>
#include <QHostAddress>
#include <QTimer>
#include <QRgb>
// TODO Remove this class if third-party apps have been migrated (eg. Hyperion Android Grabber, Windows Screen grabber etc.)
ProtoClientConnection::ProtoClientConnection(QTcpSocket* socket, int timeout, QObject *parent)
: QObject(parent)
, _log(Logger::getInstance("PROTOSERVER"))
, _socket(socket)
, _clientAddress(socket->peerAddress().toString())
, _timeoutTimer(new QTimer(this))
, _timeout(timeout * 1000)
, _priority()
{
// timer setup
_timeoutTimer->setSingleShot(true);
_timeoutTimer->setInterval(_timeout);
connect(_timeoutTimer, &QTimer::timeout, this, &ProtoClientConnection::forceClose);
// connect socket signals
connect(_socket, &QTcpSocket::readyRead, this, &ProtoClientConnection::readyRead);
connect(_socket, &QTcpSocket::disconnected, this, &ProtoClientConnection::disconnected);
}
void ProtoClientConnection::readyRead()
{
_receiveBuffer += _socket->readAll();
// check if we can read a message size
if (_receiveBuffer.size() <= 4)
{
return;
}
// read the message size
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;
}
// read a message
proto::HyperionRequest message;
if (!message.ParseFromArray(_receiveBuffer.data() + 4, messageSize))
{
sendErrorReply("Unable to parse message");
}
// handle the message
handleMessage(message);
// remove message data from buffer
_receiveBuffer = _receiveBuffer.mid(messageSize + 4);
}
void ProtoClientConnection::forceClose()
{
_socket->close();
}
void ProtoClientConnection::disconnected()
{
Debug(_log, "Socket Closed");
_socket->deleteLater();
emit clearGlobalInput(_priority);
emit clientDisconnected();
}
void ProtoClientConnection::handleMessage(const proto::HyperionRequest & message)
{
switch (message.command())
{
case proto::HyperionRequest::COLOR:
if (!message.HasExtension(proto::ColorRequest::colorRequest))
{
sendErrorReply("Received COLOR command without ColorRequest");
break;
}
handleColorCommand(message.GetExtension(proto::ColorRequest::colorRequest));
break;
case proto::HyperionRequest::IMAGE:
if (!message.HasExtension(proto::ImageRequest::imageRequest))
{
sendErrorReply("Received IMAGE command without ImageRequest");
break;
}
handleImageCommand(message.GetExtension(proto::ImageRequest::imageRequest));
break;
case proto::HyperionRequest::CLEAR:
if (!message.HasExtension(proto::ClearRequest::clearRequest))
{
sendErrorReply("Received CLEAR command without ClearRequest");
break;
}
handleClearCommand(message.GetExtension(proto::ClearRequest::clearRequest));
break;
case proto::HyperionRequest::CLEARALL:
handleClearallCommand();
break;
default:
handleNotImplemented();
}
}
void ProtoClientConnection::handleColorCommand(const proto::ColorRequest &message)
{
// extract parameters
int priority = message.priority();
int duration = message.has_duration() ? message.duration() : -1;
std::vector<ColorRgb> color{ ColorRgb{ uint8_t(qRed(message.rgbcolor())), uint8_t(qGreen(message.rgbcolor())), uint8_t(qBlue(message.rgbcolor())) } };
if (priority < 100 || priority >= 200)
{
sendErrorReply("The priority " + std::to_string(priority) + " is not in the valid priority range between 100 and 199.");
Error(_log, "The priority %d is not in the proto-connection range between 100 and 199.", priority);
return;
}
// make sure the prio is registered before setColor()
if(priority != _priority)
{
emit clearGlobalInput(_priority);
emit registerGlobalInput(priority, hyperion::COMP_PROTOSERVER, "Proto@"+_clientAddress);
_priority = priority;
}
// set output
emit setGlobalInputColor(_priority, color, duration);
// send reply
sendSuccessReply();
}
void ProtoClientConnection::handleImageCommand(const proto::ImageRequest &message)
{
// extract parameters
int priority = message.priority();
int duration = message.has_duration() ? message.duration() : -1;
int width = message.imagewidth();
int height = message.imageheight();
const std::string & imageData = message.imagedata();
if (priority < 100 || priority >= 200)
{
sendErrorReply("The priority " + std::to_string(priority) + " is not in the valid priority range between 100 and 199.");
Error(_log, "The priority %d is not in the proto-connection range between 100 and 199.", priority);
return;
}
// make sure the prio is registered before setInput()
if(priority != _priority)
{
emit clearGlobalInput(_priority);
emit registerGlobalInput(priority, hyperion::COMP_PROTOSERVER, "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.c_str(), imageData.size());
emit setGlobalInputImage(_priority, image, duration);
// send reply
sendSuccessReply();
}
void ProtoClientConnection::handleClearCommand(const proto::ClearRequest &message)
{
// extract parameters
int priority = message.priority();
// clear priority
emit clearGlobalInput(priority);
// send reply
sendSuccessReply();
}
void ProtoClientConnection::handleClearallCommand()
{
// clear all priority
emit clearGlobalInput(-1);
// send reply
sendSuccessReply();
}
void ProtoClientConnection::handleNotImplemented()
{
sendErrorReply("Command not implemented");
}
void ProtoClientConnection::sendMessage(const google::protobuf::Message &message)
{
std::string serializedReply = message.SerializeAsString();
uint32_t size = static_cast<uint32_t>(serializedReply.size());
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(serializedReply.data(), serializedReply.length());
_socket->flush();
}
void ProtoClientConnection::sendSuccessReply()
{
// create reply
proto::HyperionReply reply;
reply.set_type(proto::HyperionReply::REPLY);
reply.set_success(true);
// send reply
sendMessage(reply);
}
void ProtoClientConnection::sendErrorReply(const std::string &error)
{
// create reply
proto::HyperionReply reply;
reply.set_type(proto::HyperionReply::REPLY);
reply.set_success(false);
reply.set_error(error);
// send reply
sendMessage(reply);
}