Proto server added for better performance of remote application using a binary protocol;

gpio2spi added which can be used to switch the GPIO pins into SPI mode if necessary;


Former-commit-id: 237f495289ce2f4afae49b36684f464937dbd30f
This commit is contained in:
johan
2013-10-11 10:06:24 +02:00
parent c8d9d79f0f
commit 9247c577ba
11 changed files with 784 additions and 2 deletions

View File

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

View File

@@ -66,7 +66,8 @@ void DispmanxWrapper::setGrabbingMode(const GrabbingMode mode)
switch (mode)
{
case GRABBINGMODE_VIDEO:
_frameGrabber->setFlags(DISPMANX_SNAPSHOT_NO_RGB|DISPMANX_SNAPSHOT_FILL);
_frameGrabber->setFlags(0);
//_frameGrabber->setFlags(DISPMANX_SNAPSHOT_NO_RGB|DISPMANX_SNAPSHOT_FILL);
start();
case GRABBINGMODE_AUDIO:
case GRABBINGMODE_PHOTO:

View File

@@ -0,0 +1,52 @@
# Define the current source locations
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/protoserver)
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/protoserver)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${PROTOBUF_INCLUDE_DIRS})
# Group the headers that go through the MOC compiler
set(ProtoServer_QT_HEADERS
${CURRENT_HEADER_DIR}/ProtoServer.h
${CURRENT_SOURCE_DIR}/ProtoClientConnection.h
)
set(ProtoServer_HEADERS
)
set(ProtoServer_SOURCES
${CURRENT_SOURCE_DIR}/ProtoServer.cpp
${CURRENT_SOURCE_DIR}/ProtoClientConnection.cpp
)
set(ProtoServer_PROTOS
${CURRENT_SOURCE_DIR}/message.proto
)
protobuf_generate_cpp(ProtoServer_PROTO_SRCS ProtoServer_PROTO_HDRS
${ProtoServer_PROTOS}
)
qt4_wrap_cpp(ProtoServer_HEADERS_MOC ${ProtoServer_QT_HEADERS})
add_library(protoserver
${ProtoServer_HEADERS}
${ProtoServer_QT_HEADERS}
${ProtoServer_SOURCES}
${ProtoServer_HEADERS_MOC}
${ProtoServer_PROTOS}
${ProtoServer_PROTO_SRCS}
${ProtoServer_PROTO_HDRS}
)
target_link_libraries(protoserver
hyperion
hyperion-utils
${PROTOBUF_LIBRARIES})
qt4_use_modules(protoserver
Core
Gui
Network)

View File

@@ -0,0 +1,231 @@
// system includes
#include <stdexcept>
#include <cassert>
// 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 "ProtoClientConnection.h"
ProtoClientConnection::ProtoClientConnection(QTcpSocket *socket, Hyperion * hyperion) :
QObject(),
_socket(socket),
_imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()),
_hyperion(hyperion),
_receiveBuffer()
{
// connect internal signals and slots
connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
connect(_socket, SIGNAL(readyRead()), this, SLOT(readData()));
}
ProtoClientConnection::~ProtoClientConnection()
{
delete _socket;
}
void ProtoClientConnection::readData()
{
_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;
try
{
message.ParseFromArray(_receiveBuffer.data() + 4, messageSize);
}
catch(const std::exception & e)
{
sendErrorReply(std::string("Unable to parse message: ") + e.what());
}
// handle the message
handleMessage(message);
// remove message data from buffer
_receiveBuffer = _receiveBuffer.mid(messageSize + 4);
}
void ProtoClientConnection::socketClosed()
{
emit connectionClosed(this);
}
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)
{
if (message.rgbcolor().size() != 3)
{
sendErrorReply("The rgbcolor field requires a length of 3");
return;
}
// extract parameters
int priority = message.priority();
int duration = message.has_duration() ? message.duration() : -1;
const std::string & rgbColor = message.rgbcolor();
RgbColor color = {uint8_t(rgbColor[0]), uint8_t(rgbColor[1]), uint8_t(rgbColor[2])};
// set output
_hyperion->setColor(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();
// 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;
}
// set width and height of the image processor
_imageProcessor->setSize(width, height);
// create RgbImage
RgbImage image(width, height);
memcpy(image.memptr(), imageData.c_str(), imageData.size());
// process the image
std::vector<RgbColor> ledColors = _imageProcessor->process(image);
_hyperion->setColors(priority, ledColors, duration);
// send reply
sendSuccessReply();
}
void ProtoClientConnection::handleClearCommand(const proto::ClearRequest &message)
{
// extract parameters
int priority = message.priority();
// clear priority
_hyperion->clear(priority);
// send reply
sendSuccessReply();
}
void ProtoClientConnection::handleClearallCommand()
{
// clear priority
_hyperion->clearall();
// 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 = serializedReply.size();
uint8_t sizeData[] = {uint8_t(size >> 24), uint8_t(size >> 16), uint8_t(size >> 8), uint8_t(size)};
std::cout << sizeData[0] << " " << sizeData[0] << " " << sizeData[0] << " " << sizeData[0] << " <data>" << std::endl;
_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_success(true);
// send reply
sendMessage(reply);
}
void ProtoClientConnection::sendErrorReply(const std::string &error)
{
// create reply
proto::HyperionReply reply;
reply.set_success(false);
reply.set_error(error);
// send reply
sendMessage(reply);
}

