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:
@@ -15,4 +15,6 @@ add_subdirectory(leddevice)
|
||||
add_subdirectory(utils)
|
||||
add_subdirectory(effectengine)
|
||||
add_subdirectory(grabber)
|
||||
add_subdirectory(webconfig)
|
||||
add_subdirectory(webserver)
|
||||
add_subdirectory(api)
|
||||
add_subdirectory(python)
|
||||
|
@@ -84,17 +84,6 @@
|
||||
"minItems": 3,
|
||||
"maxItems": 3
|
||||
},
|
||||
"black": {
|
||||
"type": "array",
|
||||
"required": false,
|
||||
"items" : {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 255
|
||||
},
|
||||
"minItems": 3,
|
||||
"maxItems": 3
|
||||
},
|
||||
"white": {
|
||||
"type": "array",
|
||||
"required": false,
|
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer",
|
||||
"minimum" : 1,
|
||||
"minimum" : -1,
|
||||
"maximum" : 253,
|
||||
"required": true
|
||||
}
|
@@ -7,6 +7,9 @@
|
||||
"required" : true,
|
||||
"enum" : ["serverinfo"]
|
||||
},
|
||||
"subscribe" : {
|
||||
"type" : "array"
|
||||
},
|
||||
"tan" : {
|
||||
"type" : "integer"
|
||||
}
|
@@ -6,7 +6,6 @@
|
||||
<file alias="schema-serverinfo">JSONRPC_schema/schema-serverinfo.json</file>
|
||||
<file alias="schema-sysinfo">JSONRPC_schema/schema-sysinfo.json</file>
|
||||
<file alias="schema-clear">JSONRPC_schema/schema-clear.json</file>
|
||||
<file alias="schema-clearall">JSONRPC_schema/schema-clearall.json</file>
|
||||
<file alias="schema-adjustment">JSONRPC_schema/schema-adjustment.json</file>
|
||||
<file alias="schema-effect">JSONRPC_schema/schema-effect.json</file>
|
||||
<file alias="schema-create-effect">JSONRPC_schema/schema-create-effect.json</file>
|
@@ -23,7 +23,7 @@ uint8_t BlackBorderDetector::calculateThreshold(double threshold)
|
||||
|
||||
uint8_t blackborderThreshold = uint8_t(rgbThreshold);
|
||||
|
||||
Debug(Logger::getInstance("BLACKBORDER"), "threshold set to %f (%d)", threshold , int(blackborderThreshold));
|
||||
//Debug(Logger::getInstance("BLACKBORDER"), "threshold set to %f (%d)", threshold , int(blackborderThreshold));
|
||||
|
||||
return blackborderThreshold;
|
||||
}
|
||||
|
@@ -1,32 +1,105 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
// Blackborder includes
|
||||
#include <blackborder/BlackBorderProcessor.h>
|
||||
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
BlackBorderProcessor::BlackBorderProcessor(const QJsonObject &blackborderConfig)
|
||||
: _enabled(blackborderConfig["enable"].toBool(true))
|
||||
, _unknownSwitchCnt(blackborderConfig["unknownFrameCnt"].toInt(600))
|
||||
, _borderSwitchCnt(blackborderConfig["borderFrameCnt"].toInt(50))
|
||||
, _maxInconsistentCnt(blackborderConfig["maxInconsistentCnt"].toInt(10))
|
||||
, _blurRemoveCnt(blackborderConfig["blurRemoveCnt"].toInt(1))
|
||||
, _detectionMode(blackborderConfig["mode"].toString("default"))
|
||||
, _detector(blackborderConfig["threshold"].toDouble(5.0)/100)
|
||||
BlackBorderProcessor::BlackBorderProcessor(Hyperion* hyperion, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _hyperion(hyperion)
|
||||
, _enabled(false)
|
||||
, _unknownSwitchCnt(600)
|
||||
, _borderSwitchCnt(50)
|
||||
, _maxInconsistentCnt(10)
|
||||
, _blurRemoveCnt(1)
|
||||
, _detectionMode("default")
|
||||
, _detector(nullptr)
|
||||
, _currentBorder({true, -1, -1})
|
||||
, _previousDetectedBorder({true, -1, -1})
|
||||
, _consistentCnt(0)
|
||||
, _inconsistentCnt(10)
|
||||
, _oldThreshold(-0.1)
|
||||
, _hardDisabled(false)
|
||||
, _userEnabled(false)
|
||||
{
|
||||
if (_enabled)
|
||||
// init
|
||||
handleSettingsUpdate(settings::BLACKBORDER, _hyperion->getSetting(settings::BLACKBORDER));
|
||||
|
||||
// listen for settings updates
|
||||
connect(_hyperion, &Hyperion::settingsChanged, this, &BlackBorderProcessor::handleSettingsUpdate);
|
||||
|
||||
// listen for component state changes
|
||||
connect(_hyperion, &Hyperion::componentStateChanged, this, &BlackBorderProcessor::componentStateChanged);
|
||||
}
|
||||
|
||||
BlackBorderProcessor::~BlackBorderProcessor()
|
||||
{
|
||||
delete _detector;
|
||||
}
|
||||
|
||||
void BlackBorderProcessor::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::BLACKBORDER)
|
||||
{
|
||||
Debug(Logger::getInstance("BLACKBORDER"), "mode: %s", QSTRING_CSTR(_detectionMode));
|
||||
const QJsonObject& obj = config.object();
|
||||
_unknownSwitchCnt = obj["unknownFrameCnt"].toInt(600);
|
||||
_borderSwitchCnt = obj["borderFrameCnt"].toInt(50);
|
||||
_maxInconsistentCnt = obj["maxInconsistentCnt"].toInt(10);
|
||||
_blurRemoveCnt = obj["blurRemoveCnt"].toInt(1);
|
||||
_detectionMode = obj["mode"].toString("default");
|
||||
|
||||
if(_oldThreshold != obj["threshold"].toDouble(5.0/100))
|
||||
{
|
||||
_oldThreshold = obj["threshold"].toDouble(5.0/100);
|
||||
if(_detector != nullptr) delete _detector;
|
||||
_detector = new BlackBorderDetector(obj["threshold"].toDouble(5.0/100));
|
||||
}
|
||||
|
||||
Debug(Logger::getInstance("BLACKBORDER"), "Set mode to: %s", QSTRING_CSTR(_detectionMode));
|
||||
|
||||
// eval the comp state
|
||||
componentStateChanged(hyperion::COMP_BLACKBORDER, obj["enable"].toBool(true));
|
||||
}
|
||||
}
|
||||
|
||||
void BlackBorderProcessor::componentStateChanged(const hyperion::Components component, bool enable)
|
||||
{
|
||||
if(component == hyperion::COMP_BLACKBORDER)
|
||||
{
|
||||
_userEnabled = enable;
|
||||
if(enable)
|
||||
{
|
||||
// eg effects and probably other components don't want a BB, mimik a wrong comp state to the comp register
|
||||
if(!_hardDisabled)
|
||||
_enabled = enable;
|
||||
}
|
||||
else
|
||||
{
|
||||
_enabled = enable;
|
||||
}
|
||||
|
||||
_hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_BLACKBORDER, enable);
|
||||
}
|
||||
}
|
||||
|
||||
void BlackBorderProcessor::setHardDisable(const bool& disable) {
|
||||
|
||||
if (disable)
|
||||
{
|
||||
_enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the user has the last word to enable
|
||||
if(_userEnabled)
|
||||
_enabled = true;
|
||||
}
|
||||
_hardDisabled = disable;
|
||||
};
|
||||
|
||||
BlackBorder BlackBorderProcessor::getCurrentBorder() const
|
||||
{
|
||||
return _currentBorder;
|
||||
|
@@ -7,4 +7,7 @@ FILE ( GLOB Blackborder_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_D
|
||||
|
||||
add_library(blackborder ${Blackborder_SOURCES} )
|
||||
|
||||
target_link_libraries(blackborder hyperion-utils )
|
||||
target_link_libraries(blackborder
|
||||
hyperion-utils
|
||||
hyperion
|
||||
)
|
||||
|
@@ -15,8 +15,8 @@
|
||||
#include <QHostInfo>
|
||||
|
||||
// hyperion util includes
|
||||
#include "hyperion/ImageProcessorFactory.h"
|
||||
#include "hyperion/ImageProcessor.h"
|
||||
//#include "hyperion/ImageProcessorFactory.h"
|
||||
//#include "hyperion/ImageProcessor.h"
|
||||
#include "utils/ColorRgb.h"
|
||||
#include "HyperionConfig.h"
|
||||
|
||||
@@ -27,7 +27,7 @@ BoblightClientConnection::BoblightClientConnection(QTcpSocket *socket, const int
|
||||
: QObject()
|
||||
, _locale(QLocale::C)
|
||||
, _socket(socket)
|
||||
, _imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor())
|
||||
//, _imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor())
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
, _receiveBuffer()
|
||||
, _priority(priority)
|
||||
@@ -167,7 +167,7 @@ void BoblightClientConnection::handleMessage(const QString & message)
|
||||
// send current color values to hyperion if this is the last led assuming leds values are send in order of id
|
||||
if ((ledIndex == _ledColors.size() -1) && _priority < 255)
|
||||
{
|
||||
_hyperion->setColors(_priority, _ledColors, -1, true, hyperion::COMP_BOBLIGHTSERVER, _clientAddress);
|
||||
_hyperion->setInput(_priority, _ledColors);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -205,7 +205,7 @@ void BoblightClientConnection::handleMessage(const QString & message)
|
||||
// send current color values to hyperion
|
||||
if (_priority < 255)
|
||||
{
|
||||
_hyperion->setColors(_priority, _ledColors, -1, true, hyperion::COMP_BOBLIGHTSERVER, _clientAddress);
|
||||
_hyperion->setInput(_priority, _ledColors);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -227,11 +227,11 @@ void BoblightClientConnection::sendLightMessage()
|
||||
int n = snprintf(buffer, sizeof(buffer), "lights %d\n", _hyperion->getLedCount());
|
||||
sendMessage(QByteArray(buffer, n));
|
||||
|
||||
double h0, h1, v0, v1;
|
||||
//double h0, h1, v0, v1;
|
||||
for (unsigned i = 0; i < _hyperion->getLedCount(); ++i)
|
||||
{
|
||||
_imageProcessor->getScanParameters(i, h0, h1, v0, v1);
|
||||
n = snprintf(buffer, sizeof(buffer), "light %03d scan %f %f %f %f\n", i, 100*v0, 100*v1, 100*h0, 100*h1);
|
||||
sendMessage(QByteArray(buffer, n));
|
||||
//_imageProcessor->getScanParameters(i, h0, h1, v0, v1);
|
||||
//n = snprintf(buffer, sizeof(buffer), "light %03d scan %f %f %f %f\n", i, 100*v0, 100*v1, 100*h0, 100*h1);
|
||||
//sendMessage(QByteArray(buffer, n));
|
||||
}
|
||||
}
|
||||
|
@@ -9,8 +9,6 @@
|
||||
#include <hyperion/Hyperion.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
class ImageProcessor;
|
||||
|
||||
///
|
||||
/// The Connection object created by \a BoblightServer when a new connection is establshed
|
||||
///
|
||||
@@ -76,9 +74,6 @@ private:
|
||||
/// The TCP-Socket that is connected tot the boblight-client
|
||||
QTcpSocket * _socket;
|
||||
|
||||
/// The processor for translating images to led-values
|
||||
ImageProcessor * _imageProcessor;
|
||||
|
||||
/// Link to Hyperion for writing led-values to a priority channel
|
||||
Hyperion * _hyperion;
|
||||
|
||||
@@ -90,7 +85,7 @@ private:
|
||||
|
||||
/// The latest led color data
|
||||
std::vector<ColorRgb> _ledColors;
|
||||
|
||||
|
||||
/// logger instance
|
||||
Logger * _log;
|
||||
|
||||
|
@@ -5,20 +5,31 @@
|
||||
#include <boblightserver/BoblightServer.h>
|
||||
#include "BoblightClientConnection.h"
|
||||
|
||||
// hyperion includes
|
||||
#include <hyperion/Hyperion.h>
|
||||
// qt incl
|
||||
#include <QTcpServer>
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
BoblightServer::BoblightServer(const int priority, uint16_t port)
|
||||
BoblightServer::BoblightServer(const QJsonDocument& config)
|
||||
: QObject()
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
, _server()
|
||||
, _server(new QTcpServer(this))
|
||||
, _openConnections()
|
||||
, _priority(priority)
|
||||
, _priority(0)
|
||||
, _log(Logger::getInstance("BOBLIGHT"))
|
||||
, _isActive(false)
|
||||
, _port(port)
|
||||
, _port(0)
|
||||
{
|
||||
// Set trigger for incoming connections
|
||||
connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
||||
Debug(_log, "Instance created");
|
||||
|
||||
// listen for component change
|
||||
connect(_hyperion, SIGNAL(componentStateChanged(hyperion::Components,bool)), this, SLOT(componentStateChanged(hyperion::Components,bool)));
|
||||
// listen new connection signal from server
|
||||
connect(_server, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
||||
|
||||
// init
|
||||
handleSettingsUpdate(settings::BOBLSERVER, config);
|
||||
}
|
||||
|
||||
BoblightServer::~BoblightServer()
|
||||
@@ -28,63 +39,63 @@ BoblightServer::~BoblightServer()
|
||||
|
||||
void BoblightServer::start()
|
||||
{
|
||||
if ( active() )
|
||||
if ( _server->isListening() )
|
||||
return;
|
||||
|
||||
if (!_server.listen(QHostAddress::Any, _port))
|
||||
|
||||
if (!_server->listen(QHostAddress::Any, _port))
|
||||
{
|
||||
throw std::runtime_error("BOBLIGHT ERROR: server could not bind to port");
|
||||
Error(_log, "Could not bind to port '%d', please use an available port", _port);
|
||||
return;
|
||||
}
|
||||
Info(_log, "Boblight server started on port %d", _port);
|
||||
Info(_log, "Started on port %d", _port);
|
||||
|
||||
_isActive = true;
|
||||
emit statusChanged(_isActive);
|
||||
|
||||
_hyperion->registerPriority("Boblight", _priority);
|
||||
_hyperion->getComponentRegister().componentStateChanged(COMP_BOBLIGHTSERVER, _server->isListening());
|
||||
}
|
||||
|
||||
void BoblightServer::stop()
|
||||
{
|
||||
if ( ! active() )
|
||||
if ( ! _server->isListening() )
|
||||
return;
|
||||
|
||||
|
||||
foreach (BoblightClientConnection * connection, _openConnections) {
|
||||
delete connection;
|
||||
}
|
||||
_server.close();
|
||||
_isActive = false;
|
||||
emit statusChanged(_isActive);
|
||||
_server->close();
|
||||
|
||||
_hyperion->unRegisterPriority("Boblight");
|
||||
Info(_log, "Stopped");
|
||||
_hyperion->getComponentRegister().componentStateChanged(COMP_BOBLIGHTSERVER, _server->isListening());
|
||||
}
|
||||
|
||||
bool BoblightServer::active()
|
||||
{
|
||||
return _server->isListening();
|
||||
}
|
||||
|
||||
void BoblightServer::componentStateChanged(const hyperion::Components component, bool enable)
|
||||
{
|
||||
if (component == COMP_BOBLIGHTSERVER)
|
||||
{
|
||||
if (_isActive != enable)
|
||||
if (_server->isListening() != enable)
|
||||
{
|
||||
if (enable) start();
|
||||
else stop();
|
||||
Info(_log, "change state to %s", (_isActive ? "enabled" : "disabled") );
|
||||
}
|
||||
_hyperion->getComponentRegister().componentStateChanged(component, _isActive);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t BoblightServer::getPort() const
|
||||
{
|
||||
return _server.serverPort();
|
||||
return _server->serverPort();
|
||||
}
|
||||
|
||||
void BoblightServer::newConnection()
|
||||
{
|
||||
QTcpSocket * socket = _server.nextPendingConnection();
|
||||
QTcpSocket * socket = _server->nextPendingConnection();
|
||||
|
||||
if (socket != nullptr)
|
||||
{
|
||||
Info(_log, "new connection");
|
||||
_hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(socket->peerAddress().toString()));
|
||||
BoblightClientConnection * connection = new BoblightClientConnection(socket, _priority);
|
||||
_openConnections.insert(connection);
|
||||
|
||||
@@ -101,3 +112,16 @@ void BoblightServer::closedConnection(BoblightClientConnection *connection)
|
||||
// schedule to delete the connection object
|
||||
connection->deleteLater();
|
||||
}
|
||||
|
||||
void BoblightServer::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::BOBLSERVER)
|
||||
{
|
||||
QJsonObject obj = config.object();
|
||||
_port = obj["port"].toInt();
|
||||
_priority = obj["priority"].toInt();
|
||||
stop();
|
||||
if(obj["enable"].toBool())
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
81
libsrc/bonjour/bonjourbrowserwrapper.cpp
Normal file
81
libsrc/bonjour/bonjourbrowserwrapper.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include <bonjour/bonjourbrowserwrapper.h>
|
||||
|
||||
//qt incl
|
||||
#include <QTimer>
|
||||
|
||||
// bonjour
|
||||
#include <bonjour/bonjourservicebrowser.h>
|
||||
#include <bonjour/bonjourserviceresolver.h>
|
||||
|
||||
BonjourBrowserWrapper* BonjourBrowserWrapper::instance = nullptr;
|
||||
|
||||
BonjourBrowserWrapper::BonjourBrowserWrapper(QObject * parent)
|
||||
: QObject(parent)
|
||||
, _bonjourResolver(new BonjourServiceResolver(this))
|
||||
, _timerBonjourResolver( new QTimer(this))
|
||||
{
|
||||
BonjourBrowserWrapper::instance = this;
|
||||
connect(_bonjourResolver, &BonjourServiceResolver::bonjourRecordResolved, this, &BonjourBrowserWrapper::bonjourRecordResolved);
|
||||
|
||||
connect(_timerBonjourResolver, &QTimer::timeout, this, &BonjourBrowserWrapper::bonjourResolve);
|
||||
_timerBonjourResolver->setInterval(1000);
|
||||
_timerBonjourResolver->start();
|
||||
|
||||
// browse for _hyperiond-http._tcp
|
||||
browseForServiceType(QLatin1String("_hyperiond-http._tcp"));
|
||||
}
|
||||
|
||||
bool BonjourBrowserWrapper::browseForServiceType(const QString &serviceType)
|
||||
{
|
||||
if(!_browsedServices.contains(serviceType))
|
||||
{
|
||||
BonjourServiceBrowser* newBrowser = new BonjourServiceBrowser(this);
|
||||
connect(newBrowser, &BonjourServiceBrowser::currentBonjourRecordsChanged, this, &BonjourBrowserWrapper::currentBonjourRecordsChanged);
|
||||
newBrowser->browseForServiceType(serviceType);
|
||||
_browsedServices.insert(serviceType, newBrowser);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BonjourBrowserWrapper::currentBonjourRecordsChanged(const QList<BonjourRecord> &list)
|
||||
{
|
||||
_hyperionSessions.clear();
|
||||
for ( auto rec : list )
|
||||
{
|
||||
_hyperionSessions.insert(rec.serviceName, rec);
|
||||
}
|
||||
}
|
||||
|
||||
void BonjourBrowserWrapper::bonjourRecordResolved(const QHostInfo &hostInfo, int port)
|
||||
{
|
||||
if ( _hyperionSessions.contains(_bonjourCurrentServiceToResolve))
|
||||
{
|
||||
QString host = hostInfo.hostName();
|
||||
QString domain = _hyperionSessions[_bonjourCurrentServiceToResolve].replyDomain;
|
||||
if (host.endsWith("."+domain))
|
||||
{
|
||||
host.remove(host.length()-domain.length()-1,domain.length()+1);
|
||||
}
|
||||
_hyperionSessions[_bonjourCurrentServiceToResolve].hostName = host;
|
||||
_hyperionSessions[_bonjourCurrentServiceToResolve].port = port;
|
||||
_hyperionSessions[_bonjourCurrentServiceToResolve].address = hostInfo.addresses().isEmpty() ? "" : hostInfo.addresses().first().toString();
|
||||
//Debug(_log, "found hyperion session: %s:%d",QSTRING_CSTR(hostInfo.hostName()), port);
|
||||
|
||||
//emit change
|
||||
emit browserChange(_hyperionSessions);
|
||||
}
|
||||
}
|
||||
|
||||
void BonjourBrowserWrapper::bonjourResolve()
|
||||
{
|
||||
for(auto key : _hyperionSessions.keys())
|
||||
{
|
||||
if (_hyperionSessions[key].port < 0)
|
||||
{
|
||||
_bonjourCurrentServiceToResolve = key;
|
||||
_bonjourResolver->resolveBonjourRecord(_hyperionSessions[key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -30,8 +30,12 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <QtCore/QSocketNotifier>
|
||||
#include <QHostInfo>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <HyperionConfig.h>
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
|
||||
BonjourServiceRegister::BonjourServiceRegister(QObject *parent)
|
||||
: QObject(parent), dnssref(0), bonjourSocket(0)
|
||||
@@ -48,6 +52,19 @@ BonjourServiceRegister::~BonjourServiceRegister()
|
||||
}
|
||||
}
|
||||
|
||||
void BonjourServiceRegister::registerService(const QString& service, const int& port)
|
||||
{
|
||||
// zeroconf $configname@$hostname:port
|
||||
QString prettyName = Hyperion::getInstance()->getQJsonConfig()["general"].toObject()["name"].toString();
|
||||
registerService(
|
||||
BonjourRecord(prettyName+"@"+QHostInfo::localHostName()+ ":" + QString::number(port),
|
||||
service,
|
||||
QString()
|
||||
),
|
||||
port
|
||||
);
|
||||
}
|
||||
|
||||
void BonjourServiceRegister::registerService(const BonjourRecord &record, quint16 servicePort, std::vector<std::pair<std::string, std::string>> txt)
|
||||
{
|
||||
if (dnssref)
|
||||
@@ -61,22 +78,25 @@ void BonjourServiceRegister::registerService(const BonjourRecord &record, quint1
|
||||
bigEndianPort = 0 | ((servicePort & 0x00ff) << 8) | ((servicePort & 0xff00) >> 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
// base txtRec
|
||||
std::vector<std::pair<std::string, std::string> > txtBase = {{"id",Hyperion::getInstance()->getId().toStdString()},{"version",HYPERION_VERSION}};
|
||||
// create txt record
|
||||
TXTRecordRef txtRec;
|
||||
TXTRecordCreate(&txtRec,0,NULL);
|
||||
|
||||
// add txt records
|
||||
if(!txt.empty())
|
||||
if(!txt.empty())
|
||||
{
|
||||
for(std::vector<std::pair<std::string, std::string> >::const_iterator it = txt.begin(); it != txt.end(); ++it)
|
||||
{
|
||||
//Debug(Logger::getInstance("BonJour"), "TXTRecord: key:%s, value:%s",it->first.c_str(),it->second.c_str());
|
||||
uint8_t txtLen = (uint8_t)strlen(it->second.c_str());
|
||||
TXTRecordSetValue(&txtRec, it->first.c_str(), txtLen, it->second.c_str());
|
||||
}
|
||||
txtBase.insert(txtBase.end(), txt.begin(), txt.end());
|
||||
}
|
||||
// add txt records
|
||||
for(std::vector<std::pair<std::string, std::string> >::const_iterator it = txtBase.begin(); it != txtBase.end(); ++it)
|
||||
{
|
||||
//Debug(Logger::getInstance("BonJour"), "TXTRecord: key:%s, value:%s",it->first.c_str(),it->second.c_str());
|
||||
uint8_t txtLen = (uint8_t)strlen(it->second.c_str());
|
||||
TXTRecordSetValue(&txtRec, it->first.c_str(), txtLen, it->second.c_str());
|
||||
}
|
||||
|
||||
|
||||
DNSServiceErrorType err = DNSServiceRegister(&dnssref, 0, 0, record.serviceName.toUtf8().constData(),
|
||||
record.registeredType.toUtf8().constData(),
|
||||
(record.replyDomain.isEmpty() ? 0 : record.replyDomain.toUtf8().constData()),
|
||||
|
@@ -27,6 +27,7 @@ add_library(effectengine
|
||||
|
||||
target_link_libraries(effectengine
|
||||
hyperion
|
||||
python
|
||||
Qt5::Core
|
||||
Qt5::Gui
|
||||
${PYTHON_LIBRARIES}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,108 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Python includes
|
||||
// collide of qt slots macro
|
||||
#undef slots
|
||||
#include <Python.h>
|
||||
#define slots
|
||||
|
||||
// Qt includes
|
||||
#include <QThread>
|
||||
#include <QSize>
|
||||
#include <QImage>
|
||||
#include <QPainter>
|
||||
#include <QMap>
|
||||
|
||||
// Hyperion includes
|
||||
#include <hyperion/ImageProcessor.h>
|
||||
#include <utils/Components.h>
|
||||
|
||||
class Effect : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Effect(PyThreadState* mainThreadState, int priority, int timeout, const QString & script, const QString & name, const QJsonObject & args = QJsonObject(), const QString & origin="System", unsigned smoothCfg=0);
|
||||
virtual ~Effect();
|
||||
|
||||
virtual void run();
|
||||
|
||||
int getPriority() const { return _priority; };
|
||||
|
||||
QString getScript() const { return _script; }
|
||||
QString getName() const { return _name; }
|
||||
|
||||
int getTimeout() const {return _timeout; }
|
||||
|
||||
QJsonObject getArgs() const { return _args; }
|
||||
|
||||
/// This function registers the extension module in Python
|
||||
static void registerHyperionExtensionModule();
|
||||
|
||||
signals:
|
||||
void setColors(int priority, const std::vector<ColorRgb> &ledColors, const int timeout_ms, bool clearEffects, hyperion::Components componentconst, QString origin, unsigned smoothCfg);
|
||||
|
||||
private:
|
||||
PyObject * json2python(const QJsonValue & jsonData) const;
|
||||
|
||||
// Wrapper methods for Python interpreter extra buildin methods
|
||||
static PyMethodDef effectMethods[];
|
||||
static PyObject* wrapSetColor (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapSetImage (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapGetImage (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapAbort (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageShow (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageLinearGradient (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageConicalGradient (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageRadialGradient (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageSolidFill (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageDrawLine (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageDrawPoint (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageDrawRect (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageDrawPolygon (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageDrawPie (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageSetPixel (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageGetPixel (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageSave (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageMinSize (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageWidth (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageHeight (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageCRotate (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageCOffset (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageCShear (PyObject *self, PyObject *args);
|
||||
static PyObject* wrapImageResetT (PyObject *self, PyObject *args);
|
||||
static Effect * getEffect();
|
||||
|
||||
static struct PyModuleDef moduleDef;
|
||||
static PyObject* PyInit_hyperion();
|
||||
|
||||
void addImage();
|
||||
|
||||
PyThreadState* _mainThreadState;
|
||||
|
||||
const int _priority;
|
||||
|
||||
const int _timeout;
|
||||
|
||||
const QString _script;
|
||||
const QString _name;
|
||||
unsigned _smoothCfg;
|
||||
|
||||
const QJsonObject _args;
|
||||
|
||||
int64_t _endTime;
|
||||
|
||||
/// The processor for translating images to led-values
|
||||
ImageProcessor * _imageProcessor;
|
||||
|
||||
/// Buffer for colorData
|
||||
QVector<ColorRgb> _colors;
|
||||
|
||||
Logger* _log;
|
||||
|
||||
QString _origin;
|
||||
QSize _imageSize;
|
||||
QImage _image;
|
||||
QPainter* _painter;
|
||||
QVector<QImage> _imageStack;
|
||||
};
|
@@ -16,7 +16,8 @@
|
||||
|
||||
// effect engine includes
|
||||
#include <effectengine/EffectEngine.h>
|
||||
#include "Effect.h"
|
||||
#include <effectengine/Effect.h>
|
||||
#include <effectengine/EffectModule.h>
|
||||
#include "HyperionConfig.h"
|
||||
|
||||
EffectEngine::EffectEngine(Hyperion * hyperion, const QJsonObject & jsonEffectConfig)
|
||||
@@ -25,11 +26,11 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const QJsonObject & jsonEffectCo
|
||||
, _availableEffects()
|
||||
, _activeEffects()
|
||||
, _log(Logger::getInstance("EFFECTENGINE"))
|
||||
, _mainThreadState(nullptr)
|
||||
{
|
||||
|
||||
Q_INIT_RESOURCE(EffectEngine);
|
||||
qRegisterMetaType<std::vector<ColorRgb>>("std::vector<ColorRgb>");
|
||||
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
||||
qRegisterMetaType<hyperion::Components>("hyperion::Components");
|
||||
|
||||
// connect the Hyperion channel clear feedback
|
||||
@@ -38,21 +39,10 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const QJsonObject & jsonEffectCo
|
||||
|
||||
// read all effects
|
||||
readEffects();
|
||||
|
||||
// initialize the python interpreter
|
||||
Debug(_log, "Initializing Python interpreter");
|
||||
Effect::registerHyperionExtensionModule();
|
||||
Py_InitializeEx(0);
|
||||
PyEval_InitThreads(); // Create the GIL
|
||||
_mainThreadState = PyEval_SaveThread();
|
||||
}
|
||||
|
||||
EffectEngine::~EffectEngine()
|
||||
{
|
||||
// clean up the Python interpreter
|
||||
Debug(_log, "Cleaning up Python interpreter");
|
||||
PyEval_RestoreThread(_mainThreadState);
|
||||
Py_Finalize();
|
||||
}
|
||||
|
||||
const std::list<ActiveEffectDefinition> &EffectEngine::getActiveEffects()
|
||||
@@ -73,6 +63,33 @@ const std::list<ActiveEffectDefinition> &EffectEngine::getActiveEffects()
|
||||
return _availableActiveEffects;
|
||||
}
|
||||
|
||||
void EffectEngine::cacheRunningEffects()
|
||||
{
|
||||
_cachedActiveEffects.clear();
|
||||
|
||||
for (Effect * effect : _activeEffects)
|
||||
{
|
||||
ActiveEffectDefinition activeEffectDefinition;
|
||||
activeEffectDefinition.script = effect->getScript();
|
||||
activeEffectDefinition.name = effect->getName();
|
||||
activeEffectDefinition.priority = effect->getPriority();
|
||||
activeEffectDefinition.timeout = effect->getTimeout();
|
||||
activeEffectDefinition.args = effect->getArgs();
|
||||
_cachedActiveEffects.push_back(activeEffectDefinition);
|
||||
channelCleared(effect->getPriority());
|
||||
}
|
||||
}
|
||||
|
||||
void EffectEngine::startCachedEffects()
|
||||
{
|
||||
for (const auto & def : _cachedActiveEffects)
|
||||
{
|
||||
// the smooth cfg AND origin are ignored for this start!
|
||||
runEffect(def.name, def.args, def.priority, def.timeout, def.script);
|
||||
}
|
||||
_cachedActiveEffects.clear();
|
||||
}
|
||||
|
||||
bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effectConfigFile, EffectDefinition & effectDefinition)
|
||||
{
|
||||
QString fileName = path + QDir::separator() + effectConfigFile;
|
||||
@@ -241,6 +258,8 @@ void EffectEngine::readEffects()
|
||||
}
|
||||
|
||||
ErrorIf(_availableEffects.size()==0, _log, "no effects found, check your effect directories");
|
||||
|
||||
emit effectListUpdated();
|
||||
}
|
||||
|
||||
int EffectEngine::runEffect(const QString &effectName, int priority, int timeout, const QString &origin)
|
||||
@@ -266,7 +285,7 @@ int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args,
|
||||
if (effectDefinition == nullptr)
|
||||
{
|
||||
// no such effect
|
||||
Error(_log, "effect %s not found", QSTRING_CSTR(effectName));
|
||||
Error(_log, "Effect %s not found", QSTRING_CSTR(effectName));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -281,13 +300,14 @@ int EffectEngine::runEffectScript(const QString &script, const QString &name, co
|
||||
channelCleared(priority);
|
||||
|
||||
// create the effect
|
||||
Effect * effect = new Effect(_mainThreadState, priority, timeout, script, name, args, origin, smoothCfg);
|
||||
connect(effect, SIGNAL(setColors(int,std::vector<ColorRgb>,int,bool,hyperion::Components,const QString,unsigned)), _hyperion, SLOT(setColors(int,std::vector<ColorRgb>,int,bool,hyperion::Components,const QString,unsigned)), Qt::QueuedConnection);
|
||||
Effect * effect = new Effect(_hyperion, priority, timeout, script, name, args);
|
||||
connect(effect, &Effect::setInput, _hyperion, &Hyperion::setInput, Qt::QueuedConnection);
|
||||
connect(effect, &Effect::setInputImage, _hyperion, &Hyperion::setInputImage, Qt::QueuedConnection);
|
||||
connect(effect, &QThread::finished, this, &EffectEngine::effectFinished);
|
||||
_activeEffects.push_back(effect);
|
||||
|
||||
// start the effect
|
||||
_hyperion->registerPriority(name, priority);
|
||||
_hyperion->registerInput(priority, hyperion::COMP_EFFECT, origin, name ,smoothCfg);
|
||||
effect->start();
|
||||
|
||||
return 0;
|
||||
@@ -299,7 +319,7 @@ void EffectEngine::channelCleared(int priority)
|
||||
{
|
||||
if (effect->getPriority() == priority)
|
||||
{
|
||||
effect->requestInterruption();
|
||||
effect->setInteruptionFlag();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,7 +330,7 @@ void EffectEngine::allChannelsCleared()
|
||||
{
|
||||
if (effect->getPriority() != 254)
|
||||
{
|
||||
effect->requestInterruption();
|
||||
effect->setInteruptionFlag();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -318,7 +338,7 @@ void EffectEngine::allChannelsCleared()
|
||||
void EffectEngine::effectFinished()
|
||||
{
|
||||
Effect* effect = qobject_cast<Effect*>(sender());
|
||||
if (!effect->isInterruptionRequested())
|
||||
if (!effect->hasInteruptionFlag())
|
||||
{
|
||||
// effect stopped by itself. Clear the channel
|
||||
_hyperion->clear(effect->getPriority());
|
||||
@@ -336,5 +356,4 @@ void EffectEngine::effectFinished()
|
||||
|
||||
// cleanup the effect
|
||||
effect->deleteLater();
|
||||
_hyperion->unRegisterPriority(effect->getName());
|
||||
}
|
||||
|
1077
libsrc/effectengine/EffectModule.cpp
Normal file
1077
libsrc/effectengine/EffectModule.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
#include <grabber/AmlogicWrapper.h>
|
||||
|
||||
AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, const int priority)
|
||||
: GrabberWrapper("AmLogic", &_grabber, grabWidth, grabHeight, updateRate_Hz, priority, hyperion::COMP_GRABBER)
|
||||
AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz)
|
||||
: GrabberWrapper("AmLogic", &_grabber, grabWidth, grabHeight, updateRate_Hz)
|
||||
, _grabber(grabWidth, grabHeight)
|
||||
{}
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
#include "grabber/DispmanxFrameGrabber.h"
|
||||
|
||||
DispmanxFrameGrabber::DispmanxFrameGrabber(const unsigned width, const unsigned height)
|
||||
: Grabber("DISPMANXGRABBER", width, height)
|
||||
: Grabber("DISPMANXGRABBER", 0, 0)
|
||||
, _vc_display(0)
|
||||
, _vc_resource(0)
|
||||
, _vc_flags(0)
|
||||
@@ -20,48 +20,60 @@ DispmanxFrameGrabber::DispmanxFrameGrabber(const unsigned width, const unsigned
|
||||
// Initiase BCM
|
||||
bcm_host_init();
|
||||
|
||||
{
|
||||
// Check if the display can be opened and display the current resolution
|
||||
// Open the connection to the display
|
||||
_vc_display = vc_dispmanx_display_open(0);
|
||||
assert(_vc_display > 0);
|
||||
// Check if the display can be opened and display the current resolution
|
||||
// Open the connection to the display
|
||||
_vc_display = vc_dispmanx_display_open(0);
|
||||
assert(_vc_display > 0);
|
||||
|
||||
// Obtain the display information
|
||||
DISPMANX_MODEINFO_T vc_info;
|
||||
int result = vc_dispmanx_display_get_info(_vc_display, &vc_info);
|
||||
// Keep compiler happy in 'release' mode
|
||||
(void)result;
|
||||
assert(result == 0);
|
||||
Info(_log, "Display opened with resolution: %dx%d", vc_info.width, vc_info.height);
|
||||
// Obtain the display information
|
||||
DISPMANX_MODEINFO_T vc_info;
|
||||
int result = vc_dispmanx_display_get_info(_vc_display, &vc_info);
|
||||
// Keep compiler happy in 'release' mode
|
||||
(void)result;
|
||||
assert(result == 0);
|
||||
Info(_log, "Display opened with resolution: %dx%d", vc_info.width, vc_info.height);
|
||||
|
||||
// Close the displaye
|
||||
vc_dispmanx_display_close(_vc_display);
|
||||
}
|
||||
// Close the displaye
|
||||
vc_dispmanx_display_close(_vc_display);
|
||||
|
||||
// Create the resources for capturing image
|
||||
uint32_t vc_nativeImageHandle;
|
||||
_vc_resource = vc_dispmanx_resource_create(
|
||||
VC_IMAGE_RGBA32,
|
||||
width,
|
||||
height,
|
||||
&vc_nativeImageHandle);
|
||||
assert(_vc_resource);
|
||||
|
||||
// Define the capture rectangle with the same size
|
||||
vc_dispmanx_rect_set(&_rectangle, 0, 0, width, height);
|
||||
// init the resource and capture rectangle
|
||||
setWidthHeight(width, height);
|
||||
}
|
||||
|
||||
DispmanxFrameGrabber::~DispmanxFrameGrabber()
|
||||
{
|
||||
delete[] _captureBuffer;
|
||||
|
||||
// Clean up resources
|
||||
vc_dispmanx_resource_delete(_vc_resource);
|
||||
freeResources();
|
||||
|
||||
// De-init BCM
|
||||
bcm_host_deinit();
|
||||
}
|
||||
|
||||
void DispmanxFrameGrabber::freeResources()
|
||||
{
|
||||
delete[] _captureBuffer;
|
||||
// Clean up resources
|
||||
vc_dispmanx_resource_delete(_vc_resource);
|
||||
}
|
||||
|
||||
void DispmanxFrameGrabber::setWidthHeight(int width, int height)
|
||||
{
|
||||
if(_width != width || _height != height)
|
||||
{
|
||||
freeResources();
|
||||
// Create the resources for capturing image
|
||||
uint32_t vc_nativeImageHandle;
|
||||
_vc_resource = vc_dispmanx_resource_create(
|
||||
VC_IMAGE_RGBA32,
|
||||
width,
|
||||
height,
|
||||
&vc_nativeImageHandle);
|
||||
assert(_vc_resource);
|
||||
|
||||
// Define the capture rectangle with the same size
|
||||
vc_dispmanx_rect_set(&_rectangle, 0, 0, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void DispmanxFrameGrabber::setFlags(const int vc_flags)
|
||||
{
|
||||
_vc_flags = vc_flags;
|
||||
@@ -143,8 +155,8 @@ int DispmanxFrameGrabber::grabFrame(Image<ColorRgb> & image)
|
||||
unsigned capturePitch = (_rectangle.width * sizeof(ColorRgba) + 63) & (~63);
|
||||
|
||||
// grab to temp buffer if image pitch isn't valid or if we are cropping
|
||||
if (imagePitch != capturePitch
|
||||
|| (unsigned)_rectangle.width != imageWidth
|
||||
if (imagePitch != capturePitch
|
||||
|| (unsigned)_rectangle.width != imageWidth
|
||||
|| (unsigned)_rectangle.height != imageHeight)
|
||||
{
|
||||
// check if we need to resize the capture buffer
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#include <grabber/DispmanxWrapper.h>
|
||||
|
||||
DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, const int priority)
|
||||
: GrabberWrapper("Dispmanx", &_grabber, grabWidth, grabHeight, updateRate_Hz, priority, hyperion::COMP_GRABBER)
|
||||
DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz)
|
||||
: GrabberWrapper("Dispmanx", &_grabber, grabWidth, grabHeight, updateRate_Hz)
|
||||
, _grabber(grabWidth, grabHeight)
|
||||
{
|
||||
setImageProcessorEnabled(false);
|
||||
|
@@ -16,31 +16,9 @@ FramebufferFrameGrabber::FramebufferFrameGrabber(const QString & device, const u
|
||||
: Grabber("FRAMEBUFFERGRABBER", width, height)
|
||||
, _fbfd(0)
|
||||
, _fbp(0)
|
||||
, _fbDevice(device)
|
||||
, _fbDevice()
|
||||
{
|
||||
int result;
|
||||
struct fb_var_screeninfo vinfo;
|
||||
|
||||
// Check if the framebuffer device can be opened and display the current resolution
|
||||
_fbfd = open(QSTRING_CSTR(_fbDevice), O_RDONLY);
|
||||
if (_fbfd == 0)
|
||||
{
|
||||
Error(_log, "Error openning %s", QSTRING_CSTR(_fbDevice));
|
||||
}
|
||||
else
|
||||
{
|
||||
// get variable screen information
|
||||
result = ioctl (_fbfd, FBIOGET_VSCREENINFO, &vinfo);
|
||||
if (result != 0)
|
||||
{
|
||||
Error(_log, "Could not get screen information");
|
||||
}
|
||||
else
|
||||
{
|
||||
Info(_log, "Display opened with resolution: %dx%d@%dbit", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
|
||||
}
|
||||
close(_fbfd);
|
||||
}
|
||||
setDevicePath(device);
|
||||
}
|
||||
|
||||
FramebufferFrameGrabber::~FramebufferFrameGrabber()
|
||||
@@ -63,7 +41,7 @@ int FramebufferFrameGrabber::grabFrame(Image<ColorRgb> & image)
|
||||
|
||||
bytesPerPixel = vinfo.bits_per_pixel / 8;
|
||||
capSize = vinfo.xres * vinfo.yres * bytesPerPixel;
|
||||
|
||||
|
||||
switch (vinfo.bits_per_pixel)
|
||||
{
|
||||
case 16: pixelFormat = PIXELFORMAT_BGR16; break;
|
||||
@@ -76,7 +54,7 @@ int FramebufferFrameGrabber::grabFrame(Image<ColorRgb> & image)
|
||||
}
|
||||
|
||||
/* map the device to memory */
|
||||
_fbp = (unsigned char*)mmap(0, capSize, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, _fbfd, 0);
|
||||
_fbp = (unsigned char*)mmap(0, capSize, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, _fbfd, 0);
|
||||
|
||||
_imageResampler.setHorizontalPixelDecimation(vinfo.xres/_width);
|
||||
_imageResampler.setVerticalPixelDecimation(vinfo.yres/_height);
|
||||
@@ -86,9 +64,41 @@ int FramebufferFrameGrabber::grabFrame(Image<ColorRgb> & image)
|
||||
vinfo.xres * bytesPerPixel,
|
||||
pixelFormat,
|
||||
image);
|
||||
|
||||
|
||||
munmap(_fbp, capSize);
|
||||
close(_fbfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FramebufferFrameGrabber::setDevicePath(const QString& path)
|
||||
{
|
||||
if(_fbDevice != path)
|
||||
{
|
||||
_fbDevice = path;
|
||||
int result;
|
||||
struct fb_var_screeninfo vinfo;
|
||||
|
||||
// Check if the framebuffer device can be opened and display the current resolution
|
||||
_fbfd = open(QSTRING_CSTR(_fbDevice), O_RDONLY);
|
||||
if (_fbfd == 0)
|
||||
{
|
||||
Error(_log, "Error openning %s", QSTRING_CSTR(_fbDevice));
|
||||
}
|
||||
else
|
||||
{
|
||||
// get variable screen information
|
||||
result = ioctl (_fbfd, FBIOGET_VSCREENINFO, &vinfo);
|
||||
if (result != 0)
|
||||
{
|
||||
Error(_log, "Could not get screen information");
|
||||
}
|
||||
else
|
||||
{
|
||||
Info(_log, "Display opened with resolution: %dx%d@%dbit", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
|
||||
}
|
||||
close(_fbfd);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#include <grabber/FramebufferWrapper.h>
|
||||
|
||||
FramebufferWrapper::FramebufferWrapper(const QString & device, const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, const int priority)
|
||||
: GrabberWrapper("FrameBuffer", &_grabber, grabWidth, grabHeight, updateRate_Hz, priority, hyperion::COMP_GRABBER)
|
||||
FramebufferWrapper::FramebufferWrapper(const QString & device, const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz)
|
||||
: GrabberWrapper("FrameBuffer", &_grabber, grabWidth, grabHeight, updateRate_Hz)
|
||||
, _grabber(device, grabWidth, grabHeight)
|
||||
{}
|
||||
|
||||
|
@@ -7,30 +7,10 @@
|
||||
|
||||
OsxFrameGrabber::OsxFrameGrabber(const unsigned display, const unsigned width, const unsigned height)
|
||||
: Grabber("OSXGRABBER", width, height)
|
||||
, _screenIndex(display)
|
||||
, _screenIndex(100)
|
||||
{
|
||||
CGImageRef image;
|
||||
CGDisplayCount displayCount;
|
||||
CGDirectDisplayID displays[8];
|
||||
|
||||
// get list of displays
|
||||
CGGetActiveDisplayList(8, displays, &displayCount);
|
||||
if (_screenIndex + 1 > displayCount)
|
||||
{
|
||||
Error(_log, "Display with index %d is not available. Using main display", _screenIndex);
|
||||
_display = kCGDirectMainDisplay;
|
||||
}
|
||||
else
|
||||
{
|
||||
_display = displays[_screenIndex];
|
||||
}
|
||||
|
||||
image = CGDisplayCreateImage(_display);
|
||||
assert(image != NULL);
|
||||
|
||||
Info(_log, "Display opened with resolution: %dx%d@%dbit", CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerPixel(image));
|
||||
|
||||
CGImageRelease(image);
|
||||
// check if display is available
|
||||
setDisplayIndex(display);
|
||||
}
|
||||
|
||||
OsxFrameGrabber::~OsxFrameGrabber()
|
||||
@@ -43,11 +23,11 @@ int OsxFrameGrabber::grabFrame(Image<ColorRgb> & image)
|
||||
|
||||
CGImageRef dispImage;
|
||||
CFDataRef imgData;
|
||||
unsigned char * pImgData;
|
||||
unsigned char * pImgData;
|
||||
unsigned dspWidth, dspHeight;
|
||||
|
||||
|
||||
dispImage = CGDisplayCreateImage(_display);
|
||||
|
||||
|
||||
// display lost, use main
|
||||
if (dispImage == NULL && _display)
|
||||
{
|
||||
@@ -63,7 +43,7 @@ int OsxFrameGrabber::grabFrame(Image<ColorRgb> & image)
|
||||
pImgData = (unsigned char*) CFDataGetBytePtr(imgData);
|
||||
dspWidth = CGImageGetWidth(dispImage);
|
||||
dspHeight = CGImageGetHeight(dispImage);
|
||||
|
||||
|
||||
_imageResampler.setHorizontalPixelDecimation(dspWidth/_width);
|
||||
_imageResampler.setVerticalPixelDecimation(dspHeight/_height);
|
||||
_imageResampler.processImage( pImgData,
|
||||
@@ -72,9 +52,40 @@ int OsxFrameGrabber::grabFrame(Image<ColorRgb> & image)
|
||||
CGImageGetBytesPerRow(dispImage),
|
||||
PIXELFORMAT_BGR32,
|
||||
image);
|
||||
|
||||
|
||||
CFRelease(imgData);
|
||||
CGImageRelease(dispImage);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OsxFrameGrabber::setDisplayIndex(int index)
|
||||
{
|
||||
if(_screenIndex != index)
|
||||
{
|
||||
_screenIndex = index;
|
||||
|
||||
CGImageRef image;
|
||||
CGDisplayCount displayCount;
|
||||
CGDirectDisplayID displays[8];
|
||||
|
||||
// get list of displays
|
||||
CGGetActiveDisplayList(8, displays, &displayCount);
|
||||
if (_screenIndex + 1 > displayCount)
|
||||
{
|
||||
Error(_log, "Display with index %d is not available. Using main display", _screenIndex);
|
||||
_display = kCGDirectMainDisplay;
|
||||
}
|
||||
else
|
||||
{
|
||||
_display = displays[_screenIndex];
|
||||
}
|
||||
|
||||
image = CGDisplayCreateImage(_display);
|
||||
assert(image != NULL);
|
||||
|
||||
Info(_log, "Display opened with resolution: %dx%d@%dbit", CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerPixel(image));
|
||||
|
||||
CGImageRelease(image);
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#include <grabber/OsxWrapper.h>
|
||||
|
||||
OsxWrapper::OsxWrapper(const unsigned display, const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, const int priority)
|
||||
: GrabberWrapper("OSX FrameGrabber", &_grabber, grabWidth, grabHeight, updateRate_Hz, priority, hyperion::COMP_GRABBER)
|
||||
OsxWrapper::OsxWrapper(const unsigned display, const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz)
|
||||
: GrabberWrapper("OSX FrameGrabber", &_grabber, grabWidth, grabHeight, updateRate_Hz)
|
||||
, _grabber(display, grabWidth, grabHeight)
|
||||
{}
|
||||
|
||||
|
@@ -27,13 +27,9 @@ V4L2Grabber::V4L2Grabber(const QString & device
|
||||
, int input
|
||||
, VideoStandard videoStandard
|
||||
, PixelFormat pixelFormat
|
||||
, unsigned width
|
||||
, unsigned height
|
||||
, int frameDecimation
|
||||
, int horizontalPixelDecimation
|
||||
, int verticalPixelDecimation
|
||||
, int pixelDecimation
|
||||
)
|
||||
: Grabber("V4L2:"+device, width, height)
|
||||
: Grabber("V4L2:"+device)
|
||||
, _deviceName(device)
|
||||
, _input(input)
|
||||
, _videoStandard(videoStandard)
|
||||
@@ -41,9 +37,9 @@ V4L2Grabber::V4L2Grabber(const QString & device
|
||||
, _fileDescriptor(-1)
|
||||
, _buffers()
|
||||
, _pixelFormat(pixelFormat)
|
||||
, _pixelDecimation(pixelDecimation)
|
||||
, _lineLength(-1)
|
||||
, _frameByteSize(-1)
|
||||
, _frameDecimation(qMax(1, frameDecimation))
|
||||
, _noSignalCounterThreshold(50)
|
||||
, _noSignalThresholdColor(ColorRgb{0,0,0})
|
||||
, _signalDetectionEnabled(true)
|
||||
@@ -53,14 +49,13 @@ V4L2Grabber::V4L2Grabber(const QString & device
|
||||
, _y_frac_min(0.25)
|
||||
, _x_frac_max(0.75)
|
||||
, _y_frac_max(0.75)
|
||||
, _currentFrame(0)
|
||||
, _streamNotifier(nullptr)
|
||||
, _initialized(false)
|
||||
, _deviceAutoDiscoverEnabled(false)
|
||||
|
||||
{
|
||||
_imageResampler.setHorizontalPixelDecimation(qMax(1, horizontalPixelDecimation));
|
||||
_imageResampler.setVerticalPixelDecimation(qMax(1, verticalPixelDecimation));
|
||||
//_imageResampler.setHorizontalPixelDecimation(pixelDecimation);
|
||||
//_imageResampler.setVerticalPixelDecimation(pixelDecimation);
|
||||
|
||||
getV4Ldevices();
|
||||
}
|
||||
@@ -245,11 +240,13 @@ void V4L2Grabber::open_device()
|
||||
if (-1 == stat(QSTRING_CSTR(_deviceName), &st))
|
||||
{
|
||||
throw_errno_exception("Cannot identify '" + _deviceName + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!S_ISCHR(st.st_mode))
|
||||
{
|
||||
throw_exception("'" + _deviceName + "' is no device");
|
||||
return;
|
||||
}
|
||||
|
||||
_fileDescriptor = open(QSTRING_CSTR(_deviceName), O_RDWR | O_NONBLOCK, 0);
|
||||
@@ -257,6 +254,7 @@ void V4L2Grabber::open_device()
|
||||
if (-1 == _fileDescriptor)
|
||||
{
|
||||
throw_errno_exception("Cannot open '" + _deviceName + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
// create the notifier for when a new frame is available
|
||||
@@ -268,7 +266,10 @@ void V4L2Grabber::open_device()
|
||||
void V4L2Grabber::close_device()
|
||||
{
|
||||
if (-1 == close(_fileDescriptor))
|
||||
{
|
||||
throw_errno_exception("close");
|
||||
return;
|
||||
}
|
||||
|
||||
_fileDescriptor = -1;
|
||||
|
||||
@@ -288,6 +289,7 @@ void V4L2Grabber::init_read(unsigned int buffer_size)
|
||||
|
||||
if (!_buffers[0].start) {
|
||||
throw_exception("Out of memory");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,13 +306,16 @@ void V4L2Grabber::init_mmap()
|
||||
if (-1 == xioctl(VIDIOC_REQBUFS, &req)) {
|
||||
if (EINVAL == errno) {
|
||||
throw_exception("'" + _deviceName + "' does not support memory mapping");
|
||||
return;
|
||||
} else {
|
||||
throw_errno_exception("VIDIOC_REQBUFS");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.count < 2) {
|
||||
throw_exception("Insufficient buffer memory on " + _deviceName);
|
||||
return;
|
||||
}
|
||||
|
||||
_buffers.resize(req.count);
|
||||
@@ -325,7 +330,10 @@ void V4L2Grabber::init_mmap()
|
||||
buf.index = n_buffers;
|
||||
|
||||
if (-1 == xioctl(VIDIOC_QUERYBUF, &buf))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_QUERYBUF");
|
||||
return;
|
||||
}
|
||||
|
||||
_buffers[n_buffers].length = buf.length;
|
||||
_buffers[n_buffers].start =
|
||||
@@ -336,7 +344,10 @@ void V4L2Grabber::init_mmap()
|
||||
_fileDescriptor, buf.m.offset);
|
||||
|
||||
if (MAP_FAILED == _buffers[n_buffers].start)
|
||||
{
|
||||
throw_errno_exception("mmap");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,8 +365,10 @@ void V4L2Grabber::init_userp(unsigned int buffer_size)
|
||||
if (EINVAL == errno)
|
||||
{
|
||||
throw_exception("'" + _deviceName + "' does not support user pointer");
|
||||
return;
|
||||
} else {
|
||||
throw_errno_exception("VIDIOC_REQBUFS");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,6 +380,7 @@ void V4L2Grabber::init_userp(unsigned int buffer_size)
|
||||
|
||||
if (!_buffers[n_buffers].start) {
|
||||
throw_exception("Out of memory");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -378,14 +392,17 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
{
|
||||
if (EINVAL == errno) {
|
||||
throw_exception("'" + _deviceName + "' is no V4L2 device");
|
||||
return;
|
||||
} else {
|
||||
throw_errno_exception("VIDIOC_QUERYCAP");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
|
||||
{
|
||||
throw_exception("'" + _deviceName + "' is no video capture device");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_ioMethod) {
|
||||
@@ -393,6 +410,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
if (!(cap.capabilities & V4L2_CAP_READWRITE))
|
||||
{
|
||||
throw_exception("'" + _deviceName + "' does not support read i/o");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -401,6 +419,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
if (!(cap.capabilities & V4L2_CAP_STREAMING))
|
||||
{
|
||||
throw_exception("'" + _deviceName + "' does not support streaming i/o");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -438,6 +457,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
if (-1 == xioctl(VIDIOC_S_INPUT, &input))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_S_INPUT");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,6 +470,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_S_STD");
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -459,6 +480,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_S_STD");
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -468,6 +490,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_S_STD");
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -485,6 +508,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
if (-1 == xioctl(VIDIOC_G_FMT, &fmt))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_G_FMT");
|
||||
return;
|
||||
}
|
||||
|
||||
// set the requested pixel format
|
||||
@@ -505,19 +529,9 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
break;
|
||||
}
|
||||
|
||||
// set the requested withd and height
|
||||
if (_width > 0 || _height > 0)
|
||||
{
|
||||
if (_width > 0)
|
||||
{
|
||||
fmt.fmt.pix.width = _width;
|
||||
}
|
||||
|
||||
if (fmt.fmt.pix.height > 0)
|
||||
{
|
||||
fmt.fmt.pix.height = _height;
|
||||
}
|
||||
}
|
||||
// calc the size based on pixelDecimation
|
||||
fmt.fmt.pix.width = fmt.fmt.pix.width / _pixelDecimation;
|
||||
fmt.fmt.pix.height = fmt.fmt.pix.height / _pixelDecimation;
|
||||
|
||||
// set the line length
|
||||
_lineLength = fmt.fmt.pix.bytesperline;
|
||||
@@ -526,6 +540,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
if (-1 == xioctl(VIDIOC_S_FMT, &fmt))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_S_FMT");
|
||||
return;
|
||||
}
|
||||
|
||||
// get the format settings again
|
||||
@@ -533,6 +548,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
if (-1 == xioctl(VIDIOC_G_FMT, &fmt))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_G_FMT");
|
||||
return;
|
||||
}
|
||||
|
||||
// store width & height
|
||||
@@ -563,6 +579,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
break;
|
||||
default:
|
||||
throw_exception("Only pixel formats UYVY, YUYV, and RGB32 are supported");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_ioMethod) {
|
||||
@@ -590,7 +607,10 @@ void V4L2Grabber::uninit_device()
|
||||
case IO_METHOD_MMAP:
|
||||
for (size_t i = 0; i < _buffers.size(); ++i)
|
||||
if (-1 == munmap(_buffers[i].start, _buffers[i].length))
|
||||
{
|
||||
throw_errno_exception("munmap");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case IO_METHOD_USERPTR:
|
||||
@@ -620,11 +640,17 @@ void V4L2Grabber::start_capturing()
|
||||
buf.index = i;
|
||||
|
||||
if (-1 == xioctl(VIDIOC_QBUF, &buf))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_QBUF");
|
||||
return;
|
||||
}
|
||||
}
|
||||
v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (-1 == xioctl(VIDIOC_STREAMON, &type))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_STREAMON");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IO_METHOD_USERPTR:
|
||||
@@ -640,11 +666,17 @@ void V4L2Grabber::start_capturing()
|
||||
buf.length = _buffers[i].length;
|
||||
|
||||
if (-1 == xioctl(VIDIOC_QBUF, &buf))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_QBUF");
|
||||
return;
|
||||
}
|
||||
}
|
||||
v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (-1 == xioctl(VIDIOC_STREAMON, &type))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_STREAMON");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -692,6 +724,7 @@ int V4L2Grabber::read_frame()
|
||||
|
||||
default:
|
||||
throw_errno_exception("read");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -718,6 +751,7 @@ int V4L2Grabber::read_frame()
|
||||
|
||||
default:
|
||||
throw_errno_exception("VIDIOC_DQBUF");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -728,6 +762,7 @@ int V4L2Grabber::read_frame()
|
||||
if (-1 == xioctl(VIDIOC_QBUF, &buf))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_QBUF");
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -752,6 +787,7 @@ int V4L2Grabber::read_frame()
|
||||
|
||||
default:
|
||||
throw_errno_exception("VIDIOC_DQBUF");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,6 +804,7 @@ int V4L2Grabber::read_frame()
|
||||
if (-1 == xioctl(VIDIOC_QBUF, &buf))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_QBUF");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -783,19 +820,15 @@ int V4L2Grabber::read_frame()
|
||||
|
||||
bool V4L2Grabber::process_image(const void *p, int size)
|
||||
{
|
||||
if (++_currentFrame >= _frameDecimation)
|
||||
// We do want a new frame...
|
||||
if (size != _frameByteSize)
|
||||
{
|
||||
// We do want a new frame...
|
||||
if (size != _frameByteSize)
|
||||
{
|
||||
Error(_log, "Frame too small: %d != %d", size, _frameByteSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
process_image(reinterpret_cast<const uint8_t *>(p));
|
||||
_currentFrame = 0; // restart counting
|
||||
return true;
|
||||
}
|
||||
Error(_log, "Frame too small: %d != %d", size, _frameByteSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
process_image(reinterpret_cast<const uint8_t *>(p));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -874,20 +907,44 @@ int V4L2Grabber::xioctl(int request, void *arg)
|
||||
|
||||
void V4L2Grabber::throw_exception(const QString & error)
|
||||
{
|
||||
throw std::runtime_error(error.toStdString());
|
||||
Error(_log, "Throws error: %s", QSTRING_CSTR(error));
|
||||
}
|
||||
|
||||
void V4L2Grabber::throw_errno_exception(const QString & error)
|
||||
{
|
||||
throw std::runtime_error(QString(error + " error code " + QString::number(errno) + ", " + strerror(errno)).toStdString());
|
||||
Error(_log, "Throws error nr: %s", QSTRING_CSTR(QString(error + " error code " + QString::number(errno) + ", " + strerror(errno))));
|
||||
}
|
||||
|
||||
void V4L2Grabber::setSignalDetectionEnable(bool enable)
|
||||
{
|
||||
_signalDetectionEnabled = enable;
|
||||
if(_signalDetectionEnabled != enable)
|
||||
{
|
||||
_signalDetectionEnabled = enable;
|
||||
Info(_log, "Signal detection is now %s", enable ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
bool V4L2Grabber::getSignalDetectionEnabled()
|
||||
{
|
||||
return _signalDetectionEnabled;
|
||||
}
|
||||
|
||||
void V4L2Grabber::setPixelDecimation(int pixelDecimation)
|
||||
{
|
||||
if(_pixelDecimation != pixelDecimation)
|
||||
{
|
||||
uninit();
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
void V4L2Grabber::setInputVideoStandard(int input, VideoStandard videoStandard)
|
||||
{
|
||||
if(_input != input || _videoStandard != videoStandard)
|
||||
{
|
||||
_input = input;
|
||||
_videoStandard = videoStandard;
|
||||
uninit();
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
@@ -2,45 +2,29 @@
|
||||
|
||||
#include <grabber/V4L2Wrapper.h>
|
||||
|
||||
#include <hyperion/ImageProcessorFactory.h>
|
||||
// qt
|
||||
#include <QTimer>
|
||||
|
||||
V4L2Wrapper::V4L2Wrapper(const QString &device,
|
||||
int input,
|
||||
VideoStandard videoStandard,
|
||||
PixelFormat pixelFormat,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
int frameDecimation,
|
||||
int pixelDecimation,
|
||||
double redSignalThreshold,
|
||||
double greenSignalThreshold,
|
||||
double blueSignalThreshold,
|
||||
const int priority)
|
||||
: GrabberWrapper("V4L2:"+device, &_grabber, width, height, 8, priority, hyperion::COMP_V4L)
|
||||
int pixelDecimation )
|
||||
: GrabberWrapper("V4L2:"+device, &_grabber, 0, 0, 10)
|
||||
, _grabber(device,
|
||||
input,
|
||||
videoStandard,
|
||||
pixelFormat,
|
||||
width,
|
||||
height,
|
||||
frameDecimation,
|
||||
pixelDecimation,
|
||||
pixelDecimation)
|
||||
{
|
||||
// set the signal detection threshold of the grabber
|
||||
_grabber.setSignalThreshold( redSignalThreshold, greenSignalThreshold, blueSignalThreshold, 50);
|
||||
_ggrabber = &_grabber;
|
||||
|
||||
// register the image type
|
||||
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
||||
qRegisterMetaType<std::vector<ColorRgb>>("std::vector<ColorRgb>");
|
||||
qRegisterMetaType<hyperion::Components>("hyperion::Components");
|
||||
|
||||
// Handle the image in the captured thread using a direct connection
|
||||
QObject::connect(&_grabber, SIGNAL(newFrame(Image<ColorRgb>)), this, SLOT(newFrame(Image<ColorRgb>)), Qt::DirectConnection);
|
||||
QObject::connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
|
||||
|
||||
_timer.setInterval(500);
|
||||
}
|
||||
|
||||
bool V4L2Wrapper::start()
|
||||
@@ -54,6 +38,11 @@ void V4L2Wrapper::stop()
|
||||
GrabberWrapper::stop();
|
||||
}
|
||||
|
||||
void V4L2Wrapper::setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold)
|
||||
{
|
||||
_grabber.setSignalThreshold( redSignalThreshold, greenSignalThreshold, blueSignalThreshold, 50);
|
||||
}
|
||||
|
||||
void V4L2Wrapper::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom)
|
||||
{
|
||||
_grabber.setCropping(cropLeft, cropRight, cropTop, cropBottom);
|
||||
@@ -66,11 +55,7 @@ void V4L2Wrapper::setSignalDetectionOffset(double verticalMin, double horizontal
|
||||
|
||||
void V4L2Wrapper::newFrame(const Image<ColorRgb> &image)
|
||||
{
|
||||
emit emitImage(_priority, image, _timeout_ms);
|
||||
|
||||
// process the new image
|
||||
_processor->process(image, _ledColors);
|
||||
setColors(_ledColors, _timeout_ms);
|
||||
emit systemImage(image);
|
||||
}
|
||||
|
||||
void V4L2Wrapper::readError(const char* err)
|
||||
@@ -79,21 +64,9 @@ void V4L2Wrapper::readError(const char* err)
|
||||
stop();
|
||||
}
|
||||
|
||||
void V4L2Wrapper::checkSources()
|
||||
{
|
||||
if ( _hyperion->isCurrentPriority(_priority))
|
||||
{
|
||||
_grabber.start();
|
||||
}
|
||||
else
|
||||
{
|
||||
_grabber.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void V4L2Wrapper::action()
|
||||
{
|
||||
checkSources();
|
||||
|
||||
}
|
||||
|
||||
void V4L2Wrapper::setSignalDetectionEnable(bool enable)
|
||||
|
@@ -1,17 +1,15 @@
|
||||
#include <utils/Logger.h>
|
||||
#include <grabber/X11Grabber.h>
|
||||
|
||||
X11Grabber::X11Grabber(bool useXGetImage, int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation)
|
||||
X11Grabber::X11Grabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation)
|
||||
: Grabber("X11GRABBER", 0, 0, cropLeft, cropRight, cropTop, cropBottom)
|
||||
, _useXGetImage(useXGetImage)
|
||||
, _x11Display(nullptr)
|
||||
, _pixmap(None)
|
||||
, _srcFormat(nullptr)
|
||||
, _dstFormat(nullptr)
|
||||
, _srcPicture(None)
|
||||
, _dstPicture(None)
|
||||
, _horizontalDecimation(horizontalPixelDecimation)
|
||||
, _verticalDecimation(verticalPixelDecimation)
|
||||
, _pixelDecimation(pixelDecimation)
|
||||
, _screenWidth(0)
|
||||
, _screenHeight(0)
|
||||
, _src_x(cropLeft)
|
||||
@@ -37,13 +35,13 @@ void X11Grabber::freeResources()
|
||||
{
|
||||
// Cleanup allocated resources of the X11 grab
|
||||
XDestroyImage(_xImage);
|
||||
if(_XShmAvailable && !_useXGetImage)
|
||||
if(_XShmAvailable)
|
||||
{
|
||||
XShmDetach(_x11Display, &_shminfo);
|
||||
shmdt(_shminfo.shmaddr);
|
||||
shmctl(_shminfo.shmid, IPC_RMID, 0);
|
||||
}
|
||||
if (_XRenderAvailable && !_useXGetImage)
|
||||
if (_XRenderAvailable)
|
||||
{
|
||||
XRenderFreePicture(_x11Display, _srcPicture);
|
||||
XRenderFreePicture(_x11Display, _dstPicture);
|
||||
@@ -53,7 +51,7 @@ void X11Grabber::freeResources()
|
||||
|
||||
void X11Grabber::setupResources()
|
||||
{
|
||||
if(_XShmAvailable && !_useXGetImage)
|
||||
if(_XShmAvailable)
|
||||
{
|
||||
_xImage = XShmCreateImage(_x11Display, _windowAttr.visual, _windowAttr.depth, ZPixmap, NULL, &_shminfo, _width, _height);
|
||||
_shminfo.shmid = shmget(IPC_PRIVATE, _xImage->bytes_per_line * _xImage->height, IPC_CREAT|0777);
|
||||
@@ -62,7 +60,7 @@ void X11Grabber::setupResources()
|
||||
_shminfo.readOnly = False;
|
||||
XShmAttach(_x11Display, &_shminfo);
|
||||
}
|
||||
if (_XRenderAvailable && !_useXGetImage)
|
||||
if (_XRenderAvailable)
|
||||
{
|
||||
if(_XShmPixmapAvailable)
|
||||
{
|
||||
@@ -96,19 +94,19 @@ bool X11Grabber::Setup()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
_window = DefaultRootWindow(_x11Display);
|
||||
|
||||
int dummy, pixmaps_supported;
|
||||
|
||||
|
||||
_XRenderAvailable = XRenderQueryExtension(_x11Display, &dummy, &dummy);
|
||||
_XShmAvailable = XShmQueryExtension(_x11Display);
|
||||
XShmQueryVersion(_x11Display, &dummy, &dummy, &pixmaps_supported);
|
||||
_XShmPixmapAvailable = pixmaps_supported && XShmPixmapFormat(_x11Display) == ZPixmap;
|
||||
|
||||
|
||||
// Image scaling is performed by XRender when available, otherwise by ImageResampler
|
||||
_imageResampler.setHorizontalPixelDecimation(_XRenderAvailable ? 1 : _horizontalDecimation);
|
||||
_imageResampler.setVerticalPixelDecimation(_XRenderAvailable ? 1 : _verticalDecimation);
|
||||
_imageResampler.setHorizontalPixelDecimation(_XRenderAvailable ? 1 : _pixelDecimation);
|
||||
_imageResampler.setVerticalPixelDecimation(_XRenderAvailable ? 1 : _pixelDecimation);
|
||||
|
||||
bool result = (updateScreenDimensions(true) >=0);
|
||||
ErrorIf(!result, _log, "X11 Grabber start failed");
|
||||
@@ -119,14 +117,15 @@ int X11Grabber::grabFrame(Image<ColorRgb> & image, bool forceUpdate)
|
||||
{
|
||||
if (!_enabled) return 0;
|
||||
|
||||
updateScreenDimensions(forceUpdate);
|
||||
|
||||
if (_XRenderAvailable && !_useXGetImage)
|
||||
if (forceUpdate)
|
||||
updateScreenDimensions(forceUpdate);
|
||||
|
||||
if (_XRenderAvailable)
|
||||
{
|
||||
double scale_x = static_cast<double>(_windowAttr.width / _horizontalDecimation) / static_cast<double>(_windowAttr.width);
|
||||
double scale_y = static_cast<double>(_windowAttr.height / _verticalDecimation) / static_cast<double>(_windowAttr.height);
|
||||
double scale_x = static_cast<double>(_windowAttr.width / _pixelDecimation) / static_cast<double>(_windowAttr.width);
|
||||
double scale_y = static_cast<double>(_windowAttr.height / _pixelDecimation) / static_cast<double>(_windowAttr.height);
|
||||
double scale = qMin(scale_y, scale_x);
|
||||
|
||||
|
||||
_transform =
|
||||
{
|
||||
{
|
||||
@@ -147,27 +146,27 @@ int X11Grabber::grabFrame(Image<ColorRgb> & image, bool forceUpdate)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
XRenderSetPictureTransform (_x11Display, _srcPicture, &_transform);
|
||||
|
||||
|
||||
// display, op, src, mask, dest, src_x = cropLeft,
|
||||
// src_y = cropTop, mask_x, mask_y, dest_x, dest_y, width, height
|
||||
// src_y = cropTop, mask_x, mask_y, dest_x, dest_y, width, height
|
||||
XRenderComposite(
|
||||
_x11Display, PictOpSrc, _srcPicture, None, _dstPicture, ( _src_x/_horizontalDecimation),
|
||||
(_src_y/_verticalDecimation), 0, 0, 0, 0, _width, _height);
|
||||
|
||||
_x11Display, PictOpSrc, _srcPicture, None, _dstPicture, ( _src_x/_pixelDecimation),
|
||||
(_src_y/_pixelDecimation), 0, 0, 0, 0, _width, _height);
|
||||
|
||||
XSync(_x11Display, False);
|
||||
|
||||
|
||||
if (_XShmAvailable)
|
||||
{
|
||||
XShmGetImage(_x11Display, _pixmap, _xImage, 0, 0, AllPlanes);
|
||||
}
|
||||
else
|
||||
{
|
||||
_xImage = XGetImage(_x11Display, _pixmap, 0, 0, _width, _height, AllPlanes, ZPixmap);
|
||||
_xImage = XGetImage(_x11Display, _pixmap, 0, 0, _width, _height, AllPlanes, ZPixmap);
|
||||
}
|
||||
}
|
||||
else if (_XShmAvailable && !_useXGetImage)
|
||||
else if (_XShmAvailable)
|
||||
{
|
||||
// use xshm
|
||||
XShmGetImage(_x11Display, _window, _xImage, _src_x, _src_y, AllPlanes);
|
||||
@@ -212,20 +211,20 @@ int X11Grabber::updateScreenDimensions(bool force)
|
||||
Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _screenWidth, _screenHeight, _windowAttr.width, _windowAttr.height);
|
||||
_screenWidth = _windowAttr.width;
|
||||
_screenHeight = _windowAttr.height;
|
||||
|
||||
|
||||
int width=0, height=0;
|
||||
|
||||
|
||||
// Image scaling is performed by XRender when available, otherwise by ImageResampler
|
||||
if (_XRenderAvailable && !_useXGetImage)
|
||||
if (_XRenderAvailable)
|
||||
{
|
||||
width = (_screenWidth > unsigned(_cropLeft + _cropRight))
|
||||
? ((_screenWidth - _cropLeft - _cropRight) / _horizontalDecimation)
|
||||
: _screenWidth / _horizontalDecimation;
|
||||
|
||||
? ((_screenWidth - _cropLeft - _cropRight) / _pixelDecimation)
|
||||
: _screenWidth / _pixelDecimation;
|
||||
|
||||
height = (_screenHeight > unsigned(_cropTop + _cropBottom))
|
||||
? ((_screenHeight - _cropTop - _cropBottom) / _verticalDecimation)
|
||||
: _screenHeight / _verticalDecimation;
|
||||
|
||||
? ((_screenHeight - _cropTop - _cropBottom) / _pixelDecimation)
|
||||
: _screenHeight / _pixelDecimation;
|
||||
|
||||
Info(_log, "Using XRender for grabbing");
|
||||
}
|
||||
else
|
||||
@@ -233,11 +232,11 @@ int X11Grabber::updateScreenDimensions(bool force)
|
||||
width = (_screenWidth > unsigned(_cropLeft + _cropRight))
|
||||
? (_screenWidth - _cropLeft - _cropRight)
|
||||
: _screenWidth;
|
||||
|
||||
|
||||
height = (_screenHeight > unsigned(_cropTop + _cropBottom))
|
||||
? (_screenHeight - _cropTop - _cropBottom)
|
||||
: _screenHeight;
|
||||
|
||||
|
||||
Info(_log, "Using XGetImage for grabbing");
|
||||
}
|
||||
|
||||
@@ -264,7 +263,7 @@ int X11Grabber::updateScreenDimensions(bool force)
|
||||
_src_y = _cropTop;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Info(_log, "Update output image resolution: [%dx%d] to [%dx%d]", _image.width(), _image.height(), _width, _height);
|
||||
|
||||
_image.resize(_width, _height);
|
||||
@@ -275,7 +274,26 @@ int X11Grabber::updateScreenDimensions(bool force)
|
||||
|
||||
void X11Grabber::setVideoMode(VideoMode mode)
|
||||
{
|
||||
Info(_log, "a %d", mode);
|
||||
Grabber::setVideoMode(mode);
|
||||
updateScreenDimensions(true);
|
||||
}
|
||||
|
||||
void X11Grabber::setWidthHeight(int width, int height)
|
||||
{
|
||||
// empty overwrite
|
||||
}
|
||||
|
||||
void X11Grabber::setPixelDecimation(int pixelDecimation)
|
||||
{
|
||||
if(_pixelDecimation != pixelDecimation)
|
||||
{
|
||||
_pixelDecimation = pixelDecimation;
|
||||
updateScreenDimensions(true);
|
||||
}
|
||||
}
|
||||
|
||||
void X11Grabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom)
|
||||
{
|
||||
Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom);
|
||||
if(_x11Display != nullptr) updateScreenDimensions(true); // segfault on init
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#include <grabber/X11Wrapper.h>
|
||||
|
||||
X11Wrapper::X11Wrapper(bool useXGetImage, int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation, const unsigned updateRate_Hz, const int priority)
|
||||
: GrabberWrapper("X11", &_grabber, 0, 0, updateRate_Hz, priority, hyperion::COMP_GRABBER)
|
||||
, _grabber(useXGetImage, cropLeft, cropRight, cropTop, cropBottom, horizontalPixelDecimation, verticalPixelDecimation)
|
||||
X11Wrapper::X11Wrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, const unsigned updateRate_Hz)
|
||||
: GrabberWrapper("X11", &_grabber, 0, 0, updateRate_Hz)
|
||||
, _grabber(cropLeft, cropRight, cropTop, cropBottom, pixelDecimation)
|
||||
, _init(false)
|
||||
{}
|
||||
|
||||
|
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
|
||||
}
|
@@ -8,6 +8,7 @@ FILE ( GLOB JsonServer_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DI
|
||||
add_library(jsonserver ${JsonServer_SOURCES} )
|
||||
|
||||
target_link_libraries(jsonserver
|
||||
hyperion-api
|
||||
hyperion
|
||||
Qt5::Network
|
||||
Qt5::Gui
|
||||
|
@@ -1,7 +1,10 @@
|
||||
// project includes
|
||||
#include "JsonClientConnection.h"
|
||||
#include <utils/JsonProcessor.h>
|
||||
#include <api/JsonAPI.h>
|
||||
|
||||
// qt inc
|
||||
#include <QTcpSocket>
|
||||
#include <QHostAddress>
|
||||
|
||||
JsonClientConnection::JsonClientConnection(QTcpSocket *socket)
|
||||
: QObject()
|
||||
@@ -11,10 +14,10 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket)
|
||||
{
|
||||
connect(_socket, &QTcpSocket::disconnected, this, &JsonClientConnection::disconnected);
|
||||
connect(_socket, &QTcpSocket::readyRead, this, &JsonClientConnection::readRequest);
|
||||
// create a new instance of JsonProcessor
|
||||
_jsonProcessor = new JsonProcessor(socket->peerAddress().toString(), _log, this);
|
||||
// get the callback messages from JsonProcessor and send it to the client
|
||||
connect(_jsonProcessor,SIGNAL(callbackMessage(QJsonObject)),this,SLOT(sendMessage(QJsonObject)));
|
||||
// create a new instance of JsonAPI
|
||||
_jsonAPI = new JsonAPI(socket->peerAddress().toString(), _log, this);
|
||||
// get the callback messages from JsonAPI and send it to the client
|
||||
connect(_jsonAPI,SIGNAL(callbackMessage(QJsonObject)),this,SLOT(sendMessage(QJsonObject)));
|
||||
}
|
||||
|
||||
void JsonClientConnection::readRequest()
|
||||
@@ -31,7 +34,7 @@ void JsonClientConnection::readRequest()
|
||||
_receiveBuffer = _receiveBuffer.mid(bytes);
|
||||
|
||||
// handle message
|
||||
_jsonProcessor->handleMessage(message);
|
||||
_jsonAPI->handleMessage(message);
|
||||
|
||||
// try too look up '\n' again
|
||||
bytes = _receiveBuffer.indexOf('\n') + 1;
|
||||
|
@@ -3,11 +3,12 @@
|
||||
// Qt includes
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
// util includes
|
||||
#include <utils/Logger.h>
|
||||
|
||||
class JsonProcessor;
|
||||
class JsonAPI;
|
||||
class QTcpSocket;
|
||||
|
||||
///
|
||||
@@ -40,8 +41,8 @@ private slots:
|
||||
|
||||
private:
|
||||
QTcpSocket* _socket;
|
||||
/// new instance of JsonProcessor
|
||||
JsonProcessor * _jsonProcessor;
|
||||
/// new instance of JsonAPI
|
||||
JsonAPI * _jsonAPI;
|
||||
|
||||
/// The buffer used for reading data from the socket
|
||||
QByteArray _receiveBuffer;
|
||||
|
@@ -6,41 +6,41 @@
|
||||
#include "JsonClientConnection.h"
|
||||
|
||||
// hyperion include
|
||||
#include <hyperion/Hyperion.h>
|
||||
#include <hyperion/MessageForwarder.h>
|
||||
#include <bonjour/bonjourserviceregister.h>
|
||||
#include <hyperion/ComponentRegister.h>
|
||||
|
||||
// qt includes
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QJsonDocument>
|
||||
#include <QByteArray>
|
||||
|
||||
JsonServer::JsonServer(uint16_t port)
|
||||
JsonServer::JsonServer(const QJsonDocument& config)
|
||||
: QObject()
|
||||
, _server()
|
||||
, _server(new QTcpServer(this))
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
, _openConnections()
|
||||
, _log(Logger::getInstance("JSONSERVER"))
|
||||
, _componentRegister( & _hyperion->getComponentRegister())
|
||||
{
|
||||
if (!_server.listen(QHostAddress::Any, port))
|
||||
{
|
||||
throw std::runtime_error("JSONSERVER ERROR: could not bind to port");
|
||||
}
|
||||
|
||||
QList<MessageForwarder::JsonSlaveAddress> list = Hyperion::getInstance()->getForwarder()->getJsonSlaves();
|
||||
for ( int i=0; i<list.size(); i++ )
|
||||
{
|
||||
if ( list.at(i).addr == QHostAddress::LocalHost && list.at(i).port == port ) {
|
||||
throw std::runtime_error("JSONSERVER ERROR: Loop between proto server and forwarder detected. Fix your config!");
|
||||
}
|
||||
}
|
||||
Debug(_log, "Created instance");
|
||||
|
||||
// Set trigger for incoming connections
|
||||
connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
||||
connect(_server, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
||||
|
||||
// receive state of forwarder
|
||||
connect(_hyperion, &Hyperion::componentStateChanged, this, &JsonServer::componentStateChanged);
|
||||
connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonServer::componentStateChanged);
|
||||
|
||||
// initial connect TODO get initial state from config to stop messing
|
||||
// listen for component register changes
|
||||
connect(_hyperion, &Hyperion::forwardJsonMessage, this, &JsonServer::forwardJsonMessage);
|
||||
|
||||
// init
|
||||
handleSettingsUpdate(settings::JSONSERVER, config);
|
||||
|
||||
// set initial state of forwarding
|
||||
componentStateChanged(hyperion::COMP_FORWARDER, _componentRegister->isComponentEnabled(hyperion::COMP_FORWARDER));
|
||||
}
|
||||
|
||||
JsonServer::~JsonServer()
|
||||
@@ -50,16 +50,58 @@ JsonServer::~JsonServer()
|
||||
}
|
||||
}
|
||||
|
||||
void JsonServer::start()
|
||||
{
|
||||
if(_server->isListening())
|
||||
return;
|
||||
|
||||
if (!_server->listen(QHostAddress::Any, _port))
|
||||
{
|
||||
Error(_log,"Could not bind to port '%d', please use an available port", _port);
|
||||
return;
|
||||
}
|
||||
Info(_log, "Started on port %d", _port);
|
||||
|
||||
if(_serviceRegister == nullptr)
|
||||
{
|
||||
_serviceRegister = new BonjourServiceRegister();
|
||||
_serviceRegister->registerService("_hyperiond-json._tcp", _port);
|
||||
}
|
||||
}
|
||||
|
||||
void JsonServer::stop()
|
||||
{
|
||||
if(!_server->isListening())
|
||||
return;
|
||||
|
||||
_server->close();
|
||||
Info(_log, "Stopped");
|
||||
}
|
||||
|
||||
void JsonServer::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::JSONSERVER)
|
||||
{
|
||||
QJsonObject obj = config.object();
|
||||
if(_port != obj["port"].toInt())
|
||||
{
|
||||
_port = obj["port"].toInt();
|
||||
stop();
|
||||
start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t JsonServer::getPort() const
|
||||
{
|
||||
return _server.serverPort();
|
||||
return _port;
|
||||
}
|
||||
|
||||
void JsonServer::newConnection()
|
||||
{
|
||||
while(_server.hasPendingConnections())
|
||||
while(_server->hasPendingConnections())
|
||||
{
|
||||
if (QTcpSocket * socket = _server.nextPendingConnection())
|
||||
if (QTcpSocket * socket = _server->nextPendingConnection())
|
||||
{
|
||||
Debug(_log, "New connection from: %s ",socket->localAddress().toString().toStdString().c_str());
|
||||
JsonClientConnection * connection = new JsonClientConnection(socket);
|
||||
@@ -83,11 +125,9 @@ void JsonServer::closedConnection(void)
|
||||
|
||||
void JsonServer::componentStateChanged(const hyperion::Components component, bool enable)
|
||||
{
|
||||
if (component == hyperion::COMP_FORWARDER && _forwarder_enabled != enable)
|
||||
if (component == hyperion::COMP_FORWARDER)
|
||||
{
|
||||
_forwarder_enabled = enable;
|
||||
Info(_log, "forwarder change state to %s", (enable ? "enabled" : "disabled") );
|
||||
if(_forwarder_enabled)
|
||||
if(enable)
|
||||
{
|
||||
connect(_hyperion, &Hyperion::forwardJsonMessage, this, &JsonServer::forwardJsonMessage);
|
||||
}
|
||||
@@ -101,11 +141,12 @@ void JsonServer::componentStateChanged(const hyperion::Components component, boo
|
||||
void JsonServer::forwardJsonMessage(const QJsonObject &message)
|
||||
{
|
||||
QTcpSocket client;
|
||||
QList<MessageForwarder::JsonSlaveAddress> list = _hyperion->getForwarder()->getJsonSlaves();
|
||||
QStringList list = _hyperion->getForwarder()->getJsonSlaves();
|
||||
|
||||
for ( int i=0; i<list.size(); i++ )
|
||||
for (const auto& entry : list)
|
||||
{
|
||||
client.connectToHost(list.at(i).addr, list.at(i).port);
|
||||
QStringList splitted = entry.split(":");
|
||||
client.connectToHost(splitted[0], splitted[1].toInt());
|
||||
if ( client.waitForConnected(500) )
|
||||
{
|
||||
sendMessage(message,&client);
|
||||
|
@@ -11,14 +11,10 @@
|
||||
#include <utils/JsonUtils.h>
|
||||
|
||||
LedDeviceRegistry LedDevice::_ledDeviceMap = LedDeviceRegistry();
|
||||
QString LedDevice::_activeDevice = "";
|
||||
int LedDevice::_ledCount = 0;
|
||||
int LedDevice::_ledRGBCount = 0;
|
||||
int LedDevice::_ledRGBWCount= 0;
|
||||
|
||||
LedDevice::LedDevice()
|
||||
: QObject()
|
||||
, _log(Logger::getInstance("LedDevice"))
|
||||
, _log(Logger::getInstance("LEDDEVICE"))
|
||||
, _ledBuffer(0)
|
||||
, _deviceReady(true)
|
||||
, _refresh_timer()
|
||||
@@ -77,6 +73,10 @@ void LedDevice::setActiveDevice(QString dev)
|
||||
|
||||
bool LedDevice::init(const QJsonObject &deviceConfig)
|
||||
{
|
||||
_colorOrder = deviceConfig["colorOrder"].toString("RGB");
|
||||
_activeDevice = deviceConfig["type"].toString("file").toLower();
|
||||
setLedCount(deviceConfig["currentLedCount"].toInt(1)); // property injected to reflect real led count
|
||||
|
||||
_latchTime_ms = deviceConfig["latchTime"].toInt(_latchTime_ms);
|
||||
_refresh_timer.setInterval( deviceConfig["rewriteTime"].toInt( _refresh_timer_interval) );
|
||||
if (_refresh_timer.interval() <= (signed)_latchTime_ms )
|
||||
|
@@ -13,20 +13,17 @@
|
||||
// following file is auto generated by cmake! it contains all available leddevice headers
|
||||
#include "LedDevice_headers.h"
|
||||
|
||||
LedDevice * LedDeviceFactory::construct(const QJsonObject & deviceConfig, const int ledCount)
|
||||
LedDevice * LedDeviceFactory::construct(const QJsonObject & deviceConfig)
|
||||
{
|
||||
Logger * log = Logger::getInstance("LedDevice");
|
||||
Logger * log = Logger::getInstance("LEDDEVICE");
|
||||
QJsonDocument config(deviceConfig);
|
||||
QString ss(config.toJson(QJsonDocument::Indented));
|
||||
|
||||
QString type = deviceConfig["type"].toString("UNSPECIFIED").toLower();
|
||||
|
||||
// set amount of led to leddevice
|
||||
LedDevice::setLedCount(ledCount);
|
||||
|
||||
#define REGISTER(className) LedDevice::addToDeviceMap(QString(#className).toLower(), LedDevice##className::construct);
|
||||
|
||||
// the REGISTER() calls are autogenerated by cmake.
|
||||
// the REGISTER() calls are autogenerated by cmake.
|
||||
#include "LedDevice_register.cpp"
|
||||
|
||||
#undef REGISTER
|
||||
@@ -40,12 +37,11 @@ LedDevice * LedDeviceFactory::construct(const QJsonObject & deviceConfig, const
|
||||
if (dev.first == type)
|
||||
{
|
||||
device = dev.second(deviceConfig);
|
||||
LedDevice::setActiveDevice(dev.first);
|
||||
Info(log,"LedDevice '%s' configured.", QSTRING_CSTR(dev.first));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (device == nullptr)
|
||||
{
|
||||
Error(log, "Dummy device used, because configured device '%s' is unknown", QSTRING_CSTR(type) );
|
||||
@@ -54,13 +50,13 @@ LedDevice * LedDeviceFactory::construct(const QJsonObject & deviceConfig, const
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
|
||||
|
||||
Error(log, "Dummy device used, because configured device '%s' throws error '%s'", QSTRING_CSTR(type), e.what());
|
||||
const QJsonObject dummyDeviceConfig;
|
||||
device = LedDeviceFile::construct(QJsonObject());
|
||||
}
|
||||
|
||||
device->open();
|
||||
|
||||
|
||||
return device;
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ ProviderUdp::ProviderUdp()
|
||||
, _defaultHost("127.0.0.1")
|
||||
{
|
||||
_latchTime_ms = 1;
|
||||
_udpSocket = new QUdpSocket();
|
||||
_udpSocket = new QUdpSocket(this);
|
||||
}
|
||||
|
||||
ProviderUdp::~ProviderUdp()
|
||||
@@ -34,7 +34,7 @@ bool ProviderUdp::init(const QJsonObject &deviceConfig)
|
||||
LedDevice::init(deviceConfig);
|
||||
|
||||
QString host = deviceConfig["host"].toString(_defaultHost);
|
||||
|
||||
|
||||
if (_address.setAddress(host) )
|
||||
{
|
||||
Debug( _log, "Successfully parsed %s as an ip address.", deviceConfig["host"].toString().toStdString().c_str());
|
||||
@@ -57,9 +57,9 @@ bool ProviderUdp::init(const QJsonObject &deviceConfig)
|
||||
{
|
||||
throw std::runtime_error("invalid target port");
|
||||
}
|
||||
|
||||
|
||||
Debug( _log, "UDP using %s:%d", _address.toString().toStdString().c_str() , _port );
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -1,11 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <QUdpSocket>
|
||||
|
||||
// Hyperion includes
|
||||
#include <leddevice/LedDevice.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
// qt
|
||||
#include <QHostAddress>
|
||||
|
||||
class QUdpSocket;
|
||||
|
||||
///
|
||||
/// The ProviderUdp implements an abstract base-class for LedDevices using UDP packets.
|
||||
///
|
||||
|
@@ -7,24 +7,42 @@ include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${PROTOBUF_INCLUDE_DIRS}
|
||||
)
|
||||
FILE ( GLOB ProtoServer_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
set(ProtoServer_PROTOS ${CURRENT_SOURCE_DIR}/message.proto )
|
||||
|
||||
protobuf_generate_cpp(ProtoServer_PROTO_SRCS ProtoServer_PROTO_HDRS ${ProtoServer_PROTOS} )
|
||||
|
||||
add_library(protoserver
|
||||
${ProtoServer_SOURCES}
|
||||
${ProtoServer_PROTOS}
|
||||
### Split protoclient from protoserver as protoserver relates to HyperionDaemon and standalone capture binarys can't link to it
|
||||
|
||||
add_library(protoclient
|
||||
${CURRENT_HEADER_DIR}/ProtoConnection.h
|
||||
${CURRENT_SOURCE_DIR}/ProtoConnection.cpp
|
||||
${CURRENT_HEADER_DIR}/ProtoConnectionWrapper.h
|
||||
${CURRENT_SOURCE_DIR}/ProtoConnectionWrapper.cpp
|
||||
${CURRENT_SOURCE_DIR}/ProtoClientConnection.h
|
||||
${CURRENT_SOURCE_DIR}/ProtoClientConnection.cpp
|
||||
${ProtoServer_PROTO_SRCS}
|
||||
${ProtoServer_PROTO_HDRS}
|
||||
)
|
||||
|
||||
add_library(protoserver
|
||||
${CURRENT_HEADER_DIR}/ProtoServer.h
|
||||
${CURRENT_SOURCE_DIR}/ProtoServer.cpp
|
||||
)
|
||||
|
||||
# disable warnings for auto generatet proto files, we can't change the files ....
|
||||
SET_SOURCE_FILES_PROPERTIES ( ${ProtoServer_PROTO_SRCS} ${ProtoServer_PROTO_HDRS} ${ProtoServer_PROTOS} PROPERTIES COMPILE_FLAGS -w )
|
||||
|
||||
target_link_libraries(protoserver
|
||||
target_link_libraries(protoclient
|
||||
hyperion
|
||||
hyperion-utils
|
||||
protobuf
|
||||
Qt5::Gui
|
||||
)
|
||||
|
||||
target_link_libraries(protoserver
|
||||
hyperion
|
||||
hyperion-utils
|
||||
protoclient
|
||||
Qt5::Gui
|
||||
)
|
||||
|
@@ -14,8 +14,6 @@
|
||||
#include <QHostInfo>
|
||||
|
||||
// hyperion util includes
|
||||
#include "hyperion/ImageProcessorFactory.h"
|
||||
#include "hyperion/ImageProcessor.h"
|
||||
#include "utils/ColorRgb.h"
|
||||
|
||||
// project includes
|
||||
@@ -24,17 +22,14 @@
|
||||
ProtoClientConnection::ProtoClientConnection(QTcpSocket *socket)
|
||||
: QObject()
|
||||
, _socket(socket)
|
||||
, _imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor())
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
, _receiveBuffer()
|
||||
, _priority(-1)
|
||||
, _priorityChannelName("Proto-Server")
|
||||
, _clientAddress(QHostInfo::fromName(socket->peerAddress().toString()).hostName())
|
||||
{
|
||||
// connect internal signals and slots
|
||||
connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
|
||||
connect(_socket, SIGNAL(readyRead()), this, SLOT(readData()));
|
||||
connect(_hyperion, SIGNAL(imageToLedsMappingChanged(int)), _imageProcessor, SLOT(setLedMappingType(int)));
|
||||
}
|
||||
|
||||
ProtoClientConnection::~ProtoClientConnection()
|
||||
@@ -81,7 +76,7 @@ void ProtoClientConnection::readData()
|
||||
|
||||
void ProtoClientConnection::socketClosed()
|
||||
{
|
||||
_hyperion->unRegisterPriority(_priorityChannelName);
|
||||
_hyperion->clear(_priority);
|
||||
emit connectionClosed(this);
|
||||
}
|
||||
|
||||
@@ -103,7 +98,6 @@ void ProtoClientConnection::handleMessage(const proto::HyperionRequest & message
|
||||
// forward messages
|
||||
emit newMessage(&message);
|
||||
|
||||
int prevPriority = _priority;
|
||||
switch (message.command())
|
||||
{
|
||||
case proto::HyperionRequest::COLOR:
|
||||
@@ -136,24 +130,26 @@ void ProtoClientConnection::handleMessage(const proto::HyperionRequest & message
|
||||
default:
|
||||
handleNotImplemented();
|
||||
}
|
||||
|
||||
if (prevPriority != _priority)
|
||||
{
|
||||
_hyperion->registerPriority(_priorityChannelName, _priority);
|
||||
prevPriority = _priority;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoClientConnection::handleColorCommand(const proto::ColorRequest &message)
|
||||
{
|
||||
// extract parameters
|
||||
_priority = message.priority();
|
||||
int priority = message.priority();
|
||||
int duration = message.has_duration() ? message.duration() : -1;
|
||||
ColorRgb color;
|
||||
color.red = qRed(message.rgbcolor());
|
||||
color.green = qGreen(message.rgbcolor());
|
||||
color.blue = qBlue(message.rgbcolor());
|
||||
|
||||
// make sure the prio is registered before setColor()
|
||||
if(priority != _priority)
|
||||
{
|
||||
_hyperion->clear(_priority);
|
||||
_hyperion->registerInput(priority, hyperion::COMP_PROTOSERVER, "proto@"+_clientAddress);
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
// set output
|
||||
_hyperion->setColor(_priority, color, duration);
|
||||
|
||||
@@ -164,12 +160,20 @@ void ProtoClientConnection::handleColorCommand(const proto::ColorRequest &messag
|
||||
void ProtoClientConnection::handleImageCommand(const proto::ImageRequest &message)
|
||||
{
|
||||
// extract parameters
|
||||
_priority = message.priority();
|
||||
int priority = message.priority();
|
||||
int duration = message.has_duration() ? message.duration() : -1;
|
||||
int width = message.imagewidth();
|
||||
int height = message.imageheight();
|
||||
const std::string & imageData = message.imagedata();
|
||||
|
||||
// make sure the prio is registered before setInput()
|
||||
if(priority != _priority)
|
||||
{
|
||||
_hyperion->clear(_priority);
|
||||
_hyperion->registerInput(priority, hyperion::COMP_PROTOSERVER, "proto@"+_clientAddress);
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
// check consistency of the size of the received data
|
||||
if ((int) imageData.size() != width*height*3)
|
||||
{
|
||||
@@ -177,17 +181,11 @@ void ProtoClientConnection::handleImageCommand(const proto::ImageRequest &messag
|
||||
return;
|
||||
}
|
||||
|
||||
// set width and height of the image processor
|
||||
_imageProcessor->setSize(width, height);
|
||||
|
||||
// create ImageRgb
|
||||
Image<ColorRgb> image(width, height);
|
||||
memcpy(image.memptr(), imageData.c_str(), imageData.size());
|
||||
|
||||
// process the image
|
||||
std::vector<ColorRgb> ledColors = _imageProcessor->process(image);
|
||||
_hyperion->setColors(_priority, ledColors, duration, true, hyperion::COMP_PROTOSERVER , "proto@"+_clientAddress);
|
||||
_hyperion->setImage(_priority, image, duration);
|
||||
_hyperion->setInputImage(_priority, image, duration);
|
||||
|
||||
// send reply
|
||||
sendSuccessReply();
|
||||
@@ -197,11 +195,10 @@ void ProtoClientConnection::handleImageCommand(const proto::ImageRequest &messag
|
||||
void ProtoClientConnection::handleClearCommand(const proto::ClearRequest &message)
|
||||
{
|
||||
// extract parameters
|
||||
_priority = message.priority();
|
||||
int priority = message.priority();
|
||||
|
||||
// clear priority
|
||||
_hyperion->clear(_priority);
|
||||
_hyperion->unRegisterPriority(_priorityChannelName);
|
||||
_hyperion->clear(priority);
|
||||
// send reply
|
||||
sendSuccessReply();
|
||||
}
|
||||
|
@@ -19,8 +19,6 @@
|
||||
#include "message.pb.h"
|
||||
#include "protoserver/ProtoConnection.h"
|
||||
|
||||
class ImageProcessor;
|
||||
|
||||
///
|
||||
/// The Connection object created by a ProtoServer when a new connection is establshed
|
||||
///
|
||||
@@ -128,9 +126,6 @@ private:
|
||||
/// The TCP-Socket that is connected tot the Proto-client
|
||||
QTcpSocket * _socket;
|
||||
|
||||
/// The processor for translating images to led-values
|
||||
ImageProcessor * _imageProcessor;
|
||||
|
||||
/// Link to Hyperion for writing led-values to a priority channel
|
||||
Hyperion * _hyperion;
|
||||
|
||||
@@ -139,8 +134,6 @@ private:
|
||||
|
||||
int _priority;
|
||||
|
||||
QString _priorityChannelName;
|
||||
|
||||
/// address of client
|
||||
QString _clientAddress;
|
||||
};
|
||||
|
@@ -1,43 +1,44 @@
|
||||
// system includes
|
||||
#include <stdexcept>
|
||||
|
||||
// qt incl
|
||||
#include <QTcpServer>
|
||||
|
||||
// project includes
|
||||
#include <hyperion/Hyperion.h>
|
||||
#include <hyperion/MessageForwarder.h>
|
||||
#include <protoserver/ProtoServer.h>
|
||||
#include "protoserver/ProtoConnection.h"
|
||||
#include "ProtoClientConnection.h"
|
||||
#include <bonjour/bonjourserviceregister.h>
|
||||
#include <hyperion/ComponentRegister.h>
|
||||
|
||||
ProtoServer::ProtoServer(uint16_t port)
|
||||
ProtoServer::ProtoServer(const QJsonDocument& config)
|
||||
: QObject()
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
, _server()
|
||||
, _server(new QTcpServer(this))
|
||||
, _openConnections()
|
||||
, _log(Logger::getInstance("PROTOSERVER"))
|
||||
, _forwarder_enabled(true)
|
||||
, _componentRegister( & _hyperion->getComponentRegister())
|
||||
{
|
||||
Debug(_log,"Instance created");
|
||||
connect( _server, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
||||
handleSettingsUpdate(settings::PROTOSERVER, config);
|
||||
|
||||
MessageForwarder * forwarder = _hyperion->getForwarder();
|
||||
QStringList slaves = forwarder->getProtoSlaves();
|
||||
QStringList slaves = _hyperion->getForwarder()->getProtoSlaves();
|
||||
|
||||
for (int i = 0; i < slaves.size(); ++i) {
|
||||
if ( QString("127.0.0.1:%1").arg(port) == slaves.at(i) ) {
|
||||
throw std::runtime_error("PROTOSERVER ERROR: Loop between proto server and forwarder detected. Fix your config!");
|
||||
}
|
||||
|
||||
ProtoConnection* p = new ProtoConnection(slaves.at(i).toLocal8Bit().constData());
|
||||
for (const auto& entry : slaves)
|
||||
{
|
||||
ProtoConnection* p = new ProtoConnection(entry.toLocal8Bit().constData());
|
||||
p->setSkipReply(true);
|
||||
_proxy_connections << p;
|
||||
}
|
||||
|
||||
if (!_server.listen(QHostAddress::Any, port))
|
||||
{
|
||||
throw std::runtime_error("PROTOSERVER ERROR: Could not bind to port");
|
||||
}
|
||||
|
||||
// Set trigger for incoming connections
|
||||
connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
||||
connect( _hyperion, SIGNAL(componentStateChanged(hyperion::Components,bool)), this, SLOT(componentStateChanged(hyperion::Components,bool)));
|
||||
// listen for component changes
|
||||
connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &ProtoServer::componentStateChanged);
|
||||
|
||||
// get inital forwarder state
|
||||
componentStateChanged(hyperion::COMP_FORWARDER, _componentRegister->isComponentEnabled(hyperion::COMP_FORWARDER));
|
||||
}
|
||||
|
||||
ProtoServer::~ProtoServer()
|
||||
@@ -50,27 +51,70 @@ ProtoServer::~ProtoServer()
|
||||
delete _proxy_connections.takeFirst();
|
||||
}
|
||||
|
||||
void ProtoServer::start()
|
||||
{
|
||||
if(_server->isListening())
|
||||
return;
|
||||
|
||||
if (!_server->listen(QHostAddress::Any, _port))
|
||||
{
|
||||
Error(_log,"Could not bind to port '%d', please use an available port",_port);
|
||||
return;
|
||||
}
|
||||
Info(_log, "Started on port %d", _port);
|
||||
|
||||
if(_serviceRegister == nullptr)
|
||||
{
|
||||
_serviceRegister = new BonjourServiceRegister();
|
||||
_serviceRegister->registerService("_hyperiond-proto._tcp", _port);
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoServer::stop()
|
||||
{
|
||||
if(!_server->isListening())
|
||||
return;
|
||||
|
||||
_server->close();
|
||||
Info(_log, "Stopped");
|
||||
}
|
||||
|
||||
void ProtoServer::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::PROTOSERVER)
|
||||
{
|
||||
QJsonObject obj = config.object();
|
||||
if(obj["port"].toInt() != _port)
|
||||
{
|
||||
_port = obj["port"].toInt();
|
||||
stop();
|
||||
start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t ProtoServer::getPort() const
|
||||
{
|
||||
return _server.serverPort();
|
||||
return _port;
|
||||
}
|
||||
|
||||
void ProtoServer::newConnection()
|
||||
{
|
||||
QTcpSocket * socket = _server.nextPendingConnection();
|
||||
|
||||
if (socket != nullptr)
|
||||
while(_server->hasPendingConnections())
|
||||
{
|
||||
Debug(_log, "New connection");
|
||||
ProtoClientConnection * connection = new ProtoClientConnection(socket);
|
||||
_openConnections.insert(connection);
|
||||
if(QTcpSocket * socket = _server->nextPendingConnection())
|
||||
{
|
||||
Debug(_log, "New connection");
|
||||
ProtoClientConnection * connection = new ProtoClientConnection(socket);
|
||||
_openConnections.insert(connection);
|
||||
|
||||
// register slot for cleaning up after the connection closed
|
||||
connect(connection, SIGNAL(connectionClosed(ProtoClientConnection*)), this, SLOT(closedConnection(ProtoClientConnection*)));
|
||||
connect(connection, SIGNAL(newMessage(const proto::HyperionRequest*)), this, SLOT(newMessage(const proto::HyperionRequest*)));
|
||||
// register slot for cleaning up after the connection closed
|
||||
connect(connection, SIGNAL(connectionClosed(ProtoClientConnection*)), this, SLOT(closedConnection(ProtoClientConnection*)));
|
||||
connect(connection, SIGNAL(newMessage(const proto::HyperionRequest*)), this, SLOT(newMessage(const proto::HyperionRequest*)));
|
||||
|
||||
// register forward signal for video mode
|
||||
connect(this, SIGNAL(videoMode(VideoMode)), connection, SLOT(setVideoMode(VideoMode)));
|
||||
// register forward signal for video mode
|
||||
connect(this, SIGNAL(videoMode(VideoMode)), connection, SLOT(setVideoMode(VideoMode)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +137,7 @@ void ProtoServer::componentStateChanged(const hyperion::Components component, bo
|
||||
{
|
||||
if (component == hyperion::COMP_FORWARDER)
|
||||
{
|
||||
if (_forwarder_enabled != enable)
|
||||
{
|
||||
_forwarder_enabled = enable;
|
||||
Info(_log, "forwarder change state to %s", (_forwarder_enabled ? "enabled" : "disabled") );
|
||||
}
|
||||
_hyperion->getComponentRegister().componentStateChanged(component, _forwarder_enabled);
|
||||
_forwarder_enabled = enable;
|
||||
}
|
||||
}
|
||||
|
||||
|
21
libsrc/python/CMakeLists.txt
Normal file
21
libsrc/python/CMakeLists.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
find_package(PythonLibs 3.4 REQUIRED)
|
||||
|
||||
# Include the python directory. Also include the parent (which is for example /usr/include)
|
||||
# which may be required when it is not includes by the (cross-) compiler by default.
|
||||
include_directories(${PYTHON_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}/..)
|
||||
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/python)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/python)
|
||||
|
||||
FILE ( GLOB PYTHON_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(python
|
||||
${PYTHON_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(python
|
||||
effectengine
|
||||
hyperion-utils
|
||||
${PYTHON_LIBRARIES}
|
||||
)
|
31
libsrc/python/PythonInit.cpp
Normal file
31
libsrc/python/PythonInit.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#undef slots
|
||||
#include <Python.h>
|
||||
#define slots
|
||||
|
||||
// utils
|
||||
#include <utils/Logger.h>
|
||||
|
||||
#include <python/PythonInit.h>
|
||||
#include <python/PythonUtils.h>
|
||||
|
||||
// modules to init
|
||||
#include <effectengine/EffectModule.h>
|
||||
|
||||
PythonInit::PythonInit()
|
||||
{
|
||||
// register modules
|
||||
EffectModule::registerHyperionExtensionModule();
|
||||
|
||||
// init Python
|
||||
Debug(Logger::getInstance("DAEMON"), "Initializing Python interpreter");
|
||||
Py_InitializeEx(0);
|
||||
PyEval_InitThreads(); // Create the GIL
|
||||
mainThreadState = PyEval_SaveThread();
|
||||
}
|
||||
|
||||
PythonInit::~PythonInit()
|
||||
{
|
||||
Debug(Logger::getInstance("DAEMON"), "Cleaning up Python interpreter");
|
||||
PyEval_RestoreThread(mainThreadState);
|
||||
Py_Finalize();
|
||||
}
|
@@ -1,33 +1,38 @@
|
||||
// project includes
|
||||
#include <udplistener/UDPListener.h>
|
||||
|
||||
// hyperion includes
|
||||
#include <hyperion/Hyperion.h>
|
||||
#include <bonjour/bonjourserviceregister.h>
|
||||
|
||||
// hyperion util includes
|
||||
#include "hyperion/ImageProcessorFactory.h"
|
||||
#include "hyperion/ImageProcessor.h"
|
||||
#include "utils/ColorRgb.h"
|
||||
#include "HyperionConfig.h"
|
||||
|
||||
// qt includes
|
||||
#include <QUdpSocket>
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
UDPListener::UDPListener(const int priority, const int timeout, const QString& address, quint16 listenPort, bool shared) :
|
||||
UDPListener::UDPListener(const QJsonDocument& config) :
|
||||
QObject(),
|
||||
_hyperion(Hyperion::getInstance()),
|
||||
_server(),
|
||||
_server(new QUdpSocket(this)),
|
||||
_openConnections(),
|
||||
_priority(priority),
|
||||
_timeout(timeout),
|
||||
_priority(0),
|
||||
_timeout(0),
|
||||
_log(Logger::getInstance("UDPLISTENER")),
|
||||
_isActive(false),
|
||||
_listenPort(listenPort),
|
||||
_bondage(shared ? QAbstractSocket::ShareAddress : QAbstractSocket::DefaultForPlatform)
|
||||
_listenPort(0)
|
||||
{
|
||||
_server = new QUdpSocket(this);
|
||||
_listenAddress = address.isEmpty()? QHostAddress::AnyIPv4 : QHostAddress(address);
|
||||
|
||||
Debug(_log, "Instance created");
|
||||
// listen for comp changes
|
||||
connect(_hyperion, SIGNAL(componentStateChanged(hyperion::Components,bool)), this, SLOT(componentStateChanged(hyperion::Components,bool)));
|
||||
// Set trigger for incoming connections
|
||||
connect(_server, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
|
||||
|
||||
_hyperion->registerPriority("UDPLISTENER", _priority);
|
||||
// init
|
||||
handleSettingsUpdate(settings::UDPLISTENER, config);
|
||||
}
|
||||
|
||||
UDPListener::~UDPListener()
|
||||
@@ -51,7 +56,7 @@ void UDPListener::start()
|
||||
|
||||
if (!_server->bind(_listenAddress, _listenPort, _bondage))
|
||||
{
|
||||
Warning(_log, "Could not bind to %s:%d", _listenAddress.toString().toStdString().c_str(), _listenPort);
|
||||
Error(_log, "Could not bind to %s:%d", _listenAddress.toString().toStdString().c_str(), _listenPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -62,7 +67,13 @@ void UDPListener::start()
|
||||
WarningIf( ! joinGroupOK, _log, "Multicast failed");
|
||||
}
|
||||
_isActive = true;
|
||||
emit statusChanged(_isActive);
|
||||
_hyperion->getComponentRegister().componentStateChanged(COMP_UDPLISTENER, _isActive);
|
||||
|
||||
if(_bonjourService == nullptr)
|
||||
{
|
||||
_bonjourService = new BonjourServiceRegister();
|
||||
_bonjourService->registerService("_hyperiond-udp._udp", _listenPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +84,9 @@ void UDPListener::stop()
|
||||
|
||||
_server->close();
|
||||
_isActive = false;
|
||||
emit statusChanged(_isActive);
|
||||
Info(_log, "Stopped");
|
||||
_hyperion->clear(_priority);
|
||||
_hyperion->getComponentRegister().componentStateChanged(COMP_UDPLISTENER, _isActive);
|
||||
}
|
||||
|
||||
void UDPListener::componentStateChanged(const hyperion::Components component, bool enable)
|
||||
@@ -84,9 +97,7 @@ void UDPListener::componentStateChanged(const hyperion::Components component, bo
|
||||
{
|
||||
if (enable) start();
|
||||
else stop();
|
||||
Info(_log, "change state to %s", (enable ? "enabled" : "disabled") );
|
||||
}
|
||||
_hyperion->getComponentRegister().componentStateChanged(component, enable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,9 +116,7 @@ void UDPListener::readPendingDatagrams()
|
||||
quint16 senderPort;
|
||||
|
||||
_server->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
|
||||
|
||||
processTheDatagram(&datagram, &sender);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,9 +135,26 @@ void UDPListener::processTheDatagram(const QByteArray * datagram, const QHostAdd
|
||||
rgb.green = datagram->at(ledIndex*3+1);
|
||||
rgb.blue = datagram->at(ledIndex*3+2);
|
||||
}
|
||||
|
||||
_hyperion->setColors(_priority, _ledColors, _timeout, -1, hyperion::COMP_UDPLISTENER, sender->toString());
|
||||
// TODO provide a setInput with origin arg to overwrite senders smarter
|
||||
_hyperion->registerInput(_priority, hyperion::COMP_UDPLISTENER, QString("UDPListener@%1").arg(sender->toString()));
|
||||
_hyperion->setInput(_priority, _ledColors, _timeout);
|
||||
}
|
||||
|
||||
void UDPListener::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::UDPLISTENER)
|
||||
{
|
||||
QJsonObject obj = config.object();
|
||||
// if we change the prio we need to make sure the old one is cleared before we apply the new one!
|
||||
stop();
|
||||
|
||||
|
||||
QString addr = obj["address"].toString("");
|
||||
_priority = obj["priority"].toInt();
|
||||
_listenPort = obj["port"].toInt();
|
||||
_listenAddress = addr.isEmpty()? QHostAddress::AnyIPv4 : QHostAddress(addr);
|
||||
_bondage = (obj["shared"].toBool(false)) ? QAbstractSocket::ShareAddress : QAbstractSocket::DefaultForPlatform;
|
||||
_timeout = obj["timeout"].toInt(10000);
|
||||
if(obj["enable"].toBool())
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
@@ -9,11 +9,8 @@ if ( NOT ENABLE_PROFILER )
|
||||
LIST ( REMOVE_ITEM Utils_SOURCES ${CURRENT_HEADER_DIR}/Profiler.h ${CURRENT_SOURCE_DIR}/Profiler.cpp )
|
||||
endif()
|
||||
|
||||
set(Utils_RESOURCES ${CURRENT_SOURCE_DIR}/JSONRPC_schemas.qrc )
|
||||
|
||||
add_library(hyperion-utils
|
||||
${Utils_SOURCES}
|
||||
${Utils_RESOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(hyperion-utils
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#include <utils/FileUtils.h>
|
||||
|
||||
// qt incl
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QDebug>
|
||||
|
||||
@@ -21,6 +22,17 @@ namespace FileUtils {
|
||||
return fi.path();
|
||||
}
|
||||
|
||||
bool removeDir(const QString& path, Logger* log)
|
||||
{
|
||||
//QDir dir(path);
|
||||
if(!QDir(path).removeRecursively())
|
||||
{
|
||||
Error(log, "Failed to remove directory: %s", QSTRING_CSTR(path));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fileExists(const QString& path, Logger* log, bool ignError)
|
||||
{
|
||||
QFile file(path);
|
||||
@@ -71,17 +83,18 @@ namespace FileUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool removeFile(const QString& path, Logger* log)
|
||||
bool removeFile(const QString& path, Logger* log, bool ignError)
|
||||
{
|
||||
QFile file(path);
|
||||
if(!file.remove())
|
||||
{
|
||||
resolveFileError(file,log);
|
||||
if(!ignError)
|
||||
resolveFileError(file,log);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
QString convertPath(const QString path)
|
||||
{
|
||||
QString p = path;
|
||||
|
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"type":"object",
|
||||
"required":true,
|
||||
"properties":{
|
||||
"command": {
|
||||
"type" : "string",
|
||||
"required" : true,
|
||||
"enum" : ["clearall"]
|
||||
},
|
||||
"tan" : {
|
||||
"type" : "integer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -9,8 +9,6 @@
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
namespace JsonUtils {
|
||||
|
||||
bool readFile(const QString& path, QJsonObject& obj, Logger* log, bool ignError)
|
||||
@@ -38,13 +36,33 @@ namespace JsonUtils {
|
||||
}
|
||||
|
||||
bool parse(const QString& path, const QString& data, QJsonObject& obj, Logger* log)
|
||||
{
|
||||
QJsonDocument doc;
|
||||
if(!parse(path, data, doc, log))
|
||||
return false;
|
||||
|
||||
obj = doc.object();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse(const QString& path, const QString& data, QJsonArray& arr, Logger* log)
|
||||
{
|
||||
QJsonDocument doc;
|
||||
if(!parse(path, data, doc, log))
|
||||
return false;
|
||||
|
||||
arr = doc.array();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse(const QString& path, const QString& data, QJsonDocument& doc, Logger* log)
|
||||
{
|
||||
//remove Comments in data
|
||||
QString cleanData = data;
|
||||
cleanData.remove(QRegularExpression("([^:]?\\/\\/.*)"));
|
||||
//cleanData .remove(QRegularExpression("([^:]?\\/\\/.*)"));
|
||||
|
||||
QJsonParseError error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(cleanData.toUtf8(), &error);
|
||||
doc = QJsonDocument::fromJson(cleanData.toUtf8(), &error);
|
||||
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
{
|
||||
@@ -63,7 +81,6 @@ namespace JsonUtils {
|
||||
Error(log,"Failed to parse json data from %s: Error: %s at Line: %i, Column: %i", QSTRING_CSTR(path), QSTRING_CSTR(error.errorString()), errorLine, errorColumn);
|
||||
return false;
|
||||
}
|
||||
obj = doc.object();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -74,6 +91,14 @@ namespace JsonUtils {
|
||||
if(!readFile(schemaPath, schema, log))
|
||||
return false;
|
||||
|
||||
if(!validate(file, json, schema, log))
|
||||
return false;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool validate(const QString& file, const QJsonObject& json, const QJsonObject& schema, Logger* log)
|
||||
{
|
||||
QJsonSchemaChecker schemaChecker;
|
||||
schemaChecker.setSchema(schema);
|
||||
if (!schemaChecker.validate(json).first)
|
||||
@@ -120,7 +145,7 @@ namespace JsonUtils {
|
||||
obj.insert(attribute, resolveRefs(attributeValue.toObject(), obj, log));
|
||||
else
|
||||
{
|
||||
qDebug() <<"ADD ATTR:VALUE"<<attribute<<attributeValue;
|
||||
//qDebug() <<"ADD ATTR:VALUE"<<attribute<<attributeValue;
|
||||
obj.insert(attribute, attributeValue);
|
||||
}
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Process {
|
||||
|
||||
|
||||
void restartHyperion(bool asNewProcess)
|
||||
{
|
||||
Logger* log = Logger::getInstance("Process");
|
||||
@@ -19,8 +19,8 @@ void restartHyperion(bool asNewProcess)
|
||||
<< " *******************************************" << std::endl
|
||||
<< " * hyperion will restart now *" << std::endl
|
||||
<< " *******************************************" << std::endl << std::endl;
|
||||
|
||||
|
||||
|
||||
|
||||
QStringList qargs = QCoreApplication::arguments();
|
||||
int size = qargs.size();
|
||||
char *args[size+1];
|
||||
@@ -43,7 +43,7 @@ QByteArray command_exec(QString cmd, QByteArray data)
|
||||
QString result = "";
|
||||
|
||||
std::shared_ptr<FILE> pipe(popen(cmd.toLocal8Bit().constData(), "r"), pclose);
|
||||
if (pipe)
|
||||
if (pipe)
|
||||
{
|
||||
while (!feof(pipe.get()))
|
||||
{
|
||||
@@ -54,4 +54,4 @@ QByteArray command_exec(QString cmd, QByteArray data)
|
||||
return QSTRING_CSTR(result);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
@@ -21,7 +21,7 @@ RgbChannelAdjustment::~RgbChannelAdjustment()
|
||||
|
||||
void RgbChannelAdjustment::resetInitialized()
|
||||
{
|
||||
Debug(_log, "initialize mapping with %d,%d,%d", _adjust[RED], _adjust[GREEN], _adjust[BLUE]);
|
||||
//Debug(_log, "initialize mapping with %d,%d,%d", _adjust[RED], _adjust[GREEN], _adjust[BLUE]);
|
||||
memset(_initialized, false, sizeof(_initialized));
|
||||
}
|
||||
|
||||
|
@@ -144,8 +144,8 @@ void RgbTransform::transform(uint8_t & red, uint8_t & green, uint8_t & blue)
|
||||
{
|
||||
// apply gamma
|
||||
red = _mappingR[red];
|
||||
green = _mappingG[green];
|
||||
blue = _mappingB[blue];
|
||||
green = _mappingR[green];
|
||||
blue = _mappingR[blue];
|
||||
|
||||
// apply brightnesss
|
||||
int rgbSum = red+green+blue;
|
||||
|
@@ -23,7 +23,7 @@ Stats::Stats()
|
||||
{
|
||||
if (!(interface.flags() & QNetworkInterface::IsLoopBack))
|
||||
{
|
||||
_hyperion->id = QString(QCryptographicHash::hash(interface.hardwareAddress().toLocal8Bit().append(_hyperion->getConfigFileName().toLocal8Bit()),QCryptographicHash::Sha1).toHex());
|
||||
_hyperion->setId(QString(QCryptographicHash::hash(interface.hardwareAddress().toLocal8Bit().append(_hyperion->getConfigFileName().toLocal8Bit()),QCryptographicHash::Sha1).toHex()));
|
||||
_hash = QString(QCryptographicHash::hash(interface.hardwareAddress().toLocal8Bit(),QCryptographicHash::Sha1).toHex());
|
||||
break;
|
||||
}
|
||||
@@ -34,12 +34,12 @@ Stats::Stats()
|
||||
{
|
||||
Warning(_log, "No interface found, abort");
|
||||
// fallback id
|
||||
_hyperion->id = QString(QCryptographicHash::hash(_hyperion->getConfigFileName().toLocal8Bit(),QCryptographicHash::Sha1).toHex());
|
||||
_hyperion->setId(QString(QCryptographicHash::hash(_hyperion->getConfigFileName().toLocal8Bit(),QCryptographicHash::Sha1).toHex()));
|
||||
return;
|
||||
}
|
||||
|
||||
// prepare content
|
||||
QJsonObject config = _hyperion->getConfig();
|
||||
QJsonObject config = _hyperion->getQJsonConfig();
|
||||
SysInfo::HyperionSysInfo data = SysInfo::get();
|
||||
|
||||
QJsonObject system;
|
||||
@@ -49,8 +49,8 @@ Stats::Stats()
|
||||
system["pVersion" ] = data.productVersion;
|
||||
system["pName" ] = data.prettyName;
|
||||
system["version" ] = QString(HYPERION_VERSION);
|
||||
system["device" ] = LedDevice::activeDevice();
|
||||
system["id" ] = _hyperion->id;
|
||||
system["device" ] = Hyperion::getInstance()->getActiveDevice();
|
||||
system["id" ] = _hyperion->getId();
|
||||
system["hw_id" ] = _hash;
|
||||
system["ledCount" ] = QString::number(Hyperion::getInstance()->getLedCount());
|
||||
system["comp_sm" ] = config["smoothing"].toObject().take("enable");
|
||||
@@ -100,7 +100,7 @@ void Stats::sendHTTP()
|
||||
|
||||
void Stats::sendHTTPp()
|
||||
{
|
||||
_req.setUrl(QUrl("https://api.hyperion-project.org/api/stats/"+_hyperion->id));
|
||||
_req.setUrl(QUrl("https://api.hyperion-project.org/api/stats/"+_hyperion->getId()));
|
||||
_mgr.put(_req,_ba);
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ bool Stats::trigger(bool set)
|
||||
{
|
||||
QString path = _hyperion->getRootPath()+"/misc/";
|
||||
QDir dir;
|
||||
QFile file(path + _hyperion->id);
|
||||
QFile file(path + _hyperion->getId());
|
||||
|
||||
if(set && file.open(QIODevice::ReadWrite) )
|
||||
{
|
||||
|
@@ -1,67 +0,0 @@
|
||||
#include "webconfig/WebConfig.h"
|
||||
#include "StaticFileServing.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
WebConfig::WebConfig(QObject * parent)
|
||||
: QObject(parent)
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
, _server(nullptr)
|
||||
{
|
||||
Logger* log = Logger::getInstance("WEBSERVER");
|
||||
_port = WEBCONFIG_DEFAULT_PORT;
|
||||
_baseUrl = WEBCONFIG_DEFAULT_PATH;
|
||||
const QJsonObject config = _hyperion->getQJsonConfig();
|
||||
|
||||
bool webconfigEnable = true;
|
||||
|
||||
if (config.contains("webConfig"))
|
||||
{
|
||||
const QJsonObject webconfigConfig = config["webConfig"].toObject();
|
||||
webconfigEnable = webconfigConfig["enable"].toBool(true);
|
||||
_port = webconfigConfig["port"].toInt(_port);
|
||||
_baseUrl = webconfigConfig["document_root"].toString(_baseUrl);
|
||||
}
|
||||
|
||||
if ( (_baseUrl != ":/webconfig") && !_baseUrl.trimmed().isEmpty())
|
||||
{
|
||||
QFileInfo info(_baseUrl);
|
||||
if (!info.exists() || !info.isDir())
|
||||
{
|
||||
Error(log, "document_root '%s' is invalid, set to default '%s'", _baseUrl.toUtf8().constData(), WEBCONFIG_DEFAULT_PATH.toUtf8().constData());
|
||||
_baseUrl = WEBCONFIG_DEFAULT_PATH;
|
||||
}
|
||||
}
|
||||
else
|
||||
_baseUrl = WEBCONFIG_DEFAULT_PATH;
|
||||
|
||||
Debug(log, "WebUI initialized, document root: %s", _baseUrl.toUtf8().constData());
|
||||
if ( webconfigEnable )
|
||||
{
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WebConfig::~WebConfig()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
|
||||
void WebConfig::start()
|
||||
{
|
||||
if ( _server == nullptr )
|
||||
_server = new StaticFileServing (_hyperion, _baseUrl, _port, this);
|
||||
}
|
||||
|
||||
void WebConfig::stop()
|
||||
{
|
||||
if ( _server != nullptr )
|
||||
{
|
||||
delete _server;
|
||||
_server = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
|
||||
# Define the current source locations
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/webconfig)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/webconfig)
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/webserver)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/webserver)
|
||||
|
||||
FILE ( GLOB WebConfig_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
FILE ( GLOB_RECURSE webFiles RELATIVE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/assets/webconfig/* )
|
||||
@@ -13,13 +13,14 @@ ENDFOREACH()
|
||||
CONFIGURE_FILE(${CURRENT_SOURCE_DIR}/WebConfig.qrc.in ${CMAKE_BINARY_DIR}/WebConfig.qrc )
|
||||
SET(WebConfig_RESOURCES ${CMAKE_BINARY_DIR}/WebConfig.qrc)
|
||||
|
||||
add_library(webconfig
|
||||
add_library(webserver
|
||||
${WebConfig_SOURCES}
|
||||
${WebConfig_RESOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(webconfig
|
||||
target_link_libraries(webserver
|
||||
hyperion
|
||||
hyperion-utils
|
||||
hyperion-api
|
||||
Qt5::Network
|
||||
)
|
@@ -13,12 +13,12 @@
|
||||
#include <utils/Process.h>
|
||||
#include <utils/jsonschema/QJsonFactory.h>
|
||||
|
||||
CgiHandler::CgiHandler (Hyperion * hyperion, QString baseUrl, QObject * parent)
|
||||
CgiHandler::CgiHandler (Hyperion * hyperion, QObject * parent)
|
||||
: QObject(parent)
|
||||
, _hyperion(hyperion)
|
||||
, _args(QStringList())
|
||||
, _hyperionConfig(_hyperion->getQJsonConfig())
|
||||
, _baseUrl(baseUrl)
|
||||
, _baseUrl()
|
||||
, _log(Logger::getInstance("WEBSERVER"))
|
||||
{
|
||||
}
|
||||
@@ -27,6 +27,11 @@ CgiHandler::~CgiHandler()
|
||||
{
|
||||
}
|
||||
|
||||
void CgiHandler::setBaseUrl(const QString& url)
|
||||
{
|
||||
_baseUrl = url;
|
||||
}
|
||||
|
||||
void CgiHandler::exec(const QStringList & args, QtHttpRequest * request, QtHttpReply * reply)
|
||||
{
|
||||
try
|
@@ -15,25 +15,24 @@ class CgiHandler : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CgiHandler (Hyperion * hyperion, QString baseUrl, QObject * parent = NULL);
|
||||
CgiHandler (Hyperion * hyperion, QObject * parent = NULL);
|
||||
virtual ~CgiHandler (void);
|
||||
|
||||
void setBaseUrl(const QString& url);
|
||||
void exec(const QStringList & args,QtHttpRequest * request, QtHttpReply * reply);
|
||||
|
||||
|
||||
// cgi commands
|
||||
void cmd_cfg_jsonserver();
|
||||
void cmd_runscript ();
|
||||
|
||||
|
||||
private:
|
||||
Hyperion* _hyperion;
|
||||
QtHttpReply * _reply;
|
||||
QtHttpRequest * _request;
|
||||
QStringList _args;
|
||||
const QJsonObject & _hyperionConfig;
|
||||
const QString _baseUrl;
|
||||
QString _baseUrl;
|
||||
Logger * _log;
|
||||
};
|
||||
|
||||
#endif // CGIHANDLER_H
|
||||
|
||||
|
@@ -159,9 +159,8 @@ void QtHttpClientWrapper::onClientDataReceived (void) {
|
||||
this, &QtHttpClientWrapper::onReplySendHeadersRequested);
|
||||
connect (&reply, &QtHttpReply::requestSendData,
|
||||
this, &QtHttpClientWrapper::onReplySendDataRequested);
|
||||
|
||||
emit m_serverHandle->requestNeedsReply (m_currentRequest, &reply); // allow app to handle request
|
||||
m_parsingStatus = sendReplyToClient (&reply);
|
||||
m_parsingStatus = sendReplyToClient (&reply);
|
||||
break;
|
||||
}
|
||||
case ParsingError: { // there was an error durin one of parsing steps
|
@@ -53,23 +53,20 @@ QString QtHttpServer::getErrorString (void) const {
|
||||
}
|
||||
|
||||
void QtHttpServer::start (quint16 port) {
|
||||
if (m_sockServer->listen (QHostAddress::Any, port)) {
|
||||
emit started (m_sockServer->serverPort ());
|
||||
}
|
||||
else {
|
||||
emit error (m_sockServer->errorString ());
|
||||
}
|
||||
if(!m_sockServer->isListening())
|
||||
{
|
||||
if (m_sockServer->listen (QHostAddress::Any, port)) {
|
||||
emit started (m_sockServer->serverPort ());
|
||||
}
|
||||
else {
|
||||
emit error (m_sockServer->errorString ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpServer::stop (void) {
|
||||
if (m_sockServer->isListening ()) {
|
||||
m_sockServer->close ();
|
||||
// disconnect clients
|
||||
const QList<QTcpSocket*> socks = m_socksClientsHash.keys();
|
||||
for(auto sock : socks)
|
||||
{
|
||||
sock->close();
|
||||
}
|
||||
emit stopped ();
|
||||
}
|
||||
}
|
||||
@@ -94,22 +91,22 @@ void QtHttpServer::setCertificates (const QList<QSslCertificate> & certs) {
|
||||
void QtHttpServer::onClientConnected (void) {
|
||||
while (m_sockServer->hasPendingConnections ()) {
|
||||
if (QTcpSocket * sock = m_sockServer->nextPendingConnection ()) {
|
||||
connect (sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);
|
||||
if (m_useSsl) {
|
||||
if (QSslSocket * ssl = qobject_cast<QSslSocket *> (sock)) {
|
||||
connect (ssl, SslErrorSignal (&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors);
|
||||
connect (ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted);
|
||||
connect (ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError);
|
||||
connect (ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged);
|
||||
ssl->setLocalCertificateChain (m_sslCerts);
|
||||
ssl->setPrivateKey (m_sslKey);
|
||||
ssl->setPeerVerifyMode (QSslSocket::AutoVerifyPeer);
|
||||
ssl->startServerEncryption ();
|
||||
}
|
||||
}
|
||||
QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sock, this);
|
||||
m_socksClientsHash.insert (sock, wrapper);
|
||||
emit clientConnected (wrapper->getGuid ());
|
||||
connect (sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);
|
||||
if (m_useSsl) {
|
||||
if (QSslSocket * ssl = qobject_cast<QSslSocket *> (sock)) {
|
||||
connect (ssl, SslErrorSignal (&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors);
|
||||
connect (ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted);
|
||||
connect (ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError);
|
||||
connect (ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged);
|
||||
ssl->setLocalCertificateChain (m_sslCerts);
|
||||
ssl->setPrivateKey (m_sslKey);
|
||||
ssl->setPeerVerifyMode (QSslSocket::AutoVerifyPeer);
|
||||
ssl->startServerEncryption ();
|
||||
}
|
||||
}
|
||||
QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sock, this);
|
||||
m_socksClientsHash.insert (sock, wrapper);
|
||||
emit clientConnected (wrapper->getGuid ());
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user