diff --git a/src/hyperion-remote/CMakeLists.txt b/src/hyperion-remote/CMakeLists.txt index d4141b86..a9291ba4 100644 --- a/src/hyperion-remote/CMakeLists.txt +++ b/src/hyperion-remote/CMakeLists.txt @@ -12,23 +12,23 @@ LINK_DIRECTORIES(${LINK_DIRECTORIES} ${CMAKE_FIND_ROOT_PATH}/lib/arm-linux-gnuea include_directories(${QT_INCLUDES}) set(hyperion-remote_HEADERS - CustomParameter.h - Connection.h - ColorTransformValues.h) + CustomParameter.h + Connection.h + ColorTransformValues.h) set(hyperion-remote_SOURCES - hyperion-remote.cpp - Connection.cpp) + hyperion-remote.cpp + Connection.cpp) add_executable(hyperion-remote - ${hyperion-remote_HEADERS} - ${hyperion-remote_SOURCES}) + ${hyperion-remote_HEADERS} + ${hyperion-remote_SOURCES}) qt4_use_modules(hyperion-remote - Core - Gui - Network) + Core + Gui + Network) target_link_libraries(hyperion-remote - hyperion-utils - getoptPlusPlus) + hyperion-utils + getoptPlusPlus) diff --git a/src/hyperion-remote/ColorTransformValues.h b/src/hyperion-remote/ColorTransformValues.h index 1649b191..d36e2c91 100644 --- a/src/hyperion-remote/ColorTransformValues.h +++ b/src/hyperion-remote/ColorTransformValues.h @@ -1,8 +1,9 @@ #pragma once +/// Simple structure to contain the values of a color transformation struct ColorTransformValues { - double valueRed; - double valueGreen; - double valueBlue; + double valueRed; + double valueGreen; + double valueBlue; }; diff --git a/src/hyperion-remote/Connection.cpp b/src/hyperion-remote/Connection.cpp index 342585b5..1a0c7986 100644 --- a/src/hyperion-remote/Connection.cpp +++ b/src/hyperion-remote/Connection.cpp @@ -1,113 +1,116 @@ +// stl includes #include +// hyperion-remote includes #include "Connection.h" Connection::Connection(const std::string & a, bool printJson) : - _printJson(printJson), - _socket() + _printJson(printJson), + _socket() { - QString address(a.c_str()); - QStringList parts = address.split(":"); - if (parts.size() != 2) - { - throw std::runtime_error(QString("Wrong address: unable to parse address (%1)").arg(address).toStdString()); - } + QString address(a.c_str()); + QStringList parts = address.split(":"); + if (parts.size() != 2) + { + throw std::runtime_error(QString("Wrong address: unable to parse address (%1)").arg(address).toStdString()); + } - bool ok; - uint16_t port = parts[1].toUShort(&ok); - if (!ok) - { - throw std::runtime_error(QString("Wrong address: Unable to parse the port number (%1)").arg(parts[1]).toStdString()); - } + bool ok; + uint16_t port = parts[1].toUShort(&ok); + if (!ok) + { + throw std::runtime_error(QString("Wrong address: Unable to parse the port number (%1)").arg(parts[1]).toStdString()); + } - _socket.connectToHost(parts[0], port); - if (!_socket.waitForConnected()) - { - throw std::runtime_error("Unable to connect to host"); - } + _socket.connectToHost(parts[0], port); + if (!_socket.waitForConnected()) + { + throw std::runtime_error("Unable to connect to host"); + } } Connection::~Connection() { - _socket.close(); + _socket.close(); } bool Connection::setColor(QColor color, int priority, int duration) { - std::cout << "Set color to " << color.red() << " " << color.green() << " " << color.blue() << std::endl; - sendMessage("Set color message"); - return false; + std::cout << "Set color to " << color.red() << " " << color.green() << " " << color.blue() << std::endl; + sendMessage("Set color message"); + return false; } bool Connection::setImage(QImage image, int priority, int duration) { - std::cout << "Set image has size: " << image.width() << "x" << image.height() << std::endl; - return false; + std::cout << "Set image has size: " << image.width() << "x" << image.height() << std::endl; + return false; } bool Connection::listPriorities() { - std::cout << "List priority channels" << std::endl; - return false; + std::cout << "List priority channels" << std::endl; + return false; } bool Connection::clear(int priority) { - std::cout << "Clear priority channel " << priority << std::endl; - return false; + std::cout << "Clear priority channel " << priority << std::endl; + return false; } bool Connection::clearAll() { - std::cout << "Clear all priority channels" << std::endl; - return false; + std::cout << "Clear all priority channels" << std::endl; + return false; } bool Connection::setTransform(ColorTransformValues *threshold, ColorTransformValues *gamma, ColorTransformValues *blacklevel, ColorTransformValues *whitelevel) { - std::cout << "Set color transforms" << std::endl; - return false; + std::cout << "Set color transforms" << std::endl; + return false; } -bool Connection::sendMessage(const Json::Value & message) +Json::Value Connection::sendMessage(const Json::Value & message) { - // rpint if requested - if (_printJson) - { - std::cout << "Command: " << message << std::endl; - } + // print command if requested + if (_printJson) + { + std::cout << "Command: " << message << std::endl; + } - // serialize message - Json::FastWriter jsonWriter; - std::string serializedMessage = jsonWriter.write(message); + // serialize message + Json::FastWriter jsonWriter; + std::string serializedMessage = jsonWriter.write(message); - // write message - _socket.write(serializedMessage.c_str()); - if (!_socket.waitForBytesWritten()) - { - throw std::runtime_error("Error while writing data to host"); - } + // write message + _socket.write(serializedMessage.c_str()); + if (!_socket.waitForBytesWritten()) + { + throw std::runtime_error("Error while writing data to host"); + } - // receive reply - if (!_socket.waitForReadyRead()) - { - throw std::runtime_error("Error while reading data from host"); - } - char data[1024 * 100]; - uint64_t count = _socket.read(data, sizeof(data)); - std::string serializedReply(data, count); - Json::Reader jsonReader; - Json::Value reply; - if (!jsonReader.parse(serializedReply, reply)) - { - throw std::runtime_error("Error while parsing reply:" + serializedReply); - } + // receive reply + if (!_socket.waitForReadyRead()) + { + throw std::runtime_error("Error while reading data from host"); + } + char data[1024 * 100]; + uint64_t count = _socket.read(data, sizeof(data)); + std::string serializedReply(data, count); + Json::Reader jsonReader; + Json::Value reply; + if (!jsonReader.parse(serializedReply, reply)) + { + throw std::runtime_error("Error while parsing reply:" + serializedReply); + } - if (_printJson) - { - std::cout << "Reply:" << reply << std::endl; - } + // print reply if requested + if (_printJson) + { + std::cout << "Reply:" << reply << std::endl; + } - return true; + return reply; } diff --git a/src/hyperion-remote/Connection.h b/src/hyperion-remote/Connection.h index 51ca7493..43746634 100644 --- a/src/hyperion-remote/Connection.h +++ b/src/hyperion-remote/Connection.h @@ -1,33 +1,51 @@ #pragma once +// stl includes #include +// Qt includes #include #include #include +// jsoncpp includes #include +// hyperion-remote includes #include "ColorTransformValues.h" +/// Connection class to setup an connection to the hyperion server and execute commands class Connection { public: - Connection(const std::string & address, bool printJson); - ~Connection(); + Connection(const std::string & address, bool printJson); + ~Connection(); - bool setColor(QColor color, int priority, int duration); - bool setImage(QImage image, int priority, int duration); - bool listPriorities(); - bool clear(int priority); - bool clearAll(); - bool setTransform(ColorTransformValues * threshold, ColorTransformValues * gamma, ColorTransformValues * blacklevel, ColorTransformValues * whitelevel); + /// Set all leds to the specified color + bool setColor(QColor color, int priority, int duration); + + /// Set the leds according to the given image (assume the image is stretched to the display size) + bool setImage(QImage image, int priority, int duration); + + /// Retrieve a list of all occupied priority channels + bool listPriorities(); + + /// Clear the given priority channel + bool clear(int priority); + + /// Clear all priority channels + bool clearAll(); + + /// Set the color transform of the leds + /// Note that providing a NULL will leave the settings on the server unchanged + bool setTransform(ColorTransformValues * threshold, ColorTransformValues * gamma, ColorTransformValues * blacklevel, ColorTransformValues * whitelevel); private: - bool sendMessage(const Json::Value & message); + /// Send a json command message and receive its reply + Json::Value sendMessage(const Json::Value & message); private: - bool _printJson; + bool _printJson; - QTcpSocket _socket; + QTcpSocket _socket; }; diff --git a/src/hyperion-remote/CustomParameter.h b/src/hyperion-remote/CustomParameter.h index 199f4c85..1292e3a8 100644 --- a/src/hyperion-remote/CustomParameter.h +++ b/src/hyperion-remote/CustomParameter.h @@ -1,97 +1,102 @@ #pragma once +// Qt includes #include #include +// getoptPlusPLus includes #include +// hyperion-remote includes #include "ColorTransformValues.h" + typedef vlofgren::PODParameter ColorParameter; typedef vlofgren::PODParameter ImageParameter; typedef vlofgren::PODParameter TransformParameter; + namespace vlofgren { - template<> - QColor ColorParameter::validate(const std::string& s) throw (Parameter::ParameterRejected) - { - // Check if we can create the color by name - QColor color(s.c_str()); - if (color.isValid()) - { - return color; - } + template<> + QColor ColorParameter::validate(const std::string& s) throw (Parameter::ParameterRejected) + { + // Check if we can create the color by name + QColor color(s.c_str()); + if (color.isValid()) + { + return color; + } - // check if we can create the color by hex RRGGBB value - if (s.length() == 6 && isxdigit(s[0]) && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]) && isxdigit(s[5])) - { - bool ok = true; - int rgb[3]; - for (int i = 0; i < 3 && ok; ++i) - { - QString colorComponent(s.substr(2*i, 2).c_str()); - rgb[i] = colorComponent.toInt(&ok, 16); - } + // check if we can create the color by hex RRGGBB value + if (s.length() == 6 && isxdigit(s[0]) && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]) && isxdigit(s[5])) + { + bool ok = true; + int rgb[3]; + for (int i = 0; i < 3 && ok; ++i) + { + QString colorComponent(s.substr(2*i, 2).c_str()); + rgb[i] = colorComponent.toInt(&ok, 16); + } - // check if all components parsed succesfully - if (ok) - { - color.setRgb(rgb[0], rgb[1], rgb[2]); - return color; - } - } + // check if all components parsed succesfully + if (ok) + { + color.setRgb(rgb[0], rgb[1], rgb[2]); + return color; + } + } - std::stringstream errorMessage; - errorMessage << "Invalid color. A color is specified by a six lettered RRGGBB hex value or one of the following names:"; - foreach (const QString & colorname, QColor::colorNames()) { - errorMessage << "\n " << colorname.toStdString(); - } - throw Parameter::ParameterRejected(errorMessage.str()); + std::stringstream errorMessage; + errorMessage << "Invalid color. A color is specified by a six lettered RRGGBB hex value or one of the following names:"; + foreach (const QString & colorname, QColor::colorNames()) { + errorMessage << "\n " << colorname.toStdString(); + } + throw Parameter::ParameterRejected(errorMessage.str()); - return color; - } + return color; + } - template<> - QImage ImageParameter::validate(const std::string& s) throw (Parameter::ParameterRejected) - { - QImage image(s.c_str()); + template<> + QImage ImageParameter::validate(const std::string& s) throw (Parameter::ParameterRejected) + { + QImage image(s.c_str()); - if (image.isNull()) - { - std::stringstream errorMessage; - errorMessage << "File " << s << " could not be opened as an image"; - throw Parameter::ParameterRejected(errorMessage.str()); - } + if (image.isNull()) + { + std::stringstream errorMessage; + errorMessage << "File " << s << " could not be opened as an image"; + throw Parameter::ParameterRejected(errorMessage.str()); + } - return image; - } + return image; + } - template<> - ColorTransformValues TransformParameter::validate(const std::string& s) throw (Parameter::ParameterRejected) - { - ColorTransformValues transform; + template<> + ColorTransformValues TransformParameter::validate(const std::string& s) throw (Parameter::ParameterRejected) + { + ColorTransformValues transform; - // s should be split in 3 parts - // seperators are either a ',' or a space - QStringList components = QString(s.c_str()).split(" ", QString::SkipEmptyParts); + // s should be split in 3 parts + // seperators are either a ',' or a space + QStringList components = QString(s.c_str()).split(" ", QString::SkipEmptyParts); - if (components.size() == 3) - { - bool ok1, ok2, ok3; - transform.valueRed = components[0].toDouble(&ok1); - transform.valueGreen = components[1].toDouble(&ok2); - transform.valueBlue = components[2].toDouble(&ok3); + if (components.size() == 3) + { + bool ok1, ok2, ok3; + transform.valueRed = components[0].toDouble(&ok1); + transform.valueGreen = components[1].toDouble(&ok2); + transform.valueBlue = components[2].toDouble(&ok3); - if (ok1 && ok2 && ok3) - { - return transform; - } - } + if (ok1 && ok2 && ok3) + { + return transform; + } + } - std::stringstream errorMessage; - errorMessage << "Argument " << s << " can not be parsed to 3 double values"; - throw Parameter::ParameterRejected(errorMessage.str()); + std::stringstream errorMessage; + errorMessage << "Argument " << s << " can not be parsed to 3 double values"; + throw Parameter::ParameterRejected(errorMessage.str()); - return transform; - } + return transform; + } } diff --git a/src/hyperion-remote/hyperion-remote.cpp b/src/hyperion-remote/hyperion-remote.cpp index 6b60bac0..1585ff07 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -1,137 +1,137 @@ +// stl includes #include +// Qt includes #include +// getoptPlusPLus includes #include +// hyperion-remote include #include "CustomParameter.h" #include "Connection.h" - using namespace vlofgren; +/// Count the number of true values in a list of booleans int count(std::initializer_list values) { - int count = 0; - for (auto& value : values) { - if (value) - count++; - } - return count; + int count = 0; + for (bool value : values) { + if (value) + count++; + } + return count; } int main(int argc, char * argv[]) { - QCoreApplication app(argc, argv); + QCoreApplication app(argc, argv); - try - { - // some settings - QString defaultServerAddress = "localhost:19444"; - int defaultPriority = 100; + try + { + // some default settings + QString defaultServerAddress = "localhost:19444"; + int defaultPriority = 100; - OptionsParser optionParser("Simple application to send a command to hyperion using the Json interface"); - ParameterSet & parameters = optionParser.getParameters(); - StringParameter & argAddress = parameters.add ('a', "address" , QString("Set the address of the hyperion server [default: %1]").arg(defaultServerAddress).toAscii().constData()); - IntParameter & argPriority = parameters.add ('p', "priority" , QString("Use to the provided priority channel (the lower the number, the higher the priority) [default: %1]").arg(defaultPriority).toAscii().constData()); - IntParameter & argDuration = parameters.add ('d', "duration" , "Specify how long the leds should be switched on in millseconds [default: infinity]"); - ColorParameter & argColor = parameters.add ('c', "color" , "Set all leds to a constant color (either RRGGBB hex value or a color name)"); - ImageParameter & argImage = parameters.add ('i', "image" , "Set the leds to the colors according to the given image file"); - SwitchParameter<> & argList = parameters.add >('l', "list" , "List all priority channels which are in use"); - SwitchParameter<> & argClear = parameters.add >('x', "clear" , "Clear data for the priority channel provided by the -p option"); - SwitchParameter<> & argClearAll = parameters.add >(0x0, "clear-all" , "Clear data for all priority channels"); - TransformParameter & argGamma = parameters.add('g', "gamma" , "Set the gamma of the leds (requires 3 values)"); - TransformParameter & argThreshold = parameters.add('t', "threshold" , "Set the threshold of the leds (requires 3 space seperated values between 0.0 and 1.0)"); - TransformParameter & argBlacklevel = parameters.add('b', "blacklevel", "Set the blacklevel of the leds (requires 3 space seperated values which are normally between 0.0 and 1.0)"); - TransformParameter & argWhitelevel = parameters.add('w', "whitelevel", "Set the whitelevel of the leds (requires 3 space seperated values which are normally between 0.0 and 1.0)"); - SwitchParameter<> & argPrint = parameters.add >(0x0, "print" , "Print the json input and output messages on stdout"); - SwitchParameter<> & argHelp = parameters.add >('h', "help" , "Show this help message and exit"); + // create the option parser and initialize all parameters + OptionsParser optionParser("Simple application to send a command to hyperion using the Json interface"); + ParameterSet & parameters = optionParser.getParameters(); + StringParameter & argAddress = parameters.add ('a', "address" , QString("Set the address of the hyperion server [default: %1]").arg(defaultServerAddress).toAscii().constData()); + IntParameter & argPriority = parameters.add ('p', "priority" , QString("Use to the provided priority channel (the lower the number, the higher the priority) [default: %1]").arg(defaultPriority).toAscii().constData()); + IntParameter & argDuration = parameters.add ('d', "duration" , "Specify how long the leds should be switched on in millseconds [default: infinity]"); + ColorParameter & argColor = parameters.add ('c', "color" , "Set all leds to a constant color (either RRGGBB hex value or a color name)"); + ImageParameter & argImage = parameters.add ('i', "image" , "Set the leds to the colors according to the given image file"); + SwitchParameter<> & argList = parameters.add >('l', "list" , "List all priority channels which are in use"); + SwitchParameter<> & argClear = parameters.add >('x', "clear" , "Clear data for the priority channel provided by the -p option"); + SwitchParameter<> & argClearAll = parameters.add >(0x0, "clear-all" , "Clear data for all priority channels"); + TransformParameter & argGamma = parameters.add('g', "gamma" , "Set the gamma of the leds (requires 3 values)"); + TransformParameter & argThreshold = parameters.add('t', "threshold" , "Set the threshold of the leds (requires 3 space seperated values between 0.0 and 1.0)"); + TransformParameter & argBlacklevel = parameters.add('b', "blacklevel", "Set the blacklevel of the leds (requires 3 space seperated values which are normally between 0.0 and 1.0)"); + TransformParameter & argWhitelevel = parameters.add('w', "whitelevel", "Set the whitelevel of the leds (requires 3 space seperated values which are normally between 0.0 and 1.0)"); + SwitchParameter<> & argPrint = parameters.add >(0x0, "print" , "Print the json input and output messages on stdout"); + SwitchParameter<> & argHelp = parameters.add >('h', "help" , "Show this help message and exit"); - argAddress.setDefault(defaultServerAddress.toStdString()); - argPriority.setDefault(defaultPriority); - argDuration.setDefault(-1); + // set the default values + argAddress.setDefault(defaultServerAddress.toStdString()); + argPriority.setDefault(defaultPriority); + argDuration.setDefault(-1); - // parse the options - optionParser.parse(argc, const_cast(argv)); + // parse all options + optionParser.parse(argc, const_cast(argv)); - // check if we need to display the usage - if (argHelp.isSet()) - { - optionParser.usage(); - return 0; - } + // check if we need to display the usage. exit if we do. + if (argHelp.isSet()) + { + optionParser.usage(); + return 0; + } - // check if a color transform is set - bool colorTransform = argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet(); + // check if at least one of the available color transforms is set + bool colorTransform = argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet(); - // check if exactly one command was given - int commandCount = count({argColor.isSet(), argImage.isSet(), argList.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform}); - if (commandCount != 1) - { - if (commandCount == 0) - { - std::cerr << "No command found. Provide one of the following options:" << std::endl; - } - else - { - std::cerr << "Multiple commands found. Provide one of the following options:" << std::endl; - } - std::cerr << " " << argColor.usageLine() << std::endl; - std::cerr << " " << argImage.usageLine() << std::endl; - std::cerr << " " << argList.usageLine() << std::endl; - std::cerr << " " << argClear.usageLine() << std::endl; - std::cerr << " " << argClearAll.usageLine() << std::endl; - std::cerr << "or one or more of the color transformations:" << std::endl; - std::cerr << " " << argThreshold.usageLine() << std::endl; - std::cerr << " " << argGamma.usageLine() << std::endl; - std::cerr << " " << argBlacklevel.usageLine() << std::endl; - std::cerr << " " << argWhitelevel.usageLine() << std::endl; - return 1; - } + // check that exactly one command was given + int commandCount = count({argColor.isSet(), argImage.isSet(), argList.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform}); + if (commandCount != 1) + { + std::cerr << (commandCount == 0 ? "No command found." : "Multiple commands found.") << " Provide exactly one of the following options:" << std::endl; + std::cerr << " " << argColor.usageLine() << std::endl; + std::cerr << " " << argImage.usageLine() << std::endl; + std::cerr << " " << argList.usageLine() << std::endl; + std::cerr << " " << argClear.usageLine() << std::endl; + std::cerr << " " << argClearAll.usageLine() << std::endl; + std::cerr << "or one or more of the available color transformations:" << std::endl; + std::cerr << " " << argThreshold.usageLine() << std::endl; + std::cerr << " " << argGamma.usageLine() << std::endl; + std::cerr << " " << argBlacklevel.usageLine() << std::endl; + std::cerr << " " << argWhitelevel.usageLine() << std::endl; + return 1; + } - // create the connection - Connection connection(argAddress.getValue(), argPrint.isSet()); + // create the connection to the hyperion server + Connection connection(argAddress.getValue(), argPrint.isSet()); - // now execute the given command - if (argColor.isSet()) - { - connection.setColor(argColor.getValue(), argPriority.getValue(), argDuration.getValue()); - } - else if (argImage.isSet()) - { - connection.setImage(argImage.getValue(), argPriority.getValue(), argDuration.getValue()); - } - else if (argList.isSet()) - { - connection.listPriorities(); - } - else if (argClear.isSet()) - { - connection.clear(argPriority.getValue()); - } - else if (argClearAll.isSet()) - { - connection.clearAll(); - } - else if (colorTransform) - { - ColorTransformValues threshold = argThreshold.getValue(); - ColorTransformValues gamma = argGamma.getValue(); - ColorTransformValues blacklevel = argBlacklevel.getValue(); - ColorTransformValues whitelevel = argWhitelevel.getValue(); + // now execute the given command + if (argColor.isSet()) + { + connection.setColor(argColor.getValue(), argPriority.getValue(), argDuration.getValue()); + } + else if (argImage.isSet()) + { + connection.setImage(argImage.getValue(), argPriority.getValue(), argDuration.getValue()); + } + else if (argList.isSet()) + { + connection.listPriorities(); + } + else if (argClear.isSet()) + { + connection.clear(argPriority.getValue()); + } + else if (argClearAll.isSet()) + { + connection.clearAll(); + } + else if (colorTransform) + { + ColorTransformValues threshold = argThreshold.getValue(); + ColorTransformValues gamma = argGamma.getValue(); + ColorTransformValues blacklevel = argBlacklevel.getValue(); + ColorTransformValues whitelevel = argWhitelevel.getValue(); - connection.setTransform( - argThreshold.isSet() ? &threshold : nullptr, - argGamma.isSet() ? &gamma : nullptr, - argBlacklevel.isSet() ? &blacklevel : nullptr, - argWhitelevel.isSet() ? &whitelevel : nullptr); - } - } - catch (const std::runtime_error & e) - { - std::cerr << e.what() << std::endl; - return 1; - } + connection.setTransform( + argThreshold.isSet() ? &threshold : nullptr, + argGamma.isSet() ? &gamma : nullptr, + argBlacklevel.isSet() ? &blacklevel : nullptr, + argWhitelevel.isSet() ? &whitelevel : nullptr); + } + } + catch (const std::runtime_error & e) + { + // An error occured. Display error and quit + std::cerr << e.what() << std::endl; + return 1; + } - return 0; + return 0; }