View File

@@ -0,0 +1,132 @@
#pragma once
// stl includes
#include <string>
// Qt includes
#include <QByteArray>
#include <QTcpSocket>
// jsoncpp includes
#include <json/json.h>
// Hyperion includes
#include <hyperion/Hyperion.h>
// util includes
#include <utils/jsonschema/JsonSchemaChecker.h>
// proto includes
#include "message.pb.h"
class ImageProcessor;
///
/// The Connection object created by \a ProtoServer when a new connection is establshed
///
class ProtoClientConnection : public QObject
{
Q_OBJECT
public:
///
/// Constructor
/// @param socket The Socket object for this connection
/// @param hyperion The Hyperion server
///
ProtoClientConnection(QTcpSocket * socket, Hyperion * hyperion);
///
/// Destructor
///
~ProtoClientConnection();
signals:
///
/// Signal which is emitted when the connection is being closed
/// @param connection This connection object
///
void connectionClosed(ProtoClientConnection * 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 Proto message
///
/// @param message the incoming message as string
///
void handleMessage(const proto::HyperionRequest &message);
///
/// Handle an incoming Proto Color message
///
/// @param message the incoming message
///
void handleColorCommand(const proto::ColorRequest & message);
///
/// Handle an incoming Proto Image message
///
/// @param message the incoming message
///
void handleImageCommand(const proto::ImageRequest & message);
///
/// Handle an incoming Proto Clear message
///
/// @param message the incoming message
///
void handleClearCommand(const proto::ClearRequest & message);
///
/// Handle an incoming Proto Clearall message
///
void handleClearallCommand();
///
/// Handle an incoming Proto message of unknown type
///
void handleNotImplemented();
///
/// Send a message to the connected client
///
/// @param message The Proto message to send
///
void sendMessage(const google::protobuf::Message &message);
///
/// 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:
/// The TCP-Socket that is connected tot the Proto-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;
};

View File

@@ -0,0 +1,57 @@
// system includes
#include <stdexcept>
// project includes
#include <protoserver/ProtoServer.h>
#include "ProtoClientConnection.h"
ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) :
QObject(),
_hyperion(hyperion),
_server(),
_openConnections()
{
if (!_server.listen(QHostAddress::Any, port))
{
throw std::runtime_error("Proto server could not bind to port");
}
// Set trigger for incoming connections
connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection()));
}
ProtoServer::~ProtoServer()
{
foreach (ProtoClientConnection * connection, _openConnections) {
delete connection;
}
}
uint16_t ProtoServer::getPort() const
{
return _server.serverPort();
}
void ProtoServer::newConnection()
{
QTcpSocket * socket = _server.nextPendingConnection();
if (socket != nullptr)
{
std::cout << "New proto connection" << std::endl;
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*)));
}
}
void ProtoServer::closedConnection(ProtoClientConnection *connection)
{
std::cout << "Proto connection closed" << std::endl;
_openConnections.remove(connection);
// schedule to delete the connection object
connection->deleteLater();
}

View File

@@ -0,0 +1,69 @@
package proto;
message HyperionRequest {
enum Command {
COLOR = 1;
IMAGE = 2;
CLEAR = 3;
CLEARALL = 4;
}
// command specification
required Command command = 1;
// extensions to define all specific requests
extensions 10 to 100;
}
message ColorRequest {
extend HyperionRequest {
required ColorRequest colorRequest = 10;
}
// priority to use when setting the color
required int32 priority = 1;
// 3-byte value containing the rgb color
required bytes rgbColor = 2;
// duration of the request (negative results in infinite)
optional int32 duration = 3;
}
message ImageRequest {
extend HyperionRequest {
required ImageRequest imageRequest = 11;
}
// priority to use when setting the image
required int32 priority = 1;
// width of the image
required int32 imagewidth = 2;
// height of the image
required int32 imageheight = 3;
// image data
required bytes imagedata = 4;
// duration of the request (negative results in infinite)
optional int32 duration = 5;
}
message ClearRequest {
extend HyperionRequest {
required ClearRequest clearRequest = 12;
}
// priority which need to be cleared
required int32 priority = 1;
}
message HyperionReply {
// flag indication success or failure
required bool success = 1;
// string indicating the reason for failure (if applicable)
optional string error = 2;
}