JSON RPC Writer (configSet) (#175)

* Remove "endOfJson" Value

Deprecated value from Hypercon

* Remove "endOfJson" Value

Deprecated value from Hypercon

* Add writeJson function to JsonFactory

* ability to ignore required value in schema file

* Remove "endOfJson" Value

* Add handleConfigSetCommand function

* Add handleConfigSetCommand function

* Update JsonSchemas.qrc

* Update schema.json

* Update JsonSchemaChecker.cpp

* Add configSet command to Hyperion-remote

* Add setConfigFile function

* Add setConfigFile function

* Add schema-configset.json
This commit is contained in:
Paulchen-Panther 2016-08-14 20:17:12 +02:00 committed by redPanther
parent 97181fa83c
commit 68fd395670
14 changed files with 138 additions and 20 deletions

View File

@ -604,7 +604,5 @@
"hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}
],
"endOfJson" : "endOfJson"
]
}

View File

@ -437,7 +437,5 @@
"hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 },
"vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 }
}
],
"endOfJson" : "endOfJson"
]
}

View File

@ -63,4 +63,12 @@ public:
}
return jsonTree;
}
static void writeJson(const std::string& filename, Json::Value& jsonTree)
{
Json::StyledStreamWriter writer;
std::ofstream ofs(filename.c_str());
writer.write(ofs, jsonTree);
}
};

View File

@ -43,7 +43,7 @@ public:
/// @param value The JSON value to check
/// @return true when the arguments is valid according to the schema
///
bool validate(const Json::Value & value);
bool validate(const Json::Value & value, bool ignoreRequired = false);
///
/// @return A list of error messages
@ -179,6 +179,8 @@ private:
private:
/// The schema of the entire json-configuration
Json::Value _schema;
/// ignore the required value in json schema
bool _ignoreRequired;
/// The current location into a json-configuration structure being checked
std::list<std::string> _currentPath;

View File

@ -896,10 +896,6 @@
},
"additionalProperties" : false
}
},
"endOfJson" :
{
"type" : "string"
}
},
"additionalProperties" : false

View File

