From 2c97353a11fa8714ea01160646681916aeb6adfa Mon Sep 17 00:00:00 2001 From: johan Date: Sat, 17 Aug 2013 11:54:16 +0200 Subject: [PATCH] implemented some more commands --- src/hyperion-remote/CMakeLists.txt | 4 +- src/hyperion-remote/Connection.cpp | 186 ------------ src/hyperion-remote/JsonConnection.cpp | 283 ++++++++++++++++++ .../{Connection.h => JsonConnection.h} | 19 +- src/hyperion-remote/hyperion-remote.cpp | 27 +- 5 files changed, 310 insertions(+), 209 deletions(-) delete mode 100644 src/hyperion-remote/Connection.cpp create mode 100644 src/hyperion-remote/JsonConnection.cpp rename src/hyperion-remote/{Connection.h => JsonConnection.h} (73%) diff --git a/src/hyperion-remote/CMakeLists.txt b/src/hyperion-remote/CMakeLists.txt index a9291ba4..4a52a29d 100644 --- a/src/hyperion-remote/CMakeLists.txt +++ b/src/hyperion-remote/CMakeLists.txt @@ -13,12 +13,12 @@ include_directories(${QT_INCLUDES}) set(hyperion-remote_HEADERS CustomParameter.h - Connection.h + JsonConnection.h ColorTransformValues.h) set(hyperion-remote_SOURCES hyperion-remote.cpp - Connection.cpp) + JsonConnection.cpp) add_executable(hyperion-remote ${hyperion-remote_HEADERS} diff --git a/src/hyperion-remote/Connection.cpp b/src/hyperion-remote/Connection.cpp deleted file mode 100644 index 8580aa68..00000000 --- a/src/hyperion-remote/Connection.cpp +++ /dev/null @@ -1,186 +0,0 @@ -// stl includes -#include - -// hyperion-remote includes -#include "Connection.h" - -Connection::Connection(const std::string & a, bool printJson) : - _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()); - } - - 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"); - } - - std::cout << "Connected to " << a << std::endl; -} - -Connection::~Connection() -{ - _socket.close(); -} - -bool Connection::setColor(QColor color, int priority, int duration) -{ - std::cout << "Set color to " << color.red() << " " << color.green() << " " << color.blue() << std::endl; - - // create command - Json::Value command; - command["command"] = "color"; - command["priority"] = priority; - Json::Value & rgbValue = command["color"]; - rgbValue[0] = color.red(); - rgbValue[1] = color.green(); - rgbValue[2] = color.blue(); - if (duration > 0) - { - command["duration"] = duration; - } - - // send command message - Json::Value reply = sendMessage(command); - - // parse reply message - return parseReply(reply); -} - -bool Connection::setImage(QImage image, int priority, int duration) -{ - std::cout << "Set image has size: " << image.width() << "x" << image.height() << std::endl; - - // ensure the image has RGB888 format - image = image.convertToFormat(QImage::Format_RGB888); - QByteArray binaryImage; - binaryImage.reserve(image.width() * image.height() * 3); - for (int i = 0; i < image.height(); ++i) - { - binaryImage.append(reinterpret_cast(image.constScanLine(i)), image.width() * 3); - } - const QByteArray base64Image = binaryImage.toBase64(); - - // create command - Json::Value command; - command["command"] = "image"; - command["priority"] = priority; - command["width"] = image.width(); - command["height"] = image.height(); - command["data"] = std::string(base64Image.data(), base64Image.size()); - if (duration > 0) - { - command["duration"] = duration; - } - - // send command message - Json::Value reply = sendMessage(command); - - // parse reply message - return parseReply(reply); -} - - -bool Connection::listPriorities() -{ - std::cout << "List priority channels" << std::endl; - return false; -} - -bool Connection::clear(int priority) -{ - std::cout << "Clear priority channel " << priority << std::endl; - return false; -} - -bool Connection::clearAll() -{ - 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; -} - -Json::Value Connection::sendMessage(const Json::Value & message) -{ - // print command if requested - if (_printJson) - { - std::cout << "Command: " << message << std::endl; - } - - // 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"); - } - - // 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: invalid json"); - } - - // print reply if requested - if (_printJson) - { - std::cout << "Reply:" << reply << std::endl; - } - - return reply; -} - -bool Connection::parseReply(const Json::Value &reply) -{ - bool success = false; - std::string reason = "No error info"; - - try - { - success = reply.get("success", false).asBool(); - if (!success) - reason = reply.get("error", reason).asString(); - } - catch (const std::runtime_error &) - { - // Some json paring error: ignore and set parsing error - } - - if (!success) - { - throw std::runtime_error("Error while paring reply: " + reason); - } - - return success; -} diff --git a/src/hyperion-remote/JsonConnection.cpp b/src/hyperion-remote/JsonConnection.cpp new file mode 100644 index 00000000..17c7ffdb --- /dev/null +++ b/src/hyperion-remote/JsonConnection.cpp @@ -0,0 +1,283 @@ +// stl includes +#include + +// Qt includes +#include + +// hyperion-remote includes +#include "JsonConnection.h" + +JsonConnection::JsonConnection(const std::string & a, bool printJson) : + _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()); + } + + 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"); + } + + std::cout << "Connected to " << a << std::endl; +} + +JsonConnection::~JsonConnection() +{ + _socket.close(); +} + +void JsonConnection::setColor(QColor color, int priority, int duration) +{ + std::cout << "Set color to " << color.red() << " " << color.green() << " " << color.blue() << std::endl; + + // create command + Json::Value command; + command["command"] = "color"; + command["priority"] = priority; + Json::Value & rgbValue = command["color"]; + rgbValue[0] = color.red(); + rgbValue[1] = color.green(); + rgbValue[2] = color.blue(); + if (duration > 0) + { + command["duration"] = duration; + } + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + parseReply(reply); +} + +void JsonConnection::setImage(QImage image, int priority, int duration) +{ + std::cout << "Set image has size: " << image.width() << "x" << image.height() << std::endl; + + // ensure the image has RGB888 format + image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + QByteArray binaryImage; + binaryImage.reserve(image.width() * image.height() * 3); + for (int i = 0; i < image.height(); ++i) + { + const QRgb * scanline = reinterpret_cast(image.scanLine(i)); + for (int j = 0; j < image.width(); ++j) + { + binaryImage.append((char) qRed(scanline[j])); + binaryImage.append((char) qGreen(scanline[j])); + binaryImage.append((char) qBlue(scanline[j])); + } + } + const QByteArray base64Image = binaryImage.toBase64(); + + // create command + Json::Value command; + command["command"] = "image"; + command["priority"] = priority; + command["width"] = image.width(); + command["height"] = image.height(); + command["data"] = std::string(base64Image.data(), base64Image.size()); + if (duration > 0) + { + command["duration"] = duration; + } + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + parseReply(reply); +} + + +QString JsonConnection::getServerInfo() +{ + std::cout << "Get server info" << std::endl; + + // create command + Json::Value command; + command["command"] = "serverinfo"; + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + if (parseReply(reply)) + { + if (!reply.isMember("info") || !reply["info"].isObject()) + { + throw std::runtime_error("No info available in result"); + } + + const Json::Value & info = reply["info"]; + return QString(info.toStyledString().c_str()); + } + + return QString(); +} + +void JsonConnection::clear(int priority) +{ + std::cout << "Clear priority channel " << priority << std::endl; + + // create command + Json::Value command; + command["command"] = "clear"; + command["priority"] = priority; + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + parseReply(reply); +} + +void JsonConnection::clearAll() +{ + std::cout << "Clear all priority channels" << std::endl; + + // create command + Json::Value command; + command["command"] = "clearall"; + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + parseReply(reply); +} + +void JsonConnection::setTransform(ColorTransformValues *threshold, ColorTransformValues *gamma, ColorTransformValues *blacklevel, ColorTransformValues *whitelevel) +{ + std::cout << "Set color transforms" << std::endl; + + // create command + Json::Value command; + command["command"] = "transform"; + Json::Value & transform = command["transform"]; + + if (threshold != nullptr) + { + Json::Value & v = transform["threshold"]; + v.append(threshold->valueRed); + v.append(threshold->valueGreen); + v.append(threshold->valueBlue); + } + + if (gamma != nullptr) + { + Json::Value & v = transform["gamma"]; + v.append(threshold->valueRed); + v.append(threshold->valueGreen); + v.append(threshold->valueBlue); + } + + if (blacklevel != nullptr) + { + Json::Value & v = transform["blacklevel"]; + v.append(threshold->valueRed); + v.append(threshold->valueGreen); + v.append(threshold->valueBlue); + } + + if (whitelevel != nullptr) + { + Json::Value & v = transform["whitelevel"]; + v.append(threshold->valueRed); + v.append(threshold->valueGreen); + v.append(threshold->valueBlue); + } + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + parseReply(reply); +} + +Json::Value JsonConnection::sendMessage(const Json::Value & message) +{ + // print command if requested + if (_printJson) + { + std::cout << "Command: " << message << std::endl; + } + + // serialize message (FastWriter already appends a newline + 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"); + } + + // read reply data + QByteArray serializedReply; + while (!serializedReply.contains('\n')) + { + // receive reply + if (!_socket.waitForReadyRead()) + { + throw std::runtime_error("Error while reading data from host"); + } + + serializedReply += _socket.readAll(); + } + int bytes = serializedReply.indexOf('\n') + 1; // Find the end of message + + // parse reply data + Json::Reader jsonReader; + Json::Value reply; + if (!jsonReader.parse(serializedReply.constData(), serializedReply.constData() + bytes, reply)) + { + throw std::runtime_error("Error while parsing reply: invalid json"); + } + + // print reply if requested + if (_printJson) + { + std::cout << "Reply:" << reply << std::endl; + } + + return reply; +} + +bool JsonConnection::parseReply(const Json::Value &reply) +{ + bool success = false; + std::string reason = "No error info"; + + try + { + success = reply.get("success", false).asBool(); + if (!success) + reason = reply.get("error", reason).asString(); + } + catch (const std::runtime_error &) + { + // Some json paring error: ignore and set parsing error + } + + if (!success) + { + throw std::runtime_error("Error while paring reply: " + reason); + } + + return success; +} diff --git a/src/hyperion-remote/Connection.h b/src/hyperion-remote/JsonConnection.h similarity index 73% rename from src/hyperion-remote/Connection.h rename to src/hyperion-remote/JsonConnection.h index 93de4318..da4a7dc3 100644 --- a/src/hyperion-remote/Connection.h +++ b/src/hyperion-remote/JsonConnection.h @@ -7,6 +7,7 @@ #include #include #include +#include // jsoncpp includes #include @@ -15,30 +16,30 @@ #include "ColorTransformValues.h" /// Connection class to setup an connection to the hyperion server and execute commands -class Connection +class JsonConnection { public: - Connection(const std::string & address, bool printJson); - ~Connection(); + JsonConnection(const std::string & address, bool printJson); + ~JsonConnection(); /// Set all leds to the specified color - bool setColor(QColor color, int priority, int duration); + void 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); + void setImage(QImage image, int priority, int duration); /// Retrieve a list of all occupied priority channels - bool listPriorities(); + QString getServerInfo(); /// Clear the given priority channel - bool clear(int priority); + void clear(int priority); /// Clear all priority channels - bool clearAll(); + void 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); + void setTransform(ColorTransformValues * threshold, ColorTransformValues * gamma, ColorTransformValues * blacklevel, ColorTransformValues * whitelevel); private: /// Send a json command message and receive its reply diff --git a/src/hyperion-remote/hyperion-remote.cpp b/src/hyperion-remote/hyperion-remote.cpp index 1585ff07..56c98a95 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -9,7 +9,7 @@ // hyperion-remote include #include "CustomParameter.h" -#include "Connection.h" +#include "JsonConnection.h" using namespace vlofgren; @@ -42,10 +42,10 @@ int main(int argc, char * argv[]) 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<> & argServerInfo = parameters.add >('s', "info" , "List server info"); 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 & argGamma = parameters.add('g', "gamma" , "Set the gamma of the leds (requires 3 space seperated 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)"); @@ -71,13 +71,13 @@ int main(int argc, char * argv[]) bool colorTransform = argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet(); // check that exactly one command was given - int commandCount = count({argColor.isSet(), argImage.isSet(), argList.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform}); + int commandCount = count({argColor.isSet(), argImage.isSet(), argServerInfo.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 << " " << argServerInfo.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; @@ -89,7 +89,7 @@ int main(int argc, char * argv[]) } // create the connection to the hyperion server - Connection connection(argAddress.getValue(), argPrint.isSet()); + JsonConnection connection(argAddress.getValue(), argPrint.isSet()); // now execute the given command if (argColor.isSet()) @@ -100,9 +100,10 @@ int main(int argc, char * argv[]) { connection.setImage(argImage.getValue(), argPriority.getValue(), argDuration.getValue()); } - else if (argList.isSet()) + else if (argServerInfo.isSet()) { - connection.listPriorities(); + QString info = connection.getServerInfo(); + std::cout << "Server info:\n" << info.toStdString() << std::endl; } else if (argClear.isSet()) { @@ -114,10 +115,12 @@ int main(int argc, char * argv[]) } else if (colorTransform) { - ColorTransformValues threshold = argThreshold.getValue(); - ColorTransformValues gamma = argGamma.getValue(); - ColorTransformValues blacklevel = argBlacklevel.getValue(); - ColorTransformValues whitelevel = argWhitelevel.getValue(); + ColorTransformValues threshold, gamma, blacklevel, whitelevel; + + if (argThreshold.isSet()) threshold = argThreshold.getValue(); + if (argGamma.isSet()) gamma = argGamma.getValue(); + if (argBlacklevel.isSet()) blacklevel = argBlacklevel.getValue(); + if (argWhitelevel.isSet()) whitelevel = argWhitelevel.getValue(); connection.setTransform( argThreshold.isSet() ? &threshold : nullptr,