This commit is contained in:
T. van der Zwan 2013-08-21 20:46:43 +00:00
commit decc41965d
27 changed files with 913 additions and 164 deletions

View File

@ -12,11 +12,14 @@
}, },
"color" : "color" :
{ {
"hsv" : {
"saturationGain" : 1.0,
"valueGain" : 1.0
},
"red" : "red" :
{ {
"threshold" : 0.0, "threshold" : 0.0,
"gamma" : 1.0, "gamma" : 1.0,
"adjust" : 1.0,
"blacklevel" : 0.0, "blacklevel" : 0.0,
"whitelevel" : 1.0 "whitelevel" : 1.0
}, },
@ -24,7 +27,6 @@
{ {
"threshold" : 0.0, "threshold" : 0.0,
"gamma" : 1.0, "gamma" : 1.0,
"adjust" : 1.0,
"blacklevel" : 0.0, "blacklevel" : 0.0,
"whitelevel" : 1.0 "whitelevel" : 1.0
}, },
@ -32,7 +34,6 @@
{ {
"threshold" : 0.0, "threshold" : 0.0,
"gamma" : 1.0, "gamma" : 1.0,
"adjust" : 1.0,
"blacklevel" : 0.0, "blacklevel" : 0.0,
"whitelevel" : 1.0 "whitelevel" : 1.0
} }

View File

@ -39,7 +39,7 @@ public slots:
private: private:
const int _updateInterval_ms; const int _updateInterval_ms;
const int _timeout_ms; const int _timeout_ms;
const int _priority;
QTimer _timer; QTimer _timer;

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
// stl includes
#include <list>
// QT includes // QT includes
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
@ -14,13 +16,27 @@
#include <hyperion/PriorityMuxer.h> #include <hyperion/PriorityMuxer.h>
// Forward class declaration // Forward class declaration
namespace hyperion { class ColorTransform; } namespace hyperion {
class HsvTransform;
class ColorTransform;
}
class Hyperion : public QObject class Hyperion : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
typedef PriorityMuxer::InputInfo InputInfo;
enum Color
{
RED, GREEN, BLUE, INVALID
};
enum Transform
{
SATURATION_GAIN, VALUE_GAIN, THRESHOLD, GAMMA, BLACKLEVEL, WHITELEVEL
};
static LedString createLedString(const Json::Value& ledsConfig); static LedString createLedString(const Json::Value& ledsConfig);
static Json::Value loadConfig(const std::string& configFile); static Json::Value loadConfig(const std::string& configFile);
@ -32,7 +48,21 @@ public:
unsigned getLedCount() const; unsigned getLedCount() const;
void setValue(int priority, std::vector<RgbColor> &ledColors, const int timeout_ms); void setColor(int priority, RgbColor &ledColor, const int timeout_ms);
void setColors(int priority, std::vector<RgbColor> &ledColors, const int timeout_ms);
void setTransform(Transform transform, Color color, double value);
void clear(int priority);
void clearall();
double getTransform(Transform transform, Color color) const;
QList<int> getActivePriorities() const;
const InputInfo& getPriorityInfo(const int priority) const;
private slots: private slots:
void update(); void update();
@ -40,15 +70,16 @@ private slots:
private: private:
void applyTransform(std::vector<RgbColor>& colors) const; void applyTransform(std::vector<RgbColor>& colors) const;
LedString mLedString; LedString _ledString;
PriorityMuxer mMuxer; PriorityMuxer _muxer;
hyperion::ColorTransform* mRedTransform; hyperion::HsvTransform * _hsvTransform;
hyperion::ColorTransform* mGreenTransform; hyperion::ColorTransform * _redTransform;
hyperion::ColorTransform* mBlueTransform; hyperion::ColorTransform * _greenTransform;
hyperion::ColorTransform * _blueTransform;
LedDevice* mDevice; LedDevice* _device;
QTimer _timer; QTimer _timer;
}; };

View File

