Details coming soon.

This commit is contained in:
Paulchen-Panther
2018-12-27 23:11:32 +01:00
parent e3be03ea73
commit d762aa2f3e
186 changed files with 6156 additions and 5444 deletions

View 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);
}
}

View File

@@ -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";
*/
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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;
};

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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);
}
}

View 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;
}

View File

@@ -71,6 +71,10 @@
{
"$ref": "schema-effects.json"
},
"instCapture":
{
"$ref": "schema-instCapture.json"
},
"ledConfig":
{
"$ref": "schema-ledConfig.json"

View File

@@ -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>

View File

@@ -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",

View File

@@ -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

View File

@@ -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",

View 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
}