mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
817dabae8c
* add --sourceOff to hyperion-remote - this will select "off" source and set all leds to black refactor new json stuff make schema checker not so strict, do not require values that have defaults (not finished yet) initialEffect config: effect is always an array, regardless if it is a color or an effect name * make off source visible in active priority list * transform initialeffect to qjson (except part of effect-args, this needs effectengine transformed to qjson) * remove unneeded comment * add web ui for source selection. call http://hyperion_host:8099/select/index.html current example needed json server on port 19444
926 lines
27 KiB
C++
926 lines
27 KiB
C++
|
|
// STL includes
|
|
#include <cassert>
|
|
#include <exception>
|
|
#include <sstream>
|
|
|
|
// QT includes
|
|
#include <QDateTime>
|
|
#include <QThread>
|
|
#include <QRegExp>
|
|
#include <QString>
|
|
#include <QStringList>
|
|
|
|
// JsonSchema include
|
|
#include <utils/jsonschema/JsonFactory.h>
|
|
|
|
// hyperion include
|
|
#include <hyperion/Hyperion.h>
|
|
#include <hyperion/ImageProcessorFactory.h>
|
|
#include <hyperion/ColorTransform.h>
|
|
#include <hyperion/ColorCorrection.h>
|
|
#include <hyperion/ColorAdjustment.h>
|
|
|
|
// Leddevice includes
|
|
#include <leddevice/LedDevice.h>
|
|
#include <leddevice/LedDeviceFactory.h>
|
|
|
|
#include "MultiColorTransform.h"
|
|
#include "MultiColorCorrection.h"
|
|
#include "MultiColorAdjustment.h"
|
|
#include "LinearColorSmoothing.h"
|
|
|
|
// effect engine includes
|
|
#include <effectengine/EffectEngine.h>
|
|
|
|
Hyperion* Hyperion::_hyperion = nullptr;
|
|
|
|
Hyperion* Hyperion::initInstance(const Json::Value& jsonConfig, const std::string configFile)
|
|
{
|
|
if ( Hyperion::_hyperion != nullptr )
|
|
throw std::runtime_error("Hyperion::initInstance can be called only one time");
|
|
Hyperion::_hyperion = new Hyperion(jsonConfig,configFile);
|
|
|
|
return Hyperion::_hyperion;
|
|
}
|
|
|
|
Hyperion* Hyperion::getInstance()
|
|
{
|
|
if ( Hyperion::_hyperion == nullptr )
|
|
throw std::runtime_error("Hyperion::getInstance used without call of Hyperion::initInstance before");
|
|
|
|
return Hyperion::_hyperion;
|
|
}
|
|
|
|
ColorOrder Hyperion::createColorOrder(const Json::Value &deviceConfig)
|
|
{
|
|
std::string order = deviceConfig.get("colorOrder", "rgb").asString();
|
|
if (order == "bgr")
|
|
{
|
|
return ORDER_BGR;
|
|
}
|
|
if (order == "rbg")
|
|
{
|
|
return ORDER_RBG;
|
|
}
|
|
if (order == "brg")
|
|
{
|
|
return ORDER_BRG;
|
|
}
|
|
if (order == "gbr")
|
|
{
|
|
return ORDER_GBR;
|
|
}
|
|
if (order == "grb")
|
|
{
|
|
return ORDER_GRB;
|
|
}
|
|
|
|
WarningIf( order != "rgb", Logger::getInstance("Core"), "Unknown color order defined (%s). Using RGB.", order.c_str());
|
|
|
|
return ORDER_RGB;
|
|
}
|
|
|
|
ColorTransform * Hyperion::createColorTransform(const Json::Value & transformConfig)
|
|
{
|
|
const std::string id = transformConfig.get("id", "default").asString();
|
|
|
|
RgbChannelTransform * redTransform = createRgbChannelTransform(transformConfig["red"]);
|
|
RgbChannelTransform * greenTransform = createRgbChannelTransform(transformConfig["green"]);
|
|
RgbChannelTransform * blueTransform = createRgbChannelTransform(transformConfig["blue"]);
|
|
|
|
HsvTransform * hsvTransform = createHsvTransform(transformConfig["hsv"]);
|
|
HslTransform * hslTransform = createHslTransform(transformConfig["hsl"]);
|
|
|
|
ColorTransform * transform = new ColorTransform();
|
|
transform->_id = id;
|
|
transform->_rgbRedTransform = *redTransform;
|
|
transform->_rgbGreenTransform = *greenTransform;
|
|
transform->_rgbBlueTransform = *blueTransform;
|
|
transform->_hsvTransform = *hsvTransform;
|
|
transform->_hslTransform = *hslTransform;
|
|
|
|
|
|
// Cleanup the allocated individual transforms
|
|
delete redTransform;
|
|
delete greenTransform;
|
|
delete blueTransform;
|
|
delete hsvTransform;
|
|
delete hslTransform;
|
|
|
|
return transform;
|
|
}
|
|
|
|
|
|
ColorCorrection * Hyperion::createColorCorrection(const Json::Value & correctionConfig)
|
|
{
|
|
const std::string id = correctionConfig.get("id", "default").asString();
|
|
|
|
RgbChannelAdjustment * rgbCorrection = createRgbChannelCorrection(correctionConfig["correctionValues"]);
|
|
|
|
ColorCorrection * correction = new ColorCorrection();
|
|
correction->_id = id;
|
|
correction->_rgbCorrection = *rgbCorrection;
|
|
|
|
// Cleanup the allocated individual transforms
|
|
delete rgbCorrection;
|
|
|
|
return correction;
|
|
}
|
|
|
|
|
|
ColorAdjustment * Hyperion::createColorAdjustment(const Json::Value & adjustmentConfig)
|
|
{
|
|
const std::string id = adjustmentConfig.get("id", "default").asString();
|
|
|
|
RgbChannelAdjustment * redAdjustment = createRgbChannelAdjustment(adjustmentConfig["pureRed"],RED);
|
|
RgbChannelAdjustment * greenAdjustment = createRgbChannelAdjustment(adjustmentConfig["pureGreen"],GREEN);
|
|
RgbChannelAdjustment * blueAdjustment = createRgbChannelAdjustment(adjustmentConfig["pureBlue"],BLUE);
|
|
|
|
ColorAdjustment * adjustment = new ColorAdjustment();
|
|
adjustment->_id = id;
|
|
adjustment->_rgbRedAdjustment = *redAdjustment;
|
|
adjustment->_rgbGreenAdjustment = *greenAdjustment;
|
|
adjustment->_rgbBlueAdjustment = *blueAdjustment;
|
|
|
|
// Cleanup the allocated individual adjustments
|
|
delete redAdjustment;
|
|
delete greenAdjustment;
|
|
delete blueAdjustment;
|
|
|
|
return adjustment;
|
|
}
|
|
|
|
|
|
MultiColorTransform * Hyperion::createLedColorsTransform(const unsigned ledCnt, const Json::Value & colorConfig)
|
|
{
|
|
// Create the result, the transforms are added to this
|
|
MultiColorTransform * transform = new MultiColorTransform(ledCnt);
|
|
Logger * log = Logger::getInstance("Core");
|
|
|
|
const Json::Value transformConfig = colorConfig.get("transform", Json::nullValue);
|
|
if (transformConfig.isNull())
|
|
{
|
|
// Old style color transformation config (just one for all leds)
|
|
ColorTransform * colorTransform = createColorTransform(colorConfig);
|
|
transform->addTransform(colorTransform);
|
|
transform->setTransformForLed(colorTransform->_id, 0, ledCnt-1);
|
|
}
|
|
else if (!transformConfig.isArray())
|
|
{
|
|
ColorTransform * colorTransform = createColorTransform(transformConfig);
|
|
transform->addTransform(colorTransform);
|
|
transform->setTransformForLed(colorTransform->_id, 0, ledCnt-1);
|
|
}
|
|
else
|
|
{
|
|
const QRegExp overallExp("([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*");
|
|
|
|
for (Json::UInt i = 0; i < transformConfig.size(); ++i)
|
|
{
|
|
const Json::Value & config = transformConfig[i];
|
|
ColorTransform * colorTransform = createColorTransform(config);
|
|
transform->addTransform(colorTransform);
|
|
|
|
const QString ledIndicesStr = QString(config.get("leds", "").asCString()).trimmed();
|
|
if (ledIndicesStr.compare("*") == 0)
|
|
{
|
|
// Special case for indices '*' => all leds
|
|
transform->setTransformForLed(colorTransform->_id, 0, ledCnt-1);
|
|
Info(log, "ColorTransform '%s' => [0; %d]", colorTransform->_id.c_str(), ledCnt-1);
|
|
continue;
|
|
}
|
|
|
|
if (!overallExp.exactMatch(ledIndicesStr))
|
|
{
|
|
Error(log, "Given led indices %d not correct format: %s", i, ledIndicesStr.toStdString().c_str());
|
|
continue;
|
|
}
|
|
|
|
std::stringstream ss;
|
|
const QStringList ledIndexList = ledIndicesStr.split(",");
|
|
for (int i=0; i<ledIndexList.size(); ++i) {
|
|
if (i > 0)
|
|
{
|
|
ss << ", ";
|
|
}
|
|
if (ledIndexList[i].contains("-"))
|
|
{
|
|
QStringList ledIndices = ledIndexList[i].split("-");
|
|
int startInd = ledIndices[0].toInt();
|
|
int endInd = ledIndices[1].toInt();
|
|
|
|
transform->setTransformForLed(colorTransform->_id, startInd, endInd);
|
|
ss << startInd << "-" << endInd;
|
|
}
|
|
else
|
|
{
|
|
int index = ledIndexList[i].toInt();
|
|
transform->setTransformForLed(colorTransform->_id, index, index);
|
|
ss << index;
|
|
}
|
|
}
|
|
Info(log, "ColorTransform '%s' => [%s]", colorTransform->_id.c_str(), ss.str().c_str());
|
|
}
|
|
}
|
|
return transform;
|
|
}
|
|
|
|
MultiColorCorrection * Hyperion::createLedColorsTemperature(const unsigned ledCnt, const Json::Value & colorConfig)
|
|
{
|
|
// Create the result, the corrections are added to this
|
|
MultiColorCorrection * correction = new MultiColorCorrection(ledCnt);
|
|
Logger * log = Logger::getInstance("Core");
|
|
|
|
const std::string jsonKey = colorConfig.isMember("temperature") ? "temperature" : "correction";
|
|
const Json::Value correctionConfig = colorConfig.get(jsonKey, Json::nullValue);
|
|
if (correctionConfig.isNull())
|
|
{
|
|
// Old style color correction config (just one for all leds)
|
|
ColorCorrection * colorCorrection = createColorCorrection(colorConfig);
|
|
correction->addCorrection(colorCorrection);
|
|
correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1);
|
|
}
|
|
else if (!correctionConfig.isArray())
|
|
{
|
|
ColorCorrection * colorCorrection = createColorCorrection(correctionConfig);
|
|
correction->addCorrection(colorCorrection);
|
|
correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1);
|
|
}
|
|
else
|
|
{
|
|
const QRegExp overallExp("([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*");
|
|
|
|
for (Json::UInt i = 0; i < correctionConfig.size(); ++i)
|
|
{
|
|
const Json::Value & config = correctionConfig[i];
|
|
ColorCorrection * colorCorrection = createColorCorrection(config);
|
|
correction->addCorrection(colorCorrection);
|
|
|
|
const QString ledIndicesStr = QString(config.get("leds", "").asCString()).trimmed();
|
|
if (ledIndicesStr.compare("*") == 0)
|
|
{
|
|
// Special case for indices '*' => all leds
|
|
correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1);
|
|
Info(log, "ColorTemperature '%s' => [0; %d]", colorCorrection->_id.c_str(), ledCnt-1);
|
|
continue;
|
|
}
|
|
|
|
if (!overallExp.exactMatch(ledIndicesStr))
|
|
{
|
|
Error(log, "Given led indices %d not correct format: %s", i, ledIndicesStr.toStdString().c_str());
|
|
continue;
|
|
}
|
|
|
|
std::stringstream ss;
|
|
const QStringList ledIndexList = ledIndicesStr.split(",");
|
|
for (int i=0; i<ledIndexList.size(); ++i) {
|
|
if (i > 0)
|
|
{
|
|
ss << ", ";
|
|
}
|
|
if (ledIndexList[i].contains("-"))
|
|
{
|
|
QStringList ledIndices = ledIndexList[i].split("-");
|
|
int startInd = ledIndices[0].toInt();
|
|
int endInd = ledIndices[1].toInt();
|
|
|
|
correction->setCorrectionForLed(colorCorrection->_id, startInd, endInd);
|
|
ss << startInd << "-" << endInd;
|
|
}
|
|
else
|
|
{
|
|
int index = ledIndexList[i].toInt();
|
|
correction->setCorrectionForLed(colorCorrection->_id, index, index);
|
|
ss << index;
|
|
}
|
|
}
|
|
Info(log, "ColorTemperature '%s' => [%s]", colorCorrection->_id.c_str(), ss.str().c_str());
|
|
}
|
|
}
|
|
return correction;
|
|
}
|
|
|
|
|
|
MultiColorAdjustment * Hyperion::createLedColorsAdjustment(const unsigned ledCnt, const Json::Value & colorConfig)
|
|
{
|
|
// Create the result, the transforms are added to this
|
|
MultiColorAdjustment * adjustment = new MultiColorAdjustment(ledCnt);
|
|
Logger * log = Logger::getInstance("Core");
|
|
|
|
const Json::Value adjustmentConfig = colorConfig.get("channelAdjustment", Json::nullValue);
|
|
if (adjustmentConfig.isNull())
|
|
{
|
|
// Old style color transformation config (just one for all leds)
|
|
ColorAdjustment * colorAdjustment = createColorAdjustment(colorConfig);
|
|
adjustment->addAdjustment(colorAdjustment);
|
|
adjustment->setAdjustmentForLed(colorAdjustment->_id, 0, ledCnt-1);
|
|
}
|
|
else if (!adjustmentConfig.isArray())
|
|
{
|
|
ColorAdjustment * colorAdjustment = createColorAdjustment(adjustmentConfig);
|
|
adjustment->addAdjustment(colorAdjustment);
|
|
adjustment->setAdjustmentForLed(colorAdjustment->_id, 0, ledCnt-1);
|
|
}
|
|
else
|
|
{
|
|
const QRegExp overallExp("([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*");
|
|
|
|
for (Json::UInt i = 0; i < adjustmentConfig.size(); ++i)
|
|
{
|
|
const Json::Value & config = adjustmentConfig[i];
|
|
ColorAdjustment * colorAdjustment = createColorAdjustment(config);
|
|
adjustment->addAdjustment(colorAdjustment);
|
|
|
|
const QString ledIndicesStr = QString(config.get("leds", "").asCString()).trimmed();
|
|
if (ledIndicesStr.compare("*") == 0)
|
|
{
|
|
// Special case for indices '*' => all leds
|
|
adjustment->setAdjustmentForLed(colorAdjustment->_id, 0, ledCnt-1);
|
|
Info(log, "ColorAdjustment '%s' => [0; %d]", colorAdjustment->_id.c_str(), ledCnt-1);
|
|
continue;
|
|
}
|
|
|
|
if (!overallExp.exactMatch(ledIndicesStr))
|
|
{
|
|
Error(log, "Given led indices %d not correct format: %s", i, ledIndicesStr.toStdString().c_str());
|
|
continue;
|
|
}
|
|
|
|
std::stringstream ss;
|
|
const QStringList ledIndexList = ledIndicesStr.split(",");
|
|
for (int i=0; i<ledIndexList.size(); ++i) {
|
|
if (i > 0)
|
|
{
|
|
ss << ", ";
|
|
}
|
|
if (ledIndexList[i].contains("-"))
|
|
{
|
|
QStringList ledIndices = ledIndexList[i].split("-");
|
|
int startInd = ledIndices[0].toInt();
|
|
int endInd = ledIndices[1].toInt();
|
|
|
|
adjustment->setAdjustmentForLed(colorAdjustment->_id, startInd, endInd);
|
|
ss << startInd << "-" << endInd;
|
|
}
|
|
else
|
|
{
|
|
int index = ledIndexList[i].toInt();
|
|
adjustment->setAdjustmentForLed(colorAdjustment->_id, index, index);
|
|
ss << index;
|
|
}
|
|
}
|
|
Info(log, "ColorAdjustment '%s' => [%s]", colorAdjustment->_id.c_str(), ss.str().c_str());
|
|
}
|
|
}
|
|
return adjustment;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
HslTransform * Hyperion::createHslTransform(const Json::Value & hslConfig)
|
|
{
|
|
const double saturationGain = hslConfig.get("saturationGain", 1.0).asDouble();
|
|
const double luminanceGain = hslConfig.get("luminanceGain", 1.0).asDouble();
|
|
const double luminanceMinimum = hslConfig.get("luminanceMinimum", 0.0).asDouble();
|
|
|
|
return new HslTransform(saturationGain, luminanceGain, luminanceMinimum);
|
|
}
|
|
|
|
RgbChannelTransform* Hyperion::createRgbChannelTransform(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();
|
|
|
|
RgbChannelTransform* transform = new RgbChannelTransform(threshold, gamma, blacklevel, whitelevel);
|
|
return transform;
|
|
}
|
|
|
|
RgbChannelAdjustment* Hyperion::createRgbChannelCorrection(const Json::Value& colorConfig)
|
|
{
|
|
const int varR = colorConfig.get("red", 255).asInt();
|
|
const int varG = colorConfig.get("green", 255).asInt();
|
|
const int varB = colorConfig.get("blue", 255).asInt();
|
|
|
|
RgbChannelAdjustment* correction = new RgbChannelAdjustment(varR, varG, varB);
|
|
return correction;
|
|
}
|
|
|
|
RgbChannelAdjustment* Hyperion::createRgbChannelAdjustment(const Json::Value& colorConfig, const RgbChannel color)
|
|
{
|
|
int varR=0, varG=0, varB=0;
|
|
if (color == RED)
|
|
{
|
|
varR = colorConfig.get("redChannel", 255).asInt();
|
|
varG = colorConfig.get("greenChannel", 0).asInt();
|
|
varB = colorConfig.get("blueChannel", 0).asInt();
|
|
}
|
|
else if (color == GREEN)
|
|
{
|
|
varR = colorConfig.get("redChannel", 0).asInt();
|
|
varG = colorConfig.get("greenChannel", 255).asInt();
|
|
varB = colorConfig.get("blueChannel", 0).asInt();
|
|
}
|
|
else if (color == BLUE)
|
|
{
|
|
varR = colorConfig.get("redChannel", 0).asInt();
|
|
varG = colorConfig.get("greenChannel", 0).asInt();
|
|
varB = colorConfig.get("blueChannel", 255).asInt();
|
|
}
|
|
|
|
RgbChannelAdjustment* adjustment = new RgbChannelAdjustment(varR, varG, varB);
|
|
return adjustment;
|
|
}
|
|
|
|
LedString Hyperion::createLedString(const Json::Value& ledsConfig, const ColorOrder deviceOrder)
|
|
{
|
|
LedString ledString;
|
|
|
|
const std::string deviceOrderStr = colorOrderToString(deviceOrder);
|
|
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);
|
|
}
|
|
|
|
// Get the order of the rgb channels for this led (default is device order)
|
|
const std::string ledOrderStr = ledConfig.get("colorOrder", deviceOrderStr).asString();
|
|
led.colorOrder = stringToColorOrder(ledOrderStr);
|
|
|
|
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)
|
|
{
|
|
Logger * log = Logger::getInstance("Core");
|
|
std::string type = smoothingConfig.get("type", "linear").asString();
|
|
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
|
|
|
|
type = "linear"; // TODO currently hardcoded type, delete it if we have more types
|
|
if ( ! smoothingConfig.get("enable", true).asBool() )
|
|
{
|
|
Info(log,"Smoothing disabled");
|
|
return ledDevice;
|
|
}
|
|
|
|
if (type == "linear")
|
|
{
|
|
Info(log, "Creating linear smoothing");
|
|
return new LinearColorSmoothing(
|
|
ledDevice,
|
|
smoothingConfig.get("updateFrequency", 25.0).asDouble(),
|
|
smoothingConfig.get("time_ms", 200).asInt(),
|
|
smoothingConfig.get("updateDelay", 0).asUInt(),
|
|
smoothingConfig.get("continuousOutput", true).asBool()
|
|
);
|
|
}
|
|
|
|
Error(log, "Smoothing disabled, because of unknown type '%s'.", type.c_str());
|
|
return ledDevice;
|
|
}
|
|
|
|
MessageForwarder * Hyperion::createMessageForwarder(const Json::Value & forwarderConfig)
|
|
{
|
|
MessageForwarder * forwarder = new MessageForwarder();
|
|
if ( ( ! forwarderConfig.isNull() ) && ( forwarderConfig.get("enable", true).asBool() ) )
|
|
{
|
|
if ( ! forwarderConfig["json"].isNull() && forwarderConfig["json"].isArray() )
|
|
{
|
|
for (const Json::Value& addr : forwarderConfig["json"])
|
|
{
|
|
Info(Logger::getInstance("Core"), "Json forward to %s", addr.asString().c_str());
|
|
forwarder->addJsonSlave(addr.asString());
|
|
}
|
|
}
|
|
|
|
if ( ! forwarderConfig["proto"].isNull() && forwarderConfig["proto"].isArray() )
|
|
{
|
|
for (const Json::Value& addr : forwarderConfig["proto"])
|
|
{
|
|
Info(Logger::getInstance("Core"), "Proto forward to %s", addr.asString().c_str());
|
|
forwarder->addProtoSlave(addr.asString());
|
|
}
|
|
}
|
|
}
|
|
|
|
return forwarder;
|
|
}
|
|
|
|
MessageForwarder * Hyperion::getForwarder()
|
|
{
|
|
return _messageForwarder;
|
|
}
|
|
|
|
Hyperion::Hyperion(const Json::Value &jsonConfig, const std::string configFile)
|
|
: _ledString(createLedString(jsonConfig["leds"], createColorOrder(jsonConfig["device"])))
|
|
, _muxer(_ledString.leds().size())
|
|
, _raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"]))
|
|
, _raw2ledTemperature(createLedColorsTemperature(_ledString.leds().size(), jsonConfig["color"]))
|
|
, _raw2ledAdjustment(createLedColorsAdjustment(_ledString.leds().size(), jsonConfig["color"]))
|
|
, _device(LedDeviceFactory::construct(jsonConfig["device"]))
|
|
, _effectEngine(nullptr)
|
|
, _messageForwarder(createMessageForwarder(jsonConfig["forwarder"]))
|
|
, _jsonConfig(jsonConfig)
|
|
, _configFile(configFile)
|
|
, _timer()
|
|
, _log(Logger::getInstance("Core"))
|
|
, _hwLedCount(_ledString.leds().size())
|
|
, _sourceAutoSelectEnabled(true)
|
|
{
|
|
registerPriority("Off", PriorityMuxer::LOWEST_PRIORITY);
|
|
|
|
if (!_raw2ledAdjustment->verifyAdjustments())
|
|
{
|
|
throw std::runtime_error("Color adjustment incorrectly set");
|
|
}
|
|
if (!_raw2ledTemperature->verifyCorrections())
|
|
{
|
|
throw std::runtime_error("Color temperature incorrectly set");
|
|
}
|
|
if (!_raw2ledTransform->verifyTransforms())
|
|
{
|
|
throw std::runtime_error("Color transformation incorrectly set");
|
|
}
|
|
// set color correction activity state
|
|
_transformEnabled = jsonConfig["color"].get("transform_enable",true).asBool();
|
|
_adjustmentEnabled = jsonConfig["color"].get("channelAdjustment_enable",true).asBool();
|
|
_temperatureEnabled = jsonConfig["color"].get("temperature_enable",true).asBool();
|
|
|
|
InfoIf(!_transformEnabled , _log, "Color transformation disabled" );
|
|
InfoIf(!_adjustmentEnabled , _log, "Color adjustment disabled" );
|
|
InfoIf(!_temperatureEnabled, _log, "Color temperature disabled" );
|
|
|
|
// initialize the image processor factory
|
|
ImageProcessorFactory::getInstance().init(
|
|
_ledString,
|
|
jsonConfig["blackborderdetector"]
|
|
);
|
|
|
|
// initialize the color smoothing filter
|
|
_device = createColorSmoothing(jsonConfig["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"]);
|
|
|
|
unsigned int hwLedCount = jsonConfig["device"].get("ledCount",getLedCount()).asUInt();
|
|
_hwLedCount = std::max(hwLedCount, getLedCount());
|
|
Debug(_log,"configured leds: %d hw leds: %d", getLedCount(), _hwLedCount);
|
|
WarningIf(hwLedCount < getLedCount(), _log, "more leds configured than available. check 'ledCount' in 'device' section");
|
|
|
|
// initialize the leds
|
|
update();
|
|
}
|
|
|
|
|
|
Hyperion::~Hyperion()
|
|
{
|
|
// switch off all leds
|
|
clearall();
|
|
_device->switchOff();
|
|
|
|
// delete components on exit of hyperion core
|
|
delete _effectEngine;
|
|
delete _device;
|
|
delete _raw2ledTransform;
|
|
delete _raw2ledTemperature;
|
|
delete _raw2ledAdjustment;
|
|
delete _messageForwarder;
|
|
}
|
|
|
|
unsigned Hyperion::getLedCount() const
|
|
{
|
|
return _ledString.leds().size();
|
|
}
|
|
|
|
void Hyperion::registerPriority(const std::string name, const int priority)
|
|
{
|
|
Info(_log, "Register new input source named '%s' for priority channel '%d'", name.c_str(), priority );
|
|
|
|
for(auto const &entry : _priorityRegister)
|
|
{
|
|
WarningIf( ( entry.first != name && entry.second == priority), _log,
|
|
"Input source '%s' uses same priority channel (%d) as '%s'.", name.c_str(), priority, entry.first.c_str());
|
|
}
|
|
|
|
_priorityRegister.emplace(name,priority);
|
|
}
|
|
|
|
void Hyperion::unRegisterPriority(const std::string name)
|
|
{
|
|
Info(_log, "Unregister input source named '%s' from priority register", name.c_str());
|
|
_priorityRegister.erase(name);
|
|
}
|
|
|
|
void Hyperion::setSourceAutoSelectEnabled(bool enabled)
|
|
{
|
|
_sourceAutoSelectEnabled = enabled;
|
|
if (! _sourceAutoSelectEnabled)
|
|
{
|
|
setCurrentSourcePriority(_muxer.getCurrentPriority());
|
|
}
|
|
DebugIf( !_sourceAutoSelectEnabled, _log, "source auto select is disabled");
|
|
InfoIf(_sourceAutoSelectEnabled, _log, "set current input source to auto select");
|
|
}
|
|
|
|
bool Hyperion::setCurrentSourcePriority(int priority )
|
|
{
|
|
bool priorityValid = _muxer.hasPriority(priority);
|
|
if (priorityValid)
|
|
{
|
|
DebugIf(_sourceAutoSelectEnabled, _log, "source auto select is disabled");
|
|
_sourceAutoSelectEnabled = false;
|
|
_currentSourcePriority = priority;
|
|
Info(_log, "set current input source to priority channel %d", _currentSourcePriority);
|
|
}
|
|
|
|
return priorityValid;
|
|
}
|
|
|
|
void Hyperion::setComponentState(const Components component, const bool state)
|
|
{
|
|
switch(component)
|
|
{
|
|
case SMOOTHING:
|
|
break;
|
|
case BLACKBORDER:
|
|
break;
|
|
case KODICHECKER:
|
|
{
|
|
KODIVideoChecker* kodiVideoChecker = KODIVideoChecker::getInstance();
|
|
if (kodiVideoChecker != nullptr)
|
|
state ? kodiVideoChecker->start() : kodiVideoChecker->stop();
|
|
else
|
|
Debug(_log, "Can't get instance from: '%s'", componentToString(component));
|
|
break;
|
|
}
|
|
case FORWARDER:
|
|
//_messageForwarder
|
|
break;
|
|
case UDPLISTENER:
|
|
break;
|
|
case BOBLIGHTSERVER:
|
|
break;
|
|
case GRABBER:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Hyperion::setColor(int priority, const ColorRgb &color, const int timeout_ms, bool clearEffects)
|
|
{
|
|
// create led output
|
|
std::vector<ColorRgb> ledColors(_ledString.leds().size(), color);
|
|
|
|
// set colors
|
|
setColors(priority, ledColors, timeout_ms, clearEffects);
|
|
}
|
|
|
|
void Hyperion::setColors(int priority, const std::vector<ColorRgb>& ledColors, const int timeout_ms, bool clearEffects)
|
|
{
|
|
// clear effects if this call does not come from an effect
|
|
if (clearEffects)
|
|
{
|
|
_effectEngine->channelCleared(priority);
|
|
}
|
|
|
|
if (timeout_ms > 0)
|
|
{
|
|
const uint64_t timeoutTime = QDateTime::currentMSecsSinceEpoch() + timeout_ms;
|
|
_muxer.setInput(priority, ledColors, timeoutTime);
|
|
}
|
|
else
|
|
{
|
|
_muxer.setInput(priority, ledColors);
|
|
}
|
|
|
|
if (priority == _muxer.getCurrentPriority())
|
|
{
|
|
update();
|
|
}
|
|
}
|
|
|
|
const std::vector<std::string> & Hyperion::getTransformIds() const
|
|
{
|
|
return _raw2ledTransform->getTransformIds();
|
|
}
|
|
|
|
const std::vector<std::string> & Hyperion::getTemperatureIds() const
|
|
{
|
|
return _raw2ledTemperature->getCorrectionIds();
|
|
}
|
|
|
|
const std::vector<std::string> & Hyperion::getAdjustmentIds() const
|
|
{
|
|
return _raw2ledAdjustment->getAdjustmentIds();
|
|
}
|
|
|
|
ColorTransform * Hyperion::getTransform(const std::string& id)
|
|
{
|
|
return _raw2ledTransform->getTransform(id);
|
|
}
|
|
|
|
ColorCorrection * Hyperion::getTemperature(const std::string& id)
|
|
{
|
|
return _raw2ledTemperature->getCorrection(id);
|
|
}
|
|
|
|
ColorAdjustment * Hyperion::getAdjustment(const std::string& id)
|
|
{
|
|
return _raw2ledAdjustment->getAdjustment(id);
|
|
}
|
|
|
|
void Hyperion::transformsUpdated()
|
|
{
|
|
update();
|
|
}
|
|
|
|
void Hyperion::correctionsUpdated()
|
|
{
|
|
update();
|
|
}
|
|
|
|
void Hyperion::temperaturesUpdated()
|
|
{
|
|
update();
|
|
}
|
|
|
|
void Hyperion::adjustmentsUpdated()
|
|
{
|
|
update();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void Hyperion::clearall()
|
|
{
|
|
_muxer.clearAll();
|
|
|
|
// update leds
|
|
update();
|
|
|
|
// send clearall signal to the effect engine
|
|
_effectEngine->allChannelsCleared();
|
|
}
|
|
|
|
int Hyperion::getCurrentPriority() const
|
|
{
|
|
|
|
return _sourceAutoSelectEnabled || !_muxer.hasPriority(_currentSourcePriority) ? _muxer.getCurrentPriority() : _currentSourcePriority;
|
|
}
|
|
|
|
QList<int> Hyperion::getActivePriorities() const
|
|
{
|
|
return _muxer.getPriorities();
|
|
}
|
|
|
|
const Hyperion::InputInfo &Hyperion::getPriorityInfo(const int priority) const
|
|
{
|
|
return _muxer.getInputInfo(priority);
|
|
}
|
|
|
|
const std::list<EffectDefinition> & Hyperion::getEffects() const
|
|
{
|
|
return _effectEngine->getEffects();
|
|
}
|
|
|
|
const std::list<ActiveEffectDefinition> & Hyperion::getActiveEffects()
|
|
{
|
|
return _effectEngine->getActiveEffects();
|
|
}
|
|
|
|
int Hyperion::setEffect(const std::string &effectName, int priority, int timeout)
|
|
{
|
|
return _effectEngine->runEffect(effectName, priority, timeout);
|
|
}
|
|
|
|
int Hyperion::setEffect(const std::string &effectName, const Json::Value &args, int priority, int timeout)
|
|
{
|
|
return _effectEngine->runEffect(effectName, args, priority, timeout);
|
|
}
|
|
|
|
void Hyperion::update()
|
|
{
|
|
// Update the muxer, cleaning obsolete priorities
|
|
_muxer.setCurrentTime(QDateTime::currentMSecsSinceEpoch());
|
|
|
|
// Obtain the current priority channel
|
|
int priority = _sourceAutoSelectEnabled || !_muxer.hasPriority(_currentSourcePriority) ? _muxer.getCurrentPriority() : _currentSourcePriority;
|
|
const PriorityMuxer::InputInfo & priorityInfo = _muxer.getInputInfo(priority);
|
|
|
|
// copy ledcolors to local buffer
|
|
_ledBuffer.reserve(_hwLedCount);
|
|
_ledBuffer = priorityInfo.ledColors;
|
|
|
|
// Apply the correction and the transform to each led and color-channel
|
|
// Avoid applying correction, the same task is performed by adjustment
|
|
if (_transformEnabled) _raw2ledTransform->applyTransform(_ledBuffer);
|
|
if (_adjustmentEnabled) _raw2ledAdjustment->applyAdjustment(_ledBuffer);
|
|
if (_temperatureEnabled) _raw2ledTemperature->applyCorrection(_ledBuffer);
|
|
|
|
const std::vector<Led>& leds = _ledString.leds();
|
|
|
|
int i = 0;
|
|
for (ColorRgb& color : _ledBuffer)
|
|
{
|
|
const ColorOrder ledColorOrder = leds.at(i).colorOrder;
|
|
// correct the color byte order
|
|
switch (ledColorOrder)
|
|
{
|
|
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:
|
|
{
|
|
std::swap(color.red, color.green);
|
|
std::swap(color.green, color.blue);
|
|
break;
|
|
}
|
|
case ORDER_BRG:
|
|
{
|
|
std::swap(color.red, color.blue);
|
|
std::swap(color.green, color.blue);
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if ( _hwLedCount > _ledBuffer.size() )
|
|
{
|
|
_ledBuffer.resize(_hwLedCount, ColorRgb::BLACK);
|
|
}
|
|
|
|
// Write the data to the device
|
|
_device->write(_ledBuffer);
|
|
|
|
// 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);
|
|
}
|
|
}
|