@ -24,6 +24,7 @@
#include <hyperion/ColorAdjustment.h>
#include <utils/ColorRgb.h>
#include <HyperionConfig.h>
#include <utils/jsonschema/JsonFactory.h>
// project includes
#include "JsonClientConnection.h"
@ -267,6 +268,8 @@ void JsonClientConnection::handleMessage(const std::string &messageString)
handleSourceSelectCommand(message);
else if (command == "configget")
handleConfigGetCommand(message);
else if (command == "configset")
handleConfigSetCommand(message);
else if (command == "componentstate")
handleComponentStateCommand(message);
else
@ -842,6 +845,53 @@ void JsonClientConnection::handleConfigGetCommand(const Json::Value &)
sendMessage(result);
}
void JsonClientConnection::handleConfigSetCommand(const Json::Value &message)
{
struct nested
{
static void configSetCommand(const Json::Value& message, Json::Value& config, bool& create)
{
if (!config.isObject() || !message.isObject())
return;
for (const auto& key : message.getMemberNames()) {
if ((config.isObject() && config.isMember(key)) || create)
{
if (config[key].type() == Json::objectValue && message[key].type() == Json::objectValue)
{
configSetCommand(message[key], config[key], create);
}
else
if ( !config[key].empty() || create)
config[key] = message[key];
}
}
}
};
if(message.size() > 0)
{
if (message.isObject() && message.isMember("configset"))
{
std::string errors;
if (!checkJson(message["configset"], ":/hyperion-schema", errors, true))
{
sendErrorReply("Error while validating json: " + errors);
return;
}
bool createKey = message.isMember("create");
Json::Value hyperionConfig = _hyperion->getJsonConfig();
nested::configSetCommand(message["configset"], hyperionConfig, createKey);
JsonFactory::writeJson(_hyperion->getConfigFileName(), hyperionConfig);
sendSuccessReply();
}
} else
sendErrorReply("Error while parsing json: Message size " + message.size());
}
void JsonClientConnection::handleComponentStateCommand(const Json::Value& message)
{
const Json::Value & componentState = message["componentstate"];
@ -956,7 +1006,7 @@ void JsonClientConnection::sendErrorReply(const std::string &error)
sendMessage(reply);
}
bool JsonClientConnection::checkJson(const Json::Value & message, const QString & schemaResource, std::string & errorMessage)
bool JsonClientConnection::checkJson(const Json::Value & message, const QString & schemaResource, std::string & errorMessage, bool ignoreRequired)
{
// read the json schema from the resource
QResource schemaData(schemaResource);
@ -974,7 +1024,7 @@ bool JsonClientConnection::checkJson(const Json::Value & message, const QString
schema.setSchema(schemaJson);
// check the message
if (!schema.validate(message))
if (!schema.validate(message, ignoreRequired))
{
const std::list<std::string> & errors = schema.getMessages();
std::stringstream ss;

View File

@ -144,6 +144,11 @@ private:
/// @param message the incoming message
///
void handleConfigGetCommand(const Json::Value & message);
///
/// Handle an incoming JSON SetConfig message
///
void handleConfigSetCommand(const Json::Value & message);
///
/// Handle an incoming JSON Component State message
@ -197,12 +202,13 @@ private:
/// Check if a JSON messag is valid according to a given JSON schema
///
/// @param message JSON message which need to be checked
/// @param schemaResource Qt esource identifier with the JSON schema
/// @param schemaResource Qt Resource identifier with the JSON schema
/// @param errors Output error message
/// @param ignoreRequired ignore the required value in JSON schema
///
/// @return true if message conforms the given JSON schema
///
bool checkJson(const Json::Value & message, const QString &schemaResource, std::string & errors);
bool checkJson(const Json::Value & message, const QString &schemaResource, std::string & errors, bool ignoreRequired = false);
private:
/// The TCP-Socket that is connected tot the Json-client

View File

@ -13,6 +13,7 @@
<file alias="schema-effect">schema/schema-effect.json</file>
<file alias="schema-sourceselect">schema/schema-sourceselect.json</file>
<file alias="schema-configget">schema/schema-configget.json</file>
<file alias="schema-configset">schema/schema-configset.json</file>
<file alias="schema-componentstate">schema/schema-componentstate.json</file>
</qresource>
</RCC>

View File

@ -0,0 +1,20 @@
{
"type" : "object",
"required" : true,
"properties" : {
"command": {
"type" : "string",
"required" : true,
"enum" : ["configset"]
},
"configset": {
"type" : "object",
"required" : true
},
"create": {
"type" : "boolean",
"required" : false
}
},
"additionalProperties": false
}

View File

@ -5,7 +5,7 @@
"command": {
"type" : "string",
"required" : true,
"enum" : ["color", "image", "effect", "serverinfo", "clear", "clearall", "transform", "correction", "temperature", "adjustment", "sourceselect", "configget", "componentstate"]
"enum" : ["color", "image", "effect", "serverinfo", "clear", "clearall", "transform", "correction", "temperature", "adjustment", "sourceselect", "configget", "configset", "componentstate"]
}
}
}

View File

@ -26,9 +26,10 @@ bool JsonSchemaChecker::setSchema(const Json::Value & schema)
return true;
}
bool JsonSchemaChecker::validate(const Json::Value & value)
bool JsonSchemaChecker::validate(const Json::Value & value, bool ignoreRequired)
{
// initialize state
_ignoreRequired = ignoreRequired;
_error = false;
_messages.clear();
_currentPath.clear();
@ -201,7 +202,7 @@ void JsonSchemaChecker::checkProperties(const Json::Value & value, const Json::V
{
validate(value[property], propertyValue);
}
else if (propertyValue.get("required", false).asBool())
else if (propertyValue.get("required", false).asBool() && !_ignoreRequired)
{
_error = true;
setMessage("missing member");

View File

@ -265,6 +265,29 @@ QString JsonConnection::getConfigFile()
return QString();
}
void JsonConnection::setConfigFile(const std::string &jsonString, bool create)
{
// create command
Json::Value command;
command["command"] = "configset";
command["create"] = create;
Json::Value & config = command["configset"];
if (jsonString.size() > 0)
{
Json::Reader reader;
if (!reader.parse(jsonString, config, false))
{
throw std::runtime_error("Error in configset arguments: " + reader.getFormattedErrorMessages());
}
}
// send command message
Json::Value reply = sendMessage(command);
// parse reply message
parseReply(reply);
}
void JsonConnection::setTransform(std::string * transformId, double * saturation, double * value, double * saturationL, double * luminance, double * luminanceMin, ColorTransformValues *threshold, ColorTransformValues *gamma, ColorTransformValues *blacklevel, ColorTransformValues *whitelevel)
{
std::cout << "Set color transforms" << std::endl;

View File

@ -108,6 +108,14 @@ public:
///
QString getConfigFile();
///
/// Write JSON Value(s) to the actual loaded configuration file
///
/// @param jsonString The JSON String(s) to write
/// @param create Specifies whether the nonexistent json string to be created
///
void setConfigFile(const std::string & jsonString, bool create);
///
/// Set the color transform of the leds
///

View File

@ -87,7 +87,9 @@ int main(int argc, char * argv[])
IntParameter & argSource = parameters.add<IntParameter> (0x0, "sourceSelect" , "Set current active priority channel and deactivate auto source switching");
SwitchParameter<> & argSourceAuto = parameters.add<SwitchParameter<> >(0x0, "sourceAutoSelect", "Enables auto source, if disabled prio by manual selecting input source");
SwitchParameter<> & argSourceOff = parameters.add<SwitchParameter<> >(0x0, "sourceOff", "select no source, this results in leds activly set to black (=off)");
SwitchParameter<> & argConfigGet = parameters.add<SwitchParameter<> >(0x0, "configget" , "Print the current loaded Hyperion configuration file");
SwitchParameter<> & argConfigGet = parameters.add<SwitchParameter<> >(0x0, "configGet" , "Print the current loaded Hyperion configuration file");
StringParameter & argConfigSet = parameters.add<StringParameter>('W', "configSet", "Write to the actual loaded configuration file. Should be a Json object string.");
SwitchParameter<> & argCreate = parameters.add<SwitchParameter<> >(0x0, "createkeys", "Create non exist Json Entry(s) in the actual loaded configuration file. Argument to use in combination with configSet.");
// set the default values
argAddress.setDefault(defaultServerAddress.toStdString());
@ -111,7 +113,7 @@ int main(int argc, char * argv[])
bool colorModding = colorTransform || colorAdjust || argCorrection.isSet() || argTemperature.isSet();
// check that exactly one command was given
int commandCount = count({argColor.isSet(), argImage.isSet(), argEffect.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), argEnableComponent.isSet(), argDisableComponent.isSet(), colorModding, argSource.isSet(), argSourceAuto.isSet(), argSourceOff.isSet(), argConfigGet.isSet()});
int commandCount = count({argColor.isSet(), argImage.isSet(), argEffect.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), argEnableComponent.isSet(), argDisableComponent.isSet(), colorModding, argSource.isSet(), argSourceAuto.isSet(), argSourceOff.isSet(), argConfigGet.isSet(), argConfigSet.isSet()});
if (commandCount != 1)
{
std::cerr << (commandCount == 0 ? "No command found." : "Multiple commands found.") << " Provide exactly one of the following options:" << std::endl;
@ -126,6 +128,7 @@ int main(int argc, char * argv[])
std::cerr << " " << argSource.usageLine() << std::endl;
std::cerr << " " << argSourceAuto.usageLine() << std::endl;
std::cerr << " " << argConfigGet.usageLine() << std::endl;
std::cerr << " " << argConfigSet.usageLine() << std::endl;
std::cerr << "or one or more of the available color modding operations:" << std::endl;
std::cerr << " " << argId.usageLine() << std::endl;
std::cerr << " " << argSaturation.usageLine() << std::endl;
@ -202,6 +205,10 @@ int main(int argc, char * argv[])
QString info = connection.getConfigFile();
std::cout << "Configuration File:\n" << info.toStdString() << std::endl;
}
else if (argConfigSet.isSet())
{
connection.setConfigFile(argConfigSet.getValue(), argCreate.isSet());
}
else if (colorModding)
{
if (argCorrection.isSet())