mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Details coming soon.
This commit is contained in:
106
libsrc/hyperion/CaptureCont.cpp
Normal file
106
libsrc/hyperion/CaptureCont.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#include <hyperion/CaptureCont.h>
|
||||
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
CaptureCont::CaptureCont(Hyperion* hyperion)
|
||||
: QObject()
|
||||
, _hyperion(hyperion)
|
||||
, _systemCaptEnabled(false)
|
||||
, _v4lCaptEnabled(false)
|
||||
{
|
||||
// settings changes
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &CaptureCont::handleSettingsUpdate);
|
||||
|
||||
// comp changes
|
||||
connect(_hyperion, &Hyperion::componentStateChanged, this, &CaptureCont::componentStateChanged);
|
||||
|
||||
// init
|
||||
handleSettingsUpdate(settings::INSTCAPTURE, _hyperion->getSetting(settings::INSTCAPTURE));
|
||||
}
|
||||
|
||||
CaptureCont::~CaptureCont()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CaptureCont::handleV4lImage(const Image<ColorRgb> & image)
|
||||
{
|
||||
_hyperion->setInputImage(_v4lCaptPrio, image);
|
||||
}
|
||||
|
||||
void CaptureCont::handleSystemImage(const Image<ColorRgb>& image)
|
||||
{
|
||||
_hyperion->setInputImage(_systemCaptPrio, image);
|
||||
}
|
||||
|
||||
|
||||
void CaptureCont::setSystemCaptureEnable(const bool& enable)
|
||||
{
|
||||
if(_systemCaptEnabled != enable)
|
||||
{
|
||||
if(enable)
|
||||
{
|
||||
_hyperion->registerInput(_systemCaptPrio, hyperion::COMP_GRABBER, "System", "DoNotKnow");
|
||||
connect(_hyperion, &Hyperion::systemImage, this, &CaptureCont::handleSystemImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
disconnect(_hyperion, &Hyperion::systemImage, this, &CaptureCont::handleSystemImage);
|
||||
_hyperion->clear(_systemCaptPrio);
|
||||
}
|
||||
_systemCaptEnabled = enable;
|
||||
_hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_GRABBER, enable);
|
||||
}
|
||||
}
|
||||
|
||||
void CaptureCont::setV4LCaptureEnable(const bool& enable)
|
||||
{
|
||||
if(_v4lCaptEnabled != enable)
|
||||
{
|
||||
if(enable)
|
||||
{
|
||||
_hyperion->registerInput(_v4lCaptPrio, hyperion::COMP_V4L, "System", "DoNotKnow");
|
||||
connect(_hyperion, &Hyperion::v4lImage, this, &CaptureCont::handleV4lImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
disconnect(_hyperion, &Hyperion::v4lImage, this, &CaptureCont::handleV4lImage);
|
||||
_hyperion->clear(_v4lCaptPrio);
|
||||
}
|
||||
_v4lCaptEnabled = enable;
|
||||
_hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_V4L, enable);
|
||||
}
|
||||
}
|
||||
|
||||
void CaptureCont::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::INSTCAPTURE)
|
||||
{
|
||||
const QJsonObject& obj = config.object();
|
||||
if(_v4lCaptPrio != obj["v4lPriority"].toInt(240))
|
||||
{
|
||||
setV4LCaptureEnable(false); // clear prio
|
||||
_v4lCaptPrio = obj["v4lPriority"].toInt(240);
|
||||
}
|
||||
if(_systemCaptPrio != obj["systemPriority"].toInt(250))
|
||||
{
|
||||
setSystemCaptureEnable(false); // clear prio
|
||||
_systemCaptPrio = obj["systemPriority"].toInt(250);
|
||||
}
|
||||
|
||||
setV4LCaptureEnable(obj["v4lEnable"].toBool(true));
|
||||
setSystemCaptureEnable(obj["systemEnable"].toBool(true));
|
||||
}
|
||||
}
|
||||
|
||||
void CaptureCont::componentStateChanged(const hyperion::Components component, bool enable)
|
||||
{
|
||||
if(component == hyperion::COMP_GRABBER)
|
||||
{
|
||||
setSystemCaptureEnable(enable);
|
||||
}
|
||||
else if(component == hyperion::COMP_V4L)
|
||||
{
|
||||
setV4LCaptureEnable(enable);
|
||||
}
|
||||
}
|
@@ -1,27 +1,72 @@
|
||||
#include <hyperion/ComponentRegister.h>
|
||||
#include <iostream>
|
||||
|
||||
ComponentRegister::ComponentRegister()
|
||||
: _log(Logger::getInstance("ComponentRegister"))
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
ComponentRegister::ComponentRegister(Hyperion* hyperion)
|
||||
: _hyperion(hyperion)
|
||||
, _log(Logger::getInstance("ComponentRegister"))
|
||||
{
|
||||
// init all comps to false
|
||||
QVector<hyperion::Components> vect;
|
||||
vect << COMP_ALL << COMP_SMOOTHING << COMP_BLACKBORDER << COMP_FORWARDER << COMP_UDPLISTENER << COMP_BOBLIGHTSERVER << COMP_GRABBER << COMP_V4L << COMP_LEDDEVICE;
|
||||
for(auto e : vect)
|
||||
{
|
||||
_componentStates.emplace(e, ((e == COMP_ALL) ? true : false));
|
||||
}
|
||||
}
|
||||
|
||||
ComponentRegister::~ComponentRegister()
|
||||
{
|
||||
}
|
||||
|
||||
bool ComponentRegister::setHyperionEnable(const bool& state)
|
||||
{
|
||||
if(!state && _prevComponentStates.empty())
|
||||
{
|
||||
Debug(_log,"Disable Hyperion, store current component states");
|
||||
for(const auto comp : _componentStates)
|
||||
{
|
||||
// save state
|
||||
_prevComponentStates.emplace(comp.first, comp.second);
|
||||
// disable if enabled
|
||||
if(comp.second)
|
||||
_hyperion->setComponentState(comp.first, false);
|
||||
}
|
||||
componentStateChanged(COMP_ALL, false);
|
||||
return true;
|
||||
}
|
||||
else if(state && !_prevComponentStates.empty())
|
||||
{
|
||||
Debug(_log,"Enable Hyperion, recover previous component states");
|
||||
for(const auto comp : _prevComponentStates)
|
||||
{
|
||||
// if comp was enabled, enable again
|
||||
if(comp.second)
|
||||
_hyperion->setComponentState(comp.first, true);
|
||||
|
||||
}
|
||||
_prevComponentStates.clear();
|
||||
componentStateChanged(COMP_ALL, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ComponentRegister::isComponentEnabled(const hyperion::Components& comp) const
|
||||
{
|
||||
return _componentStates.at(comp);
|
||||
}
|
||||
|
||||
void ComponentRegister::componentStateChanged(const hyperion::Components comp, const bool activated)
|
||||
{
|
||||
Info(_log, "%s: %s", componentToString(comp), (activated? "activated" : "off"));
|
||||
_componentStates.emplace(comp,activated);
|
||||
_componentStates[comp] = activated;
|
||||
|
||||
/* for(auto comp : _componentStates)
|
||||
if(_componentStates[comp] != activated)
|
||||
{
|
||||
std::cout << hyperion::componentToIdString(comp.first) << " " << comp.second << std::endl;
|
||||
Debug( _log, "%s: %s", componentToString(comp), (activated? "enabled" : "disabled"));
|
||||
_componentStates[comp] = activated;
|
||||
// emit component has changed state
|
||||
emit updatedComponentState(comp, activated);
|
||||
}
|
||||
std::cout << "\n";
|
||||
*/
|
||||
}
|
||||
|
||||
|
@@ -13,7 +13,6 @@ Grabber::Grabber(QString grabberName, int width, int height, int cropLeft, int c
|
||||
, _cropBottom(0)
|
||||
, _enabled(true)
|
||||
, _log(Logger::getInstance(grabberName))
|
||||
|
||||
{
|
||||
setVideoMode(VIDEO_2D);
|
||||
setCropping(cropLeft, cropRight, cropTop, cropBottom);
|
||||
@@ -44,7 +43,7 @@ void Grabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTo
|
||||
{
|
||||
if (cropLeft + cropRight >= (unsigned)_width || cropTop + cropBottom >= (unsigned)_height)
|
||||
{
|
||||
Error(_log, "Rejecting invalid crop values: left: %d, right: %d, top: %d, bottom: %d", cropLeft, cropRight, cropTop, cropBottom);
|
||||
Error(_log, "Rejecting invalid crop values: left: %d, right: %d, top: %d, bottom: %d, higher than height/width %d/%d", cropLeft, cropRight, cropTop, cropBottom, _height, _width);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -68,3 +67,19 @@ void Grabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTo
|
||||
Info(_log, "Cropping image: width=%d height=%d; crop: left=%d right=%d top=%d bottom=%d ", _width, _height, cropLeft, cropRight, cropTop, cropBottom);
|
||||
}
|
||||
}
|
||||
|
||||
void Grabber::setWidthHeight(int width, int height)
|
||||
{
|
||||
// eval changes with crop
|
||||
if (width>0 && height>0)
|
||||
{
|
||||
if (_cropLeft + _cropRight >= width || _cropTop + _cropBottom >= height)
|
||||
{
|
||||
Error(_log, "Rejecting invalid width/height values as it collides with image cropping: width: %d, height: %d", width, height);
|
||||
return;
|
||||
}
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,111 +1,51 @@
|
||||
// Hyperion includes
|
||||
#include <hyperion/ImageProcessorFactory.h>
|
||||
#include <hyperion/ImageProcessor.h>
|
||||
#include <hyperion/GrabberWrapper.h>
|
||||
#include <hyperion/Grabber.h>
|
||||
#include <HyperionConfig.h>
|
||||
|
||||
GrabberWrapper::GrabberWrapper(QString grabberName, Grabber * ggrabber, unsigned width, unsigned height, const unsigned updateRate_Hz, const int priority, hyperion::Components grabberComponentId)
|
||||
//forwarder
|
||||
#include <hyperion/MessageForwarder.h>
|
||||
|
||||
// qt
|
||||
#include <QTimer>
|
||||
|
||||
GrabberWrapper::GrabberWrapper(QString grabberName, Grabber * ggrabber, unsigned width, unsigned height, const unsigned updateRate_Hz)
|
||||
: _grabberName(grabberName)
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
, _priority(priority)
|
||||
, _timer()
|
||||
, _timer(new QTimer(this))
|
||||
, _updateInterval_ms(1000/updateRate_Hz)
|
||||
, _timeout_ms(2 * _updateInterval_ms)
|
||||
, _log(Logger::getInstance(grabberName))
|
||||
, _forward(true)
|
||||
, _processor(ImageProcessorFactory::getInstance().newImageProcessor())
|
||||
, _grabberComponentId(grabberComponentId)
|
||||
, _ggrabber(ggrabber)
|
||||
, _image(0,0)
|
||||
, _ledColors(Hyperion::getInstance()->getLedCount(), ColorRgb{0,0,0})
|
||||
, _imageProcessorEnabled(true)
|
||||
{
|
||||
_timer.setSingleShot(false);
|
||||
// Configure the timer to generate events every n milliseconds
|
||||
_timer.setInterval(_updateInterval_ms);
|
||||
_timer->setInterval(_updateInterval_ms);
|
||||
|
||||
_image.resize(width, height);
|
||||
_processor->setSize(width, height);
|
||||
|
||||
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
|
||||
_hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_BLACKBORDER, _processor->blackBorderDetectorEnabled());
|
||||
qRegisterMetaType<hyperion::Components>("hyperion::Components");
|
||||
|
||||
connect(_hyperion, SIGNAL(imageToLedsMappingChanged(int)), _processor, SLOT(setLedMappingType(int)));
|
||||
connect(_hyperion, SIGNAL(componentStateChanged(hyperion::Components,bool)), this, SLOT(componentStateChanged(hyperion::Components,bool)));
|
||||
connect(_hyperion, SIGNAL(videoMode(VideoMode)), this, SLOT(setVideoMode(VideoMode)));
|
||||
connect(this, SIGNAL(emitImage(int, const Image<ColorRgb>&, const int)), _hyperion, SLOT(setImage(int, const Image<ColorRgb>&, const int)) );
|
||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(actionWrapper()));
|
||||
|
||||
connect(_timer, &QTimer::timeout, this, &GrabberWrapper::action);
|
||||
}
|
||||
|
||||
GrabberWrapper::~GrabberWrapper()
|
||||
{
|
||||
stop();
|
||||
Debug(_log,"Close grabber: %s", QSTRING_CSTR(_grabberName));
|
||||
delete _processor;
|
||||
}
|
||||
|
||||
bool GrabberWrapper::start()
|
||||
{
|
||||
// Start the timer with the pre configured interval
|
||||
_timer.start();
|
||||
_hyperion->registerPriority(_grabberName, _priority);
|
||||
return _timer.isActive();
|
||||
|
||||
_timer->start();
|
||||
return _timer->isActive();
|
||||
}
|
||||
|
||||
void GrabberWrapper::stop()
|
||||
{
|
||||
// Stop the timer, effectivly stopping the process
|
||||
_timer.stop();
|
||||
_hyperion->unRegisterPriority(_grabberName);
|
||||
}
|
||||
|
||||
void GrabberWrapper::actionWrapper()
|
||||
{
|
||||
_ggrabber->setEnabled(_hyperion->isCurrentPriority(_priority));
|
||||
action();
|
||||
}
|
||||
|
||||
void GrabberWrapper::componentStateChanged(const hyperion::Components component, bool enable)
|
||||
{
|
||||
if (component == _grabberComponentId)
|
||||
{
|
||||
if (_timer.isActive() != enable)
|
||||
{
|
||||
if (enable) start();
|
||||
else stop();
|
||||
|
||||
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
|
||||
|
||||
if ( enable == _timer.isActive() )
|
||||
{
|
||||
Info(_log, "grabber change state to %s", (_timer.isActive() ? "enabled" : "disabled") );
|
||||
}
|
||||
else
|
||||
{
|
||||
WarningIf( enable, _log, "enable grabber failed");
|
||||
}
|
||||
}
|
||||
_hyperion->getComponentRegister().componentStateChanged(component, _timer.isActive());
|
||||
}
|
||||
|
||||
if (component == hyperion::COMP_BLACKBORDER)
|
||||
{
|
||||
if (_processor->blackBorderDetectorEnabled() != enable)
|
||||
{
|
||||
_processor->enableBlackBorderDetector(enable);
|
||||
Info(_log, "bb detector change state to %s", (_processor->blackBorderDetectorEnabled() ? "enabled" : "disabled") );
|
||||
}
|
||||
_hyperion->getComponentRegister().componentStateChanged(component, _processor->blackBorderDetectorEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
void GrabberWrapper::setColors(const std::vector<ColorRgb> &ledColors, const int timeout_ms)
|
||||
{
|
||||
_hyperion->setColors(_priority, ledColors, timeout_ms, true, _grabberComponentId);
|
||||
_timer->stop();
|
||||
}
|
||||
|
||||
QStringList GrabberWrapper::availableGrabbers()
|
||||
@@ -140,7 +80,7 @@ QStringList GrabberWrapper::availableGrabbers()
|
||||
}
|
||||
|
||||
|
||||
void GrabberWrapper::setVideoMode(const VideoMode mode)
|
||||
void GrabberWrapper::setVideoMode(const VideoMode& mode)
|
||||
{
|
||||
if (_ggrabber != nullptr)
|
||||
{
|
||||
@@ -154,7 +94,77 @@ void GrabberWrapper::setCropping(unsigned cropLeft, unsigned cropRight, unsigned
|
||||
_ggrabber->setCropping(cropLeft, cropRight, cropTop, cropBottom);
|
||||
}
|
||||
|
||||
void GrabberWrapper::setImageProcessorEnabled(bool enable)
|
||||
void GrabberWrapper::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
_imageProcessorEnabled = enable;
|
||||
if(type == settings::V4L2 || type == settings::SYSTEMCAPTURE)
|
||||
{
|
||||
// extract settings
|
||||
QJsonObject obj;
|
||||
if(config.isArray() && !config.isEmpty())
|
||||
obj = config.array().at(0).toObject();
|
||||
else
|
||||
obj = config.object();
|
||||
|
||||
if(type == settings::SYSTEMCAPTURE)
|
||||
{
|
||||
// width/height
|
||||
_ggrabber->setWidthHeight(obj["width"].toInt(96), obj["height"].toInt(96));
|
||||
|
||||
// display index for MAC
|
||||
_ggrabber->setDisplayIndex(obj["display"].toInt(0));
|
||||
|
||||
// device path for Framebuffer
|
||||
_ggrabber->setDevicePath(obj["device"].toString("/dev/fb0"));
|
||||
|
||||
// pixel decimation for x11
|
||||
_ggrabber->setPixelDecimation(obj["pixelDecimation"].toInt(8));
|
||||
|
||||
// crop for system capture
|
||||
_ggrabber->setCropping(
|
||||
obj["cropLeft"].toInt(0),
|
||||
obj["cropRight"].toInt(0),
|
||||
obj["cropTop"].toInt(0),
|
||||
obj["cropBottom"].toInt(0));
|
||||
|
||||
// eval new update timer (not for v4l)
|
||||
if(_updateInterval_ms != 1000/obj["frequency_Hz"].toInt(10))
|
||||
{
|
||||
_updateInterval_ms = 1000/obj["frequency_Hz"].toInt(10);
|
||||
const bool& timerWasActive = _timer->isActive();
|
||||
_timer->stop();
|
||||
_timer->setInterval(_updateInterval_ms);
|
||||
if(timerWasActive)
|
||||
_timer->start();
|
||||
}
|
||||
}
|
||||
|
||||
if(type == settings::V4L2)
|
||||
{
|
||||
// pixel decimation for v4l
|
||||
_ggrabber->setPixelDecimation(obj["sizeDecimation"].toInt(8));
|
||||
|
||||
// crop for v4l
|
||||
_ggrabber->setCropping(
|
||||
obj["cropLeft"].toInt(0),
|
||||
obj["cropRight"].toInt(0),
|
||||
obj["cropTop"].toInt(0),
|
||||
obj["cropBottom"].toInt(0));
|
||||
|
||||
_ggrabber->setSignalDetectionEnable(obj["signalDetection"].toBool(true));
|
||||
_ggrabber->setSignalDetectionOffset(
|
||||
obj["sDHOffsetMin"].toDouble(0.25),
|
||||
obj["sDVOffsetMin"].toDouble(0.25),
|
||||
obj["sDHOffsetMax"].toDouble(0.75),
|
||||
obj["sDVOffsetMax"].toDouble(0.75));
|
||||
_ggrabber->setSignalThreshold(
|
||||
obj["redSignalThreshold"].toDouble(0.0)/100.0,
|
||||
obj["greenSignalThreshold"].toDouble(0.0)/100.0,
|
||||
obj["blueSignalThreshold"].toDouble(0.0)/100.0);
|
||||
_ggrabber->setInputVideoStandard(
|
||||
obj["input"].toInt(0),
|
||||
parseVideoStandard(obj["standard"].toString("no-change")));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -9,27 +9,56 @@
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
ImageProcessor::ImageProcessor(const LedString& ledString, const QJsonObject & blackborderConfig)
|
||||
: QObject()
|
||||
// global transform method
|
||||
int ImageProcessor::mappingTypeToInt(QString mappingType)
|
||||
{
|
||||
if (mappingType == "unicolor_mean" )
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
// global transform method
|
||||
QString ImageProcessor::mappingTypeToStr(int mappingType)
|
||||
{
|
||||
if (mappingType == 1 )
|
||||
return "unicolor_mean";
|
||||
|
||||
return "multicolor_mean";
|
||||
}
|
||||
|
||||
ImageProcessor::ImageProcessor(const LedString& ledString, Hyperion* hyperion)
|
||||
: QObject(hyperion)
|
||||
, _log(Logger::getInstance("BLACKBORDER"))
|
||||
, _ledString(ledString)
|
||||
, _borderProcessor(new BlackBorderProcessor(blackborderConfig) )
|
||||
, _borderProcessor(new BlackBorderProcessor(hyperion, this))
|
||||
, _imageToLeds(nullptr)
|
||||
, _mappingType(0)
|
||||
, _userMappingType(0)
|
||||
, _hardMappingType(0)
|
||||
, _hyperion(hyperion)
|
||||
{
|
||||
// this is when we want to change the mapping for all input sources
|
||||
// connect(Hyperion::getInstance(), SIGNAL(imageToLedsMappingChanged(int)), this, SLOT(setLedMappingType(int)));
|
||||
// init
|
||||
handleSettingsUpdate(settings::COLOR, _hyperion->getSetting(settings::COLOR));
|
||||
// listen for changes in color - ledmapping
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &ImageProcessor::handleSettingsUpdate);
|
||||
}
|
||||
|
||||
ImageProcessor::~ImageProcessor()
|
||||
{
|
||||
delete _imageToLeds;
|
||||
delete _borderProcessor;
|
||||
}
|
||||
|
||||
unsigned ImageProcessor::getLedCount() const
|
||||
void ImageProcessor::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
return _ledString.leds().size();
|
||||
if(type == settings::COLOR)
|
||||
{
|
||||
const QJsonObject& obj = config.object();
|
||||
int newType = mappingTypeToInt(obj["imageToLedMappingType"].toString());
|
||||
if(_userMappingType != newType)
|
||||
{
|
||||
setLedMappingType(newType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageProcessor::setSize(const unsigned width, const unsigned height)
|
||||
@@ -47,9 +76,24 @@ void ImageProcessor::setSize(const unsigned width, const unsigned height)
|
||||
_imageToLeds = (width>0 && height>0) ? (new ImageToLedsMap(width, height, 0, 0, _ledString.leds())) : nullptr;
|
||||
}
|
||||
|
||||
void ImageProcessor::enableBlackBorderDetector(bool enable)
|
||||
void ImageProcessor::setLedString(const LedString& ledString)
|
||||
{
|
||||
_borderProcessor->setEnabled(enable);
|
||||
_ledString = ledString;
|
||||
|
||||
// get current width/height
|
||||
const unsigned width = _imageToLeds->width();
|
||||
const unsigned height = _imageToLeds->height();
|
||||
|
||||
// Clean up the old buffer and mapping
|
||||
delete _imageToLeds;
|
||||
|
||||
// Construct a new buffer and mapping
|
||||
_imageToLeds = new ImageToLedsMap(width, height, 0, 0, _ledString.leds());
|
||||
}
|
||||
|
||||
void ImageProcessor::setBlackbarDetectDisable(bool enable)
|
||||
{
|
||||
_borderProcessor->setHardDisable(enable);
|
||||
}
|
||||
|
||||
bool ImageProcessor::blackBorderDetectorEnabled()
|
||||
@@ -59,29 +103,23 @@ bool ImageProcessor::blackBorderDetectorEnabled()
|
||||
|
||||
void ImageProcessor::setLedMappingType(int mapType)
|
||||
{
|
||||
Debug(_log, "set led mapping to type %d", mapType);
|
||||
_mappingType = mapType;
|
||||
// if the _hardMappingType is >-1 we aren't allowed to overwrite it
|
||||
_userMappingType = mapType;
|
||||
Debug(_log, "set user led mapping to %s", QSTRING_CSTR(mappingTypeToStr(mapType)));
|
||||
if(_hardMappingType == -1)
|
||||
{
|
||||
_mappingType = mapType;
|
||||
}
|
||||
}
|
||||
|
||||
int ImageProcessor::ledMappingType()
|
||||
void ImageProcessor::setHardLedMappingType(int mapType)
|
||||
{
|
||||
return _mappingType;
|
||||
}
|
||||
|
||||
int ImageProcessor::mappingTypeToInt(QString mappingType)
|
||||
{
|
||||
if (mappingType == "unicolor_mean" )
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString ImageProcessor::mappingTypeToStr(int mappingType)
|
||||
{
|
||||
if (mappingType == 1 )
|
||||
return "unicolor_mean";
|
||||
|
||||
return "multicolor_mean";
|
||||
// force the maptype, if set to -1 we use the last requested _userMappingType
|
||||
_hardMappingType = mapType;
|
||||
if(mapType == -1)
|
||||
_mappingType = _userMappingType;
|
||||
else
|
||||
_mappingType = mapType;
|
||||
}
|
||||
|
||||
bool ImageProcessor::getScanParameters(size_t led, double &hscanBegin, double &hscanEnd, double &vscanBegin, double &vscanEnd) const
|
||||
@@ -97,4 +135,3 @@ bool ImageProcessor::getScanParameters(size_t led, double &hscanBegin, double &h
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -1,25 +0,0 @@
|
||||
// Hyperion includes
|
||||
#include <hyperion/ImageProcessorFactory.h>
|
||||
#include <hyperion/ImageProcessor.h>
|
||||
|
||||
ImageProcessorFactory& ImageProcessorFactory::getInstance()
|
||||
{
|
||||
static ImageProcessorFactory instance;
|
||||
// Return the singleton instance
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ImageProcessorFactory::init(const LedString& ledString, const QJsonObject & blackborderConfig, int mappingType)
|
||||
{
|
||||
_ledString = ledString;
|
||||
_blackborderConfig = blackborderConfig;
|
||||
_mappingType = mappingType;
|
||||
}
|
||||
|
||||
ImageProcessor* ImageProcessorFactory::newImageProcessor() const
|
||||
{
|
||||
ImageProcessor* ip = new ImageProcessor(_ledString, _blackborderConfig);
|
||||
ip->setLedMappingType(_mappingType);
|
||||
|
||||
return ip;
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
// Qt includes
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
|
||||
#include "LinearColorSmoothing.h"
|
||||
#include <hyperion/Hyperion.h>
|
||||
@@ -8,38 +9,60 @@
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
// ledUpdateFrequency_hz = 0 > cause divide by zero!
|
||||
LinearColorSmoothing::LinearColorSmoothing( LedDevice * ledDevice, double ledUpdateFrequency_hz, int settlingTime_ms, unsigned updateDelay, bool continuousOutput)
|
||||
LinearColorSmoothing::LinearColorSmoothing( LedDevice * ledDevice, const QJsonDocument& config, Hyperion* hyperion)
|
||||
: LedDevice()
|
||||
, _ledDevice(ledDevice)
|
||||
, _updateInterval(1000 / ledUpdateFrequency_hz)
|
||||
, _settlingTime(settlingTime_ms)
|
||||
, _timer()
|
||||
, _outputDelay(updateDelay)
|
||||
, _log(Logger::getInstance("SMOOTHING"))
|
||||
, _hyperion(hyperion)
|
||||
, _updateInterval(1000)
|
||||
, _settlingTime(200)
|
||||
, _timer(new QTimer(this))
|
||||
, _outputDelay(0)
|
||||
, _writeToLedsEnable(true)
|
||||
, _continuousOutput(continuousOutput)
|
||||
, _continuousOutput(false)
|
||||
, _pause(false)
|
||||
, _currentConfigId(0)
|
||||
{
|
||||
_log = Logger::getInstance("Smoothing");
|
||||
_timer.setSingleShot(false);
|
||||
_timer.setInterval(_updateInterval);
|
||||
Debug(_log, "Instance created");
|
||||
|
||||
// set initial state to true, as LedDevice::enabled() is true by default
|
||||
_hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, true);
|
||||
|
||||
// init cfg 0 (default)
|
||||
_cfgList.append({false, 200, 25, 0});
|
||||
handleSettingsUpdate(settings::SMOOTHING, config);
|
||||
|
||||
selectConfig( addConfig(_settlingTime, ledUpdateFrequency_hz, updateDelay) );
|
||||
|
||||
// add pause on cfg 1
|
||||
SMOOTHING_CFG cfg = {true, 100, 50, 0};
|
||||
SMOOTHING_CFG cfg = {true};
|
||||
_cfgList.append(cfg);
|
||||
Info( _log, "smoothing cfg %d: pause", _cfgList.count()-1);
|
||||
|
||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(updateLeds()));
|
||||
// listen for comp changes
|
||||
connect(_hyperion, &Hyperion::componentStateChanged, this, &LinearColorSmoothing::componentStateChange);
|
||||
// timer
|
||||
connect(_timer, SIGNAL(timeout()), this, SLOT(updateLeds()));
|
||||
}
|
||||
|
||||
LinearColorSmoothing::~LinearColorSmoothing()
|
||||
{
|
||||
// Make sure to switch off the underlying led-device (because switchOff is no longer forwarded)
|
||||
_ledDevice->switchOff();
|
||||
delete _ledDevice;
|
||||
}
|
||||
|
||||
void LinearColorSmoothing::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::SMOOTHING)
|
||||
{
|
||||
QJsonObject obj = config.object();
|
||||
_continuousOutput = obj["continuousOutput"].toBool(true);
|
||||
SMOOTHING_CFG cfg = {false, obj["time_ms"].toInt(200), unsigned(1000.0/obj["updateFrequency"].toDouble(25.0)), unsigned(obj["updateDelay"].toInt(0))};
|
||||
_cfgList[0] = cfg;
|
||||
// if current id is 0, we need to apply the settings (forced)
|
||||
if(!_currentConfigId)
|
||||
selectConfig(0, true);
|
||||
|
||||
if(enabled() != obj["enable"].toBool(true))
|
||||
setEnable(obj["enable"].toBool(true));
|
||||
}
|
||||
}
|
||||
|
||||
int LinearColorSmoothing::write(const std::vector<ColorRgb> &ledValues)
|
||||
@@ -53,7 +76,7 @@ int LinearColorSmoothing::write(const std::vector<ColorRgb> &ledValues)
|
||||
|
||||
_previousTime = QDateTime::currentMSecsSinceEpoch();
|
||||
_previousValues = ledValues;
|
||||
_timer.start();
|
||||
_timer->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -150,6 +173,11 @@ void LinearColorSmoothing::queueColors(const std::vector<ColorRgb> & ledColors)
|
||||
}
|
||||
}
|
||||
|
||||
void LinearColorSmoothing::componentStateChange(const hyperion::Components component, const bool state)
|
||||
{
|
||||
if(component == hyperion::COMP_SMOOTHING)
|
||||
setEnable(state);
|
||||
}
|
||||
|
||||
void LinearColorSmoothing::setEnable(bool enable)
|
||||
{
|
||||
@@ -157,9 +185,11 @@ void LinearColorSmoothing::setEnable(bool enable)
|
||||
|
||||
if (!enable)
|
||||
{
|
||||
_timer.stop();
|
||||
_timer->stop();
|
||||
_previousValues.clear();
|
||||
}
|
||||
// update comp register
|
||||
_hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, enable);
|
||||
}
|
||||
|
||||
void LinearColorSmoothing::setPause(bool pause)
|
||||
@@ -171,14 +201,14 @@ unsigned LinearColorSmoothing::addConfig(int settlingTime_ms, double ledUpdateFr
|
||||
{
|
||||
SMOOTHING_CFG cfg = {false, settlingTime_ms, int64_t(1000.0/ledUpdateFrequency_hz), updateDelay};
|
||||
_cfgList.append(cfg);
|
||||
|
||||
Info( _log, "smoothing cfg %d: interval: %d ms, settlingTime: %d ms, updateDelay: %d frames", _cfgList.count()-1, cfg.updateInterval, cfg.settlingTime, cfg.outputDelay );
|
||||
|
||||
//Debug( _log, "smoothing cfg %d: interval: %d ms, settlingTime: %d ms, updateDelay: %d frames", _cfgList.count()-1, cfg.updateInterval, cfg.settlingTime, cfg.outputDelay );
|
||||
return _cfgList.count() - 1;
|
||||
}
|
||||
|
||||
bool LinearColorSmoothing::selectConfig(unsigned cfg)
|
||||
bool LinearColorSmoothing::selectConfig(unsigned cfg, const bool& force)
|
||||
{
|
||||
if (_currentConfigId == cfg)
|
||||
if (_currentConfigId == cfg && !force)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -191,20 +221,34 @@ bool LinearColorSmoothing::selectConfig(unsigned cfg)
|
||||
|
||||
if (_cfgList[cfg].updateInterval != _updateInterval)
|
||||
{
|
||||
_timer.stop();
|
||||
_timer->stop();
|
||||
_updateInterval = _cfgList[cfg].updateInterval;
|
||||
_timer.setInterval(_updateInterval);
|
||||
_timer.start();
|
||||
_timer->setInterval(_updateInterval);
|
||||
_timer->start();
|
||||
}
|
||||
_currentConfigId = cfg;
|
||||
InfoIf( enabled() && !_pause, _log, "set smoothing cfg: %d, interval: %d ms, settlingTime: %d ms, updateDelay: %d frames", _currentConfigId, _updateInterval, _settlingTime, _outputDelay );
|
||||
//DebugIf( enabled() && !_pause, _log, "set smoothing cfg: %d, interval: %d ms, settlingTime: %d ms, updateDelay: %d frames", _currentConfigId, _updateInterval, _settlingTime, _outputDelay );
|
||||
InfoIf( _pause, _log, "set smoothing cfg: %d, pause", _currentConfigId );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// reset to default
|
||||
_currentConfigId = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void LinearColorSmoothing::startTimerDelayed()
|
||||
{
|
||||
QTimer::singleShot(500, this, SLOT(delayStartTimer()));
|
||||
}
|
||||
|
||||
void LinearColorSmoothing::stopTimer()
|
||||
{
|
||||
_timer->stop();
|
||||
}
|
||||
|
||||
void LinearColorSmoothing::delayStartTimer()
|
||||
{
|
||||
_timer->start();
|
||||
}
|
||||
|
@@ -5,13 +5,19 @@
|
||||
|
||||
|
||||
// Qt includes
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
|
||||
// hyperion incluse
|
||||
#include <leddevice/LedDevice.h>
|
||||
#include <utils/Components.h>
|
||||
|
||||
// settings
|
||||
#include <utils/settings.h>
|
||||
|
||||
class QTimer;
|
||||
class Logger;
|
||||
class Hyperion;
|
||||
|
||||
/// Linear Smooting class
|
||||
///
|
||||
/// This class processes the requested led values and forwards them to the device after applying
|
||||
@@ -23,10 +29,10 @@ class LinearColorSmoothing : public LedDevice
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param LedDevice the led device
|
||||
/// @param LedUpdatFrequency The frequency at which the leds will be updated (Hz)
|
||||
/// @param settingTime The time after which the updated led values have been fully applied (sec)
|
||||
/// @param updateDelay The number of frames to delay outgoing led updates
|
||||
LinearColorSmoothing(LedDevice *ledDevice, double ledUpdateFrequency, int settlingTime, unsigned updateDelay, bool continuousOutput);
|
||||
/// @param config The configuration document smoothing
|
||||
/// @param hyperion The hyperion parent instance
|
||||
///
|
||||
LinearColorSmoothing(LedDevice *ledDevice, const QJsonDocument& config, Hyperion* hyperion);
|
||||
|
||||
/// Destructor
|
||||
virtual ~LinearColorSmoothing();
|
||||
@@ -46,13 +52,53 @@ public:
|
||||
bool pause() { return _pause; } ;
|
||||
bool enabled() { return LedDevice::enabled() && !_pause; };
|
||||
|
||||
///
|
||||
/// @brief Add a new smoothing cfg which can be used with selectConfig()
|
||||
/// @param settlingTime_ms The buffer time
|
||||
/// @param ledUpdateFrequency_hz The frequency of update
|
||||
/// @param updateDelay The delay
|
||||
///
|
||||
/// @return The index of the cfg which can be passed to selectConfig()
|
||||
///
|
||||
unsigned addConfig(int settlingTime_ms, double ledUpdateFrequency_hz=25.0, unsigned updateDelay=0);
|
||||
bool selectConfig(unsigned cfg);
|
||||
|
||||
///
|
||||
/// @brief select a smoothing cfg given by cfg index from addConfig()
|
||||
/// @param cfg The index to use
|
||||
/// @param force Overwrite in any case the current values (used for cfg 0 settings udpate)
|
||||
///
|
||||
/// @return On success return else false (and falls back to cfg 0)
|
||||
///
|
||||
bool selectConfig(unsigned cfg, const bool& force = false);
|
||||
|
||||
///
|
||||
/// @ Helper methods to start the timer with delay (see delayStartSmooth())
|
||||
///
|
||||
void startTimerDelayed();
|
||||
void stopTimer();
|
||||
|
||||
public slots:
|
||||
///
|
||||
/// @brief Handle settings update from Hyperion Settingsmanager emit or this constructor
|
||||
/// @param type settingyType from enum
|
||||
/// @param config configuration object
|
||||
///
|
||||
void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config);
|
||||
|
||||
private slots:
|
||||
/// Timer callback which writes updated led values to the led device
|
||||
void updateLeds();
|
||||
|
||||
/// Delay timer slot to workaround the leddevice reconstruction segfault (dangling pointer)
|
||||
void delayStartTimer();
|
||||
|
||||
///
|
||||
/// @brief Handle component state changes
|
||||
/// @param component The component
|
||||
/// @param state The requested state
|
||||
///
|
||||
void componentStateChange(const hyperion::Components component, const bool state);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Pushes the colors into the output queue and popping the head to the led-device
|
||||
@@ -64,6 +110,12 @@ private:
|
||||
/// The led device
|
||||
LedDevice * _ledDevice;
|
||||
|
||||
/// Logger instance
|
||||
Logger* _log;
|
||||
|
||||
/// Hyperion instance
|
||||
Hyperion* _hyperion;
|
||||
|
||||
/// The interval at which to update the leds (msec)
|
||||
int64_t _updateInterval;
|
||||
|
||||
@@ -71,7 +123,7 @@ private:
|
||||
int64_t _settlingTime;
|
||||
|
||||
/// The Qt timer object
|
||||
QTimer _timer;
|
||||
QTimer * _timer;
|
||||
|
||||
/// The timestamp at which the target data should be fully applied
|
||||
int64_t _targetTime;
|
||||
@@ -92,7 +144,7 @@ private:
|
||||
|
||||
/// Prevent sending data to device when no intput data is sent
|
||||
bool _writeToLedsEnable;
|
||||
|
||||
|
||||
/// Flag for dis/enable continuous output to led device regardless there is new data or not
|
||||
bool _continuousOutput;
|
||||
|
||||
@@ -107,8 +159,8 @@ private:
|
||||
unsigned outputDelay;
|
||||
};
|
||||
|
||||
/// config list
|
||||
/// smooth config list
|
||||
QVector<SMOOTHING_CFG> _cfgList;
|
||||
|
||||
|
||||
unsigned _currentConfigId;
|
||||
};
|
||||
|
@@ -3,48 +3,110 @@
|
||||
|
||||
#include <hyperion/MessageForwarder.h>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
MessageForwarder::MessageForwarder()
|
||||
MessageForwarder::MessageForwarder(Hyperion* hyperion, const QJsonDocument & config)
|
||||
: QObject()
|
||||
, _hyperion(hyperion)
|
||||
, _log(Logger::getInstance("NETFORWARDER"))
|
||||
{
|
||||
handleSettingsUpdate(settings::NETFORWARD, config);
|
||||
// get settings updates
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &MessageForwarder::handleSettingsUpdate);
|
||||
}
|
||||
|
||||
MessageForwarder::~MessageForwarder()
|
||||
{
|
||||
}
|
||||
|
||||
void MessageForwarder::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::NETFORWARD)
|
||||
{
|
||||
// clear the current targets
|
||||
_jsonSlaves.clear();
|
||||
_protoSlaves.clear();
|
||||
// build new one
|
||||
const QJsonObject &obj = config.object();
|
||||
if ( !obj["json"].isNull() )
|
||||
{
|
||||
const QJsonArray & addr = obj["json"].toArray();
|
||||
for (const auto& entry : addr)
|
||||
{
|
||||
addJsonSlave(entry.toString());
|
||||
}
|
||||
}
|
||||
|
||||
if ( !obj["proto"].isNull() )
|
||||
{
|
||||
const QJsonArray & addr = obj["proto"].toArray();
|
||||
for (const auto& entry : addr)
|
||||
{
|
||||
addProtoSlave(entry.toString());
|
||||
}
|
||||
}
|
||||
InfoIf(obj["enable"].toBool(true), _log, "Forward now to json targets '%s' and proto targets '%s'", QSTRING_CSTR(_jsonSlaves.join(", ")), QSTRING_CSTR(_protoSlaves.join(", ")))
|
||||
// update comp state
|
||||
_hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_FORWARDER, obj["enable"].toBool(true));
|
||||
}
|
||||
}
|
||||
|
||||
void MessageForwarder::addJsonSlave(QString slave)
|
||||
{
|
||||
QStringList parts = slave.split(":");
|
||||
if (parts.size() != 2)
|
||||
throw std::runtime_error(QString("HYPERION (forwarder) ERROR: Wrong address: unable to parse address (%1)").arg(slave).toStdString());
|
||||
{
|
||||
Error(_log, "Unable to parse address (%s)",QSTRING_CSTR(slave));
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
quint16 port = parts[1].toUShort(&ok);
|
||||
parts[1].toUShort(&ok);
|
||||
if (!ok)
|
||||
throw std::runtime_error(QString("HYPERION (forwarder) ERROR: Wrong address: Unable to parse the port number (%1)").arg(parts[1]).toStdString());
|
||||
{
|
||||
Error(_log, "Unable to parse port number (%s)",QSTRING_CSTR(parts[1]));
|
||||
return;
|
||||
}
|
||||
|
||||
JsonSlaveAddress c;
|
||||
c.addr = QHostAddress(parts[0]);
|
||||
c.port = port;
|
||||
_jsonSlaves << c;
|
||||
// verify loop with jsonserver
|
||||
const QJsonObject& obj = _hyperion->getSetting(settings::JSONSERVER).object();
|
||||
if(QHostAddress(parts[0]) == QHostAddress::LocalHost && parts[1].toInt() == obj["port"].toInt())
|
||||
{
|
||||
Error(_log, "Loop between JsonServer and Forwarder! (%s)",QSTRING_CSTR(slave));
|
||||
return;
|
||||
}
|
||||
|
||||
_jsonSlaves << slave;
|
||||
}
|
||||
|
||||
void MessageForwarder::addProtoSlave(QString slave)
|
||||
{
|
||||
QStringList parts = slave.split(":");
|
||||
if (parts.size() != 2)
|
||||
{
|
||||
Error(_log, "Unable to parse address (%s)",QSTRING_CSTR(slave));
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
parts[1].toUShort(&ok);
|
||||
if (!ok)
|
||||
{
|
||||
Error(_log, "Unable to parse port number (%s)",QSTRING_CSTR(parts[1]));
|
||||
return;
|
||||
}
|
||||
|
||||
// verify loop with protoserver
|
||||
const QJsonObject& obj = _hyperion->getSetting(settings::PROTOSERVER).object();
|
||||
if(QHostAddress(parts[0]) == QHostAddress::LocalHost && parts[1].toInt() == obj["port"].toInt())
|
||||
{
|
||||
Error(_log, "Loop between ProtoServer and Forwarder! (%s)",QSTRING_CSTR(slave));
|
||||
return;
|
||||
}
|
||||
_protoSlaves << slave;
|
||||
}
|
||||
|
||||
QStringList MessageForwarder::getProtoSlaves()
|
||||
{
|
||||
return _protoSlaves;
|
||||
}
|
||||
|
||||
QList<MessageForwarder::JsonSlaveAddress> MessageForwarder::getJsonSlaves()
|
||||
{
|
||||
return _jsonSlaves;
|
||||
}
|
||||
|
||||
bool MessageForwarder::protoForwardingEnabled()
|
||||
{
|
||||
return ! _protoSlaves.empty();
|
||||
|
@@ -1,10 +1,10 @@
|
||||
// Hyperion includes
|
||||
#include <utils/Logger.h>
|
||||
#include "MultiColorAdjustment.h"
|
||||
#include <hyperion/MultiColorAdjustment.h>
|
||||
|
||||
MultiColorAdjustment::MultiColorAdjustment(const unsigned ledCnt)
|
||||
: _ledAdjustments(ledCnt, nullptr)
|
||||
, _log(Logger::getInstance("ColorAdjust"))
|
||||
, _log(Logger::getInstance("ADJUSTMENT"))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -23,10 +23,20 @@ void MultiColorAdjustment::addAdjustment(ColorAdjustment * adjustment)
|
||||
_adjustment.push_back(adjustment);
|
||||
}
|
||||
|
||||
void MultiColorAdjustment::setAdjustmentForLed(const QString& id, const unsigned startLed, const unsigned endLed)
|
||||
void MultiColorAdjustment::setAdjustmentForLed(const QString& id, const unsigned startLed, unsigned endLed)
|
||||
{
|
||||
Q_ASSERT(startLed <= endLed);
|
||||
Q_ASSERT(endLed < _ledAdjustments.size());
|
||||
// abort
|
||||
if(startLed >= endLed)
|
||||
{
|
||||
Error(_log,"startLed >= endLed -> %d >= %d", startLed, endLed);
|
||||
return;
|
||||
}
|
||||
// catch wrong values
|
||||
if(endLed > _ledAdjustments.size())
|
||||
{
|
||||
Warning(_log,"The color calibration 'LED index' field has leds specified which aren't part of your led layout");
|
||||
endLed = _ledAdjustments.size();
|
||||
}
|
||||
|
||||
// Get the identified adjustment (don't care if is nullptr)
|
||||
ColorAdjustment * adjustment = getAdjustment(id);
|
||||
@@ -38,17 +48,18 @@ void MultiColorAdjustment::setAdjustmentForLed(const QString& id, const unsigned
|
||||
|
||||
bool MultiColorAdjustment::verifyAdjustments() const
|
||||
{
|
||||
bool ok = true;
|
||||
for (unsigned iLed=0; iLed<_ledAdjustments.size(); ++iLed)
|
||||
{
|
||||
ColorAdjustment * adjustment = _ledAdjustments[iLed];
|
||||
|
||||
if (adjustment == nullptr)
|
||||
{
|
||||
Error(_log, "No adjustment set for %d", iLed);
|
||||
return false;
|
||||
Warning(_log, "No calibration set for led %d", iLed);
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return ok;
|
||||
}
|
||||
|
||||
const QStringList & MultiColorAdjustment::getAdjustmentIds()
|
||||
@@ -96,7 +107,7 @@ void MultiColorAdjustment::applyAdjustment(std::vector<ColorRgb>& ledColors)
|
||||
uint8_t ogreen = color.green;
|
||||
uint8_t oblue = color.blue;
|
||||
uint8_t B_RGB, B_CMY, B_W;
|
||||
|
||||
|
||||
adjustment->_rgbTransform.transform(ored,ogreen,oblue);
|
||||
adjustment->_rgbTransform.getBrightnessComponents(B_RGB, B_CMY, B_W);
|
||||
|
||||
@@ -104,7 +115,7 @@ void MultiColorAdjustment::applyAdjustment(std::vector<ColorRgb>& ledColors)
|
||||
uint32_t rng = (uint32_t) (ored) *(255-ogreen);
|
||||
uint32_t nrg = (uint32_t) (255-ored)*(ogreen);
|
||||
uint32_t rg = (uint32_t) (ored) *(ogreen);
|
||||
|
||||
|
||||
uint8_t black = nrng*(255-oblue)/65025;
|
||||
uint8_t red = rng *(255-oblue)/65025;
|
||||
uint8_t green = nrg *(255-oblue)/65025;
|
||||
@@ -113,7 +124,7 @@ void MultiColorAdjustment::applyAdjustment(std::vector<ColorRgb>& ledColors)
|
||||
uint8_t magenta = rng *(oblue) /65025;
|
||||
uint8_t yellow = rg *(255-oblue)/65025;
|
||||
uint8_t white = rg *(oblue) /65025;
|
||||
|
||||
|
||||
uint8_t OR, OG, OB, RR, RG, RB, GR, GG, GB, BR, BG, BB;
|
||||
uint8_t CR, CG, CB, MR, MG, MB, YR, YG, YB, WR, WG, WB;
|
||||
|
||||
|
@@ -1,69 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// STL includes
|
||||
#include <vector>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
|
||||
// Hyperion includes
|
||||
#include <utils/ColorRgb.h>
|
||||
#include <hyperion/ColorAdjustment.h>
|
||||
|
||||
///
|
||||
/// The LedColorTransform is responsible for performing color transformation from 'raw' colors
|
||||
/// received as input to colors mapped to match the color-properties of the leds.
|
||||
///
|
||||
class MultiColorAdjustment
|
||||
{
|
||||
public:
|
||||
MultiColorAdjustment(const unsigned ledCnt);
|
||||
~MultiColorAdjustment();
|
||||
|
||||
/**
|
||||
* Adds a new ColorAdjustment to this MultiColorTransform
|
||||
*
|
||||
* @param adjustment The new ColorAdjustment (ownership is transfered)
|
||||
*/
|
||||
void addAdjustment(ColorAdjustment * adjustment);
|
||||
|
||||
void setAdjustmentForLed(const QString& id, const unsigned startLed, const unsigned endLed);
|
||||
|
||||
bool verifyAdjustments() const;
|
||||
|
||||
void setBacklightEnabled(bool enable);
|
||||
|
||||
///
|
||||
/// Returns the identifier of all the unique ColorAdjustment
|
||||
///
|
||||
/// @return The list with unique id's of the ColorAdjustment
|
||||
const QStringList & getAdjustmentIds();
|
||||
|
||||
///
|
||||
/// Returns the pointer to the ColorAdjustment with the given id
|
||||
///
|
||||
/// @param id The identifier of the ColorAdjustment
|
||||
///
|
||||
/// @return The ColorAdjustment with the given id (or nullptr if it does not exist)
|
||||
///
|
||||
ColorAdjustment* getAdjustment(const QString& id);
|
||||
|
||||
///
|
||||
/// Performs the color adjustment from raw-color to led-color
|
||||
///
|
||||
/// @param ledColors The list with raw colors
|
||||
///
|
||||
void applyAdjustment(std::vector<ColorRgb>& ledColors);
|
||||
|
||||
private:
|
||||
/// List with transform ids
|
||||
QStringList _adjustmentIds;
|
||||
|
||||
/// List with unique ColorTransforms
|
||||
std::vector<ColorAdjustment*> _adjustment;
|
||||
|
||||
/// List with a pointer to the ColorAdjustment for each individual led
|
||||
std::vector<ColorAdjustment*> _ledAdjustments;
|
||||
|
||||
// logger instance
|
||||
Logger * _log;
|
||||
};
|
@@ -1,36 +1,113 @@
|
||||
// STL includes
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <limits>
|
||||
|
||||
// qt incl
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
|
||||
// Hyperion includes
|
||||
#include <hyperion/PriorityMuxer.h>
|
||||
|
||||
// utils
|
||||
#include <utils/Logger.h>
|
||||
|
||||
const int PriorityMuxer::LOWEST_PRIORITY = std::numeric_limits<uint8_t>::max();
|
||||
|
||||
PriorityMuxer::PriorityMuxer(int ledCount)
|
||||
: _currentPriority(PriorityMuxer::LOWEST_PRIORITY)
|
||||
: QObject()
|
||||
, _log(Logger::getInstance("HYPERION"))
|
||||
, _currentPriority(PriorityMuxer::LOWEST_PRIORITY)
|
||||
, _manualSelectedPriority(256)
|
||||
, _activeInputs()
|
||||
, _lowestPriorityInfo()
|
||||
, _sourceAutoSelectEnabled(true)
|
||||
, _updateTimer(new QTimer(this))
|
||||
, _timer(new QTimer(this))
|
||||
, _blockTimer(new QTimer(this))
|
||||
{
|
||||
// init lowest priority info
|
||||
_lowestPriorityInfo.priority = PriorityMuxer::LOWEST_PRIORITY;
|
||||
_lowestPriorityInfo.timeoutTime_ms = 0;
|
||||
_lowestPriorityInfo.timeoutTime_ms = -1;
|
||||
_lowestPriorityInfo.ledColors = std::vector<ColorRgb>(ledCount, {0, 0, 0});
|
||||
_lowestPriorityInfo.componentId = hyperion::COMP_COLOR;
|
||||
_lowestPriorityInfo.origin = "System";
|
||||
_lowestPriorityInfo.owner = "";
|
||||
|
||||
_activeInputs[_currentPriority] = _lowestPriorityInfo;
|
||||
_activeInputs[PriorityMuxer::LOWEST_PRIORITY] = _lowestPriorityInfo;
|
||||
|
||||
// do a reuqest after blocking timer runs out
|
||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(emitReq()));
|
||||
_timer.setSingleShot(true);
|
||||
_blockTimer.setSingleShot(true);
|
||||
// adapt to 1s interval for COLOR and EFFECT timeouts > -1
|
||||
connect(_timer, &QTimer::timeout, this, &PriorityMuxer::timeTrigger);
|
||||
_timer->setSingleShot(true);
|
||||
_blockTimer->setSingleShot(true);
|
||||
// forward timeRunner signal to prioritiesChanged signal & threading workaround
|
||||
connect(this, &PriorityMuxer::timeRunner, this, &PriorityMuxer::prioritiesChanged);
|
||||
connect(this, &PriorityMuxer::signalTimeTrigger, this, &PriorityMuxer::timeTrigger);
|
||||
|
||||
// start muxer timer
|
||||
connect(_updateTimer, &QTimer::timeout, this, &PriorityMuxer::setCurrentTime);
|
||||
_updateTimer->setInterval(250);
|
||||
_updateTimer->start();
|
||||
InputInfo ninfo;
|
||||
}
|
||||
|
||||
PriorityMuxer::~PriorityMuxer()
|
||||
{
|
||||
}
|
||||
|
||||
void PriorityMuxer::setEnable(const bool& enable)
|
||||
{
|
||||
enable ? _updateTimer->start() : _updateTimer->stop();
|
||||
}
|
||||
|
||||
bool PriorityMuxer::setSourceAutoSelectEnabled(const bool& enable, const bool& update)
|
||||
{
|
||||
if(_sourceAutoSelectEnabled != enable)
|
||||
{
|
||||
// on disable we need to make sure the last priority call to setPriority is still valid
|
||||
if(!enable && !_activeInputs.contains(_manualSelectedPriority))
|
||||
{
|
||||
Warning(_log, "Can't disable auto selection, as the last manual selected priority (%d) is no longer available", _manualSelectedPriority);
|
||||
return false;
|
||||
}
|
||||
|
||||
_sourceAutoSelectEnabled = enable;
|
||||
Debug(_log, "Source auto select is now %s", enable ? "enabled" : "disabled");
|
||||
|
||||
// update _currentPriority if called from external
|
||||
if(update)
|
||||
setCurrentTime();
|
||||
|
||||
emit autoSelectChanged(enable);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PriorityMuxer::setPriority(const uint8_t priority)
|
||||
{
|
||||
if(_activeInputs.contains(priority))
|
||||
{
|
||||
_manualSelectedPriority = priority;
|
||||
// update auto select state -> update _currentPriority
|
||||
setSourceAutoSelectEnabled(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PriorityMuxer::updateLedColorsLength(const int& ledCount)
|
||||
{
|
||||
for (auto infoIt = _activeInputs.begin(); infoIt != _activeInputs.end();)
|
||||
{
|
||||
if (infoIt->ledColors.size() >= 1)
|
||||
{
|
||||
infoIt->ledColors.resize(ledCount, infoIt->ledColors.at(0));
|
||||
}
|
||||
++infoIt;
|
||||
}
|
||||
}
|
||||
|
||||
int PriorityMuxer::getCurrentPriority() const
|
||||
{
|
||||
return _currentPriority;
|
||||
@@ -46,7 +123,7 @@ bool PriorityMuxer::hasPriority(const int priority) const
|
||||
return (priority == PriorityMuxer::LOWEST_PRIORITY) ? true : _activeInputs.contains(priority);
|
||||
}
|
||||
|
||||
const PriorityMuxer::InputInfo& PriorityMuxer::getInputInfo(const int priority) const
|
||||
const PriorityMuxer::InputInfo PriorityMuxer::getInputInfo(const int priority) const
|
||||
{
|
||||
auto elemIt = _activeInputs.find(priority);
|
||||
if (elemIt == _activeInputs.end())
|
||||
@@ -54,35 +131,127 @@ const PriorityMuxer::InputInfo& PriorityMuxer::getInputInfo(const int priority)
|
||||
elemIt = _activeInputs.find(PriorityMuxer::LOWEST_PRIORITY);
|
||||
if (elemIt == _activeInputs.end())
|
||||
{
|
||||
throw std::runtime_error("HYPERION (prioritymuxer) ERROR: no such priority");
|
||||
// fallback
|
||||
return _lowestPriorityInfo;
|
||||
}
|
||||
}
|
||||
return elemIt.value();
|
||||
}
|
||||
|
||||
void PriorityMuxer::setInput(const int priority, const std::vector<ColorRgb>& ledColors, const int64_t timeoutTime_ms, hyperion::Components component, const QString origin, unsigned smooth_cfg)
|
||||
void PriorityMuxer::registerInput(const int priority, const hyperion::Components& component, const QString& origin, const QString& owner, unsigned smooth_cfg)
|
||||
{
|
||||
// detect new registers
|
||||
bool newInput = false;
|
||||
if(!_activeInputs.contains(priority))
|
||||
newInput = true;
|
||||
|
||||
InputInfo& input = _activeInputs[priority];
|
||||
input.priority = priority;
|
||||
input.timeoutTime_ms = timeoutTime_ms;
|
||||
input.ledColors = ledColors;
|
||||
input.timeoutTime_ms = newInput ? -100 : input.timeoutTime_ms;
|
||||
input.componentId = component;
|
||||
input.origin = origin;
|
||||
input.smooth_cfg = smooth_cfg;
|
||||
_currentPriority = qMin(_currentPriority, priority);
|
||||
input.owner = owner;
|
||||
|
||||
if(newInput)
|
||||
{
|
||||
Debug(_log,"Register new input '%s/%s' with priority %d as inactive", QSTRING_CSTR(origin), hyperion::componentToIdString(component), priority);
|
||||
emit priorityChanged(priority, true);
|
||||
emit prioritiesChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PriorityMuxer::clearInput(const int priority)
|
||||
const bool PriorityMuxer::setInput(const int priority, const std::vector<ColorRgb>& ledColors, int64_t timeout_ms)
|
||||
{
|
||||
if (priority < PriorityMuxer::LOWEST_PRIORITY)
|
||||
if(!_activeInputs.contains(priority))
|
||||
{
|
||||
_activeInputs.remove(priority);
|
||||
if (_currentPriority == priority)
|
||||
{
|
||||
QList<int> keys = _activeInputs.keys();
|
||||
_currentPriority = *std::min_element(keys.begin(), keys.end());
|
||||
}
|
||||
Error(_log,"setInput() used without registerInput() for priority '%d', probably the priority reached timeout",priority);
|
||||
return false;
|
||||
}
|
||||
|
||||
// calc final timeout
|
||||
if(timeout_ms > 0)
|
||||
timeout_ms = QDateTime::currentMSecsSinceEpoch() + timeout_ms;
|
||||
|
||||
InputInfo& input = _activeInputs[priority];
|
||||
// detect active <-> inactive changes
|
||||
bool activeChange = false;
|
||||
bool active = true;
|
||||
if(input.timeoutTime_ms == -100 && timeout_ms != -100)
|
||||
{
|
||||
activeChange = true;
|
||||
}
|
||||
else if(timeout_ms == -100 && input.timeoutTime_ms != -100)
|
||||
{
|
||||
active = false;
|
||||
activeChange = true;
|
||||
}
|
||||
// update input
|
||||
input.timeoutTime_ms = timeout_ms;
|
||||
input.ledColors = ledColors;
|
||||
|
||||
// emit active change
|
||||
if(activeChange)
|
||||
{
|
||||
Debug(_log, "Priority %d is now %s", priority, active ? "active" : "inactive");
|
||||
emit activeStateChanged(priority, active);
|
||||
setCurrentTime();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool PriorityMuxer::setInputImage(const int priority, const Image<ColorRgb>& image, int64_t timeout_ms)
|
||||
{
|
||||
if(!_activeInputs.contains(priority))
|
||||
{
|
||||
Error(_log,"setInputImage() used without registerInput() for priority '%d', probably the priority reached timeout",priority);
|
||||
return false;
|
||||
}
|
||||
|
||||
// calc final timeout
|
||||
if(timeout_ms > 0)
|
||||
timeout_ms = QDateTime::currentMSecsSinceEpoch() + timeout_ms;
|
||||
|
||||
InputInfo& input = _activeInputs[priority];
|
||||
// detect active <-> inactive changes
|
||||
bool activeChange = false;
|
||||
bool active = true;
|
||||
if(input.timeoutTime_ms == -100 && timeout_ms != -100)
|
||||
{
|
||||
activeChange = true;
|
||||
}
|
||||
else if(timeout_ms == -100 && input.timeoutTime_ms != -100)
|
||||
{
|
||||
active = false;
|
||||
activeChange = true;
|
||||
}
|
||||
// update input
|
||||
input.timeoutTime_ms = timeout_ms;
|
||||
input.image = image;
|
||||
|
||||
// emit active change
|
||||
if(activeChange)
|
||||
{
|
||||
Debug(_log, "Priority %d is now %s", priority, active ? "active" : "inactive");
|
||||
emit activeStateChanged(priority, active);
|
||||
setCurrentTime();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool PriorityMuxer::clearInput(const uint8_t priority)
|
||||
{
|
||||
if (priority < PriorityMuxer::LOWEST_PRIORITY && _activeInputs.remove(priority))
|
||||
{
|
||||
Debug(_log,"Removed source priority %d",priority);
|
||||
// on clear success update _currentPriority
|
||||
setCurrentTime();
|
||||
emit priorityChanged(priority, false);
|
||||
emit prioritiesChanged();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PriorityMuxer::clearAll(bool forceClearAll)
|
||||
@@ -97,47 +266,82 @@ void PriorityMuxer::clearAll(bool forceClearAll)
|
||||
{
|
||||
for(auto key : _activeInputs.keys())
|
||||
{
|
||||
if (key < PriorityMuxer::LOWEST_PRIORITY-1)
|
||||
const InputInfo info = getInputInfo(key);
|
||||
if ((info.componentId == hyperion::COMP_COLOR || info.componentId == hyperion::COMP_EFFECT) && key < PriorityMuxer::LOWEST_PRIORITY-1)
|
||||
{
|
||||
_activeInputs.remove(key);
|
||||
clearInput(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PriorityMuxer::setCurrentTime(const int64_t& now)
|
||||
void PriorityMuxer::setCurrentTime(void)
|
||||
{
|
||||
_currentPriority = PriorityMuxer::LOWEST_PRIORITY;
|
||||
const int64_t now = QDateTime::currentMSecsSinceEpoch();
|
||||
int newPriority = PriorityMuxer::LOWEST_PRIORITY;
|
||||
|
||||
for (auto infoIt = _activeInputs.begin(); infoIt != _activeInputs.end();)
|
||||
{
|
||||
if (infoIt->timeoutTime_ms > 0 && infoIt->timeoutTime_ms <= now)
|
||||
{
|
||||
quint8 tPrio = infoIt->priority;
|
||||
infoIt = _activeInputs.erase(infoIt);
|
||||
Debug(_log,"Timeout clear for priority %d",tPrio);
|
||||
emit priorityChanged(tPrio, false);
|
||||
emit prioritiesChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentPriority = qMin(_currentPriority, infoIt->priority);
|
||||
|
||||
// call emitReq when effect or color is running with timeout > -1, blacklist prio 255
|
||||
// timeoutTime of -100 is awaiting data (inactive); skip
|
||||
if(infoIt->timeoutTime_ms >= -1)
|
||||
newPriority = qMin(newPriority, infoIt->priority);
|
||||
|
||||
// call timeTrigger when effect or color is running with timeout > -1, blacklist prio 255
|
||||
if(infoIt->priority < 254 && infoIt->timeoutTime_ms > -1 && (infoIt->componentId == hyperion::COMP_EFFECT || infoIt->componentId == hyperion::COMP_COLOR))
|
||||
{
|
||||
emitReq();
|
||||
emit signalTimeTrigger(); // as signal to prevent Threading issues
|
||||
}
|
||||
++infoIt;
|
||||
}
|
||||
}
|
||||
// eval if manual selected prio is still available
|
||||
if(!_sourceAutoSelectEnabled)
|
||||
{
|
||||
if(_activeInputs.contains(_manualSelectedPriority))
|
||||
{
|
||||
newPriority = _manualSelectedPriority;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "The manual selected priority '%d' is no longer available, switching to auto selection", _manualSelectedPriority);
|
||||
// update state, but no _currentPriority re-eval
|
||||
setSourceAutoSelectEnabled(true, false);
|
||||
}
|
||||
}
|
||||
// apply & emit on change (after apply!)
|
||||
bool changed = false;
|
||||
if(_currentPriority != newPriority)
|
||||
changed = true;
|
||||
|
||||
_currentPriority = newPriority;
|
||||
|
||||
if(changed)
|
||||
{
|
||||
Debug(_log, "Set visible priority to %d", newPriority);
|
||||
emit visiblePriorityChanged(newPriority);
|
||||
emit prioritiesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void PriorityMuxer::emitReq()
|
||||
void PriorityMuxer::timeTrigger()
|
||||
{
|
||||
if(_blockTimer.isActive())
|
||||
if(_blockTimer->isActive())
|
||||
{
|
||||
_timer.start(500);
|
||||
_timer->start(500);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit timerunner();
|
||||
_blockTimer.start(1000);
|
||||
emit timeRunner();
|
||||
_blockTimer->start(1000);
|
||||
}
|
||||
}
|
||||
|
175
libsrc/hyperion/SettingsManager.cpp
Normal file
175
libsrc/hyperion/SettingsManager.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
// proj
|
||||
#include <hyperion/SettingsManager.h>
|
||||
|
||||
// util
|
||||
#include <utils/JsonUtils.h>
|
||||
|
||||
// json schema process
|
||||
#include <utils/jsonschema/QJsonFactory.h>
|
||||
#include <utils/jsonschema/QJsonSchemaChecker.h>
|
||||
|
||||
// write config to filesystem
|
||||
#include <utils/JsonUtils.h>
|
||||
|
||||
// hyperion
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
QJsonObject SettingsManager::schemaJson;
|
||||
|
||||
SettingsManager::SettingsManager(Hyperion* hyperion, const quint8& instance, const QString& configFile)
|
||||
: _hyperion(hyperion)
|
||||
, _log(Logger::getInstance("SettingsManager"))
|
||||
{
|
||||
Q_INIT_RESOURCE(resource);
|
||||
connect(this, &SettingsManager::settingsChanged, _hyperion, &Hyperion::settingsChanged);
|
||||
// get schema
|
||||
if(schemaJson.isEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
schemaJson = QJsonFactory::readSchema(":/hyperion-schema");
|
||||
}
|
||||
catch(const std::runtime_error& error)
|
||||
{
|
||||
throw std::runtime_error(error.what());
|
||||
}
|
||||
}
|
||||
// get default config
|
||||
QJsonObject defaultConfig;
|
||||
if(!JsonUtils::readFile(":/hyperion_default.config", defaultConfig, _log))
|
||||
throw std::runtime_error("Failed to read default config");
|
||||
|
||||
Info(_log, "Selected configuration file: %s", QSTRING_CSTR(configFile));
|
||||
QJsonSchemaChecker schemaCheckerT;
|
||||
|
||||
if(!JsonUtils::readFile(configFile, _qconfig, _log))
|
||||
throw std::runtime_error("Failed to load config!");
|
||||
|
||||
// validate config with schema and correct it if required
|
||||
QPair<bool, bool> validate = schemaCheckerT.validate(_qconfig);
|
||||
|
||||
// errors in schema syntax, abort
|
||||
if (!validate.second)
|
||||
{
|
||||
foreach (auto & schemaError, schemaCheckerT.getMessages())
|
||||
Error(_log, "Schema Syntax Error: %s", QSTRING_CSTR(schemaError));
|
||||
|
||||
throw std::runtime_error("ERROR: Hyperion schema has syntax errors!");
|
||||
}
|
||||
// errors in configuration, correct it!
|
||||
if (!validate.first)
|
||||
{
|
||||
Warning(_log,"Errors have been found in the configuration file. Automatic correction has been applied");
|
||||
_qconfig = schemaCheckerT.getAutoCorrectedConfig(_qconfig);
|
||||
|
||||
foreach (auto & schemaError, schemaCheckerT.getMessages())
|
||||
Warning(_log, "Config Fix: %s", QSTRING_CSTR(schemaError));
|
||||
|
||||
if (!JsonUtils::write(configFile, _qconfig, _log))
|
||||
throw std::runtime_error("ERROR: Can't save configuration file, aborting");
|
||||
}
|
||||
|
||||
Debug(_log,"Settings database initialized")
|
||||
}
|
||||
|
||||
SettingsManager::SettingsManager(const quint8& instance, const QString& configFile)
|
||||
: _hyperion(nullptr)
|
||||
, _log(Logger::getInstance("SettingsManager"))
|
||||
{
|
||||
Q_INIT_RESOURCE(resource);
|
||||
// get schema
|
||||
if(schemaJson.isEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
schemaJson = QJsonFactory::readSchema(":/hyperion-schema");
|
||||
}
|
||||
catch(const std::runtime_error& error)
|
||||
{
|
||||
throw std::runtime_error(error.what());
|
||||
}
|
||||
}
|
||||
// get default config
|
||||
QJsonObject defaultConfig;
|
||||
if(!JsonUtils::readFile(":/hyperion_default.config", defaultConfig, _log))
|
||||
throw std::runtime_error("Failed to read default config");
|
||||
|
||||
Info(_log, "Selected configuration file: %s", QSTRING_CSTR(configFile));
|
||||
QJsonSchemaChecker schemaCheckerT;
|
||||
|
||||
if(!JsonUtils::readFile(configFile, _qconfig, _log))
|
||||
throw std::runtime_error("Failed to load config!");
|
||||
|
||||
// validate config with schema and correct it if required
|
||||
QPair<bool, bool> validate = schemaCheckerT.validate(_qconfig);
|
||||
|
||||
// errors in schema syntax, abort
|
||||
if (!validate.second)
|
||||
{
|
||||
foreach (auto & schemaError, schemaCheckerT.getMessages())
|
||||
Error(_log, "Schema Syntax Error: %s", QSTRING_CSTR(schemaError));
|
||||
|
||||
throw std::runtime_error("ERROR: Hyperion schema has syntax errors!");
|
||||
}
|
||||
// errors in configuration, correct it!
|
||||
if (!validate.first)
|
||||
{
|
||||
Warning(_log,"Errors have been found in the configuration file. Automatic correction has been applied");
|
||||
_qconfig = schemaCheckerT.getAutoCorrectedConfig(_qconfig);
|
||||
|
||||
foreach (auto & schemaError, schemaCheckerT.getMessages())
|
||||
Warning(_log, "Config Fix: %s", QSTRING_CSTR(schemaError));
|
||||
|
||||
if (!JsonUtils::write(configFile, _qconfig, _log))
|
||||
throw std::runtime_error("ERROR: Can't save configuration file, aborting");
|
||||
}
|
||||
|
||||
Debug(_log,"Settings database initialized")
|
||||
}
|
||||
|
||||
SettingsManager::~SettingsManager()
|
||||
{
|
||||
}
|
||||
|
||||
const QJsonDocument SettingsManager::getSetting(const settings::type& type)
|
||||
{
|
||||
//return _sTable->getSettingsRecord(settings::typeToString(type));
|
||||
|
||||
QString key = settings::typeToString(type);
|
||||
if(_qconfig[key].isObject())
|
||||
return QJsonDocument(_qconfig[key].toObject());
|
||||
else
|
||||
return QJsonDocument(_qconfig[key].toArray());
|
||||
}
|
||||
|
||||
const bool SettingsManager::saveSettings(QJsonObject config, const bool& correct)
|
||||
{
|
||||
// we need to validate data against schema
|
||||
QJsonSchemaChecker schemaChecker;
|
||||
schemaChecker.setSchema(schemaJson);
|
||||
if (!schemaChecker.validate(config).first)
|
||||
{
|
||||
if(!correct)
|
||||
{
|
||||
Error(_log,"Failed to save configuration, errors during validation");
|
||||
return false;
|
||||
}
|
||||
Warning(_log,"Fixing json data!");
|
||||
config = schemaChecker.getAutoCorrectedConfig(config);
|
||||
|
||||
foreach (auto & schemaError, schemaChecker.getMessages())
|
||||
Warning(_log, "Config Fix: %s", QSTRING_CSTR(schemaError));
|
||||
}
|
||||
|
||||
// save data to file
|
||||
if(_hyperion != nullptr)
|
||||
{
|
||||
if(!JsonUtils::write(_hyperion->getConfigFilePath(), config, _log))
|
||||
return false;
|
||||
}
|
||||
|
||||
// store the current state
|
||||
_qconfig = config;
|
||||
|
||||
return true;
|
||||
}
|
@@ -71,6 +71,10 @@
|
||||
{
|
||||
"$ref": "schema-effects.json"
|
||||
},
|
||||
"instCapture":
|
||||
{
|
||||
"$ref": "schema-instCapture.json"
|
||||
},
|
||||
"ledConfig":
|
||||
{
|
||||
"$ref": "schema-ledConfig.json"
|
||||
|
@@ -22,5 +22,6 @@
|
||||
<file alias="schema-effects.json">schema/schema-effects.json</file>
|
||||
<file alias="schema-ledConfig.json">schema/schema-ledConfig.json</file>
|
||||
<file alias="schema-leds.json">schema/schema-leds.json</file>
|
||||
<file alias="schema-instCapture.json">schema/schema-instCapture.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@@ -46,22 +46,6 @@
|
||||
"default" : "*",
|
||||
"propertyOrder" : 2
|
||||
},
|
||||
"black" :
|
||||
{
|
||||
"type" : "array",
|
||||
"title" : "edt_conf_color_black_title",
|
||||
"format" : "colorpicker",
|
||||
"required" : true,
|
||||
"default": [0,0,0],
|
||||
"items" : {
|
||||
"type" : "integer",
|
||||
"minimum" : 0,
|
||||
"maximum" : 255
|
||||
},
|
||||
"minItems" : 3,
|
||||
"maxItems" : 3,
|
||||
"propertyOrder" : 3
|
||||
},
|
||||
"white" :
|
||||
{
|
||||
"type" : "array",
|
||||
|
@@ -3,13 +3,6 @@
|
||||
"title" : "edt_conf_fg_heading_title",
|
||||
"properties" :
|
||||
{
|
||||
"enable" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_general_enable_title",
|
||||
"default" : true,
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"type" :
|
||||
{
|
||||
"type" : "string",
|
||||
@@ -45,15 +38,6 @@
|
||||
"append" : "edt_append_hz",
|
||||
"propertyOrder" : 4
|
||||
},
|
||||
"priority" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_general_priority_title",
|
||||
"minimum" : 100,
|
||||
"maximum" : 254,
|
||||
"default" : 250,
|
||||
"propertyOrder" : 5
|
||||
},
|
||||
"cropLeft" :
|
||||
{
|
||||
"type" : "integer",
|
||||
@@ -90,42 +74,28 @@
|
||||
"append" : "edt_append_pixel",
|
||||
"propertyOrder" : 9
|
||||
},
|
||||
"useXGetImage" :
|
||||
"pixelDecimation" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_fg_useXGetImage_title",
|
||||
"default" : false,
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_fg_pixelDecimation_title",
|
||||
"minimum" : 1,
|
||||
"maximum" : 30,
|
||||
"default" : 8,
|
||||
"propertyOrder" : 10
|
||||
},
|
||||
"horizontalPixelDecimation" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_fg_horizontalPixelDecimation_title",
|
||||
"minimum" : 0,
|
||||
"default" : 8,
|
||||
"propertyOrder" : 11
|
||||
},
|
||||
"verticalPixelDecimation" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_fg_verticalPixelDecimation_title",
|
||||
"minimum" : 0,
|
||||
"default" : 8,
|
||||
"propertyOrder" : 12
|
||||
},
|
||||
"device" :
|
||||
{
|
||||
"type" : "string",
|
||||
"title" : "edt_conf_fg_device_title",
|
||||
"default" : "/dev/fb0",
|
||||
"propertyOrder" : 13
|
||||
"propertyOrder" : 11
|
||||
},
|
||||
"display" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_fg_display_title",
|
||||
"minimum" : 0,
|
||||
"propertyOrder" : 14
|
||||
"propertyOrder" : 12
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
|
@@ -11,14 +11,6 @@
|
||||
"title" : "edt_conf_v4l2_heading_title",
|
||||
"properties" :
|
||||
{
|
||||
"enable" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"title" : "edt_conf_general_enable_title",
|
||||
"default" : false,
|
||||
"required" : true,
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"device" :
|
||||
{
|
||||
"type" : "string",
|
||||
@@ -40,62 +32,24 @@
|
||||
{
|
||||
"type" : "string",
|
||||
"title" : "edt_conf_v4l2_standard_title",
|
||||
"enum" : ["PAL","NTSC","SECAM"],
|
||||
"default" : "PAL",
|
||||
"enum" : ["PAL","NTSC","SECAM","NO_CHANGE"],
|
||||
"default" : "NO_CHANGE",
|
||||
"options" : {
|
||||
"enum_titles" : ["edt_conf_enum_PAL", "edt_conf_enum_NTSC", "edt_conf_enum_SECAM"]
|
||||
"enum_titles" : ["edt_conf_enum_PAL", "edt_conf_enum_NTSC", "edt_conf_enum_SECAM", "edt_conf_enum_NO_CHANGE"]
|
||||
},
|
||||
"required" : true,
|
||||
"propertyOrder" : 4
|
||||
},
|
||||
"width" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_v4l2_width_title",
|
||||
"minimum" : 0,
|
||||
"default" : 0,
|
||||
"append" : "edt_append_pixel",
|
||||
"required" : true,
|
||||
"propertyOrder" : 5
|
||||
},
|
||||
"height" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_v4l2_height_title",
|
||||
"minimum" : 0,
|
||||
"default" : 0,
|
||||
"append" : "edt_append_pixel",
|
||||
"required" : true,
|
||||
"propertyOrder" : 6
|
||||
},
|
||||
"frameDecimation" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "edt_conf_v4l2_frameDecimation_title",
|
||||
"minimum" : 0,
|
||||
"default" : 2,
|
||||
"required" : true,
|
||||
"propertyOrder" : 7
|
||||
},
|
||||
"sizeDecimation" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"title" : "Size decimation",
|
||||
"minimum" : 0,
|
||||
"title" : "edt_conf_v4l2_sizeDecimation_title",
|
||||
"minimum" : 1,
|
||||
"maximum" : 30,
|
||||
"default" : 6,
|
||||
"required" : true,
|
||||
"propertyOrder" : 8
|
||||
},
|
||||
"priority" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"minimum" : 100,
|
||||
"maximum" : 253,
|
||||
"title" : "edt_conf_general_priority_title",
|
||||
"default" : 240,
|
||||
"required" : true,
|
||||
"propertyOrder" : 9
|
||||
},
|
||||
"cropLeft" :
|
||||
{
|
||||
"type" : "integer",
|
||||
|
45
libsrc/hyperion/schema/schema-instCapture.json
Normal file
45
libsrc/hyperion/schema/schema-instCapture.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : true,
|
||||
"title" : "edt_conf_instC_heading_title",
|
||||
"properties" :
|
||||
{
|
||||
"systemEnable" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"required" : true,
|
||||
"title" : "edt_conf_instC_systemEnable",
|
||||
"default" : true,
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"systemPriority" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"required" : true,
|
||||
"title" : "edt_conf_general_priority_title",
|
||||
"minimum" : 100,
|
||||
"maximum" : 253,
|
||||
"default" : 250,
|
||||
"propertyOrder" : 2
|
||||
},
|
||||
"v4lEnable" :
|
||||
{
|
||||
"type" : "boolean",
|
||||
"required" : true,
|
||||
"title" : "edt_conf_instC_v4lEnable",
|
||||
"default" : false,
|
||||
"propertyOrder" : 3
|
||||
},
|
||||
"v4lPriority" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"required" : true,
|
||||
"title" : "edt_conf_general_priority_title",
|
||||
"minimum" : 100,
|
||||
"maximum" : 253,
|
||||
"default" : 240,
|
||||
"propertyOrder" : 4
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
Reference in New Issue
Block a user