mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
update
This commit is contained in:
@@ -12,9 +12,9 @@
|
||||
#include <QImage>
|
||||
#include <QBuffer>
|
||||
#include <QByteArray>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QIODevice>
|
||||
// #include <QFileInfo>
|
||||
// #include <QDir>
|
||||
// #include <QIODevice>
|
||||
#include <QDateTime>
|
||||
|
||||
// hyperion includes
|
||||
@@ -41,13 +41,14 @@ using namespace hyperion;
|
||||
|
||||
JsonAPI::JsonAPI(QString peerAddress, Logger* log, QObject* parent, bool noListener)
|
||||
: QObject(parent)
|
||||
, _jsonCB(new JsonCB(this))
|
||||
, _noListener(noListener)
|
||||
, _peerAddress(peerAddress)
|
||||
, _log(log)
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
, _jsonCB(new JsonCB(this))
|
||||
, _streaming_logging_activated(false)
|
||||
, _image_stream_timeout(0)
|
||||
, _led_stream_timeout(0)
|
||||
{
|
||||
// the JsonCB creates json messages you can subscribe to e.g. data change events; forward them to the parent client
|
||||
connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage);
|
||||
@@ -55,9 +56,8 @@ JsonAPI::JsonAPI(QString peerAddress, Logger* log, QObject* parent, bool noListe
|
||||
// notify hyperion about a jsonMessageForward
|
||||
connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage);
|
||||
|
||||
// led color stream update timer
|
||||
connect(&_timer_ledcolors, SIGNAL(timeout()), this, SLOT(streamLedcolorsUpdate()));
|
||||
_image_stream_mutex.unlock();
|
||||
_led_stream_mutex.unlock();
|
||||
}
|
||||
|
||||
void JsonAPI::handleMessage(const QString& messageString)
|
||||
@@ -207,105 +207,20 @@ void JsonAPI::handleEffectCommand(const QJsonObject& message, const QString& com
|
||||
|
||||
void JsonAPI::handleCreateEffectCommand(const QJsonObject& message, const QString &command, const int tan)
|
||||
{
|
||||
if (!message["args"].toObject().isEmpty())
|
||||
{
|
||||
QString scriptName;
|
||||
(message["script"].toString().mid(0, 1) == ":" )
|
||||
? scriptName = ":/effects//" + message["script"].toString().mid(1)
|
||||
: scriptName = message["script"].toString();
|
||||
|
||||
std::list<EffectSchema> effectsSchemas = _hyperion->getEffectSchemas();
|
||||
std::list<EffectSchema>::iterator it = std::find_if(effectsSchemas.begin(), effectsSchemas.end(), find_schema(scriptName));
|
||||
|
||||
if (it != effectsSchemas.end())
|
||||
{
|
||||
if(!JsonUtils::validate("JsonRpc@"+_peerAddress, message["args"].toObject(), it->schemaFile, _log))
|
||||
{
|
||||
sendErrorReply("Error during arg validation against schema, please consult the Hyperion Log", command, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject effectJson;
|
||||
QJsonArray effectArray;
|
||||
effectArray = _hyperion->getQJsonConfig()["effects"].toObject()["paths"].toArray();
|
||||
|
||||
if (effectArray.size() > 0)
|
||||
{
|
||||
if (message["name"].toString().trimmed().isEmpty() || message["name"].toString().trimmed().startsWith("."))
|
||||
{
|
||||
sendErrorReply("Can't save new effect. Effect name is empty or begins with a dot.", command, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
effectJson["name"] = message["name"].toString();
|
||||
effectJson["script"] = message["script"].toString();
|
||||
effectJson["args"] = message["args"].toObject();
|
||||
|
||||
std::list<EffectDefinition> availableEffects = _hyperion->getEffects();
|
||||
std::list<EffectDefinition>::iterator iter = std::find_if(availableEffects.begin(), availableEffects.end(), find_effect(message["name"].toString()));
|
||||
|
||||
QFileInfo newFileName;
|
||||
if (iter != availableEffects.end())
|
||||
{
|
||||
newFileName.setFile(iter->file);
|
||||
if (newFileName.absoluteFilePath().mid(0, 1) == ":")
|
||||
{
|
||||
sendErrorReply("The effect name '" + message["name"].toString() + "' is assigned to an internal effect. Please rename your effekt.", command, tan);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
{
|
||||
QString f = FileUtils::convertPath(effectArray[0].toString() + "/" + message["name"].toString().replace(QString(" "), QString("")) + QString(".json"));
|
||||
newFileName.setFile(f);
|
||||
}
|
||||
|
||||
if(!JsonUtils::write(newFileName.absoluteFilePath(), effectJson, _log))
|
||||
{
|
||||
sendErrorReply("Error while saving effect, please check the Hyperion Log", command, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
Info(_log, "Reload effect list");
|
||||
_hyperion->reloadEffects();
|
||||
sendSuccessReply(command, tan);
|
||||
} else
|
||||
{
|
||||
sendErrorReply("Can't save new effect. Effect path empty", command, tan);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
sendErrorReply("Missing schema file for Python script " + message["script"].toString(), command, tan);
|
||||
} else
|
||||
sendErrorReply("Missing or empty Object 'args'", command, tan);
|
||||
QString resultMsg;
|
||||
if(_hyperion->saveEffect(message, resultMsg))
|
||||
sendSuccessReply(command, tan);
|
||||
else
|
||||
sendErrorReply(resultMsg, command, tan);
|
||||
}
|
||||
|
||||
void JsonAPI::handleDeleteEffectCommand(const QJsonObject& message, const QString& command, const int tan)
|
||||
{
|
||||
QString effectName = message["name"].toString();
|
||||
std::list<EffectDefinition> effectsDefinition = _hyperion->getEffects();
|
||||
std::list<EffectDefinition>::iterator it = std::find_if(effectsDefinition.begin(), effectsDefinition.end(), find_effect(effectName));
|
||||
|
||||
if (it != effectsDefinition.end())
|
||||
{
|
||||
QFileInfo effectConfigurationFile(it->file);
|
||||
if (effectConfigurationFile.absoluteFilePath().mid(0, 1) != ":" )
|
||||
{
|
||||
if (effectConfigurationFile.exists())
|
||||
{
|
||||
bool result = QFile::remove(effectConfigurationFile.absoluteFilePath());
|
||||
if (result)
|
||||
{
|
||||
Info(_log, "Reload effect list");
|
||||
_hyperion->reloadEffects();
|
||||
sendSuccessReply(command, tan);
|
||||
} else
|
||||
sendErrorReply("Can't delete effect configuration file: " + effectConfigurationFile.absoluteFilePath() + ". Please check permissions", command, tan);
|
||||
} else
|
||||
sendErrorReply("Can't find effect configuration file: " + effectConfigurationFile.absoluteFilePath(), command, tan);
|
||||
} else
|
||||
sendErrorReply("Can't delete internal effect: " + message["name"].toString(), command, tan);
|
||||
} else
|
||||
sendErrorReply("Effect " + message["name"].toString() + " not found", command, tan);
|
||||
QString resultMsg;
|
||||
if(_hyperion->deleteEffect(message["name"].toString(), resultMsg))
|
||||
sendSuccessReply(command, tan);
|
||||
else
|
||||
sendErrorReply(resultMsg, command, tan);
|
||||
}
|
||||
|
||||
void JsonAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, const int tan)
|
||||
@@ -358,10 +273,9 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString&
|
||||
const Hyperion::InputInfo & priorityInfo = _hyperion->getPriorityInfo(priority);
|
||||
QJsonObject item;
|
||||
item["priority"] = priority;
|
||||
if (int(priorityInfo.timeoutTime_ms - now) > -1 )
|
||||
{
|
||||
if (priorityInfo.timeoutTime_ms > 0 )
|
||||
item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now);
|
||||
}
|
||||
|
||||
// owner has optional informations to the component
|
||||
if(!priorityInfo.owner.isEmpty())
|
||||
item["owner"] = priorityInfo.owner;
|
||||
@@ -861,12 +775,11 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString &
|
||||
_streaming_leds_reply["success"] = true;
|
||||
_streaming_leds_reply["command"] = command+"-ledstream-update";
|
||||
_streaming_leds_reply["tan"] = tan;
|
||||
_timer_ledcolors.setInterval(125);
|
||||
_timer_ledcolors.start(125);
|
||||
connect(_hyperion, &Hyperion::rawLedColors, this, &JsonAPI::streamLedcolorsUpdate, Qt::UniqueConnection);
|
||||
}
|
||||
else if (subcommand == "ledstream-stop")
|
||||
{
|
||||
_timer_ledcolors.stop();
|
||||
disconnect(_hyperion, &Hyperion::rawLedColors, this, &JsonAPI::streamLedcolorsUpdate);
|
||||
}
|
||||
else if (subcommand == "imagestream-start")
|
||||
{
|
||||
@@ -984,32 +897,37 @@ void JsonAPI::sendErrorReply(const QString &error, const QString &command, const
|
||||
}
|
||||
|
||||
|
||||
void JsonAPI::streamLedcolorsUpdate()
|
||||
void JsonAPI::streamLedcolorsUpdate(const std::vector<ColorRgb>& ledColors)
|
||||
{
|
||||
QJsonObject result;
|
||||
QJsonArray leds;
|
||||
|
||||
const std::vector<ColorRgb> & ledColors = _hyperion->getRawLedBuffer();
|
||||
for(auto color = ledColors.begin(); color != ledColors.end(); ++color)
|
||||
if ( (_led_stream_timeout+100) < QDateTime::currentMSecsSinceEpoch() && _led_stream_mutex.tryLock(0) )
|
||||
{
|
||||
QJsonObject item;
|
||||
item["index"] = int(color - ledColors.begin());
|
||||
item["red"] = color->red;
|
||||
item["green"] = color->green;
|
||||
item["blue"] = color->blue;
|
||||
leds.append(item);
|
||||
_led_stream_timeout = QDateTime::currentMSecsSinceEpoch();
|
||||
QJsonObject result;
|
||||
QJsonArray leds;
|
||||
|
||||
for(auto color = ledColors.begin(); color != ledColors.end(); ++color)
|
||||
{
|
||||
QJsonObject item;
|
||||
item["index"] = int(color - ledColors.begin());
|
||||
item["red"] = color->red;
|
||||
item["green"] = color->green;
|
||||
item["blue"] = color->blue;
|
||||
leds.append(item);
|
||||
}
|
||||
|
||||
result["leds"] = leds;
|
||||
_streaming_leds_reply["result"] = result;
|
||||
|
||||
// send the result
|
||||
emit callbackMessage(_streaming_leds_reply);
|
||||
|
||||
_led_stream_mutex.unlock();
|
||||
}
|
||||
|
||||
result["leds"] = leds;
|
||||
_streaming_leds_reply["result"] = result;
|
||||
|
||||
// send the result
|
||||
emit callbackMessage(_streaming_leds_reply);
|
||||
}
|
||||
|
||||
void JsonAPI::setImage(const Image<ColorRgb> & image)
|
||||
{
|
||||
if ( (_image_stream_timeout+250) < QDateTime::currentMSecsSinceEpoch() && _image_stream_mutex.tryLock(0) )
|
||||
if ( (_image_stream_timeout+100) < QDateTime::currentMSecsSinceEpoch() && _image_stream_mutex.tryLock(0) )
|
||||
{
|
||||
_image_stream_timeout = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
|
@@ -139,10 +139,9 @@ void JsonCB::handlePriorityUpdate()
|
||||
const Hyperion::InputInfo priorityInfo = _prioMuxer->getInputInfo(priority);
|
||||
QJsonObject item;
|
||||
item["priority"] = priority;
|
||||
if (int(priorityInfo.timeoutTime_ms - now) > -1 )
|
||||
{
|
||||
if (priorityInfo.timeoutTime_ms > 0 )
|
||||
item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now);
|
||||
}
|
||||
|
||||
// owner has optional informations to the component
|
||||
if(!priorityInfo.owner.isEmpty())
|
||||
item["owner"] = priorityInfo.owner;
|
||||
|
@@ -18,33 +18,46 @@
|
||||
#include <effectengine/EffectEngine.h>
|
||||
#include <effectengine/Effect.h>
|
||||
#include <effectengine/EffectModule.h>
|
||||
#include <effectengine/EffectFileHandler.h>
|
||||
#include "HyperionConfig.h"
|
||||
|
||||
EffectEngine::EffectEngine(Hyperion * hyperion, const QJsonObject & jsonEffectConfig)
|
||||
EffectEngine::EffectEngine(Hyperion * hyperion)
|
||||
: _hyperion(hyperion)
|
||||
, _effectConfig(jsonEffectConfig)
|
||||
, _availableEffects()
|
||||
, _activeEffects()
|
||||
, _log(Logger::getInstance("EFFECTENGINE"))
|
||||
, _effectFileHandler(EffectFileHandler::getInstance())
|
||||
{
|
||||
|
||||
Q_INIT_RESOURCE(EffectEngine);
|
||||
qRegisterMetaType<std::vector<ColorRgb>>("std::vector<ColorRgb>");
|
||||
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
||||
qRegisterMetaType<hyperion::Components>("hyperion::Components");
|
||||
|
||||
// connect the Hyperion channel clear feedback
|
||||
connect(_hyperion, SIGNAL(channelCleared(int)), this, SLOT(channelCleared(int)));
|
||||
connect(_hyperion, SIGNAL(allChannelsCleared()), this, SLOT(allChannelsCleared()));
|
||||
|
||||
// read all effects
|
||||
readEffects();
|
||||
// get notifications about refreshed effect list
|
||||
connect(_effectFileHandler, &EffectFileHandler::effectListChanged, this, &EffectEngine::handleUpdatedEffectList);
|
||||
|
||||
// register smooth cfgs and fill available effects
|
||||
handleUpdatedEffectList();
|
||||
}
|
||||
|
||||
EffectEngine::~EffectEngine()
|
||||
{
|
||||
}
|
||||
|
||||
const bool EffectEngine::saveEffect(const QJsonObject& obj, QString& resultMsg)
|
||||
{
|
||||
return _effectFileHandler->saveEffect(obj, resultMsg);
|
||||
}
|
||||
|
||||
const bool EffectEngine::deleteEffect(const QString& effectName, QString& resultMsg)
|
||||
{
|
||||
return _effectFileHandler->deleteEffect(effectName, resultMsg);
|
||||
}
|
||||
|
||||
const std::list<ActiveEffectDefinition> &EffectEngine::getActiveEffects()
|
||||
{
|
||||
_availableActiveEffects.clear();
|
||||
@@ -63,6 +76,11 @@ const std::list<ActiveEffectDefinition> &EffectEngine::getActiveEffects()
|
||||
return _availableActiveEffects;
|
||||
}
|
||||
|
||||
const std::list<EffectSchema> & EffectEngine::getEffectSchemas()
|
||||
{
|
||||
return _effectFileHandler->getEffectSchemas();
|
||||
}
|
||||
|
||||
void EffectEngine::cacheRunningEffects()
|
||||
{
|
||||
_cachedActiveEffects.clear();
|
||||
@@ -90,175 +108,26 @@ void EffectEngine::startCachedEffects()
|
||||
_cachedActiveEffects.clear();
|
||||
}
|
||||
|
||||
bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effectConfigFile, EffectDefinition & effectDefinition)
|
||||
void EffectEngine::handleUpdatedEffectList()
|
||||
{
|
||||
QString fileName = path + QDir::separator() + effectConfigFile;
|
||||
|
||||
// Read and parse the effect json config file
|
||||
QJsonObject configEffect;
|
||||
if(!JsonUtils::readFile(fileName, configEffect, _log))
|
||||
return false;
|
||||
|
||||
Q_INIT_RESOURCE(EffectEngine);
|
||||
// validate effect config with effect schema(path)
|
||||
if(!JsonUtils::validate(fileName, configEffect, ":effect-schema", _log))
|
||||
return false;
|
||||
|
||||
// setup the definition
|
||||
effectDefinition.file = fileName;
|
||||
QJsonObject config = configEffect;
|
||||
QString scriptName = config["script"].toString();
|
||||
effectDefinition.name = config["name"].toString();
|
||||
if (scriptName.isEmpty())
|
||||
return false;
|
||||
|
||||
QFile fileInfo(scriptName);
|
||||
|
||||
if (scriptName.mid(0, 1) == ":" )
|
||||
{
|
||||
(!fileInfo.exists())
|
||||
? effectDefinition.script = ":/effects/"+scriptName.mid(1)
|
||||
: effectDefinition.script = scriptName;
|
||||
} else
|
||||
{
|
||||
(!fileInfo.exists())
|
||||
? effectDefinition.script = path + QDir::separator() + scriptName
|
||||
: effectDefinition.script = scriptName;
|
||||
}
|
||||
|
||||
effectDefinition.args = config["args"].toObject();
|
||||
effectDefinition.smoothCfg = SMOOTHING_MODE_PAUSE;
|
||||
if (effectDefinition.args["smoothing-custom-settings"].toBool())
|
||||
{
|
||||
effectDefinition.smoothCfg = _hyperion->addSmoothingConfig(
|
||||
effectDefinition.args["smoothing-time_ms"].toInt(),
|
||||
effectDefinition.args["smoothing-updateFrequency"].toDouble(),
|
||||
0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
effectDefinition.smoothCfg = _hyperion->addSmoothingConfig(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EffectEngine::loadEffectSchema(const QString &path, const QString &effectSchemaFile, EffectSchema & effectSchema)
|
||||
{
|
||||
QString fileName = path + "schema/" + QDir::separator() + effectSchemaFile;
|
||||
|
||||
// Read and parse the effect schema file
|
||||
QJsonObject schemaEffect;
|
||||
if(!JsonUtils::readFile(fileName, schemaEffect, _log))
|
||||
return false;
|
||||
|
||||
// setup the definition
|
||||
QString scriptName = schemaEffect["script"].toString();
|
||||
effectSchema.schemaFile = fileName;
|
||||
fileName = path + QDir::separator() + scriptName;
|
||||
QFile pyFile(fileName);
|
||||
|
||||
if (scriptName.isEmpty() || !pyFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
fileName = path + "schema/" + QDir::separator() + effectSchemaFile;
|
||||
Error( _log, "Python script '%s' in effect schema '%s' could not be loaded", QSTRING_CSTR(scriptName), QSTRING_CSTR(fileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
pyFile.close();
|
||||
|
||||
effectSchema.pyFile = (scriptName.mid(0, 1) == ":" ) ? ":/effects/"+scriptName.mid(1) : path + QDir::separator() + scriptName;
|
||||
effectSchema.pySchema = schemaEffect;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EffectEngine::readEffects()
|
||||
{
|
||||
// clear all lists
|
||||
_availableEffects.clear();
|
||||
_effectSchemas.clear();
|
||||
|
||||
// read all effects
|
||||
const QJsonArray & paths = _effectConfig["paths"].toArray();
|
||||
const QJsonArray & disabledEfx = _effectConfig["disable"].toArray();
|
||||
|
||||
QStringList efxPathList;
|
||||
efxPathList << ":/effects/";
|
||||
QStringList disableList;
|
||||
|
||||
for(auto p : paths)
|
||||
for (auto def : _effectFileHandler->getEffects())
|
||||
{
|
||||
efxPathList << p.toString().replace("$ROOT",_hyperion->getRootPath());
|
||||
}
|
||||
for(auto efx : disabledEfx)
|
||||
{
|
||||
disableList << efx.toString();
|
||||
}
|
||||
|
||||
QMap<QString, EffectDefinition> availableEffects;
|
||||
for (const QString & path : efxPathList )
|
||||
{
|
||||
QDir directory(path);
|
||||
if (!directory.exists())
|
||||
// add smoothing configs to Hyperion
|
||||
if (def.args["smoothing-custom-settings"].toBool())
|
||||
{
|
||||
if(directory.mkpath(path))
|
||||
{
|
||||
Warning(_log, "New Effect path \"%s\" created successfull", QSTRING_CSTR(path) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning(_log, "Failed to create Effect path \"%s\", please check permissions", QSTRING_CSTR(path) );
|
||||
}
|
||||
def.smoothCfg = _hyperion->addSmoothingConfig(
|
||||
def.args["smoothing-time_ms"].toInt(),
|
||||
def.args["smoothing-updateFrequency"].toDouble(),
|
||||
0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
int efxCount = 0;
|
||||
QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
for (const QString & filename : filenames)
|
||||
{
|
||||
EffectDefinition def;
|
||||
if (loadEffectDefinition(path, filename, def))
|
||||
{
|
||||
InfoIf(availableEffects.find(def.name) != availableEffects.end(), _log,
|
||||
"effect overload effect '%s' is now taken from '%s'", QSTRING_CSTR(def.name), QSTRING_CSTR(path) );
|
||||
|
||||
if ( disableList.contains(def.name) )
|
||||
{
|
||||
Info(_log, "effect '%s' not loaded, because it is disabled in hyperion config", QSTRING_CSTR(def.name));
|
||||
}
|
||||
else
|
||||
{
|
||||
availableEffects[def.name] = def;
|
||||
efxCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Info(_log, "%d effects loaded from directory %s", efxCount, QSTRING_CSTR(path));
|
||||
|
||||
// collect effect schemas
|
||||
efxCount = 0;
|
||||
directory = path.endsWith("/") ? (path + "schema/") : (path + "/schema/");
|
||||
QStringList pynames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
for (const QString & pyname : pynames)
|
||||
{
|
||||
EffectSchema pyEffect;
|
||||
if (loadEffectSchema(path, pyname, pyEffect))
|
||||
{
|
||||
_effectSchemas.push_back(pyEffect);
|
||||
efxCount++;
|
||||
}
|
||||
}
|
||||
InfoIf(efxCount > 0, _log, "%d effect schemas loaded from directory %s", efxCount, QSTRING_CSTR((path + "schema/")));
|
||||
def.smoothCfg = _hyperion->addSmoothingConfig(true);
|
||||
}
|
||||
_availableEffects.push_back(def);
|
||||
}
|
||||
|
||||
for(auto item : availableEffects)
|
||||
{
|
||||
_availableEffects.push_back(item);
|
||||
}
|
||||
|
||||
ErrorIf(_availableEffects.size()==0, _log, "no effects found, check your effect directories");
|
||||
|
||||
emit effectListUpdated();
|
||||
}
|
||||
|
||||
|
322
libsrc/effectengine/EffectFileHandler.cpp
Normal file
322
libsrc/effectengine/EffectFileHandler.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
#include <effectengine/EffectFileHandler.h>
|
||||
|
||||
// util
|
||||
#include <utils/JsonUtils.h>
|
||||
|
||||
// qt
|
||||
#include <QJsonArray>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QMap>
|
||||
|
||||
// createEffect helper
|
||||
struct find_schema: std::unary_function<EffectSchema, bool>
|
||||
{
|
||||
QString pyFile;
|
||||
find_schema(QString pyFile):pyFile(pyFile) { }
|
||||
bool operator()(EffectSchema const& schema) const
|
||||
{
|
||||
return schema.pyFile == pyFile;
|
||||
}
|
||||
};
|
||||
|
||||
// deleteEffect helper
|
||||
struct find_effect: std::unary_function<EffectDefinition, bool>
|
||||
{
|
||||
QString effectName;
|
||||
find_effect(QString effectName) :effectName(effectName) { }
|
||||
bool operator()(EffectDefinition const& effectDefinition) const
|
||||
{
|
||||
return effectDefinition.name == effectName;
|
||||
}
|
||||
};
|
||||
|
||||
EffectFileHandler* EffectFileHandler::efhInstance;
|
||||
|
||||
EffectFileHandler::EffectFileHandler(const QString& rootPath, const QJsonDocument& effectConfig, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _effectConfig()
|
||||
, _log(Logger::getInstance("EFFECTFILES"))
|
||||
, _rootPath(rootPath)
|
||||
{
|
||||
EffectFileHandler::efhInstance = this;
|
||||
|
||||
Q_INIT_RESOURCE(EffectEngine);
|
||||
|
||||
// init
|
||||
handleSettingsUpdate(settings::EFFECTS, effectConfig);
|
||||
}
|
||||
|
||||
void EffectFileHandler::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::EFFECTS)
|
||||
{
|
||||
_effectConfig = config.object();
|
||||
// update effects and schemas
|
||||
updateEffects();
|
||||
}
|
||||
}
|
||||
|
||||
const bool EffectFileHandler::deleteEffect(const QString& effectName, QString& resultMsg)
|
||||
{
|
||||
std::list<EffectDefinition> effectsDefinition = getEffects();
|
||||
std::list<EffectDefinition>::iterator it = std::find_if(effectsDefinition.begin(), effectsDefinition.end(), find_effect(effectName));
|
||||
|
||||
if (it != effectsDefinition.end())
|
||||
{
|
||||
QFileInfo effectConfigurationFile(it->file);
|
||||
if (effectConfigurationFile.absoluteFilePath().mid(0, 1) != ":" )
|
||||
{
|
||||
if (effectConfigurationFile.exists())
|
||||
{
|
||||
bool result = QFile::remove(effectConfigurationFile.absoluteFilePath());
|
||||
if (result)
|
||||
{
|
||||
updateEffects();
|
||||
return true;
|
||||
} else
|
||||
resultMsg = "Can't delete effect configuration file: " + effectConfigurationFile.absoluteFilePath() + ". Please check permissions";
|
||||
} else
|
||||
resultMsg = "Can't find effect configuration file: " + effectConfigurationFile.absoluteFilePath();
|
||||
} else
|
||||
resultMsg = "Can't delete internal effect: " + effectName;
|
||||
} else
|
||||
resultMsg = "Effect " + effectName + " not found";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool EffectFileHandler::saveEffect(const QJsonObject& message, QString& resultMsg)
|
||||
{
|
||||
if (!message["args"].toObject().isEmpty())
|
||||
{
|
||||
QString scriptName;
|
||||
(message["script"].toString().mid(0, 1) == ":" )
|
||||
? scriptName = ":/effects//" + message["script"].toString().mid(1)
|
||||
: scriptName = message["script"].toString();
|
||||
|
||||
std::list<EffectSchema> effectsSchemas = getEffectSchemas();
|
||||
std::list<EffectSchema>::iterator it = std::find_if(effectsSchemas.begin(), effectsSchemas.end(), find_schema(scriptName));
|
||||
|
||||
if (it != effectsSchemas.end())
|
||||
{
|
||||
if(!JsonUtils::validate("EffectFileHandler", message["args"].toObject(), it->schemaFile, _log))
|
||||
{
|
||||
resultMsg = "Error during arg validation against schema, please consult the Hyperion Log";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonObject effectJson;
|
||||
QJsonArray effectArray;
|
||||
effectArray = _effectConfig["paths"].toArray();
|
||||
|
||||
if (effectArray.size() > 0)
|
||||
{
|
||||
if (message["name"].toString().trimmed().isEmpty() || message["name"].toString().trimmed().startsWith("."))
|
||||
{
|
||||
resultMsg = "Can't save new effect. Effect name is empty or begins with a dot.";
|
||||
return false;
|
||||
}
|
||||
|
||||
effectJson["name"] = message["name"].toString();
|
||||
effectJson["script"] = message["script"].toString();
|
||||
effectJson["args"] = message["args"].toObject();
|
||||
|
||||
std::list<EffectDefinition> availableEffects = getEffects();
|
||||
std::list<EffectDefinition>::iterator iter = std::find_if(availableEffects.begin(), availableEffects.end(), find_effect(message["name"].toString()));
|
||||
|
||||
QFileInfo newFileName;
|
||||
if (iter != availableEffects.end())
|
||||
{
|
||||
newFileName.setFile(iter->file);
|
||||
if (newFileName.absoluteFilePath().mid(0, 1) == ":")
|
||||
{
|
||||
resultMsg = "The effect name '" + message["name"].toString() + "' is assigned to an internal effect. Please rename your effekt.";
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
{
|
||||
// TODO global special keyword handling
|
||||
QString f = effectArray[0].toString().replace("$ROOT",_rootPath) + "/" + message["name"].toString().replace(QString(" "), QString("")) + QString(".json");
|
||||
newFileName.setFile(f);
|
||||
}
|
||||
|
||||
if(!JsonUtils::write(newFileName.absoluteFilePath(), effectJson, _log))
|
||||
{
|
||||
resultMsg = "Error while saving effect, please check the Hyperion Log";
|
||||
return false;
|
||||
}
|
||||
|
||||
Info(_log, "Reload effect list");
|
||||
updateEffects();
|
||||
resultMsg = "";
|
||||
return true;
|
||||
} else
|
||||
resultMsg = "Can't save new effect. Effect path empty";
|
||||
} else
|
||||
resultMsg = "Missing schema file for Python script " + message["script"].toString();
|
||||
} else
|
||||
resultMsg = "Missing or empty Object 'args'";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EffectFileHandler::updateEffects()
|
||||
{
|
||||
// clear all lists
|
||||
_availableEffects.clear();
|
||||
_effectSchemas.clear();
|
||||
|
||||
// read all effects
|
||||
const QJsonArray & paths = _effectConfig["paths"].toArray();
|
||||
const QJsonArray & disabledEfx = _effectConfig["disable"].toArray();
|
||||
|
||||
QStringList efxPathList;
|
||||
efxPathList << ":/effects/";
|
||||
QStringList disableList;
|
||||
|
||||
for(auto p : paths)
|
||||
{
|
||||
efxPathList << p.toString().replace("$ROOT",_rootPath);
|
||||
}
|
||||
for(auto efx : disabledEfx)
|
||||
{
|
||||
disableList << efx.toString();
|
||||
}
|
||||
|
||||
QMap<QString, EffectDefinition> availableEffects;
|
||||
for (const QString & path : efxPathList )
|
||||
{
|
||||
QDir directory(path);
|
||||
if (!directory.exists())
|
||||
{
|
||||
if(directory.mkpath(path))
|
||||
{
|
||||
Info(_log, "New Effect path \"%s\" created successfull", QSTRING_CSTR(path) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning(_log, "Failed to create Effect path \"%s\", please check permissions", QSTRING_CSTR(path) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int efxCount = 0;
|
||||
QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
for (const QString & filename : filenames)
|
||||
{
|
||||
EffectDefinition def;
|
||||
if (loadEffectDefinition(path, filename, def))
|
||||
{
|
||||
InfoIf(availableEffects.find(def.name) != availableEffects.end(), _log,
|
||||
"effect overload effect '%s' is now taken from '%s'", QSTRING_CSTR(def.name), QSTRING_CSTR(path) );
|
||||
|
||||
if ( disableList.contains(def.name) )
|
||||
{
|
||||
Info(_log, "effect '%s' not loaded, because it is disabled in hyperion config", QSTRING_CSTR(def.name));
|
||||
}
|
||||
else
|
||||
{
|
||||
availableEffects[def.name] = def;
|
||||
efxCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Info(_log, "%d effects loaded from directory %s", efxCount, QSTRING_CSTR(path));
|
||||
|
||||
// collect effect schemas
|
||||
efxCount = 0;
|
||||
directory = path.endsWith("/") ? (path + "schema/") : (path + "/schema/");
|
||||
QStringList pynames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
for (const QString & pyname : pynames)
|
||||
{
|
||||
EffectSchema pyEffect;
|
||||
if (loadEffectSchema(path, pyname, pyEffect))
|
||||
{
|
||||
_effectSchemas.push_back(pyEffect);
|
||||
efxCount++;
|
||||
}
|
||||
}
|
||||
InfoIf(efxCount > 0, _log, "%d effect schemas loaded from directory %s", efxCount, QSTRING_CSTR((path + "schema/")));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto item : availableEffects)
|
||||
{
|
||||
_availableEffects.push_back(item);
|
||||
}
|
||||
|
||||
ErrorIf(_availableEffects.size()==0, _log, "no effects found, check your effect directories");
|
||||
|
||||
emit effectListChanged();
|
||||
}
|
||||
|
||||
bool EffectFileHandler::loadEffectDefinition(const QString &path, const QString &effectConfigFile, EffectDefinition & effectDefinition)
|
||||
{
|
||||
QString fileName = path + QDir::separator() + effectConfigFile;
|
||||
|
||||
// Read and parse the effect json config file
|
||||
QJsonObject configEffect;
|
||||
if(!JsonUtils::readFile(fileName, configEffect, _log))
|
||||
return false;
|
||||
|
||||
// validate effect config with effect schema(path)
|
||||
if(!JsonUtils::validate(fileName, configEffect, ":effect-schema", _log))
|
||||
return false;
|
||||
|
||||
// setup the definition
|
||||
effectDefinition.file = fileName;
|
||||
QJsonObject config = configEffect;
|
||||
QString scriptName = config["script"].toString();
|
||||
effectDefinition.name = config["name"].toString();
|
||||
if (scriptName.isEmpty())
|
||||
return false;
|
||||
|
||||
QFile fileInfo(scriptName);
|
||||
|
||||
if (scriptName.mid(0, 1) == ":" )
|
||||
{
|
||||
(!fileInfo.exists())
|
||||
? effectDefinition.script = ":/effects/"+scriptName.mid(1)
|
||||
: effectDefinition.script = scriptName;
|
||||
} else
|
||||
{
|
||||
(!fileInfo.exists())
|
||||
? effectDefinition.script = path + QDir::separator() + scriptName
|
||||
: effectDefinition.script = scriptName;
|
||||
}
|
||||
|
||||
effectDefinition.args = config["args"].toObject();
|
||||
effectDefinition.smoothCfg = 1; // pause config
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EffectFileHandler::loadEffectSchema(const QString &path, const QString &effectSchemaFile, EffectSchema & effectSchema)
|
||||
{
|
||||
QString fileName = path + "schema/" + QDir::separator() + effectSchemaFile;
|
||||
|
||||
// Read and parse the effect schema file
|
||||
QJsonObject schemaEffect;
|
||||
if(!JsonUtils::readFile(fileName, schemaEffect, _log))
|
||||
return false;
|
||||
|
||||
// setup the definition
|
||||
QString scriptName = schemaEffect["script"].toString();
|
||||
effectSchema.schemaFile = fileName;
|
||||
fileName = path + QDir::separator() + scriptName;
|
||||
QFile pyFile(fileName);
|
||||
|
||||
if (scriptName.isEmpty() || !pyFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
fileName = path + "schema/" + QDir::separator() + effectSchemaFile;
|
||||
Error( _log, "Python script '%s' in effect schema '%s' could not be loaded", QSTRING_CSTR(scriptName), QSTRING_CSTR(fileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
pyFile.close();
|
||||
|
||||
effectSchema.pyFile = (scriptName.mid(0, 1) == ":" ) ? ":/effects/"+scriptName.mid(1) : path + QDir::separator() + scriptName;
|
||||
effectSchema.pySchema = schemaEffect;
|
||||
|
||||
return true;
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
#include <cmath>
|
||||
|
||||
#include <effectengine/Effect.h>
|
||||
#include <effectengine/EffectModule.h>
|
||||
@@ -44,10 +45,9 @@ PyObject *EffectModule::json2python(const QJsonValue &jsonData)
|
||||
return Py_BuildValue("");
|
||||
case QJsonValue::Double:
|
||||
{
|
||||
if (std::rint(jsonData.toDouble()) != jsonData.toDouble())
|
||||
{
|
||||
if (std::round(jsonData.toDouble()) != jsonData.toDouble())
|
||||
return Py_BuildValue("d", jsonData.toDouble());
|
||||
}
|
||||
|
||||
return Py_BuildValue("i", jsonData.toInt());
|
||||
}
|
||||
case QJsonValue::Bool:
|
||||
|
@@ -7,6 +7,7 @@ CaptureCont::CaptureCont(Hyperion* hyperion)
|
||||
: QObject()
|
||||
, _hyperion(hyperion)
|
||||
, _systemCaptEnabled(false)
|
||||
, _systemInactiveTimer(new QTimer(this))
|
||||
, _v4lCaptEnabled(false)
|
||||
, _v4lInactiveTimer(new QTimer(this))
|
||||
{
|
||||
@@ -16,6 +17,11 @@ CaptureCont::CaptureCont(Hyperion* hyperion)
|
||||
// comp changes
|
||||
connect(_hyperion, &Hyperion::componentStateChanged, this, &CaptureCont::componentStateChanged);
|
||||
|
||||
// inactive timer system
|
||||
connect(_systemInactiveTimer, &QTimer::timeout, this, &CaptureCont::setSystemInactive);
|
||||
_systemInactiveTimer->setSingleShot(true);
|
||||
_systemInactiveTimer->setInterval(5000);
|
||||
|
||||
// inactive timer v4l
|
||||
connect(_v4lInactiveTimer, &QTimer::timeout, this, &CaptureCont::setV4lInactive);
|
||||
_v4lInactiveTimer->setSingleShot(true);
|
||||
@@ -38,6 +44,7 @@ void CaptureCont::handleV4lImage(const Image<ColorRgb> & image)
|
||||
|
||||
void CaptureCont::handleSystemImage(const Image<ColorRgb>& image)
|
||||
{
|
||||
_systemInactiveTimer->start();
|
||||
_hyperion->setInputImage(_systemCaptPrio, image);
|
||||
}
|
||||
|
||||
@@ -118,3 +125,8 @@ void CaptureCont::setV4lInactive()
|
||||
{
|
||||
_hyperion->setInputInactive(_v4lCaptPrio);
|
||||
}
|
||||
|
||||
void CaptureCont::setSystemInactive()
|
||||
{
|
||||
_hyperion->setInputInactive(_systemCaptPrio);
|
||||
}
|
||||
|
@@ -95,9 +95,7 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
|
||||
, _ledBuffer(_ledString.leds().size(), ColorRgb::BLACK)
|
||||
{
|
||||
if (!_raw2ledAdjustment->verifyAdjustments())
|
||||
{
|
||||
Warning(_log, "At least one led has no color calibration, please add all leds from your led layout to an 'LED index' field!");
|
||||
}
|
||||
|
||||
// handle hwLedCount
|
||||
_hwLedCount = qMax(unsigned(getSetting(settings::DEVICE).object()["hardwareLedCount"].toInt(getLedCount())), getLedCount());
|
||||
@@ -107,6 +105,7 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
|
||||
{
|
||||
_ledStringColorOrder.push_back(led.colorOrder);
|
||||
}
|
||||
|
||||
for (Led& led : _ledStringClone.leds())
|
||||
{
|
||||
_ledStringColorOrder.insert(_ledStringColorOrder.begin() + led.index, led.colorOrder);
|
||||
@@ -135,7 +134,7 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString
|
||||
getComponentRegister().componentStateChanged(hyperion::COMP_LEDDEVICE, _device->componentState());
|
||||
|
||||
// create the effect engine and pipe the updateEmit; must be initialized after smoothing!
|
||||
_effectEngine = new EffectEngine(this,getSetting(settings::EFFECTS).object());
|
||||
_effectEngine = new EffectEngine(this);
|
||||
connect(_effectEngine, &EffectEngine::effectListUpdated, this, &Hyperion::effectListUpdated);
|
||||
|
||||
// setup config state checks and initial shot
|
||||
@@ -178,7 +177,6 @@ void Hyperion::freeObjects(bool emitCloseSignal)
|
||||
{
|
||||
// switch off all leds
|
||||
clearall(true);
|
||||
_device->switchOff();
|
||||
|
||||
if (emitCloseSignal)
|
||||
{
|
||||
@@ -507,9 +505,14 @@ const Hyperion::InputInfo Hyperion::getPriorityInfo(const int priority) const
|
||||
return _muxer.getInputInfo(priority);
|
||||
}
|
||||
|
||||
void Hyperion::reloadEffects()
|
||||
const bool Hyperion::saveEffect(const QJsonObject& obj, QString& resultMsg)
|
||||
{
|
||||
_effectEngine->readEffects();
|
||||
return _effectEngine->saveEffect(obj, resultMsg);
|
||||
}
|
||||
|
||||
const bool Hyperion::deleteEffect(const QString& effectName, QString& resultMsg)
|
||||
{
|
||||
return _effectEngine->deleteEffect(effectName, resultMsg);
|
||||
}
|
||||
|
||||
const std::list<EffectDefinition> & Hyperion::getEffects() const
|
||||
@@ -625,12 +628,14 @@ void Hyperion::update()
|
||||
{
|
||||
_ledBuffer = priorityInfo.ledColors;
|
||||
}
|
||||
// copy rawLedColors before adjustments
|
||||
_rawLedBuffer = _ledBuffer;
|
||||
|
||||
// emit rawLedColors before transform
|
||||
emit rawLedColors(_ledBuffer);
|
||||
|
||||
// apply adjustments
|
||||
if(compChanged)
|
||||
_raw2ledAdjustment->setBacklightEnabled((_prevCompId != hyperion::COMP_COLOR && _prevCompId != hyperion::COMP_EFFECT));
|
||||
|
||||
_raw2ledAdjustment->applyAdjustment(_ledBuffer);
|
||||
|
||||
// insert cloned leds into buffer
|
||||
|
@@ -302,11 +302,10 @@ void PriorityMuxer::setCurrentTime(void)
|
||||
if(infoIt->timeoutTime_ms >= -1)
|
||||
newPriority = qMin(newPriority, infoIt->priority);
|
||||
|
||||
// call timeTrigger when effect or color is running with timeout > -1, blacklist prio 255
|
||||
if(infoIt->priority < 254 && infoIt->timeoutTime_ms > -1 && (infoIt->componentId == hyperion::COMP_EFFECT || infoIt->componentId == hyperion::COMP_COLOR))
|
||||
{
|
||||
// call timeTrigger when effect or color is running with timeout > 0, blacklist prio 255
|
||||
if(infoIt->priority < 254 && infoIt->timeoutTime_ms > 0 && (infoIt->componentId == hyperion::COMP_EFFECT || infoIt->componentId == hyperion::COMP_COLOR))
|
||||
emit signalTimeTrigger(); // as signal to prevent Threading issues
|
||||
}
|
||||
|
||||
++infoIt;
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,6 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file alias="hyperion-icon.png">hyperion-icon_32px.png</file>
|
||||
<file alias="hyperion-schema">hyperion.schema.json</file>
|
||||
<file alias="hyperion_default.config">../../config/hyperion.config.json.default</file>
|
||||
<file alias="schema-general.json">schema/schema-general.json</file>
|
||||
|
@@ -171,7 +171,7 @@ int LedDeviceAurora::write(const std::vector<ColorRgb> & ledValues)
|
||||
udpbuffer[i++] = panelCount;
|
||||
for (const ColorRgb& color : ledValues)
|
||||
{
|
||||
if (i<udpBufferSize) {
|
||||
if ((unsigned)i < udpBufferSize) {
|
||||
udpbuffer[i++] = panelIds[panelCounter++ % panelCount];
|
||||
udpbuffer[i++] = 1; // No of Frames
|
||||
udpbuffer[i++] = color.red;
|
||||
@@ -180,7 +180,7 @@ int LedDeviceAurora::write(const std::vector<ColorRgb> & ledValues)
|
||||
udpbuffer[i++] = 0; // W not set manually
|
||||
udpbuffer[i++] = 1; // currently fixed at value 1 which corresponds to 100ms
|
||||
}
|
||||
if(panelCounter > panelCount) {
|
||||
if((unsigned)panelCounter > panelCount) {
|
||||
break;
|
||||
}
|
||||
//printf ("c.red %d sz c.red %d\n", color.red, sizeof(color.red));
|
||||
|
@@ -88,7 +88,7 @@ const QString SSDPDiscover::getFirstService(const searchType& type, const QStrin
|
||||
//Info(_log, "Received msearch response from '%s:%d'. Search target: %s",QSTRING_CSTR(sender.toString()), senderPort, QSTRING_CSTR(headers.value("st")));
|
||||
if(type == STY_WEBSERVER)
|
||||
{
|
||||
Info(_log, "Found Hyperion server at: %s:%d", QSTRING_CSTR(url.host()), url.port());
|
||||
Info(_log, "Found Hyperion server at: %s:%s", QSTRING_CSTR(url.host()), url.port());
|
||||
|
||||
return url.host()+":"+QString::number(url.port());
|
||||
}
|
||||
@@ -101,7 +101,7 @@ const QString SSDPDiscover::getFirstService(const searchType& type, const QStrin
|
||||
}
|
||||
else
|
||||
{
|
||||
Info(_log, "Found Hyperion server at: %s:%d", QSTRING_CSTR(url.host()), fbsport);
|
||||
Info(_log, "Found Hyperion server at: %s:%s", QSTRING_CSTR(url.host()), QSTRING_CSTR(fbsport));
|
||||
return url.host()+":"+fbsport;
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
#include "QtHttpServer.h"
|
||||
#include "QtHttpRequest.h"
|
||||
#include "QtHttpReply.h"
|
||||
@@ -9,95 +8,114 @@
|
||||
const QString & QtHttpServer::HTTP_VERSION = QStringLiteral ("HTTP/1.1");
|
||||
|
||||
QtHttpServerWrapper::QtHttpServerWrapper (QObject * parent)
|
||||
: QTcpServer (parent)
|
||||
, m_useSsl (false)
|
||||
{ }
|
||||
|
||||
QtHttpServerWrapper::~QtHttpServerWrapper (void) { }
|
||||
|
||||
void QtHttpServerWrapper::setUseSecure (const bool ssl) {
|
||||
m_useSsl = ssl;
|
||||
: QTcpServer (parent)
|
||||
, m_useSsl (false)
|
||||
{
|
||||
}
|
||||
|
||||
void QtHttpServerWrapper::incomingConnection (qintptr handle) {
|
||||
QTcpSocket * sock = (m_useSsl
|
||||
? new QSslSocket (this)
|
||||
: new QTcpSocket (this));
|
||||
if (sock->setSocketDescriptor (handle)) {
|
||||
addPendingConnection (sock);
|
||||
}
|
||||
else {
|
||||
delete sock;
|
||||
}
|
||||
QtHttpServerWrapper::~QtHttpServerWrapper (void)
|
||||
{
|
||||
}
|
||||
|
||||
void QtHttpServerWrapper::setUseSecure (const bool ssl) {
|
||||
m_useSsl = ssl;
|
||||
}
|
||||
|
||||
void QtHttpServerWrapper::incomingConnection (qintptr handle)
|
||||
{
|
||||
QTcpSocket * sock = (m_useSsl
|
||||
? new QSslSocket (this)
|
||||
: new QTcpSocket (this));
|
||||
(sock->setSocketDescriptor (handle))
|
||||
? addPendingConnection (sock)
|
||||
: delete sock;
|
||||
}
|
||||
|
||||
QtHttpServer::QtHttpServer (QObject * parent)
|
||||
: QObject (parent)
|
||||
, m_useSsl (false)
|
||||
, m_serverName (QStringLiteral ("The Qt5 HTTP Server"))
|
||||
: QObject (parent)
|
||||
, m_useSsl (false)
|
||||
, m_serverName (QStringLiteral ("The Qt5 HTTP Server"))
|
||||
{
|
||||
m_sockServer = new QtHttpServerWrapper (this);
|
||||
connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected);
|
||||
m_sockServer = new QtHttpServerWrapper (this);
|
||||
connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected);
|
||||
}
|
||||
|
||||
const QString & QtHttpServer::getServerName (void) const {
|
||||
return m_serverName;
|
||||
const QString & QtHttpServer::getServerName (void) const
|
||||
{
|
||||
return m_serverName;
|
||||
}
|
||||
|
||||
quint16 QtHttpServer::getServerPort (void) const {
|
||||
return m_sockServer->serverPort ();
|
||||
quint16 QtHttpServer::getServerPort (void) const
|
||||
{
|
||||
return m_sockServer->serverPort ();
|
||||
}
|
||||
|
||||
QString QtHttpServer::getErrorString (void) const {
|
||||
return m_sockServer->errorString ();
|
||||
QString QtHttpServer::getErrorString (void) const
|
||||
{
|
||||
return m_sockServer->errorString ();
|
||||
}
|
||||
|
||||
void QtHttpServer::start (quint16 port) {
|
||||
void QtHttpServer::start (quint16 port)
|
||||
{
|
||||
if(!m_sockServer->isListening())
|
||||
(m_sockServer->listen (QHostAddress::Any, port))
|
||||
? emit started (m_sockServer->serverPort ())
|
||||
: emit error (m_sockServer->errorString ());
|
||||
}
|
||||
|
||||
void QtHttpServer::stop (void)
|
||||
{
|
||||
if (m_sockServer->isListening ())
|
||||
{
|
||||
if (m_sockServer->listen (QHostAddress::Any, port)) {
|
||||
emit started (m_sockServer->serverPort ());
|
||||
}
|
||||
else {
|
||||
emit error (m_sockServer->errorString ());
|
||||
m_sockServer->close ();
|
||||
|
||||
// disconnect clients
|
||||
const QList<QTcpSocket*> socks = m_socksClientsHash.keys();
|
||||
for(auto sock : socks)
|
||||
{
|
||||
sock->close();
|
||||
}
|
||||
|
||||
emit stopped ();
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpServer::stop (void) {
|
||||
if (m_sockServer->isListening ()) {
|
||||
m_sockServer->close ();
|
||||
emit stopped ();
|
||||
}
|
||||
void QtHttpServer::setServerName (const QString & serverName)
|
||||
{
|
||||
m_serverName = serverName;
|
||||
}
|
||||
|
||||
void QtHttpServer::setServerName (const QString & serverName) {
|
||||
m_serverName = serverName;
|
||||
void QtHttpServer::setUseSecure (const bool ssl)
|
||||
{
|
||||
m_useSsl = ssl;
|
||||
m_sockServer->setUseSecure (m_useSsl);
|
||||
}
|
||||
|
||||
void QtHttpServer::setUseSecure (const bool ssl) {
|
||||
m_useSsl = ssl;
|
||||
m_sockServer->setUseSecure (m_useSsl);
|
||||
void QtHttpServer::setPrivateKey (const QSslKey & key)
|
||||
{
|
||||
m_sslKey = key;
|
||||
}
|
||||
|
||||
void QtHttpServer::setPrivateKey (const QSslKey & key) {
|
||||
m_sslKey = key;
|
||||
void QtHttpServer::setCertificates (const QList<QSslCertificate> & certs)
|
||||
{
|
||||
m_sslCerts = certs;
|
||||
}
|
||||
|
||||
void QtHttpServer::setCertificates (const QList<QSslCertificate> & certs) {
|
||||
m_sslCerts = certs;
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientConnected (void) {
|
||||
while (m_sockServer->hasPendingConnections ()) {
|
||||
if (QTcpSocket * sock = m_sockServer->nextPendingConnection ()) {
|
||||
void QtHttpServer::onClientConnected (void)
|
||||
{
|
||||
while (m_sockServer->hasPendingConnections ())
|
||||
{
|
||||
if (QTcpSocket * sock = m_sockServer->nextPendingConnection ())
|
||||
{
|
||||
connect (sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);
|
||||
if (m_useSsl) {
|
||||
if (QSslSocket * ssl = qobject_cast<QSslSocket *> (sock)) {
|
||||
if (m_useSsl)
|
||||
{
|
||||
if (QSslSocket * ssl = qobject_cast<QSslSocket *> (sock))
|
||||
{
|
||||
connect (ssl, SslErrorSignal (&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors);
|
||||
connect (ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted);
|
||||
connect (ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError);
|
||||
connect (ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged);
|
||||
connect (ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted);
|
||||
connect (ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError);
|
||||
connect (ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged);
|
||||
ssl->setLocalCertificateChain (m_sslCerts);
|
||||
ssl->setPrivateKey (m_sslKey);
|
||||
ssl->setPeerVerifyMode (QSslSocket::AutoVerifyPeer);
|
||||
@@ -107,30 +125,38 @@ void QtHttpServer::onClientConnected (void) {
|
||||
QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sock, this);
|
||||
m_socksClientsHash.insert (sock, wrapper);
|
||||
emit clientConnected (wrapper->getGuid ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientSslEncrypted (void) { }
|
||||
|
||||
void QtHttpServer::onClientSslPeerVerifyError (const QSslError & err) {
|
||||
Q_UNUSED (err)
|
||||
void QtHttpServer::onClientSslEncrypted (void)
|
||||
{
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientSslErrors (const QList<QSslError> & errors) {
|
||||
Q_UNUSED (errors)
|
||||
void QtHttpServer::onClientSslPeerVerifyError (const QSslError & err)
|
||||
{
|
||||
Q_UNUSED (err)
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientSslModeChanged (QSslSocket::SslMode mode) {
|
||||
Q_UNUSED (mode)
|
||||
void QtHttpServer::onClientSslErrors (const QList<QSslError> & errors)
|
||||
{
|
||||
Q_UNUSED (errors)
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientDisconnected (void) {
|
||||
if (QTcpSocket * sockClient = qobject_cast<QTcpSocket *> (sender ())) {
|
||||
if (QtHttpClientWrapper * wrapper = m_socksClientsHash.value (sockClient, Q_NULLPTR)) {
|
||||
emit clientDisconnected (wrapper->getGuid ());
|
||||
wrapper->deleteLater ();
|
||||
m_socksClientsHash.remove (sockClient);
|
||||
}
|
||||
}
|
||||
void QtHttpServer::onClientSslModeChanged (QSslSocket::SslMode mode)
|
||||
{
|
||||
Q_UNUSED (mode)
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientDisconnected (void)
|
||||
{
|
||||
if (QTcpSocket * sockClient = qobject_cast<QTcpSocket *> (sender ()))
|
||||
{
|
||||
if (QtHttpClientWrapper * wrapper = m_socksClientsHash.value (sockClient, Q_NULLPTR))
|
||||
{
|
||||
emit clientDisconnected (wrapper->getGuid ());
|
||||
wrapper->deleteLater ();
|
||||
m_socksClientsHash.remove (sockClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -13,12 +13,11 @@
|
||||
|
||||
|
||||
WebServer::WebServer(const QJsonDocument& config, QObject * parent)
|
||||
: QObject(parent)
|
||||
: QObject(parent)
|
||||
, _config(config)
|
||||
, _log(Logger::getInstance("WEBSERVER"))
|
||||
, _server()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
WebServer::~WebServer()
|
||||
|
@@ -227,8 +227,8 @@ void WebSocketClient::sendClose(int status, QString reason)
|
||||
|
||||
void WebSocketClient::handleBinaryMessage(QByteArray &data)
|
||||
{
|
||||
uint8_t priority = data.at(0);
|
||||
unsigned duration_s = data.at(1);
|
||||
//uint8_t priority = data.at(0);
|
||||
//unsigned duration_s = data.at(1);
|
||||
unsigned imgSize = data.size() - 4;
|
||||
unsigned width = ((data.at(2) << 8) & 0xFF00) | (data.at(3) & 0xFF);
|
||||
unsigned height = imgSize / width;
|
||||
@@ -244,7 +244,7 @@ void WebSocketClient::handleBinaryMessage(QByteArray &data)
|
||||
|
||||
memcpy(image.memptr(), data.data()+4, imgSize);
|
||||
//_hyperion->registerInput();
|
||||
_hyperion->setInputImage(priority, image, duration_s*1000);
|
||||
//_hyperion->setInputImage(priority, image, duration_s*1000);
|
||||
}
|
||||
|
||||
qint64 WebSocketClient::sendMessage(QJsonObject obj)
|
||||
|
Reference in New Issue
Block a user