hyperion.ng/libsrc/hyperion/Hyperion.cpp

545 lines
13 KiB
C++
Raw Normal View History

// STL includes
#include <cassert>
// QT includes
#include <QDateTime>
#include <QThread>
// JsonSchema include
#include <utils/jsonschema/JsonFactory.h>
// hyperion include
#include <hyperion/Hyperion.h>
#include <hyperion/LedDevice.h>
#include <hyperion/ImageProcessorFactory.h>
#include "device/LedDeviceLpd6803.h"
#include "device/LedDeviceLpd8806.h"
#include "device/LedDeviceSedu.h"
#include "device/LedDeviceTest.h"
#include "device/LedDeviceWs2801.h"
#include "device/LedDeviceAdalight.h"
#include "device/LedDeviceLightpack.h"
#include "device/LedDeviceMultiLightpack.h"
#include "LinearColorSmoothing.h"
#include <utils/ColorTransform.h>
#include <utils/HsvTransform.h>
// effect engine includes
#include <effectengine/EffectEngine.h>
LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig)
{
std::cout << "Device configuration: " << deviceConfig << std::endl;
std::string type = deviceConfig.get("type", "UNSPECIFIED").asString();
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
LedDevice* device = nullptr;
if (type == "ws2801")
{
const std::string output = deviceConfig["output"].asString();
const unsigned rate = deviceConfig["rate"].asInt();
LedDeviceWs2801* deviceWs2801 = new LedDeviceWs2801(output, rate);
deviceWs2801->open();
device = deviceWs2801;
}
else if (type == "lpd6803" || type == "ldp6803")
{
const std::string output = deviceConfig["output"].asString();
const unsigned rate = deviceConfig["rate"].asInt();
LedDeviceLpd6803* deviceLdp6803 = new LedDeviceLpd6803(output, rate);
deviceLdp6803->open();
device = deviceLdp6803;
}
else if (type == "lpd8806" || type == "ldp8806")
{
const std::string output = deviceConfig["output"].asString();
const unsigned rate = deviceConfig["rate"].asInt();
LedDeviceLpd8806* deviceLpd8806 = new LedDeviceLpd8806(output, rate);
deviceLpd8806->open();
device = deviceLpd8806;
}
else if (type == "sedu")
{
const std::string output = deviceConfig["output"].asString();
const unsigned rate = deviceConfig["rate"].asInt();
LedDeviceSedu* deviceSedu = new LedDeviceSedu(output, rate);
deviceSedu->open();
device = deviceSedu;
}
else if (type == "adalight")
{
const std::string output = deviceConfig["output"].asString();
const unsigned rate = deviceConfig["rate"].asInt();
LedDeviceAdalight* deviceAdalight = new LedDeviceAdalight(output, rate);
deviceAdalight->open();
device = deviceAdalight;
}
else if (type == "lightpack")
{
const std::string output = deviceConfig.get("output", "").asString();
LedDeviceLightpack* deviceLightpack = new LedDeviceLightpack();
deviceLightpack->open(output);
device = deviceLightpack;
}
else if (type == "multi-lightpack")
{
LedDeviceMultiLightpack* deviceLightpack = new LedDeviceMultiLightpack();
deviceLightpack->open();
device = deviceLightpack;
}
else if (type == "test")
{
const std::string output = deviceConfig["output"].asString();
device = new LedDeviceTest(output);
}
else
{
std::cout << "Unable to create device " << type << std::endl;
// Unknown / Unimplemented device
}
return device;
}
Hyperion::ColorOrder Hyperion::createColorOrder(const Json::Value &deviceConfig)
{
// deprecated: force BGR when the deprecated flag is present and set to true
if (deviceConfig.get("bgr-output", false).asBool())
{
return ORDER_BGR;
}
std::string order = deviceConfig.get("colorOrder", "rgb").asString();
if (order == "rgb")
{
return ORDER_RGB;
}
else if (order == "bgr")
{
return ORDER_BGR;
}
else if (order == "rbg")
{
return ORDER_RBG;
}
else if (order == "brg")
{
return ORDER_BRG;
}
else if (order == "gbr")
{
return ORDER_GBR;
}
else if (order == "grb")
{
return ORDER_GRB;
}
else
{
std::cout << "Unknown color order defined (" << order << "). Using RGB." << std::endl;
}
return ORDER_RGB;
}
HsvTransform * Hyperion::createHsvTransform(const Json::Value & hsvConfig)
{
const double saturationGain = hsvConfig.get("saturationGain", 1.0).asDouble();
const double valueGain = hsvConfig.get("valueGain", 1.0).asDouble();
return new HsvTransform(saturationGain, valueGain);
}
ColorTransform* Hyperion::createColorTransform(const Json::Value& colorConfig)
{
const double threshold = colorConfig.get("threshold", 0.0).asDouble();
const double gamma = colorConfig.get("gamma", 1.0).asDouble();
const double blacklevel = colorConfig.get("blacklevel", 0.0).asDouble();
const double whitelevel = colorConfig.get("whitelevel", 1.0).asDouble();
ColorTransform* transform = new ColorTransform(threshold, gamma, blacklevel, whitelevel);
return transform;
}
LedString Hyperion::createLedString(const Json::Value& ledsConfig)
{
LedString ledString;
for (const Json::Value& ledConfig : ledsConfig)
{
Led led;
led.index = ledConfig["index"].asInt();
const Json::Value& hscanConfig = ledConfig["hscan"];
const Json::Value& vscanConfig = ledConfig["vscan"];
led.minX_frac = std::max(0.0, std::min(1.0, hscanConfig["minimum"].asDouble()));
led.maxX_frac = std::max(0.0, std::min(1.0, hscanConfig["maximum"].asDouble()));
led.minY_frac = std::max(0.0, std::min(1.0, vscanConfig["minimum"].asDouble()));
led.maxY_frac = std::max(0.0, std::min(1.0, vscanConfig["maximum"].asDouble()));
// Fix if the user swapped min and max
if (led.minX_frac > led.maxX_frac)
{
std::swap(led.minX_frac, led.maxX_frac);
}
if (led.minY_frac > led.maxY_frac)
{
std::swap(led.minY_frac, led.maxY_frac);
}
ledString.leds().push_back(led);
}
// Make sure the leds are sorted (on their indices)
std::sort(ledString.leds().begin(), ledString.leds().end(), [](const Led& lhs, const Led& rhs){ return lhs.index < rhs.index; });
return ledString;
}
LedDevice * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice)
{
std::string type = smoothingConfig.get("type", "none").asString();
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
if (type == "none")
{
std::cout << "Not creating any smoothing" << std::endl;
return ledDevice;
}
else if (type == "linear")
{
if (!smoothingConfig.isMember("time_ms"))
{
std::cout << "Unable to create smoothing of type linear because of missing parameter 'time_ms'" << std::endl;
}
else if (!smoothingConfig.isMember("updateFrequency"))
{
std::cout << "Unable to create smoothing of type linear because of missing parameter 'updateFrequency'" << std::endl;
}
else
{
std::cout << "Creating linear smoothing" << std::endl;
return new LinearColorSmoothing(ledDevice, smoothingConfig["updateFrequency"].asDouble(), smoothingConfig["time_ms"].asInt());
}
}
else
{
std::cout << "Unable to create smoothing of type " << type << std::endl;
}
return ledDevice;
}
Hyperion::Hyperion(const Json::Value &jsonConfig) :
2013-08-18 13:33:56 +02:00
_ledString(createLedString(jsonConfig["leds"])),
_muxer(_ledString.leds().size()),
_hsvTransform(createHsvTransform(jsonConfig["color"]["hsv"])),
2013-08-18 13:33:56 +02:00
_redTransform(createColorTransform(jsonConfig["color"]["red"])),
_greenTransform(createColorTransform(jsonConfig["color"]["green"])),
_blueTransform(createColorTransform(jsonConfig["color"]["blue"])),
_colorOrder(createColorOrder(jsonConfig["device"])),
_device(createDevice(jsonConfig["device"])),
_effectEngine(nullptr),
_timer()
{
// initialize the image processor factory
ImageProcessorFactory::getInstance().init(_ledString, jsonConfig["blackborderdetector"].get("enable", true).asBool());
// initialize the color smoothing filter
_device = createColorSmoothing(jsonConfig["color"]["smoothing"], _device);
// setup the timer
_timer.setSingleShot(true);
QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
// create the effect engine
_effectEngine = new EffectEngine(this, jsonConfig["effects"]);
2013-08-18 13:33:56 +02:00
// initialize the leds
update();
}
Hyperion::~Hyperion()
{
// switch off all leds
clearall();
_device->switchOff();
// delete the effect engine
delete _effectEngine;
// Delete the Led-String
2013-08-18 13:33:56 +02:00
delete _device;
// delete he hsv transform
delete _hsvTransform;
// Delete the color-transform
2013-08-18 13:33:56 +02:00
delete _blueTransform;
delete _greenTransform;
delete _redTransform;
}
2013-08-14 10:54:49 +02:00
unsigned Hyperion::getLedCount() const
{
2013-08-18 13:33:56 +02:00
return _ledString.leds().size();
2013-08-14 10:54:49 +02:00
}
void Hyperion::setColor(int priority, const ColorRgb &color, const int timeout_ms)
{
2013-08-18 13:33:56 +02:00
// create led output
std::vector<ColorRgb> ledColors(_ledString.leds().size(), color);
2013-08-18 13:33:56 +02:00
// set colors
setColors(priority, ledColors, timeout_ms);
}
void Hyperion::setColors(int priority, const std::vector<ColorRgb>& ledColors, const int timeout_ms)
{
if (timeout_ms > 0)
{
const uint64_t timeoutTime = QDateTime::currentMSecsSinceEpoch() + timeout_ms;
2013-08-18 13:33:56 +02:00
_muxer.setInput(priority, ledColors, timeoutTime);
}
else
{
2013-08-18 13:33:56 +02:00
_muxer.setInput(priority, ledColors);
}
2013-08-18 13:33:56 +02:00
if (priority == _muxer.getCurrentPriority())
{
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();
}
2013-08-18 13:33:56 +02:00
void Hyperion::clear(int priority)
{
if (_muxer.hasPriority(priority))
{
_muxer.clearInput(priority);
// update leds if necessary
if (priority < _muxer.getCurrentPriority());
{
update();
}
}
// send clear signal to the effect engine
// (outside the check so the effect gets cleared even when the effect is not sending colors)
_effectEngine->channelCleared(priority);
2013-08-18 13:33:56 +02:00
}
void Hyperion::clearall()
{
_muxer.clearAll();
// update leds
update();
// send clearall signal to the effect engine
_effectEngine->allChannelsCleared();
2013-08-18 13:33:56 +02:00
}
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);
}
std::list<std::string> Hyperion::getEffects() const
{
return _effectEngine->getEffects();
}
int Hyperion::setEffect(const std::string &effectName, int priority, int timeout)
{
return _effectEngine->runEffect(effectName, priority, timeout);
}
void Hyperion::update()
{
// Update the muxer, cleaning obsolete priorities
2013-08-18 13:33:56 +02:00
_muxer.setCurrentTime(QDateTime::currentMSecsSinceEpoch());
// Obtain the current priority channel
2013-08-18 13:33:56 +02:00
int priority = _muxer.getCurrentPriority();
const PriorityMuxer::InputInfo & priorityInfo = _muxer.getInputInfo(priority);
// Apply the transform to each led and color-channel
std::vector<ColorRgb> ledColors(priorityInfo.ledColors);
for (ColorRgb& 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);
// correct the color byte order
switch (_colorOrder)
{
case ORDER_RGB:
// leave as it is
break;
case ORDER_BGR:
std::swap(color.red, color.blue);
break;
case ORDER_RBG:
std::swap(color.green, color.blue);
break;
case ORDER_GRB:
std::swap(color.red, color.green);
break;
case ORDER_GBR:
{
uint8_t temp = color.red;
color.red = color.green;
color.green = color.blue;
color.blue = temp;
break;
}
case ORDER_BRG:
{
uint8_t temp = color.red;
color.red = color.blue;
color.blue = color.green;
color.green = temp;
break;
}
}
}
// Write the data to the device
_device->write(ledColors);
// Start the timeout-timer
if (priorityInfo.timeoutTime_ms == -1)
{
_timer.stop();
}
else
{
int timeout_ms = std::max(0, int(priorityInfo.timeoutTime_ms - QDateTime::currentMSecsSinceEpoch()));
_timer.start(timeout_ms);
}
}