second part of PR #578

Signed-off-by: Paulchen-Panther <Paulchen-Panter@protonmail.com>
This commit is contained in:
Paulchen-Panther
2019-07-14 22:43:22 +02:00
parent ea796160af
commit 90599e820a
52 changed files with 2354 additions and 536 deletions

View File

@@ -18,7 +18,6 @@
#include <utils/Components.h>
#include <utils/JsonUtils.h>
#include <hyperion/Hyperion.h>
#include <jsonserver/JsonServer.h>
#include <udplistener/UDPListener.h>
#include <webserver/WebServer.h>
@@ -43,6 +42,9 @@
// AuthManager
#include <hyperion/AuthManager.h>
// InstanceManager Hyperion
#include <hyperion/HyperionIManager.h>
// NetOrigin checks
#include <utils/NetOrigin.h>
@@ -54,10 +56,11 @@
HyperionDaemon* HyperionDaemon::daemon = nullptr;
HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObject *parent, const bool& logLvlOverwrite)
HyperionDaemon::HyperionDaemon(const QString rootPath, QObject *parent, const bool& logLvlOverwrite)
: QObject(parent)
, _log(Logger::getInstance("DAEMON"))
, _authManager(new AuthManager(rootPath, this))
, _instanceManager(new HyperionIManager(rootPath, this))
, _authManager(new AuthManager(this))
, _bonjourBrowserWrapper(new BonjourBrowserWrapper())
, _netOrigin(new NetOrigin(this))
, _pyInit(new PythonInit())
@@ -71,7 +74,6 @@ HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObje
, _fbGrabber(nullptr)
, _osxGrabber(nullptr)
, _qtGrabber(nullptr)
, _hyperion(nullptr)
, _ssdp(nullptr)
, _currVideoMode(VIDEO_2D)
{
@@ -86,7 +88,7 @@ HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObje
qRegisterMetaType<std::vector<ColorRgb>>("std::vector<ColorRgb>");
// init settings
_settingsManager = new SettingsManager(0,configFile);
_settingsManager = new SettingsManager(0,this);
// set inital log lvl if the loglvl wasn't overwritten by arg
if(!logLvlOverwrite)
@@ -104,38 +106,33 @@ HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObje
connect(this, &HyperionDaemon::settingsChanged, _netOrigin, &NetOrigin::handleSettingsUpdate);
_netOrigin->handleSettingsUpdate(settings::NETWORK, _settingsManager->getSetting(settings::NETWORK));
// spawn all Hyperion instances before network services
_hyperion = Hyperion::initInstance(this, 0, configFile, rootPath);
Info(_log, "Hyperion initialized");
// spawn all Hyperion instances (non blocking)
_instanceManager->startAll();
//connect(_hyperion,SIGNAL(closing()),this,SLOT(freeObjects())); // TODO for app restart, refactor required
// listen for setting changes
connect(_hyperion, &Hyperion::settingsChanged, this, &HyperionDaemon::settingsChanged);
// pipe settings changes and component state changes from HyperionIManager to Daemon
connect(_instanceManager, &HyperionIManager::settingsChanged, this, &HyperionDaemon::settingsChanged);
connect(_instanceManager, &HyperionIManager::componentStateChanged, this, &HyperionDaemon::componentStateChanged);
// listen for setting changes of framegrabber and v4l2
connect(this, &HyperionDaemon::settingsChanged, this, &HyperionDaemon::handleSettingsUpdate);
// forward videoModes from Hyperion to Daemon evaluation
connect(_hyperion, &Hyperion::videoMode, this, &HyperionDaemon::setVideoMode);
// forward videoMode changes from Daemon to Hyperion
connect(this, &HyperionDaemon::videoMode, _hyperion, &Hyperion::newVideoMode);
// forward videoModes from HyperionIManager to Daemon evaluation
connect(_instanceManager, &HyperionIManager::requestVideoMode, this, &HyperionDaemon::setVideoMode);
// return videoMode changes from Daemon to HyperionIManager
connect(this, &HyperionDaemon::videoMode, _instanceManager, &HyperionIManager::newVideoMode);
// ---- grabber -----
#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) && !defined(ENABLE_FB) && !defined(ENABLE_X11) && !defined(ENABLE_AMLOGIC)
Warning(_log, "No platform capture can be instantiated, because all grabbers have been left out from the build");
#endif
// get power state of system/v4l2 capture
const QJsonObject & grabberConfig = getSetting(settings::INSTCAPTURE).object();
// init system capture (framegrabber) and update power state
// init system capture (framegrabber)
handleSettingsUpdate(settings::SYSTEMCAPTURE, getSetting(settings::SYSTEMCAPTURE));
_hyperion->setComponentState(hyperion::COMP_GRABBER, grabberConfig["systemEnable"].toBool(true));
// init v4l2 capture and update power state
// init v4l2 capture
handleSettingsUpdate(settings::V4L2, getSetting(settings::V4L2));
_hyperion->setComponentState(hyperion::COMP_V4L, grabberConfig["v4lEnable"].toBool(true));
// ---- network services -----
startNetworkServices();
@@ -144,7 +141,6 @@ HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObje
HyperionDaemon::~HyperionDaemon()
{
freeObjects();
delete _hyperion;
delete _settingsManager;
delete _pyInit;
}
@@ -165,7 +161,6 @@ const QJsonDocument HyperionDaemon::getSetting(const settings::type &type)
void HyperionDaemon::freeObjects()
{
_hyperion->clearall(true);
// destroy network first as a client might want to access hyperion
delete _jsonServer;
_flatBufferServer->thread()->quit();
@@ -178,6 +173,10 @@ void HyperionDaemon::freeObjects()
_webserver->thread()->quit();
_webserver->thread()->wait(1000);
delete _udpListener;
// stop Hyperions (non blocking)
_instanceManager->stopAll();
delete _bonjourBrowserWrapper;
delete _amlGrabber;
delete _dispmanx;
@@ -224,12 +223,13 @@ void HyperionDaemon::startNetworkServices()
connect( pThread, &QThread::started, _protoServer, &ProtoServer::initServer );
connect( pThread, &QThread::finished, _protoServer, &QObject::deleteLater );
connect( pThread, &QThread::finished, pThread, &QObject::deleteLater );
connect(this, &HyperionDaemon::settingsChanged, _protoServer, &ProtoServer::handleSettingsUpdate);
connect( this, &HyperionDaemon::settingsChanged, _protoServer, &ProtoServer::handleSettingsUpdate );
pThread->start();
// Create UDP listener
_udpListener = new UDPListener(getSetting(settings::UDPLISTENER));
connect(this, &HyperionDaemon::settingsChanged, _udpListener, &UDPListener::handleSettingsUpdate);
connect(this, &HyperionDaemon::componentStateChanged, _udpListener, &UDPListener::updatedComponentState);
// Create Webserver in thread
_webserver = new WebServer(getSetting(settings::WEBSERVER));
@@ -241,7 +241,7 @@ void HyperionDaemon::startNetworkServices()
connect(this, &HyperionDaemon::settingsChanged, _webserver, &WebServer::handleSettingsUpdate);
wsThread->start();
// create ssdp server in thread
// Create SSDP server in thread
_ssdp = new SSDPHandler(_webserver, getSetting(settings::FLATBUFSERVER).object()["port"].toInt());
QThread* ssdpThread = new QThread(this);
_ssdp->moveToThread(ssdpThread);
@@ -431,6 +431,7 @@ void HyperionDaemon::handleSettingsUpdate(const settings::type& settingsType, co
// connect to HyperionDaemon signal
connect(this, &HyperionDaemon::videoMode, _v4l2Grabber, &V4L2Wrapper::setVideoMode);
connect(this, &HyperionDaemon::settingsChanged, _v4l2Grabber, &V4L2Wrapper::handleSettingsUpdate);
connect(this, &HyperionDaemon::componentStateChanged, _v4l2Grabber, &V4L2Wrapper::componentStateChanged);
#else
Error(_log, "The v4l2 grabber can not be instantiated, because it has been left out from the build");
#endif

View File

@@ -46,13 +46,13 @@
#endif
#include <utils/Logger.h>
#include <utils/Image.h>
#include <utils/VideoMode.h>
// settings management
#include <utils/settings.h>
#include <utils/Components.h>
class Hyperion;
class HyperionIManager;
class SysTray;
class JsonServer;
class UDPListener;
@@ -73,7 +73,7 @@ class HyperionDaemon : public QObject
friend SysTray;
public:
HyperionDaemon(QString configFile, QString rootPath, QObject *parent, const bool& logLvlOverwrite );
HyperionDaemon(QString rootPath, QObject *parent, const bool& logLvlOverwrite );
~HyperionDaemon();
///
@@ -100,15 +100,28 @@ public slots:
void freeObjects();
signals:
///////////////////////////////////////
/// FROM HYPERIONDAEMON TO HYPERION ///
///////////////////////////////////////
///
/// @brief After eval of setVideoMode this signal emits with a new one on change
///
void videoMode(const VideoMode& mode);
///////////////////////////////////////
/// FROM HYPERION TO HYPERIONDAEMON ///
///////////////////////////////////////
///
/// @brief PIPE settings events from Hyperion class to HyperionDaemon components
///
void settingsChanged(const settings::type& type, const QJsonDocument& data);
///
/// @brief After eval of setVideoMode this signal emits with a new one on change
/// @brief PIPE component state changes events from Hyperion class to HyperionDaemon components
///
void videoMode(const VideoMode& mode);
void componentStateChanged(const hyperion::Components component, bool enable);
private slots:
///
@@ -133,6 +146,7 @@ private:
void createGrabberQt(const QJsonObject & grabberConfig);
Logger* _log;
HyperionIManager* _instanceManager;
AuthManager* _authManager;
BonjourBrowserWrapper* _bonjourBrowserWrapper;
NetOrigin* _netOrigin;
@@ -147,7 +161,6 @@ private:
FramebufferWrapper* _fbGrabber;
OsxWrapper* _osxGrabber;
QtWrapper* _qtGrabber;
Hyperion* _hyperion;
SSDPHandler* _ssdp;
FlatBufferServer* _flatBufferServer;
ProtoServer* _protoServer;

View File

@@ -20,6 +20,7 @@
#include <QDir>
#include <QStringList>
#include <QSystemTrayIcon>
#include <QProcess>
#include "HyperionConfig.h"
@@ -39,10 +40,84 @@ using namespace commandline;
#define PERM0664 QFileDevice::ReadOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther | QFileDevice::WriteOwner | QFileDevice::WriteGroup
unsigned int getProcessIdsByProcessName(const char* processName, QStringList &listOfPids)
{
// Clear content of returned list of PIDS
listOfPids.clear();
#if defined(WIN32)
// Get the list of process identifiers.
DWORD aProcesses[1024], cbNeeded, cProcesses;
unsigned int i;
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
return 0;
// Calculate how many process identifiers were returned.
cProcesses = cbNeeded / sizeof(DWORD);
// Search for a matching name for each process
for (i = 0; i < cProcesses; i++)
{
if (aProcesses[i] != 0)
{
char szProcessName[MAX_PATH] = {0};
DWORD processID = aProcesses[i];
// Get a handle to the process.
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
// Get the process name
if (NULL != hProcess)
{
HMODULE hMod;
DWORD cbNeeded;
if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
GetModuleBaseNameA(hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(char));
// Release the handle to the process.
CloseHandle(hProcess);
if (*szProcessName != 0 && strcmp(processName, szProcessName) == 0)
listOfPids.append(QString::number(processID));
}
}
}
return listOfPids.count();
#else
// Run pgrep, which looks through the currently running processses and lists the process IDs
// which match the selection criteria to stdout.
QProcess process;
process.start("pgrep", QStringList() << processName);
process.waitForReadyRead();
QByteArray bytes = process.readAllStandardOutput();
process.terminate();
process.waitForFinished();
process.kill();
// Output is something like "2472\n2323" for multiple instances
if (bytes.isEmpty())
return 0;
listOfPids = QString(bytes).split("\n", QString::SkipEmptyParts);
return listOfPids.count();
#endif
}
void signal_handler(const int signum)
{
/// Hyperion instance
Hyperion* _hyperion = Hyperion::getInstance();
// SIGUSR1 and SIGUSR2 must be rewritten
// Hyperion Managment instance
HyperionIManager* _hyperion = HyperionIManager::getInstance();
if(signum == SIGCHLD)
{
@@ -54,8 +129,8 @@ void signal_handler(const int signum)
{
if (_hyperion != nullptr)
{
_hyperion->setComponentState(hyperion::COMP_SMOOTHING, false);
_hyperion->setComponentState(hyperion::COMP_LEDDEVICE, false);
// _hyperion->setComponentState(hyperion::COMP_SMOOTHING, false);
// _hyperion->setComponentState(hyperion::COMP_LEDDEVICE, false);
}
return;
}
@@ -63,29 +138,18 @@ void signal_handler(const int signum)
{
if (_hyperion != nullptr)
{
_hyperion->setComponentState(hyperion::COMP_LEDDEVICE, true);
_hyperion->setComponentState(hyperion::COMP_SMOOTHING, true);
// _hyperion->setComponentState(hyperion::COMP_LEDDEVICE, true);
// _hyperion->setComponentState(hyperion::COMP_SMOOTHING, true);
}
return;
}
QCoreApplication::quit();
// reset signal handler to default (in case this handler is not capable of stopping)
signal(signum, SIG_DFL);
}
void startNewHyperion(int parentPid, std::string hyperionFile, std::string configFile)
{
pid_t childPid = fork(); // child pid should store elsewhere for later use
if ( childPid == 0 )
{
sleep(3);
execl(hyperionFile.c_str(), hyperionFile.c_str(), "--parent", QString::number(parentPid).toStdString().c_str(), configFile.c_str(), NULL);
exit(0);
}
}
QCoreApplication* createApplication(int &argc, char *argv[])
{
bool isGuiApp = false;
@@ -127,7 +191,7 @@ QCoreApplication* createApplication(int &argc, char *argv[])
QApplication* app = new QApplication(argc, argv);
app->setApplicationDisplayName("Hyperion");
app->setWindowIcon(QIcon(":/hyperion-icon-32px.png"));
return app;
return app;
}
QCoreApplication* app = new QCoreApplication(argc, argv);
@@ -144,8 +208,20 @@ int main(int argc, char** argv)
Logger* log = Logger::getInstance("MAIN");
Logger::setLogLevel(Logger::WARNING);
// check if we are running already an instance
// TODO Do not use pgrep on linux, instead iter /proc
// TODO Allow one session per user
// http://www.qtcentre.org/threads/44489-Get-Process-ID-for-a-running-application
QStringList listOfPids;
if(getProcessIdsByProcessName("hyperiond", listOfPids) > 1)
{
Error(log, "The Hyperion Daemon is already running, abort start");
return 0;
}
// Initialising QCoreApplication
QScopedPointer<QCoreApplication> app(createApplication(argc, argv));
QScopedPointer<QCoreApplication> app(createApplication(argc, argv));
bool isGuiApp = (qobject_cast<QApplication *>(app.data()) != 0 && QSystemTrayIcon::isSystemTrayAvailable());
signal(SIGINT, signal_handler);
@@ -165,20 +241,14 @@ int main(int argc, char** argv)
BooleanOption & versionOption = parser.add<BooleanOption>(0x0, "version", "Show version information");
Option & rootPathOption = parser.add<Option> (0x0, "rootPath", "Overwrite root path for all hyperion user files, defaults to home directory of current user");
IntOption & parentOption = parser.add<IntOption> ('p', "parent", "pid of parent hyperiond"); // 2^22 is the max for Linux
BooleanOption & silentOption = parser.add<BooleanOption>('s', "silent", "do not print any outputs");
BooleanOption & verboseOption = parser.add<BooleanOption>('v', "verbose", "Increase verbosity");
BooleanOption & debugOption = parser.add<BooleanOption>('d', "debug", "Show debug messages");
parser.add<BooleanOption>(0x0, "desktop", "show systray on desktop");
parser.add<BooleanOption>(0x0, "service", "force hyperion to start as console service");
Option & exportConfigOption = parser.add<Option> (0x0, "export-config", "export default config to file");
Option & exportEfxOption = parser.add<Option> (0x0, "export-effects", "export effects to given path");
parser.addPositionalArgument("config-files", QCoreApplication::translate("main", "Configuration file"), "config.file");
parser.process(*qApp);
QStringList configFiles = parser.positionalArguments();
parser.process(*qApp);
int logLevelCheck = 0;
if (parser.isSet(silentOption))
@@ -207,10 +277,10 @@ int main(int argc, char** argv)
if (parser.isSet(versionOption))
{
std::cout
<< "Hyperion Ambilight Deamon (" << getpid() << ")" << std::endl
<< "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl
<< "\tBuild Time: " << __DATE__ << " " << __TIME__ << std::endl;
std::cout
<< "Hyperion Ambilight Deamon (" << getpid() << ")" << std::endl
<< "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl
<< "\tBuild Time: " << __DATE__ << " " << __TIME__ << std::endl;
return 0;
}
@@ -229,9 +299,7 @@ int main(int argc, char** argv)
{
destFileName = destDir.dirName()+"/"+filename;
if (QFile::exists(destFileName))
{
QFile::remove(destFileName);
}
std::cout << "Extract: " << filename.toStdString() << " ... ";
if (QFile::copy(QString(":/effects/")+filename, destFileName))
@@ -267,82 +335,16 @@ int main(int argc, char** argv)
}
}
// create /.hyperion folder for default path, check if the directory is read/writeable
// Note: No further checks inside Hyperion. FileUtils::writeFile() will resolve permission errors and others that occur during runtime
// NOTE: No further checks inside Hyperion. FileUtils::writeFile() will resolve permission errors and others that occur during runtime
QDir mDir(rootPath);
QFileInfo mFi(rootPath);
if(!mDir.mkpath(rootPath) || !mFi.isWritable() || !mDir.isReadable())
{
throw std::runtime_error("The specified root path can't be created or isn't read/writeable. Please setup the permissions correctly!");
}
// determine name of config file, defaults to hyperion_main.json
// create config folder
QString cPath(rootPath+"/config");
QDir().mkpath(rootPath+"/config");
if (configFiles.size() > 0)
{
// use argument config file
// check if file has a path and ends with .json
if(configFiles[0].contains("/"))
throw std::runtime_error("Don't provide a path to config file, just a config name is allowed!");
if(!configFiles[0].endsWith(".json"))
configFiles[0].append(".json");
configFiles.prepend(cPath+"/"+configFiles[0]);
}
else
{
// use default config file
configFiles.append(cPath+"/hyperion_main.json");
}
bool exportDefaultConfig = false;
bool exitAfterExportDefaultConfig = false;
QString exportConfigFileTarget;
if (parser.isSet(exportConfigOption))
{
exportDefaultConfig = true;
exitAfterExportDefaultConfig = true;
exportConfigFileTarget = exportConfigOption.value(parser);
}
else if ( ! QFile::exists(configFiles[0]) )
{
exportDefaultConfig = true;
exportConfigFileTarget = configFiles[0];
Warning(log, "Create new config file (%s)",QSTRING_CSTR(configFiles[0]));
}
if (exportDefaultConfig)
{
Q_INIT_RESOURCE(resource);
QDir().mkpath(FileUtils::getDirName(exportConfigFileTarget));
if (QFile::copy(":/hyperion_default.config",exportConfigFileTarget))
{
QFile::setPermissions(exportConfigFileTarget, PERM0664 );
Info(log, "export complete.");
if (exitAfterExportDefaultConfig) return 0;
}
else
{
Error(log, "error while export to %s", QSTRING_CSTR(exportConfigFileTarget) );
return 1;
}
}
int parentPid = parser.value(parentOption).toInt();
if (parentPid > 0 )
{
Info(log, "hyperiond client, parent is pid %d", parentPid);
#ifndef __APPLE__
prctl(PR_SET_PDEATHSIG, SIGHUP);
#endif
}
HyperionDaemon* hyperiond = nullptr;
try
{
hyperiond = new HyperionDaemon(configFiles[0], rootPath, qApp, bool(logLevelCheck));
hyperiond = new HyperionDaemon(rootPath, qApp, bool(logLevelCheck));
}
catch (std::exception& e)
{

View File

@@ -20,6 +20,7 @@ SysTray::SysTray(HyperionDaemon *hyperiond)
, _colorDlg(this)
, _hyperiond(hyperiond)
, _hyperion(nullptr)
, _instanceManager(HyperionIManager::getInstance())
, _webPort(8090)
{
Q_INIT_RESOURCE(resources);
@@ -28,16 +29,8 @@ SysTray::SysTray(HyperionDaemon *hyperiond)
WebServer* webserver = hyperiond->getWebServerInstance();
connect(webserver, &WebServer::portChanged, this, &SysTray::webserverPortChanged);
_hyperion = Hyperion::getInstance();
createTrayIcon();
connect(_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
connect(&_colorDlg, SIGNAL(currentColorChanged(const QColor&)), this, SLOT(setColor(const QColor &)));
QIcon icon(":/hyperion-icon-32px.png");
_trayIcon->setIcon(icon);
_trayIcon->show();
setWindowIcon(icon);
_colorDlg.setOptions(QColorDialog::NoButtons);
// instance changes
connect(_instanceManager, &HyperionIManager::instanceStateChanged, this, &SysTray::handleInstanceStateChange);
}
SysTray::~SysTray()
@@ -148,3 +141,29 @@ void SysTray::clearEfxColor()
{
_hyperion->clear(1);
}
void SysTray::handleInstanceStateChange(const instanceState& state, const quint8& instance, const QString& name)
{
switch(state){
case H_STARTED:
if(instance == 0)
{
_hyperion = _instanceManager->getHyperionInstance(0);
createTrayIcon();
connect(_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
connect(&_colorDlg, SIGNAL(currentColorChanged(const QColor&)), this, SLOT(setColor(const QColor &)));
QIcon icon(":/hyperion-icon-32px.png");
_trayIcon->setIcon(icon);
_trayIcon->show();
setWindowIcon(icon);
_colorDlg.setOptions(QColorDialog::NoButtons);
}
break;
default:
break;
}
}

View File

@@ -7,6 +7,7 @@
#include <QCloseEvent>
#include <hyperion/Hyperion.h>
#include <hyperion/HyperionIManager.h>
class HyperionDaemon;
@@ -35,21 +36,27 @@ private slots:
///
void webserverPortChanged(const quint16& port) { _webPort = port; };
///
/// @brief is called whenever a hyperion isntance state changes
///
void handleInstanceStateChange(const instanceState& state, const quint8& instance, const QString& name);
private:
void createTrayIcon();
QAction *quitAction;
QAction *startAction;
QAction *stopAction;
QAction *colorAction;
QAction *settingsAction;
QAction *clearAction;
QAction *quitAction;
QAction *startAction;
QAction *stopAction;
QAction *colorAction;
QAction *settingsAction;
QAction *clearAction;
QSystemTrayIcon *_trayIcon;
QMenu *_trayIconMenu;
QMenu *_trayIconEfxMenu;
QColorDialog _colorDlg;
HyperionDaemon *_hyperiond;
Hyperion *_hyperion;
quint16 _webPort;
QSystemTrayIcon *_trayIcon;
QMenu *_trayIconMenu;
QMenu *_trayIconEfxMenu;
QColorDialog _colorDlg;
HyperionDaemon *_hyperiond;
Hyperion *_hyperion;
HyperionIManager *_instanceManager;
quint16 _webPort;
};