2013-08-17 19:20:19 +02:00
|
|
|
// system includes
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <cassert>
|
|
|
|
|
2013-08-17 15:39:29 +02:00
|
|
|
// stl includes
|
|
|
|
#include <iostream>
|
2013-08-17 19:20:19 +02:00
|
|
|
#include <sstream>
|
|
|
|
#include <iterator>
|
|
|
|
|
|
|
|
// Qt includes
|
|
|
|
#include <QResource>
|
2013-08-19 20:33:36 +02:00
|
|
|
#include <QDateTime>
|
2013-08-17 15:39:29 +02:00
|
|
|
|
2013-08-18 12:21:07 +02:00
|
|
|
// hyperion util includes
|
2013-08-18 20:55:59 +02:00
|
|
|
#include "hyperion/ImageProcessorFactory.h"
|
|
|
|
#include "hyperion/ImageProcessor.h"
|
2013-11-11 10:00:37 +01:00
|
|
|
#include "utils/ColorRgb.h"
|
2013-08-18 12:21:07 +02:00
|
|
|
|
2013-08-17 19:20:19 +02:00
|
|
|
// project includes
|
2013-08-17 15:39:29 +02:00
|
|
|
#include "JsonClientConnection.h"
|
|
|
|
|
2013-08-18 12:21:07 +02:00
|
|
|
JsonClientConnection::JsonClientConnection(QTcpSocket *socket, Hyperion * hyperion) :
|
2013-08-17 15:39:29 +02:00
|
|
|
QObject(),
|
2013-08-17 19:20:19 +02:00
|
|
|
_socket(socket),
|
2013-08-18 20:55:59 +02:00
|
|
|
_imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()),
|
2013-08-18 12:21:07 +02:00
|
|
|
_hyperion(hyperion),
|
2013-08-17 19:20:19 +02:00
|
|
|
_receiveBuffer()
|
2013-08-17 15:39:29 +02:00
|
|
|
{
|
2013-08-17 19:20:19 +02:00
|
|
|
// connect internal signals and slots
|
2013-08-17 15:39:29 +02:00
|
|
|
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;
|
2013-11-08 22:18:10 +01:00
|
|
|
while(bytes > 0)
|
2013-08-17 15:39:29 +02:00
|
|
|
{
|
|
|
|
// create message string
|
|
|
|
std::string message(_receiveBuffer.data(), bytes);
|
|
|
|
|
|
|
|
// remove message data from buffer
|
|
|
|
_receiveBuffer = _receiveBuffer.mid(bytes);
|
|
|
|
|
|
|
|
// handle message
|
|
|
|
handleMessage(message);
|
2013-11-08 22:18:10 +01:00
|
|
|
|
|
|
|
// try too look up '\n' again
|
|
|
|
bytes = _receiveBuffer.indexOf('\n') + 1;
|
2013-08-17 15:39:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void JsonClientConnection::socketClosed()
|
|
|
|
{
|
|
|
|
emit connectionClosed(this);
|
|
|
|
}
|
|
|
|
|
2013-08-18 12:02:17 +02:00
|
|
|
void JsonClientConnection::handleMessage(const std::string &messageString)
|
2013-08-17 15:39:29 +02:00
|
|
|
{
|
|
|
|
Json::Reader reader;
|
2013-08-18 12:02:17 +02:00
|
|
|
Json::Value message;
|
|
|
|
if (!reader.parse(messageString, message, false))
|
2013-08-17 15:39:29 +02:00
|
|
|
{
|
|
|
|
sendErrorReply("Error while parsing json: " + reader.getFormattedErrorMessages());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-18 12:02:17 +02:00
|
|
|
// check basic message
|
|
|
|
std::string errors;
|
|
|
|
if (!checkJson(message, ":schema", errors))
|
2013-08-17 19:20:19 +02:00
|
|
|
{
|
2013-08-18 12:02:17 +02:00
|
|
|
sendErrorReply("Error while validating json: " + errors);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check specific message
|
|
|
|
const std::string command = message["command"].asString();
|
2013-08-18 13:33:56 +02:00
|
|
|
if (!checkJson(message, QString(":schema-%1").arg(QString::fromStdString(command)), errors))
|
2013-08-18 12:02:17 +02:00
|
|
|
{
|
|
|
|
sendErrorReply("Error while validating json: " + errors);
|
2013-08-17 19:20:19 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-18 12:02:17 +02:00
|
|
|
// switch over all possible commands and handle them
|
|
|
|
if (command == "color")
|
|
|
|
handleColorCommand(message);
|
|
|
|
else if (command == "image")
|
|
|
|
handleImageCommand(message);
|
2013-11-24 16:10:48 +01:00
|
|
|
else if (command == "effect")
|
|
|
|
handleEffectCommand(message);
|
2013-08-18 12:02:17 +02:00
|
|
|
else if (command == "serverinfo")
|
|
|
|
handleServerInfoCommand(message);
|
|
|
|
else if (command == "clear")
|
|
|
|
handleClearCommand(message);
|
|
|
|
else if (command == "clearall")
|
|
|
|
handleClearallCommand(message);
|
|
|
|
else if (command == "transform")
|
|
|
|
handleTransformCommand(message);
|
|
|
|
else
|
|
|
|
handleNotImplemented();
|
|
|
|
}
|
|
|
|
|
|
|
|
void JsonClientConnection::handleColorCommand(const Json::Value &message)
|
|
|
|
{
|
2013-08-18 12:21:07 +02:00
|
|
|
// extract parameters
|
|
|
|
int priority = message["priority"].asInt();
|
|
|
|
int duration = message.get("duration", -1).asInt();
|
2013-11-11 10:00:37 +01:00
|
|
|
ColorRgb color = {uint8_t(message["color"][0u].asInt()), uint8_t(message["color"][1u].asInt()), uint8_t(message["color"][2u].asInt())};
|
2013-08-18 12:21:07 +02:00
|
|
|
|
|
|
|
// set output
|
2013-08-18 13:33:56 +02:00
|
|
|
_hyperion->setColor(priority, color, duration);
|
2013-08-18 12:21:07 +02:00
|
|
|
|
|
|
|
// send reply
|
|
|
|
sendSuccessReply();
|
2013-08-18 12:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void JsonClientConnection::handleImageCommand(const Json::Value &message)
|
|
|
|
{
|
2013-08-18 20:55:59 +02:00
|
|
|
// extract parameters
|
|
|
|
int priority = message["priority"].asInt();
|
|
|
|
int duration = message.get("duration", -1).asInt();
|
|
|
|
int width = message["imagewidth"].asInt();
|
|
|
|
int height = message["imageheight"].asInt();
|
|
|
|
QByteArray data = QByteArray::fromBase64(QByteArray(message["imagedata"].asCString()));
|
|
|
|
|
|
|
|
// check consistency of the size of the received data
|
|
|
|
if (data.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);
|
|
|
|
|
2013-11-11 10:00:37 +01:00
|
|
|
// create ImageRgb
|
|
|
|
Image<ColorRgb> image(width, height);
|
2013-08-18 20:55:59 +02:00
|
|
|
memcpy(image.memptr(), data.data(), data.size());
|
|
|
|
|
|
|
|
// process the image
|
2013-11-11 10:00:37 +01:00
|
|
|
std::vector<ColorRgb> ledColors = _imageProcessor->process(image);
|
2013-08-18 20:55:59 +02:00
|
|
|
_hyperion->setColors(priority, ledColors, duration);
|
|
|
|
|
|
|
|
// send reply
|
|
|
|
sendSuccessReply();
|
2013-08-17 15:39:29 +02:00
|
|
|
}
|
|
|
|
|
2013-11-24 16:10:48 +01:00
|
|
|
void JsonClientConnection::handleEffectCommand(const Json::Value &message)
|
|
|
|
{
|
|
|
|
// extract parameters
|
|
|
|
int priority = message["priority"].asInt();
|
|
|
|
int duration = message.get("duration", -1).asInt();
|
|
|
|
const Json::Value & effect = message["effect"];
|
|
|
|
const std::string & effectName = effect["name"].asString();
|
|
|
|
|
|
|
|
// set output
|
2013-12-01 14:09:01 +01:00
|
|
|
if (effect.isMember("args"))
|
|
|
|
{
|
|
|
|
_hyperion->setEffect(effectName, effect["args"], priority, duration);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_hyperion->setEffect(effectName, priority, duration);
|
|
|
|
}
|
2013-11-24 16:10:48 +01:00
|
|
|
|
|
|
|
// send reply
|
|
|
|
sendSuccessReply();
|
|
|
|
}
|
|
|
|
|
|
|
|
void JsonClientConnection::handleServerInfoCommand(const Json::Value &)
|
2013-08-18 12:02:17 +02:00
|
|
|
{
|
2013-08-19 20:33:36 +02:00
|
|
|
// create result
|
|
|
|
Json::Value result;
|
|
|
|
result["success"] = true;
|
|
|
|
Json::Value & info = result["info"];
|
|
|
|
|
|
|
|
// collect priority information
|
2013-11-30 12:42:08 +01:00
|
|
|
Json::Value & priorities = info["priorities"] = Json::Value(Json::arrayValue);
|
2013-08-19 20:33:36 +02:00
|
|
|
uint64_t now = QDateTime::currentMSecsSinceEpoch();
|
|
|
|
QList<int> activePriorities = _hyperion->getActivePriorities();
|
|
|
|
foreach (int priority, activePriorities) {
|
|
|
|
const Hyperion::InputInfo & priorityInfo = _hyperion->getPriorityInfo(priority);
|
|
|
|
Json::Value & item = priorities[priorities.size()];
|
|
|
|
item["priority"] = priority;
|
|
|
|
if (priorityInfo.timeoutTime_ms != -1)
|
|
|
|
{
|
2013-11-19 23:02:41 +01:00
|
|
|
item["duration_ms"] = Json::Value::UInt(priorityInfo.timeoutTime_ms - now);
|
2013-08-19 20:33:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// collect transform information
|
2013-11-30 12:42:08 +01:00
|
|
|
Json::Value & transform = info["transform"] = Json::Value(Json::objectValue);
|
2013-08-21 21:50:17 +02:00
|
|
|
transform["saturationGain"] = _hyperion->getTransform(Hyperion::SATURATION_GAIN, Hyperion::INVALID);
|
|
|
|
transform["valueGain"] = _hyperion->getTransform(Hyperion::VALUE_GAIN, Hyperion::INVALID);
|
2013-08-19 20:33:36 +02:00
|
|
|
Json::Value & threshold = transform["threshold"];
|
|
|
|
threshold.append(_hyperion->getTransform(Hyperion::THRESHOLD, Hyperion::RED));
|
|
|
|
threshold.append(_hyperion->getTransform(Hyperion::THRESHOLD, Hyperion::GREEN));
|
|
|
|
threshold.append(_hyperion->getTransform(Hyperion::THRESHOLD, Hyperion::BLUE));
|
|
|
|
Json::Value & gamma = transform["gamma"];
|
|
|
|
gamma.append(_hyperion->getTransform(Hyperion::GAMMA, Hyperion::RED));
|
|
|
|
gamma.append(_hyperion->getTransform(Hyperion::GAMMA, Hyperion::GREEN));
|
|
|
|
gamma.append(_hyperion->getTransform(Hyperion::GAMMA, Hyperion::BLUE));
|
|
|
|
Json::Value & blacklevel = transform["blacklevel"];
|
|
|
|
blacklevel.append(_hyperion->getTransform(Hyperion::BLACKLEVEL, Hyperion::RED));
|
|
|
|
blacklevel.append(_hyperion->getTransform(Hyperion::BLACKLEVEL, Hyperion::GREEN));
|
|
|
|
blacklevel.append(_hyperion->getTransform(Hyperion::BLACKLEVEL, Hyperion::BLUE));
|
|
|
|
Json::Value & whitelevel = transform["whitelevel"];
|
|
|
|
whitelevel.append(_hyperion->getTransform(Hyperion::WHITELEVEL, Hyperion::RED));
|
|
|
|
whitelevel.append(_hyperion->getTransform(Hyperion::WHITELEVEL, Hyperion::GREEN));
|
|
|
|
whitelevel.append(_hyperion->getTransform(Hyperion::WHITELEVEL, Hyperion::BLUE));
|
|
|
|
|
2013-11-24 16:10:48 +01:00
|
|
|
// collect effect info
|
2013-11-30 12:42:08 +01:00
|
|
|
Json::Value & effects = info["effects"] = Json::Value(Json::arrayValue);
|
2013-12-01 14:09:01 +01:00
|
|
|
const std::list<EffectDefinition> & effectsDefinitions = _hyperion->getEffects();
|
|
|
|
for (const EffectDefinition & effectDefinition : effectsDefinitions)
|
2013-11-24 16:10:48 +01:00
|
|
|
{
|
|
|
|
Json::Value effect;
|
2013-12-01 14:09:01 +01:00
|
|
|
effect["name"] = effectDefinition.name;
|
|
|
|
effect["script"] = effectDefinition.script;
|
|
|
|
effect["args"] = effectDefinition.args;
|
2013-11-24 16:10:48 +01:00
|
|
|
|
|
|
|
effects.append(effect);
|
|
|
|
}
|
|
|
|
|
2013-08-19 20:33:36 +02:00
|
|
|
// send the result
|
|
|
|
sendMessage(result);
|
2013-08-18 12:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void JsonClientConnection::handleClearCommand(const Json::Value &message)
|
|
|
|
{
|
2013-08-18 13:33:56 +02:00
|
|
|
// extract parameters
|
|
|
|
int priority = message["priority"].asInt();
|
|
|
|
|
|
|
|
// clear priority
|
|
|
|
_hyperion->clear(priority);
|
|
|
|
|
|
|
|
// send reply
|
|
|
|
sendSuccessReply();
|
2013-08-18 12:02:17 +02:00
|
|
|
}
|
|
|
|
|
2013-08-18 13:33:56 +02:00
|
|
|
void JsonClientConnection::handleClearallCommand(const Json::Value &)
|
2013-08-18 12:02:17 +02:00
|
|
|
{
|
2013-08-18 13:33:56 +02:00
|
|
|
// clear priority
|
|
|
|
_hyperion->clearall();
|
|
|
|
|
|
|
|
// send reply
|
|
|
|
sendSuccessReply();
|
2013-08-18 12:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void JsonClientConnection::handleTransformCommand(const Json::Value &message)
|
|
|
|
{
|
2013-08-19 19:57:35 +02:00
|
|
|
const Json::Value & transform = message["transform"];
|
|
|
|
|
2013-08-21 21:50:17 +02:00
|
|
|
if (transform.isMember("saturationGain"))
|
|
|
|
{
|
|
|
|
_hyperion->setTransform(Hyperion::SATURATION_GAIN, Hyperion::INVALID, transform["saturationGain"].asDouble());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (transform.isMember("valueGain"))
|
|
|
|
{
|
|
|
|
_hyperion->setTransform(Hyperion::VALUE_GAIN, Hyperion::INVALID, transform["valueGain"].asDouble());
|
|
|
|
}
|
|
|
|
|
2013-08-19 19:57:35 +02:00
|
|
|
if (transform.isMember("threshold"))
|
|
|
|
{
|
|
|
|
const Json::Value & threshold = transform["threshold"];
|
|
|
|
_hyperion->setTransform(Hyperion::THRESHOLD, Hyperion::RED, threshold[0u].asDouble());
|
|
|
|
_hyperion->setTransform(Hyperion::THRESHOLD, Hyperion::GREEN, threshold[1u].asDouble());
|
|
|
|
_hyperion->setTransform(Hyperion::THRESHOLD, Hyperion::BLUE, threshold[2u].asDouble());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (transform.isMember("gamma"))
|
|
|
|
{
|
|
|
|
const Json::Value & threshold = transform["gamma"];
|
|
|
|
_hyperion->setTransform(Hyperion::GAMMA, Hyperion::RED, threshold[0u].asDouble());
|
|
|
|
_hyperion->setTransform(Hyperion::GAMMA, Hyperion::GREEN, threshold[1u].asDouble());
|
|
|
|
_hyperion->setTransform(Hyperion::GAMMA, Hyperion::BLUE, threshold[2u].asDouble());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (transform.isMember("blacklevel"))
|
|
|
|
{
|
|
|
|
const Json::Value & threshold = transform["blacklevel"];
|
|
|
|
_hyperion->setTransform(Hyperion::BLACKLEVEL, Hyperion::RED, threshold[0u].asDouble());
|
|
|
|
_hyperion->setTransform(Hyperion::BLACKLEVEL, Hyperion::GREEN, threshold[1u].asDouble());
|
|
|
|
_hyperion->setTransform(Hyperion::BLACKLEVEL, Hyperion::BLUE, threshold[2u].asDouble());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (transform.isMember("whitelevel"))
|
|
|
|
{
|
|
|
|
const Json::Value & threshold = transform["whitelevel"];
|
|
|
|
_hyperion->setTransform(Hyperion::WHITELEVEL, Hyperion::RED, threshold[0u].asDouble());
|
|
|
|
_hyperion->setTransform(Hyperion::WHITELEVEL, Hyperion::GREEN, threshold[1u].asDouble());
|
|
|
|
_hyperion->setTransform(Hyperion::WHITELEVEL, Hyperion::BLUE, threshold[2u].asDouble());
|
|
|
|
}
|
|
|
|
|
|
|
|
sendSuccessReply();
|
2013-08-18 12:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void JsonClientConnection::handleNotImplemented()
|
2013-08-17 15:39:29 +02:00
|
|
|
{
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2013-08-18 12:21:07 +02:00
|
|
|
void JsonClientConnection::sendSuccessReply()
|
|
|
|
{
|
|
|
|
// create reply
|
|
|
|
Json::Value reply;
|
|
|
|
reply["success"] = true;
|
|
|
|
|
|
|
|
// send reply
|
|
|
|
sendMessage(reply);
|
|
|
|
}
|
|
|
|
|
2013-08-17 15:39:29 +02:00
|
|
|
void JsonClientConnection::sendErrorReply(const std::string &error)
|
|
|
|
{
|
|
|
|
// create reply
|
|
|
|
Json::Value reply;
|
|
|
|
reply["success"] = false;
|
|
|
|
reply["error"] = error;
|
|
|
|
|
|
|
|
// send reply
|
|
|
|
sendMessage(reply);
|
|
|
|
}
|
2013-08-18 12:02:17 +02:00
|
|
|
|
|
|
|
bool JsonClientConnection::checkJson(const Json::Value & message, const QString & schemaResource, std::string & errorMessage)
|
|
|
|
{
|
|
|
|
// read the json schema from the resource
|
|
|
|
QResource schemaData(schemaResource);
|
|
|
|
assert(schemaData.isValid());
|
|
|
|
Json::Reader jsonReader;
|
|
|
|
Json::Value schemaJson;
|
|
|
|
if (!jsonReader.parse(reinterpret_cast<const char *>(schemaData.data()), reinterpret_cast<const char *>(schemaData.data()) + schemaData.size(), schemaJson, false))
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Schema error: " + jsonReader.getFormattedErrorMessages()) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create schema checker
|
|
|
|
JsonSchemaChecker schema;
|
|
|
|
schema.setSchema(schemaJson);
|
|
|
|
|
|
|
|
// check the message
|
|
|
|
if (!schema.validate(message))
|
|
|
|
{
|
|
|
|
const std::list<std::string> & errors = schema.getMessages();
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << "{";
|
|
|
|
foreach (const std::string & error, errors) {
|
|
|
|
ss << error << " ";
|
|
|
|
}
|
|
|
|
ss << "}";
|
|
|
|
errorMessage = ss.str();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|