mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
JsonUtils & improvements (#476)
* add JsonUtils * update * repair * update * ident * Schema correct msg other adjusts * fix effDel, ExceptionLog, cleanup * fix travis qt5.2 * not so funny * use Qthread interrupt instead abort bool * update services
This commit is contained in:
@@ -24,6 +24,7 @@ add_executable(${PROJECT_NAME}
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
effectengine
|
||||
commandline
|
||||
hyperion-utils
|
||||
Qt5::Gui
|
||||
Qt5::Core
|
||||
Qt5::Network)
|
||||
|
@@ -14,8 +14,12 @@
|
||||
// hyperion-remote includes
|
||||
#include "JsonConnection.h"
|
||||
|
||||
// util includes
|
||||
#include <utils/JsonUtils.h>
|
||||
|
||||
JsonConnection::JsonConnection(const QString & address, bool printJson)
|
||||
: _printJson(printJson)
|
||||
, _log(Logger::getInstance("REMOTE"))
|
||||
, _socket()
|
||||
{
|
||||
QStringList parts = address.split(":");
|
||||
@@ -62,7 +66,7 @@ void JsonConnection::setColor(std::vector<QColor> colors, int priority, int dura
|
||||
rgbValue.append(color.blue());
|
||||
}
|
||||
command["color"] = rgbValue;
|
||||
|
||||
|
||||
if (duration > 0)
|
||||
{
|
||||
command["duration"] = duration;
|
||||
@@ -125,37 +129,20 @@ void JsonConnection::setEffect(const QString &effectName, const QString & effect
|
||||
command["origin"] = QString("hyperion-remote");
|
||||
command["priority"] = priority;
|
||||
effect["name"] = effectName;
|
||||
|
||||
|
||||
if (effectArgs.size() > 0)
|
||||
{
|
||||
QJsonParseError error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(effectArgs.toUtf8() ,&error);
|
||||
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
QJsonObject effObj;
|
||||
if(!JsonUtils::parse("hyperion-remote-args", effectArgs, effObj, _log))
|
||||
{
|
||||
// report to the user the failure and their locations in the document.
|
||||
int errorLine(0), errorColumn(0);
|
||||
|
||||
for( int i=0, count=qMin( error.offset,effectArgs.size()); i<count; ++i )
|
||||
{
|
||||
++errorColumn;
|
||||
if(effectArgs.at(i) == '\n' )
|
||||
{
|
||||
errorColumn = 0;
|
||||
++errorLine;
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream sstream;
|
||||
sstream << "Error in effect arguments: " << error.errorString().toStdString() << " at Line: " << errorLine << ", Column: " << errorColumn;
|
||||
throw std::runtime_error(sstream.str());
|
||||
throw std::runtime_error("Error in effect arguments, abort");
|
||||
}
|
||||
|
||||
effect["args"] = doc.object();
|
||||
|
||||
effect["args"] = effObj;
|
||||
}
|
||||
|
||||
|
||||
command["effect"] = effect;
|
||||
|
||||
|
||||
if (duration > 0)
|
||||
{
|
||||
command["duration"] = duration;
|
||||
@@ -177,33 +164,16 @@ void JsonConnection::createEffect(const QString &effectName, const QString &effe
|
||||
effect["command"] = QString("create-effect");
|
||||
effect["name"] = effectName;
|
||||
effect["script"] = effectScript;
|
||||
|
||||
|
||||
if (effectArgs.size() > 0)
|
||||
{
|
||||
QJsonParseError error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(effectArgs.toUtf8() ,&error);
|
||||
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
QJsonObject effObj;
|
||||
if(!JsonUtils::parse("hyperion-remote-args", effectScript, effObj, _log))
|
||||
{
|
||||
// report to the user the failure and their locations in the document.
|
||||
int errorLine(0), errorColumn(0);
|
||||
|
||||
for( int i=0, count=qMin( error.offset,effectArgs.size()); i<count; ++i )
|
||||
{
|
||||
++errorColumn;
|
||||
if(effectArgs.at(i) == '\n' )
|
||||
{
|
||||
errorColumn = 0;
|
||||
++errorLine;
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream sstream;
|
||||
sstream << "Error in effect arguments: " << error.errorString().toStdString() << " at Line: " << errorLine << ", Column: " << errorColumn;
|
||||
throw std::runtime_error(sstream.str());
|
||||
throw std::runtime_error("Error in effect arguments, abort");
|
||||
}
|
||||
|
||||
effect["args"] = doc.object();
|
||||
|
||||
effect["args"] = effObj;
|
||||
}
|
||||
|
||||
// send command message
|
||||
@@ -221,7 +191,7 @@ void JsonConnection::deleteEffect(const QString &effectName)
|
||||
QJsonObject effect;
|
||||
effect["command"] = QString("delete-effect");
|
||||
effect["name"] = effectName;
|
||||
|
||||
|
||||
// send command message
|
||||
QJsonObject reply = sendMessage(effect);
|
||||
|
||||
@@ -380,7 +350,7 @@ QString JsonConnection::getConfig(std::string type)
|
||||
{
|
||||
throw std::runtime_error("No configuration file available in result");
|
||||
}
|
||||
|
||||
|
||||
QJsonDocument doc(reply["result"].toObject());
|
||||
QString result(doc.toJson(QJsonDocument::Indented));
|
||||
return result;
|
||||
@@ -395,36 +365,22 @@ void JsonConnection::setConfig(const QString &jsonString)
|
||||
QJsonObject command;
|
||||
command["command"] = QString("config");
|
||||
command["subcommand"] = QString("setconfig");
|
||||
|
||||
|
||||
|
||||
|
||||
if (jsonString.size() > 0)
|
||||
{
|
||||
QJsonParseError error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(jsonString.toUtf8() ,&error);
|
||||
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
QJsonObject configObj;
|
||||
if(!JsonUtils::parse("hyperion-remote-args", jsonString, configObj, _log))
|
||||
{
|
||||
// report to the user the failure and their locations in the document.
|
||||
int errorLine(0), errorColumn(0);
|
||||
|
||||
for( int i=0, count=qMin( error.offset,jsonString.size()); i<count; ++i )
|
||||
{
|
||||
++errorColumn;
|
||||
if(jsonString.at(i) == '\n' )
|
||||
{
|
||||
errorColumn = 0;
|
||||
++errorLine;
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream sstream;
|
||||
sstream << "Error in configset arguments: " << error.errorString().toStdString() << " at Line: " << errorLine << ", Column: " << errorColumn;
|
||||
throw std::runtime_error(sstream.str());
|
||||
throw std::runtime_error("Error in configset arguments, abort");
|
||||
}
|
||||
|
||||
command["config"] = doc.object();
|
||||
|
||||
command["config"] = configObj;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// send command message
|
||||
QJsonObject reply = sendMessage(command);
|
||||
|
||||
@@ -460,7 +416,7 @@ void JsonConnection::setAdjustment(
|
||||
{
|
||||
adjust["id"] = adjustmentId;
|
||||
}
|
||||
|
||||
|
||||
if (redAdjustment.isValid())
|
||||
{
|
||||
QJsonArray red;
|
||||
|
@@ -6,6 +6,9 @@
|
||||
#include <QTcpSocket>
|
||||
#include <QJsonObject>
|
||||
|
||||
//forward class decl
|
||||
class Logger;
|
||||
|
||||
///
|
||||
/// Connection class to setup an connection to the hyperion server and execute commands
|
||||
///
|
||||
@@ -61,7 +64,7 @@ public:
|
||||
/// @param effectArgs The arguments of the effect
|
||||
///
|
||||
void createEffect(const QString &effectName, const QString &effectScript, const QString & effectArgs);
|
||||
|
||||
|
||||
///
|
||||
/// Delete a effect configuration file (.json)
|
||||
///
|
||||
@@ -116,7 +119,7 @@ public:
|
||||
void setSourceAutoSelect();
|
||||
|
||||
///
|
||||
/// Print the current loaded Hyperion configuration file
|
||||
/// Print the current loaded Hyperion configuration file
|
||||
///
|
||||
QString getConfig(std::string type);
|
||||
|
||||
@@ -188,10 +191,13 @@ private:
|
||||
///
|
||||
bool parseReply(const QJsonObject & reply);
|
||||
|
||||
private:
|
||||
/// Flag for printing all send and received json-messages to the standard out
|
||||
bool _printJson;
|
||||
|
||||
// Logger class
|
||||
Logger* _log;
|
||||
|
||||
/// The TCP-Socket with the connection to the server
|
||||
QTcpSocket _socket;
|
||||
|
||||
};
|
||||
|
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <utils/jsonschema/QJsonFactory.h>
|
||||
#include <utils/Components.h>
|
||||
#include <utils/JsonUtils.h>
|
||||
|
||||
#include <hyperion/Hyperion.h>
|
||||
#include <hyperion/PriorityMuxer.h>
|
||||
@@ -33,7 +34,9 @@
|
||||
|
||||
#include "hyperiond.h"
|
||||
|
||||
HyperionDaemon::HyperionDaemon(QString configFile, QObject *parent)
|
||||
#include <QDebug>
|
||||
|
||||
HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObject *parent)
|
||||
: QObject(parent)
|
||||
, _log(Logger::getInstance("MAIN"))
|
||||
, _kodiVideoChecker(nullptr)
|
||||
@@ -73,7 +76,7 @@ HyperionDaemon::HyperionDaemon(QString configFile, QObject *parent)
|
||||
WarningIf(_qconfig.contains("logger"), Logger::getInstance("LOGGER"), "Logger settings overridden by command line argument");
|
||||
}
|
||||
|
||||
_hyperion = Hyperion::initInstance(_qconfig, configFile);
|
||||
_hyperion = Hyperion::initInstance(_qconfig, configFile, rootPath);
|
||||
|
||||
Info(_log, "Hyperion initialized");
|
||||
}
|
||||
@@ -145,11 +148,12 @@ void HyperionDaemon::loadConfig(const QString & configFile)
|
||||
Q_INIT_RESOURCE(resource);
|
||||
|
||||
// read the json schema from the resource
|
||||
|
||||
QString schemaFile = ":/hyperion-schema";
|
||||
QJsonObject schemaJson;
|
||||
try
|
||||
{
|
||||
//QJsonObject obj;
|
||||
//JsonUtils::readSchema(schemaFile, obj, _log);
|
||||
schemaJson = QJsonFactory::readSchema(schemaFile);
|
||||
}
|
||||
catch(const std::runtime_error& error)
|
||||
@@ -160,25 +164,31 @@ void HyperionDaemon::loadConfig(const QString & configFile)
|
||||
QJsonSchemaChecker schemaChecker;
|
||||
schemaChecker.setSchema(schemaJson);
|
||||
|
||||
_qconfig = QJsonFactory::readConfig(configFile);
|
||||
if(!JsonUtils::readFile(configFile, _qconfig, _log))
|
||||
throw std::runtime_error("Failed to load config!");
|
||||
|
||||
// validate config with schema and correct it if required
|
||||
QPair<bool, bool> validate = schemaChecker.validate(_qconfig);
|
||||
|
||||
if (!validate.first && validate.second)
|
||||
// errors in schema syntax, abort
|
||||
if (!validate.second)
|
||||
{
|
||||
Warning(_log,"Errors have been found in the configuration file. Automatic correction is applied");
|
||||
foreach (auto & schemaError, schemaChecker.getMessages())
|
||||
Error(_log, "Schema Syntax Error: %s", QSTRING_CSTR(schemaError));
|
||||
|
||||
throw std::runtime_error("ERROR: Hyperion schema has errors!");
|
||||
}
|
||||
// errors in configuration, correct it!
|
||||
if (!validate.first)
|
||||
{
|
||||
Warning(_log,"Errors have been found in the configuration file. Automatic correction has been applied");
|
||||
_qconfig = schemaChecker.getAutoCorrectedConfig(_qconfig);
|
||||
|
||||
if (!QJsonFactory::writeJson(configFile, _qconfig))
|
||||
throw std::runtime_error("ERROR: can not save configuration file, aborting ");
|
||||
}
|
||||
else if (validate.first && !validate.second) //Error in Schema
|
||||
{
|
||||
QStringList schemaErrors = schemaChecker.getMessages();
|
||||
foreach (auto & schemaError, schemaErrors)
|
||||
std::cout << schemaError.toStdString() << std::endl;
|
||||
foreach (auto & schemaError, schemaChecker.getMessages())
|
||||
Warning(_log, "Config Fix: %s", QSTRING_CSTR(schemaError));
|
||||
|
||||
throw std::runtime_error("ERROR: Json validation failed");
|
||||
if (!JsonUtils::write(configFile, _qconfig, _log))
|
||||
throw std::runtime_error("ERROR: Can't save configuration file, aborting");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,7 +419,7 @@ void HyperionDaemon::createSystemFrameGrabber()
|
||||
#else
|
||||
QString type = grabberConfig["type"].toString("auto");
|
||||
#endif
|
||||
|
||||
|
||||
// auto eval of type
|
||||
if ( type == "auto" )
|
||||
{
|
||||
|
@@ -58,7 +58,7 @@ class HyperionDaemon : public QObject
|
||||
friend SysTray;
|
||||
|
||||
public:
|
||||
HyperionDaemon(QString configFile, QObject *parent=nullptr);
|
||||
HyperionDaemon(QString configFile, QString rootPath, QObject *parent=nullptr);
|
||||
~HyperionDaemon();
|
||||
|
||||
void loadConfig(const QString & configFile);
|
||||
@@ -95,11 +95,11 @@ private:
|
||||
X11Wrapper* _x11Grabber;
|
||||
#endif
|
||||
AmlogicWrapper* _amlGrabber;
|
||||
FramebufferWrapper* _fbGrabber;
|
||||
FramebufferWrapper* _fbGrabber;
|
||||
OsxWrapper* _osxGrabber;
|
||||
Hyperion* _hyperion;
|
||||
Stats* _stats;
|
||||
|
||||
|
||||
unsigned _grabber_width;
|
||||
unsigned _grabber_height;
|
||||
unsigned _grabber_frequency;
|
||||
|
@@ -92,7 +92,7 @@ QCoreApplication* createApplication(int &argc, char *argv[])
|
||||
// if x11, then test if xserver is available
|
||||
#ifdef ENABLE_X11
|
||||
Display* dpy = XOpenDisplay(NULL);
|
||||
if (dpy != NULL)
|
||||
if (dpy != NULL)
|
||||
{
|
||||
XCloseDisplay(dpy);
|
||||
isGuiApp = true;
|
||||
@@ -140,6 +140,7 @@ int main(int argc, char** argv)
|
||||
parser.addHelpOption();
|
||||
|
||||
BooleanOption & versionOption = parser.add<BooleanOption>(0x0, "version", "Show version information");
|
||||
Option & rootPathOption = parser.add<Option> (0x0, "rootPath", "Overwrite root path for all hyperion user files, defaults to home directory of current user");
|
||||
IntOption & parentOption = parser.add<IntOption> ('p', "parent", "pid of parent hyperiond"); // 2^22 is the max for Linux
|
||||
BooleanOption & silentOption = parser.add<BooleanOption>('s', "silent", "do not print any outputs");
|
||||
BooleanOption & verboseOption = parser.add<BooleanOption>('v', "verbose", "Increase verbosity");
|
||||
@@ -207,7 +208,7 @@ int main(int argc, char** argv)
|
||||
{
|
||||
QFile::remove(destFileName);
|
||||
}
|
||||
|
||||
|
||||
std::cout << "Extract: " << filename.toStdString() << " ... ";
|
||||
if (QFile::copy(QString(":/effects/")+filename, destFileName))
|
||||
{
|
||||
@@ -227,21 +228,48 @@ int main(int argc, char** argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// handle default config file
|
||||
if (configFiles.size() == 0)
|
||||
// handle rootPath for user data, default path is home directory + /.hyperion
|
||||
QString rootPath = QDir::homePath() + "/.hyperion";
|
||||
if (parser.isSet(rootPathOption))
|
||||
{
|
||||
QString hyperiond_path = QDir::homePath();
|
||||
QString hyperiond_config = hyperiond_path+"/.hyperion.config.json";
|
||||
QFileInfo hyperiond_pathinfo(hyperiond_path);
|
||||
|
||||
if ( ! hyperiond_pathinfo.isWritable() && ! QFile::exists(hyperiond_config) )
|
||||
QDir rDir(rootPathOption.value(parser));
|
||||
if(!rDir.mkpath(rootPathOption.value(parser)))
|
||||
{
|
||||
QFileInfo hyperiond_fileinfo(argv[0]);
|
||||
hyperiond_config = hyperiond_fileinfo.absolutePath()+"/hyperion.config.json";
|
||||
Error(log, "Can't create root path '%s', falling back to home directory", QSTRING_CSTR(rDir.absolutePath()));
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPath = rDir.absolutePath();
|
||||
}
|
||||
}
|
||||
// create /.hyperion folder for default path, check if the directory is read/writeable
|
||||
// Note: No further checks inside Hyperion. FileUtils::writeFile() will resolve permission errors and others that occur during runtime
|
||||
QDir mDir(rootPath);
|
||||
QFileInfo mFi(rootPath);
|
||||
if(!mDir.mkpath(rootPath) || !mFi.isWritable() || !mDir.isReadable())
|
||||
{
|
||||
throw std::runtime_error("The specified root path can't be created or isn't read/writeable. Please setup the permissions correctly!");
|
||||
}
|
||||
|
||||
configFiles.append(hyperiond_config);
|
||||
Info(log, "No config file given. Standard config file used: %s", QSTRING_CSTR(configFiles[0]));
|
||||
// determine name of config file, defaults to hyperion_main.json
|
||||
// create config folder
|
||||
QString cPath(rootPath+"/config");
|
||||
QDir().mkpath(rootPath+"/config");
|
||||
if (configFiles.size() > 0)
|
||||
{
|
||||
// use argument config file
|
||||
// check if file has a path and ends with .json
|
||||
if(configFiles[0].contains("/"))
|
||||
throw std::runtime_error("Don't provide a path to config file, just a config name is allowed!");
|
||||
if(!configFiles[0].endsWith(".json"))
|
||||
configFiles[0].append(".json");
|
||||
|
||||
configFiles.prepend(cPath+"/"+configFiles[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// use default config file
|
||||
configFiles.append(cPath+"/hyperion_main.json");
|
||||
}
|
||||
|
||||
bool exportDefaultConfig = false;
|
||||
@@ -257,7 +285,7 @@ int main(int argc, char** argv)
|
||||
{
|
||||
exportDefaultConfig = true;
|
||||
exportConfigFileTarget = configFiles[0];
|
||||
Warning(log, "Your configuration file does not exist. hyperion writes default config");
|
||||
Warning(log, "Create new config file (%s)",QSTRING_CSTR(configFiles[0]));
|
||||
}
|
||||
|
||||
if (exportDefaultConfig)
|
||||
@@ -277,11 +305,6 @@ int main(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (configFiles.size() > 1)
|
||||
{
|
||||
Warning(log, "You provided more than one config file. Hyperion will use only the first one");
|
||||
}
|
||||
|
||||
int parentPid = parser.value(parentOption).toInt();
|
||||
if (parentPid > 0 )
|
||||
{
|
||||
@@ -295,7 +318,7 @@ int main(int argc, char** argv)
|
||||
HyperionDaemon* hyperiond = nullptr;
|
||||
try
|
||||
{
|
||||
hyperiond = new HyperionDaemon(configFiles[0], qApp);
|
||||
hyperiond = new HyperionDaemon(configFiles[0], rootPath, qApp);
|
||||
hyperiond->run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
|
Reference in New Issue
Block a user