@ -8,8 +8,7 @@
#include <hyperion/ImageProcessorFactory.h> #include <hyperion/ImageProcessorFactory.h>
// Forward class declaration // Forward class declaration
namespace hyperion { class ImageToLedsMap; namespace hyperion { class ImageToLedsMap; }
class ColorTransform; }
/** /**
* The ImageProcessor translates an RGB-image to RGB-values for the leds. The processing is * The ImageProcessor translates an RGB-image to RGB-values for the leds. The processing is

View File

@ -26,7 +26,7 @@ public:
std::vector<RgbColor> ledColors; std::vector<RgbColor> ledColors;
}; };
PriorityMuxer(); PriorityMuxer(int ledCount);
~PriorityMuxer(); ~PriorityMuxer();
@ -47,9 +47,11 @@ public:
void setCurrentTime(const int64_t& now); void setCurrentTime(const int64_t& now);
private: private:
int mCurrentPriority; int _currentPriority;
QMap<int, InputInfo> mActiveInputs; QMap<int, InputInfo> _activeInputs;
const static int MAX_PRIORITY = std::numeric_limits<int>::max(); InputInfo _lowestPriorityInfo;
const static int LOWEST_PRIORITY = std::numeric_limits<int>::max();
}; };

View File

@ -15,6 +15,7 @@
DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) : DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) :
_updateInterval_ms(1000/updateRate_Hz), _updateInterval_ms(1000/updateRate_Hz),
_timeout_ms(2 * _updateInterval_ms), _timeout_ms(2 * _updateInterval_ms),
_priority(128),
_timer(), _timer(),
_image(grabWidth, grabHeight), _image(grabWidth, grabHeight),
_frameGrabber(new DispmanxFrameGrabber(grabWidth, grabHeight)), _frameGrabber(new DispmanxFrameGrabber(grabWidth, grabHeight)),
@ -52,8 +53,7 @@ void DispmanxWrapper::action()
_processor->process(_image, _ledColors); _processor->process(_image, _ledColors);
const int _priority = 100; _hyperion->setColors(_priority, _ledColors, _timeout_ms);
_hyperion->setValue(_priority, _ledColors, _timeout_ms);
} }
void DispmanxWrapper::stop() void DispmanxWrapper::stop()
{ {

View File

@ -21,6 +21,7 @@ SET(Hyperion_HEADERS
${CURRENT_SOURCE_DIR}/BlackBorderProcessor.h ${CURRENT_SOURCE_DIR}/BlackBorderProcessor.h
${CURRENT_SOURCE_DIR}/BlackBorderDetector.h ${CURRENT_SOURCE_DIR}/BlackBorderDetector.h
${CURRENT_SOURCE_DIR}/ColorTransform.h ${CURRENT_SOURCE_DIR}/ColorTransform.h
${CURRENT_SOURCE_DIR}/HsvTransform.h
) )
SET(Hyperion_SOURCES SET(Hyperion_SOURCES
@ -36,6 +37,7 @@ SET(Hyperion_SOURCES
${CURRENT_SOURCE_DIR}/BlackBorderProcessor.cpp ${CURRENT_SOURCE_DIR}/BlackBorderProcessor.cpp
${CURRENT_SOURCE_DIR}/BlackBorderDetector.cpp ${CURRENT_SOURCE_DIR}/BlackBorderDetector.cpp
${CURRENT_SOURCE_DIR}/ColorTransform.cpp ${CURRENT_SOURCE_DIR}/ColorTransform.cpp
${CURRENT_SOURCE_DIR}/HsvTransform.cpp
) )
set(Hyperion_RESOURCES set(Hyperion_RESOURCES

View File

@ -0,0 +1,134 @@
#include "HsvTransform.h"
using namespace hyperion;
HsvTransform::HsvTransform() :
_saturationGain(1.0),
_valueGain(1.0)
{
}
HsvTransform::HsvTransform(double saturationGain, double valueGain) :
_saturationGain(saturationGain),
_valueGain(valueGain)
{
}
HsvTransform::~HsvTransform()
{
}
void HsvTransform::setSaturationGain(double saturationGain)
{
_saturationGain = saturationGain;
}
double HsvTransform::getSaturationGain() const
{
return _saturationGain;
}
void HsvTransform::setValueGain(double valueGain)
{
_valueGain = valueGain;
}
double HsvTransform::getValueGain() const
{
return _valueGain;
}
void HsvTransform::transform(uint8_t & red, uint8_t & green, uint8_t & blue) const
{
if (_saturationGain != 1.0 || _valueGain != 1.0)
{
uint8_t hue, saturation, value;
rgb2hsv(red, green, blue, hue, saturation, value);
int s = saturation * _saturationGain;
if (s > 255)
saturation = 255;
else
saturation = s;
int v = value * _valueGain;
if (v > 255)
value = 255;
else
value = v;
hsv2rgb(hue, saturation, value, red, green, blue);
}
}
void HsvTransform::rgb2hsv(uint8_t red, uint8_t green, uint8_t blue, uint8_t & hue, uint8_t & saturation, uint8_t & value)
{
uint8_t rgbMin, rgbMax;
rgbMin = red < green ? (red < blue ? red : blue) : (green < blue ? green : blue);
rgbMax = red > green ? (red > blue ? red : blue) : (green > blue ? green : blue);
value = rgbMax;
if (value == 0)
{
hue = 0;
saturation = 0;
return;
}
saturation = 255 * long(rgbMax - rgbMin) / value;
if (saturation == 0)
{
hue = 0;
return;
}
if (rgbMax == red)
hue = 0 + 43 * (green - blue) / (rgbMax - rgbMin);
else if (rgbMax == green)
hue = 85 + 43 * (blue - red) / (rgbMax - rgbMin);
else
hue = 171 + 43 * (red - green) / (rgbMax - rgbMin);
}
void HsvTransform::hsv2rgb(uint8_t hue, uint8_t saturation, uint8_t value, uint8_t & red, uint8_t & green, uint8_t & blue)
{
uint8_t region, remainder, p, q, t;
if (saturation == 0)
{
red = value;
green = value;
blue = value;
return;
}
region = hue / 43;
remainder = (hue - (region * 43)) * 6;
p = (value * (255 - saturation)) >> 8;
q = (value * (255 - ((saturation * remainder) >> 8))) >> 8;
t = (value * (255 - ((saturation * (255 - remainder)) >> 8))) >> 8;
switch (region)
{
case 0:
red = value; green = t; blue = p;
break;
case 1:
red = q; green = value; blue = p;
break;
case 2:
red = p; green = value; blue = t;
break;
case 3:
red = p; green = q; blue = value;
break;
case 4:
red = t; green = p; blue = value;
break;
default:
red = value; green = p; blue = q;
break;
}
}

View File

@ -0,0 +1,33 @@
#pragma once
#include <cstdint>
namespace hyperion
{
class HsvTransform
{
public:
HsvTransform();
HsvTransform(double saturationGain, double valueGain);
~HsvTransform();
void setSaturationGain(double saturationGain);
double getSaturationGain() const;
void setValueGain(double valueGain);
double getValueGain() const;
void transform(uint8_t & red, uint8_t & green, uint8_t & blue) const;
private:
// integer version of the conversion are faster, but a little less accurate
static void rgb2hsv(uint8_t red, uint8_t green, uint8_t blue, uint8_t & hue, uint8_t & saturation, uint8_t & value);
static void hsv2rgb(uint8_t hue, uint8_t saturation, uint8_t value, uint8_t & red, uint8_t & green, uint8_t & blue);
private:
double _saturationGain;
double _valueGain;
};
} // namespace hyperion

View File

@ -14,6 +14,7 @@
#include "LedDeviceWs2801.h" #include "LedDeviceWs2801.h"
#include "LedDeviceTest.h" #include "LedDeviceTest.h"
#include "ColorTransform.h" #include "ColorTransform.h"
#include "HsvTransform.h"
using namespace hyperion; using namespace hyperion;
@ -44,6 +45,11 @@ LedDevice* constructDevice(const Json::Value& deviceConfig)
return device; return device;
} }
HsvTransform * createHsvTransform(const Json::Value & hsvConfig)
{
return new HsvTransform(hsvConfig["saturationGain"].asDouble(), hsvConfig["valueGain"].asDouble());
}
ColorTransform* createColorTransform(const Json::Value& colorConfig) ColorTransform* createColorTransform(const Json::Value& colorConfig)
{ {
const double threshold = colorConfig["threshold"].asDouble(); const double threshold = colorConfig["threshold"].asDouble();
@ -102,74 +108,221 @@ Hyperion::Hyperion(const std::string& configFile) :
} }
Hyperion::Hyperion(const Json::Value &jsonConfig) : Hyperion::Hyperion(const Json::Value &jsonConfig) :
mLedString(createLedString(jsonConfig["leds"])), _ledString(createLedString(jsonConfig["leds"])),
mRedTransform( createColorTransform(jsonConfig["color"]["red"])), _muxer(_ledString.leds().size()),
mGreenTransform(createColorTransform(jsonConfig["color"]["green"])), _hsvTransform(createHsvTransform(jsonConfig["color"]["hsv"])),
mBlueTransform( createColorTransform(jsonConfig["color"]["blue"])), _redTransform(createColorTransform(jsonConfig["color"]["red"])),
mDevice(constructDevice(jsonConfig["device"])), _greenTransform(createColorTransform(jsonConfig["color"]["green"])),
_blueTransform(createColorTransform(jsonConfig["color"]["blue"])),
_device(constructDevice(jsonConfig["device"])),
_timer() _timer()
{ {
ImageProcessorFactory::getInstance().init(mLedString); ImageProcessorFactory::getInstance().init(_ledString);
_timer.setSingleShot(true); _timer.setSingleShot(true);
QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update())); QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
// initialize the leds
update();
} }
Hyperion::~Hyperion() Hyperion::~Hyperion()
{ {
// Delete the Led-String // Delete the Led-String
delete mDevice; delete _device;
// delete he hsv transform
delete _hsvTransform;
// Delete the color-transform // Delete the color-transform
delete mBlueTransform; delete _blueTransform;
delete mGreenTransform; delete _greenTransform;
delete mRedTransform; delete _redTransform;
} }
unsigned Hyperion::getLedCount() const unsigned Hyperion::getLedCount() const
{ {
return mLedString.leds().size(); return _ledString.leds().size();
} }
void Hyperion::setValue(int priority, std::vector<RgbColor>& ledColors, const int timeout_ms) void Hyperion::setColor(int priority, RgbColor & color, const int timeout_ms)
{ {
// Apply the transform to each led and color-channel // create led output
for (RgbColor& color : ledColors) std::vector<RgbColor> ledColors(_ledString.leds().size(), color);
{
color.red = mRedTransform->transform(color.red);
color.green = mGreenTransform->transform(color.green);
color.blue = mBlueTransform->transform(color.blue);
}
// set colors
setColors(priority, ledColors, timeout_ms);
}
void Hyperion::setColors(int priority, std::vector<RgbColor>& ledColors, const int timeout_ms)
{
if (timeout_ms > 0) if (timeout_ms > 0)
{ {
const uint64_t timeoutTime = QDateTime::currentMSecsSinceEpoch() + timeout_ms; const uint64_t timeoutTime = QDateTime::currentMSecsSinceEpoch() + timeout_ms;
mMuxer.setInput(priority, ledColors, timeoutTime); _muxer.setInput(priority, ledColors, timeoutTime);
} }
else else
{ {
mMuxer.setInput(priority, ledColors); _muxer.setInput(priority, ledColors);
} }
if (priority == mMuxer.getCurrentPriority()) if (priority == _muxer.getCurrentPriority())
{ {
update(); update();
} }
} }
void Hyperion::setTransform(Hyperion::Transform transform, Hyperion::Color color, double value)
{
// select the transform of the requested color
ColorTransform * t = nullptr;
switch (color)
{
case RED:
t = _redTransform;
break;
case GREEN:
t = _greenTransform;
break;
case BLUE:
t = _blueTransform;
break;
default:
break;
}
// set transform value
switch (transform)
{
case SATURATION_GAIN:
_hsvTransform->setSaturationGain(value);
break;
case VALUE_GAIN:
_hsvTransform->setValueGain(value);
break;
case THRESHOLD:
assert (t != nullptr);
t->setThreshold(value);
break;
case GAMMA:
assert (t != nullptr);
t->setGamma(value);
break;
case BLACKLEVEL:
assert (t != nullptr);
t->setBlacklevel(value);
break;
case WHITELEVEL:
assert (t != nullptr);
t->setWhitelevel(value);
break;
default:
assert(false);
}
// update the led output
update();
}
void Hyperion::clear(int priority)
{
if (_muxer.hasPriority(priority))
{
_muxer.clearInput(priority);
// update leds if necessary
if (priority < _muxer.getCurrentPriority());
{
update();
}
}
}
void Hyperion::clearall()
{
_muxer.clearAll();
// update leds
update();
}
double Hyperion::getTransform(Hyperion::Transform transform, Hyperion::Color color) const
{
// select the transform of the requested color
ColorTransform * t = nullptr;
switch (color)
{
case RED:
t = _redTransform;
break;
case GREEN:
t = _greenTransform;
break;
case BLUE:
t = _blueTransform;
break;
default:
break;
}
// set transform value
switch (transform)
{
case SATURATION_GAIN:
return _hsvTransform->getSaturationGain();
case VALUE_GAIN:
return _hsvTransform->getValueGain();
case THRESHOLD:
assert (t != nullptr);
return t->getThreshold();
case GAMMA:
assert (t != nullptr);
return t->getGamma();
case BLACKLEVEL:
assert (t != nullptr);
return t->getBlacklevel();
case WHITELEVEL:
assert (t != nullptr);
return t->getWhitelevel();
default:
assert(false);
}
return 999.0;
}
QList<int> Hyperion::getActivePriorities() const
{
return _muxer.getPriorities();
}
const Hyperion::InputInfo &Hyperion::getPriorityInfo(const int priority) const
{
return _muxer.getInputInfo(priority);
}
void Hyperion::update() void Hyperion::update()
{ {
// Update the muxer, cleaning obsolete priorities // Update the muxer, cleaning obsolete priorities
mMuxer.setCurrentTime(QDateTime::currentMSecsSinceEpoch()); _muxer.setCurrentTime(QDateTime::currentMSecsSinceEpoch());
// Obtain the current priority channel // Obtain the current priority channel
int priority = mMuxer.getCurrentPriority(); int priority = _muxer.getCurrentPriority();
const PriorityMuxer::InputInfo & priorityInfo = mMuxer.getInputInfo(priority); const PriorityMuxer::InputInfo & priorityInfo = _muxer.getInputInfo(priority);
// Apply the transform to each led and color-channel
std::vector<RgbColor> ledColors(priorityInfo.ledColors);
for (RgbColor& color : ledColors)
{
_hsvTransform->transform(color.red, color.green, color.blue);
color.red = _redTransform->transform(color.red);
color.green = _greenTransform->transform(color.green);
color.blue = _blueTransform->transform(color.blue);
}
// Write the data to the device // Write the data to the device
mDevice->write(priorityInfo.ledColors); _device->write(ledColors);
// Start the timeout-timer // Start the timeout-timer
if (priorityInfo.timeoutTime_ms == -1) if (priorityInfo.timeoutTime_ms == -1)

View File

@ -6,10 +6,14 @@
// Hyperion includes // Hyperion includes
#include <hyperion/PriorityMuxer.h> #include <hyperion/PriorityMuxer.h>
PriorityMuxer::PriorityMuxer() : PriorityMuxer::PriorityMuxer(int ledCount) :
mCurrentPriority(MAX_PRIORITY) _currentPriority(LOWEST_PRIORITY),
_activeInputs(),
_lowestPriorityInfo()
{ {
// empty _lowestPriorityInfo.priority = LOWEST_PRIORITY;
_lowestPriorityInfo.timeoutTime_ms = -1;
_lowestPriorityInfo.ledColors = std::vector<RgbColor>(ledCount, {0, 0, 0});
} }
PriorityMuxer::~PriorityMuxer() PriorityMuxer::~PriorityMuxer()
@ -19,23 +23,28 @@ PriorityMuxer::~PriorityMuxer()
int PriorityMuxer::getCurrentPriority() const int PriorityMuxer::getCurrentPriority() const
{ {
return mCurrentPriority; return _currentPriority;
} }
QList<int> PriorityMuxer::getPriorities() const QList<int> PriorityMuxer::getPriorities() const
{ {
return mActiveInputs.keys(); return _activeInputs.keys();
} }
bool PriorityMuxer::hasPriority(const int priority) const bool PriorityMuxer::hasPriority(const int priority) const
{ {
return mActiveInputs.contains(priority); return _activeInputs.contains(priority);
} }
const PriorityMuxer::InputInfo& PriorityMuxer::getInputInfo(const int priority) const const PriorityMuxer::InputInfo& PriorityMuxer::getInputInfo(const int priority) const
{ {
auto elemIt = mActiveInputs.find(priority); if (priority == LOWEST_PRIORITY)
if (elemIt == mActiveInputs.end()) {
return _lowestPriorityInfo;
}
auto elemIt = _activeInputs.find(priority);
if (elemIt == _activeInputs.end())
{ {
throw std::runtime_error("no such priority"); throw std::runtime_error("no such priority");
} }
@ -44,50 +53,50 @@ const PriorityMuxer::InputInfo& PriorityMuxer::getInputInfo(const int priority)
void PriorityMuxer::setInput(const int priority, const std::vector<RgbColor>& ledColors, const int64_t timeoutTime_ms) void PriorityMuxer::setInput(const int priority, const std::vector<RgbColor>& ledColors, const int64_t timeoutTime_ms)
{ {
InputInfo& input = mActiveInputs[priority]; InputInfo& input = _activeInputs[priority];
input.priority = priority; input.priority = priority;
input.timeoutTime_ms = timeoutTime_ms; input.timeoutTime_ms = timeoutTime_ms;
input.ledColors = ledColors; input.ledColors = ledColors;
mCurrentPriority = std::min(mCurrentPriority, priority); _currentPriority = std::min(_currentPriority, priority);
} }
void PriorityMuxer::clearInput(const int priority) void PriorityMuxer::clearInput(const int priority)
{ {
mActiveInputs.remove(priority); _activeInputs.remove(priority);
if (mCurrentPriority == priority) if (_currentPriority == priority)
{ {
if (mActiveInputs.empty()) if (_activeInputs.empty())
{ {
mCurrentPriority = MAX_PRIORITY; _currentPriority = LOWEST_PRIORITY;
} }
else else
{ {
QList<int> keys = mActiveInputs.keys(); QList<int> keys = _activeInputs.keys();
mCurrentPriority = *std::min_element(keys.begin(), keys.end()); _currentPriority = *std::min_element(keys.begin(), keys.end());
} }
} }
} }
void PriorityMuxer::clearAll() void PriorityMuxer::clearAll()
{ {
mActiveInputs.clear(); _activeInputs.clear();
mCurrentPriority = MAX_PRIORITY; _currentPriority = LOWEST_PRIORITY;
} }
void PriorityMuxer::setCurrentTime(const int64_t& now) void PriorityMuxer::setCurrentTime(const int64_t& now)
{ {
mCurrentPriority = MAX_PRIORITY; _currentPriority = LOWEST_PRIORITY;
for (auto infoIt = mActiveInputs.begin(); infoIt != mActiveInputs.end();) for (auto infoIt = _activeInputs.begin(); infoIt != _activeInputs.end();)
{ {
if (infoIt->timeoutTime_ms != -1 && infoIt->timeoutTime_ms <= now) if (infoIt->timeoutTime_ms != -1 && infoIt->timeoutTime_ms <= now)
{ {
infoIt = mActiveInputs.erase(infoIt); infoIt = _activeInputs.erase(infoIt);
} }
else else
{ {
mCurrentPriority = std::min(mCurrentPriority, infoIt->priority); _currentPriority = std::min(_currentPriority, infoIt->priority);
++infoIt; ++infoIt;
} }
} }

View File

@ -33,6 +33,23 @@
"type":"object", "type":"object",
"required":true, "required":true,
"properties": { "properties": {
"hsv" : {
"type" : "object",
"required" : true,
"properties" : {
"saturationGain" : {
"type" : "number",
"required" : true,
"minimum" : 0.0
},
"valueGain" : {
"type" : "number",
"required" : true,
"minimum" : 0.0
}
},
"additionalProperties" : false
},
"red": { "red": {
"type":"object", "type":"object",
"required":true, "required":true,
@ -41,10 +58,6 @@
"type":"number", "type":"number",
"required":true "required":true
}, },
"adjust": {
"type":"number",
"required":true
},
"blacklevel": { "blacklevel": {
"type":"number", "type":"number",
"required":true "required":true
@ -55,7 +68,9 @@
}, },
"threshold": { "threshold": {
"type":"number", "type":"number",
"required":true "required":true,
"minimum" : 0.0,
"maximum" : 1.0
} }
} }
}, },
@ -67,13 +82,19 @@
"type":"number", "type":"number",
"required":true "required":true
}, },
"adjust": {
"type":"number",
"required":true
},
"blacklevel": { "blacklevel": {
"type":"number", "type":"number",
"required":true "required":true
},
"whitelevel": {
"type":"number",
"required":true
},
"threshold": {
"type":"number",
"required":true,
"minimum" : 0.0,
"maximum" : 1.0
} }
} }
}, },
@ -85,13 +106,19 @@
"type":"number", "type":"number",
"required":true "required":true
}, },
"adjust": { "whitelevel": {
"type":"number", "type":"number",
"required":true "required":true
}, },
"blacklevel": { "blacklevel": {
"type":"number", "type":"number",
"required":true "required":true
},
"threshold": {
"type":"number",
"required":true,
"minimum" : 0.0,
"maximum" : 1.0
} }
} }
} }

View File

@ -9,27 +9,23 @@
// Qt includes // Qt includes
#include <QResource> #include <QResource>
#include <QDateTime>
// hyperion util includes
#include "hyperion/ImageProcessorFactory.h"
#include "hyperion/ImageProcessor.h"
#include "utils/RgbColor.h"
// project includes // project includes
#include "JsonClientConnection.h" #include "JsonClientConnection.h"
JsonClientConnection::JsonClientConnection(QTcpSocket *socket) : JsonClientConnection::JsonClientConnection(QTcpSocket *socket, Hyperion * hyperion) :
QObject(), QObject(),
_socket(socket), _socket(socket),
_schemaChecker(), _imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()),
_hyperion(hyperion),
_receiveBuffer() _receiveBuffer()
{ {
// read the json schema from the resource
QResource schemaData(":/schema.json");
assert(schemaData.isValid());
Json::Reader jsonReader;
Json::Value schemaJson;
if (!jsonReader.parse(reinterpret_cast<const char *>(schemaData.data()), reinterpret_cast<const char *>(schemaData.data()) + schemaData.size(), schemaJson, false))
{
throw std::runtime_error("Schema error: " + jsonReader.getFormattedErrorMessages()) ;
}
_schemaChecker.setSchema(schemaJson);
// connect internal signals and slots // connect internal signals and slots
connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed())); connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
connect(_socket, SIGNAL(readyRead()), this, SLOT(readData())); connect(_socket, SIGNAL(readyRead()), this, SLOT(readData()));
@ -64,33 +60,211 @@ void JsonClientConnection::socketClosed()
emit connectionClosed(this); emit connectionClosed(this);
} }
void JsonClientConnection::handleMessage(const std::string &message) void JsonClientConnection::handleMessage(const std::string &messageString)
{ {
Json::Reader reader; Json::Reader reader;
Json::Value messageRoot; Json::Value message;
if (!reader.parse(message, messageRoot, false)) if (!reader.parse(messageString, message, false))
{ {
sendErrorReply("Error while parsing json: " + reader.getFormattedErrorMessages()); sendErrorReply("Error while parsing json: " + reader.getFormattedErrorMessages());
return; return;
} }
if (!_schemaChecker.validate(messageRoot)) // check basic message
std::string errors;
if (!checkJson(message, ":schema", errors))
{ {
const std::list<std::string> & errors = _schemaChecker.getMessages(); sendErrorReply("Error while validating json: " + errors);
std::stringstream ss;
ss << "Error while validating json: {";
foreach (const std::string & error, errors) {
ss << error << ", ";
}
ss << "}";
sendErrorReply(ss.str());
return; return;
} }
handleNotImplemented(messageRoot); // check specific message
const std::string command = message["command"].asString();
if (!checkJson(message, QString(":schema-%1").arg(QString::fromStdString(command)), errors))
{
sendErrorReply("Error while validating json: " + errors);
return;
}
// switch over all possible commands and handle them
if (command == "color")
handleColorCommand(message);
else if (command == "image")
handleImageCommand(message);
else if (command == "serverinfo")
handleServerInfoCommand(message);
else if (command == "clear")
handleClearCommand(message);
else if (command == "clearall")
handleClearallCommand(message);
else if (command == "transform")
handleTransformCommand(message);
else
handleNotImplemented();
} }
void JsonClientConnection::handleNotImplemented(const Json::Value & message) void JsonClientConnection::handleColorCommand(const Json::Value &message)
{
// extract parameters
int priority = message["priority"].asInt();
int duration = message.get("duration", -1).asInt();
RgbColor color = {uint8_t(message["color"][0u].asInt()), uint8_t(message["color"][1u].asInt()), uint8_t(message["color"][2u].asInt())};
// set output
_hyperion->setColor(priority, color, duration);
// send reply
sendSuccessReply();
}
void JsonClientConnection::handleImageCommand(const Json::Value &message)
{
// extract parameters
int priority = message["priority"].asInt();
int duration = message.get("duration", -1).asInt();
int width = message["imagewidth"].asInt();
int height = message["imageheight"].asInt();
QByteArray data = QByteArray::fromBase64(QByteArray(message["imagedata"].asCString()));
// check consistency of the size of the received data
if (data.size() != width*height*3)
{
sendErrorReply("Size of image data does not match with the width and height");
return;
}
// set width and height of the image processor
_imageProcessor->setSize(width, height);
// create RgbImage
RgbImage image(width, height);
memcpy(image.memptr(), data.data(), data.size());
// process the image
std::vector<RgbColor> ledColors = _imageProcessor->process(image);
_hyperion->setColors(priority, ledColors, duration);
// send reply
sendSuccessReply();
}
void JsonClientConnection::handleServerInfoCommand(const Json::Value &message)
{
// create result
Json::Value result;
result["success"] = true;
Json::Value & info = result["info"];
// collect priority information
Json::Value & priorities = info["priorities"];
uint64_t now = QDateTime::currentMSecsSinceEpoch();
QList<int> activePriorities = _hyperion->getActivePriorities();
foreach (int priority, activePriorities) {
const Hyperion::InputInfo & priorityInfo = _hyperion->getPriorityInfo(priority);
Json::Value & item = priorities[priorities.size()];
item["priority"] = priority;
if (priorityInfo.timeoutTime_ms != -1)
{
item["duration_ms"] = priorityInfo.timeoutTime_ms - now;
}
}
// collect transform information
Json::Value & transform = info["transform"];
transform["saturationGain"] = _hyperion->getTransform(Hyperion::SATURATION_GAIN, Hyperion::INVALID);
transform["valueGain"] = _hyperion->getTransform(Hyperion::VALUE_GAIN, Hyperion::INVALID);
Json::Value & threshold = transform["threshold"];
threshold.append(_hyperion->getTransform(Hyperion::THRESHOLD, Hyperion::RED));
threshold.append(_hyperion->getTransform(Hyperion::THRESHOLD, Hyperion::GREEN));
threshold.append(_hyperion->getTransform(Hyperion::THRESHOLD, Hyperion::BLUE));
Json::Value & gamma = transform["gamma"];
gamma.append(_hyperion->getTransform(Hyperion::GAMMA, Hyperion::RED));
gamma.append(_hyperion->getTransform(Hyperion::GAMMA, Hyperion::GREEN));
gamma.append(_hyperion->getTransform(Hyperion::GAMMA, Hyperion::BLUE));
Json::Value & blacklevel = transform["blacklevel"];
blacklevel.append(_hyperion->getTransform(Hyperion::BLACKLEVEL, Hyperion::RED));
blacklevel.append(_hyperion->getTransform(Hyperion::BLACKLEVEL, Hyperion::GREEN));
blacklevel.append(_hyperion->getTransform(Hyperion::BLACKLEVEL, Hyperion::BLUE));
Json::Value & whitelevel = transform["whitelevel"];
whitelevel.append(_hyperion->getTransform(Hyperion::WHITELEVEL, Hyperion::RED));
whitelevel.append(_hyperion->getTransform(Hyperion::WHITELEVEL, Hyperion::GREEN));
whitelevel.append(_hyperion->getTransform(Hyperion::WHITELEVEL, Hyperion::BLUE));
// send the result
sendMessage(result);
}
void JsonClientConnection::handleClearCommand(const Json::Value &message)
{
// extract parameters
int priority = message["priority"].asInt();
// clear priority
_hyperion->clear(priority);
// send reply
sendSuccessReply();
}
void JsonClientConnection::handleClearallCommand(const Json::Value &)
{
// clear priority
_hyperion->clearall();
// send reply
sendSuccessReply();
}
void JsonClientConnection::handleTransformCommand(const Json::Value &message)
{
const Json::Value & transform = message["transform"];
if (transform.isMember("saturationGain"))
{
_hyperion->setTransform(Hyperion::SATURATION_GAIN, Hyperion::INVALID, transform["saturationGain"].asDouble());
}
if (transform.isMember("valueGain"))
{
_hyperion->setTransform(Hyperion::VALUE_GAIN, Hyperion::INVALID, transform["valueGain"].asDouble());
}
if (transform.isMember("threshold"))
{
const Json::Value & threshold = transform["threshold"];
_hyperion->setTransform(Hyperion::THRESHOLD, Hyperion::RED, threshold[0u].asDouble());
_hyperion->setTransform(Hyperion::THRESHOLD, Hyperion::GREEN, threshold[1u].asDouble());
_hyperion->setTransform(Hyperion::THRESHOLD, Hyperion::BLUE, threshold[2u].asDouble());
}
if (transform.isMember("gamma"))
{
const Json::Value & threshold = transform["gamma"];
_hyperion->setTransform(Hyperion::GAMMA, Hyperion::RED, threshold[0u].asDouble());
_hyperion->setTransform(Hyperion::GAMMA, Hyperion::GREEN, threshold[1u].asDouble());
_hyperion->setTransform(Hyperion::GAMMA, Hyperion::BLUE, threshold[2u].asDouble());
}
if (transform.isMember("blacklevel"))
{
const Json::Value & threshold = transform["blacklevel"];
_hyperion->setTransform(Hyperion::BLACKLEVEL, Hyperion::RED, threshold[0u].asDouble());
_hyperion->setTransform(Hyperion::BLACKLEVEL, Hyperion::GREEN, threshold[1u].asDouble());
_hyperion->setTransform(Hyperion::BLACKLEVEL, Hyperion::BLUE, threshold[2u].asDouble());
}
if (transform.isMember("whitelevel"))
{
const Json::Value & threshold = transform["whitelevel"];
_hyperion->setTransform(Hyperion::WHITELEVEL, Hyperion::RED, threshold[0u].asDouble());
_hyperion->setTransform(Hyperion::WHITELEVEL, Hyperion::GREEN, threshold[1u].asDouble());
_hyperion->setTransform(Hyperion::WHITELEVEL, Hyperion::BLUE, threshold[2u].asDouble());
}
sendSuccessReply();
}
void JsonClientConnection::handleNotImplemented()
{ {
sendErrorReply("Command not implemented"); sendErrorReply("Command not implemented");
} }
@ -102,6 +276,16 @@ void JsonClientConnection::sendMessage(const Json::Value &message)
_socket->write(serializedReply.data(), serializedReply.length()); _socket->write(serializedReply.data(), serializedReply.length());
} }
void JsonClientConnection::sendSuccessReply()
{
// create reply
Json::Value reply;
reply["success"] = true;
// send reply
sendMessage(reply);
}
void JsonClientConnection::sendErrorReply(const std::string &error) void JsonClientConnection::sendErrorReply(const std::string &error)
{ {
// create reply // create reply
@ -112,3 +296,36 @@ void JsonClientConnection::sendErrorReply(const std::string &error)
// send reply // send reply
sendMessage(reply); sendMessage(reply);
} }
bool JsonClientConnection::checkJson(const Json::Value & message, const QString & schemaResource, std::string & errorMessage)
{
// read the json schema from the resource
QResource schemaData(schemaResource);
assert(schemaData.isValid());
Json::Reader jsonReader;
Json::Value schemaJson;
if (!jsonReader.parse(reinterpret_cast<const char *>(schemaData.data()), reinterpret_cast<const char *>(schemaData.data()) + schemaData.size(), schemaJson, false))
{
throw std::runtime_error("Schema error: " + jsonReader.getFormattedErrorMessages()) ;
}
// create schema checker
JsonSchemaChecker schema;
schema.setSchema(schemaJson);
// check the message
if (!schema.validate(message))
{
const std::list<std::string> & errors = schema.getMessages();
std::stringstream ss;
ss << "{";
foreach (const std::string & error, errors) {
ss << error << " ";
}
ss << "}";
errorMessage = ss.str();
return false;
}
return true;
}

View File

@ -10,15 +10,20 @@
// jsoncpp includes // jsoncpp includes
#include <json/json.h> #include <json/json.h>
// Hyperion includes
#include <hyperion/Hyperion.h>
// util includes // util includes
#include <utils/jsonschema/JsonSchemaChecker.h> #include <utils/jsonschema/JsonSchemaChecker.h>
class ImageProcessor;
class JsonClientConnection : public QObject class JsonClientConnection : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
JsonClientConnection(QTcpSocket * socket); JsonClientConnection(QTcpSocket * socket, Hyperion * hyperion);
~JsonClientConnection(); ~JsonClientConnection();
signals: signals:
@ -30,15 +35,27 @@ private slots:
private: private:
void handleMessage(const std::string & message); void handleMessage(const std::string & message);
void handleNotImplemented(const Json::Value & message); void handleColorCommand(const Json::Value & message);
void handleImageCommand(const Json::Value & message);
void handleServerInfoCommand(const Json::Value & message);
void handleClearCommand(const Json::Value & message);
void handleClearallCommand(const Json::Value & message);
void handleTransformCommand(const Json::Value & message);
void handleNotImplemented();
void sendMessage(const Json::Value & message); void sendMessage(const Json::Value & message);
void sendSuccessReply();
void sendErrorReply(const std::string & error); void sendErrorReply(const std::string & error);
private:
bool checkJson(const Json::Value & message, const QString &schemaResource, std::string & errors);
private: private:
QTcpSocket * _socket; QTcpSocket * _socket;
JsonSchemaChecker _schemaChecker; ImageProcessor * _imageProcessor;
Hyperion * _hyperion;
QByteArray _receiveBuffer; QByteArray _receiveBuffer;
}; };

View File

@ -37,12 +37,12 @@ uint16_t JsonServer::getPort() const
void JsonServer::newConnection() void JsonServer::newConnection()
{ {
std::cout << "New incoming json connection" << std::endl;
QTcpSocket * socket = _server.nextPendingConnection(); QTcpSocket * socket = _server.nextPendingConnection();
if (socket != nullptr) if (socket != nullptr)
{ {
JsonClientConnection * connection = new JsonClientConnection(socket); std::cout << "New json connection" << std::endl;
JsonClientConnection * connection = new JsonClientConnection(socket, _hyperion);
_openConnections.insert(connection); _openConnections.insert(connection);
// register slot for cleaning up after the connection closed // register slot for cleaning up after the connection closed

View File

@ -1,5 +1,11 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>schema.json</file> <file alias="schema">schema/schema.json</file>
</qresource> <file alias="schema-color">schema/schema-color.json</file>
<file alias="schema-image">schema/schema-image.json</file>
<file alias="schema-serverinfo">schema/schema-serverinfo.json</file>
<file alias="schema-clear">schema/schema-clear.json</file>
<file alias="schema-clearall">schema/schema-clearall.json</file>
<file alias="schema-transform">schema/schema-transform.json</file>
</qresource>
</RCC> </RCC>

View File

@ -0,0 +1,16 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["clear"]
},
"priority": {
"type": "integer",
"required": true
}
},
"additionalProperties": false
}

View File

@ -0,0 +1,12 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["clearall"]
}
},
"additionalProperties": false
}

View File

@ -0,0 +1,29 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["color"]
},
"priority": {
"type": "integer",
"required": true
},
"duration": {
"type": "integer",
"required": false
},
"color": {
"type": "array",
"required": true,
"items" :{
"type" : "integer"
},
"minItems": 3,
"maxItems": 3
}
},
"additionalProperties": false
}

View File

@ -0,0 +1,34 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["image"]
},
"priority": {
"type": "integer",
"required": true
},
"duration": {
"type": "integer",
"required": false
},
"imagewidth": {
"type" : "integer",
"required": true,
"minimum": 0
},
"imageheight": {
"type" : "integer",
"required": true,
"minimum": 0
},
"imagedata": {
"type": "string",
"required": true
}
},
"additionalProperties": false
}

View File

@ -0,0 +1,12 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["serverinfo"]
}
},
"additionalProperties": false
}

View File

@ -5,43 +5,22 @@
"command": { "command": {
"type" : "string", "type" : "string",
"required" : true, "required" : true,
"enum" : ["color", "image", "serverinfo", "clear", "clearall", "transform"] "enum" : ["transform"]
},
"priority": {
"type": "integer",
"required": false
},
"duration": {
"type": "integer",
"required": false
},
"color": {
"type": "array",
"required": false,
"items" :{
"type" : "integer"
},
"minItems": 3,
"maxItems": 3
},
"imagewidth": {
"type" : "integer",
"required": false,
"minimum": 0
},
"imageheight": {
"type" : "integer",
"required": false,
"minimum": 0
},
"imagedata": {
"type": "string",
"required": false
}, },
"transform": { "transform": {
"type": "object", "type": "object",
"required": false, "required": true,
"properties": { "properties": {
"saturationGain" : {
"type" : "double",
"required" : false,
"minimum" : 0.0
},
"valueGain" : {
"type" : "double",
"required" : false,
"minimum" : 0.0
},
"threshold": { "threshold": {
"type": "array", "type": "array",
"required": false, "required": false,

View File

@ -0,0 +1,11 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["color", "image", "serverinfo", "clear", "clearall", "transform"]
}
}
}

View File

@ -30,5 +30,5 @@ qt4_use_modules(hyperion-remote
Network) Network)
target_link_libraries(hyperion-remote target_link_libraries(hyperion-remote
hyperion-utils jsoncpp
getoptPlusPlus) getoptPlusPlus)

View File

@ -160,7 +160,7 @@ void JsonConnection::clearAll()
parseReply(reply); parseReply(reply);
} }
void JsonConnection::setTransform(ColorTransformValues *threshold, ColorTransformValues *gamma, ColorTransformValues *blacklevel, ColorTransformValues *whitelevel) void JsonConnection::setTransform(double * saturation, double * value, ColorTransformValues *threshold, ColorTransformValues *gamma, ColorTransformValues *blacklevel, ColorTransformValues *whitelevel)
{ {
std::cout << "Set color transforms" << std::endl; std::cout << "Set color transforms" << std::endl;
@ -169,6 +169,16 @@ void JsonConnection::setTransform(ColorTransformValues *threshold, ColorTransfor
command["command"] = "transform"; command["command"] = "transform";
Json::Value & transform = command["transform"]; Json::Value & transform = command["transform"];
if (saturation != nullptr)
{
transform["saturationGain"] = *saturation;
}
if (value != nullptr)
{
transform["valueGain"] = *value;
}
if (threshold != nullptr) if (threshold != nullptr)
{ {
Json::Value & v = transform["threshold"]; Json::Value & v = transform["threshold"];
@ -180,25 +190,25 @@ void JsonConnection::setTransform(ColorTransformValues *threshold, ColorTransfor
if (gamma != nullptr) if (gamma != nullptr)
{ {
Json::Value & v = transform["gamma"]; Json::Value & v = transform["gamma"];
v.append(threshold->valueRed); v.append(gamma->valueRed);
v.append(threshold->valueGreen); v.append(gamma->valueGreen);
v.append(threshold->valueBlue); v.append(gamma->valueBlue);
} }
if (blacklevel != nullptr) if (blacklevel != nullptr)
{ {
Json::Value & v = transform["blacklevel"]; Json::Value & v = transform["blacklevel"];
v.append(threshold->valueRed); v.append(blacklevel->valueRed);
v.append(threshold->valueGreen); v.append(blacklevel->valueGreen);
v.append(threshold->valueBlue); v.append(blacklevel->valueBlue);
} }
if (whitelevel != nullptr) if (whitelevel != nullptr)
{ {
Json::Value & v = transform["whitelevel"]; Json::Value & v = transform["whitelevel"];
v.append(threshold->valueRed); v.append(whitelevel->valueRed);
v.append(threshold->valueGreen); v.append(whitelevel->valueGreen);
v.append(threshold->valueBlue); v.append(whitelevel->valueBlue);
} }
// send command message // send command message
@ -275,7 +285,7 @@ bool JsonConnection::parseReply(const Json::Value &reply)
if (!success) if (!success)
{ {
throw std::runtime_error("Error while executing command: " + reason); throw std::runtime_error("Error: " + reason);
} }
return success; return success;

View File

@ -39,7 +39,13 @@ public:
/// Set the color transform of the leds /// Set the color transform of the leds
/// Note that providing a NULL will leave the settings on the server unchanged /// Note that providing a NULL will leave the settings on the server unchanged
void setTransform(ColorTransformValues * threshold, ColorTransformValues * gamma, ColorTransformValues * blacklevel, ColorTransformValues * whitelevel); void setTransform(
double * saturation,
double * value,
ColorTransformValues * threshold,
ColorTransformValues * gamma,
ColorTransformValues * blacklevel,
ColorTransformValues * whitelevel);
private: private:
/// Send a json command message and receive its reply /// Send a json command message and receive its reply

View File

@ -42,9 +42,11 @@ int main(int argc, char * argv[])
IntParameter & argDuration = parameters.add<IntParameter> ('d', "duration" , "Specify how long the leds should be switched on in millseconds [default: infinity]"); IntParameter & argDuration = parameters.add<IntParameter> ('d', "duration" , "Specify how long the leds should be switched on in millseconds [default: infinity]");
ColorParameter & argColor = parameters.add<ColorParameter> ('c', "color" , "Set all leds to a constant color (either RRGGBB hex value or a color name)"); ColorParameter & argColor = parameters.add<ColorParameter> ('c', "color" , "Set all leds to a constant color (either RRGGBB hex value or a color name)");
ImageParameter & argImage = parameters.add<ImageParameter> ('i', "image" , "Set the leds to the colors according to the given image file"); ImageParameter & argImage = parameters.add<ImageParameter> ('i', "image" , "Set the leds to the colors according to the given image file");
SwitchParameter<> & argServerInfo = parameters.add<SwitchParameter<> >('s', "info" , "List server info"); SwitchParameter<> & argServerInfo = parameters.add<SwitchParameter<> >('l', "list" , "List server info");
SwitchParameter<> & argClear = parameters.add<SwitchParameter<> >('x', "clear" , "Clear data for the priority channel provided by the -p option"); SwitchParameter<> & argClear = parameters.add<SwitchParameter<> >('x', "clear" , "Clear data for the priority channel provided by the -p option");
SwitchParameter<> & argClearAll = parameters.add<SwitchParameter<> >(0x0, "clear-all" , "Clear data for all priority channels"); SwitchParameter<> & argClearAll = parameters.add<SwitchParameter<> >(0x0, "clearall" , "Clear data for all active priority channels");
DoubleParameter & argSaturation = parameters.add<DoubleParameter> ('s', "saturation", "Set the HSV saturation gain of the leds");
DoubleParameter & argValue = parameters.add<DoubleParameter> ('v', "value ", "Set the HSV value gain of the leds");
TransformParameter & argGamma = parameters.add<TransformParameter>('g', "gamma" , "Set the gamma of the leds (requires 3 space seperated values)"); TransformParameter & argGamma = parameters.add<TransformParameter>('g', "gamma" , "Set the gamma of the leds (requires 3 space seperated values)");
TransformParameter & argThreshold = parameters.add<TransformParameter>('t', "threshold" , "Set the threshold of the leds (requires 3 space seperated values between 0.0 and 1.0)"); TransformParameter & argThreshold = parameters.add<TransformParameter>('t', "threshold" , "Set the threshold of the leds (requires 3 space seperated values between 0.0 and 1.0)");
TransformParameter & argBlacklevel = parameters.add<TransformParameter>('b', "blacklevel", "Set the blacklevel of the leds (requires 3 space seperated values which are normally between 0.0 and 1.0)"); TransformParameter & argBlacklevel = parameters.add<TransformParameter>('b', "blacklevel", "Set the blacklevel of the leds (requires 3 space seperated values which are normally between 0.0 and 1.0)");
@ -68,7 +70,7 @@ int main(int argc, char * argv[])
} }
// check if at least one of the available color transforms is set // check if at least one of the available color transforms is set
bool colorTransform = argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet(); bool colorTransform = argSaturation.isSet() || argValue.isSet() || argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet();
// check that exactly one command was given // check that exactly one command was given
int commandCount = count({argColor.isSet(), argImage.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform}); int commandCount = count({argColor.isSet(), argImage.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform});
@ -81,6 +83,8 @@ int main(int argc, char * argv[])
std::cerr << " " << argClear.usageLine() << std::endl; std::cerr << " " << argClear.usageLine() << std::endl;
std::cerr << " " << argClearAll.usageLine() << std::endl; std::cerr << " " << argClearAll.usageLine() << std::endl;
std::cerr << "or one or more of the available color transformations:" << std::endl; std::cerr << "or one or more of the available color transformations:" << std::endl;
std::cerr << " " << argSaturation.usageLine() << std::endl;
std::cerr << " " << argValue.usageLine() << std::endl;
std::cerr << " " << argThreshold.usageLine() << std::endl; std::cerr << " " << argThreshold.usageLine() << std::endl;
std::cerr << " " << argGamma.usageLine() << std::endl; std::cerr << " " << argGamma.usageLine() << std::endl;
std::cerr << " " << argBlacklevel.usageLine() << std::endl; std::cerr << " " << argBlacklevel.usageLine() << std::endl;
@ -115,14 +119,19 @@ int main(int argc, char * argv[])
} }
else if (colorTransform) else if (colorTransform)
{ {
double saturation, value;
ColorTransformValues threshold, gamma, blacklevel, whitelevel; ColorTransformValues threshold, gamma, blacklevel, whitelevel;
if (argSaturation.isSet()) saturation = argSaturation.getValue();
if (argValue.isSet()) value = argValue.getValue();
if (argThreshold.isSet()) threshold = argThreshold.getValue(); if (argThreshold.isSet()) threshold = argThreshold.getValue();
if (argGamma.isSet()) gamma = argGamma.getValue(); if (argGamma.isSet()) gamma = argGamma.getValue();
if (argBlacklevel.isSet()) blacklevel = argBlacklevel.getValue(); if (argBlacklevel.isSet()) blacklevel = argBlacklevel.getValue();
if (argWhitelevel.isSet()) whitelevel = argWhitelevel.getValue(); if (argWhitelevel.isSet()) whitelevel = argWhitelevel.getValue();
connection.setTransform( connection.setTransform(
argSaturation.isSet() ? &saturation : nullptr,
argValue.isSet() ? &value : nullptr,
argThreshold.isSet() ? &threshold : nullptr, argThreshold.isSet() ? &threshold : nullptr,
argGamma.isSet() ? &gamma : nullptr, argGamma.isSet() ? &gamma : nullptr,
argBlacklevel.isSet() ? &blacklevel : nullptr, argBlacklevel.isSet() ? &blacklevel : nullptr,