KDEAmbi/main.cpp

194 lines
6.3 KiB
C++

#include <QCommandLineParser>
#include <signal.h>
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QJsonDocument>
#include <QJsonObject>
#include <QCoreApplication>
#include <QTimer>
#include <QApplication> // Added for QApplication
#include "hyperiongrabber.h"
#include "HyperionProcessor.h"
#include "wledclient.h"
#include "LedColorMapping.h"
#include "LinearColorSmoothing.h"
// Global pointers for cleanup
static HyperionGrabber *grabber = nullptr;
static QApplication *qapp = nullptr;
static HyperionProcessor *processor = nullptr;
static WledClient *wledClient = nullptr;
static QTimer *processTimer = nullptr;
static QImage currentImage; // Store the latest captured image
// Custom message handler for logging to file
static QFile *logFile = nullptr;
void customMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QByteArray localMsg = msg.toLocal8Bit();
QString logMessage = QString("%1 %2 %3 %4 %5")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(context.category)
.arg(context.function)
.arg(context.line)
.arg(localMsg.constData());
switch (type) {
case QtDebugMsg:
logMessage = "Debug: " + logMessage;
break;
case QtInfoMsg:
logMessage = "Info: " + logMessage;
break;
case QtWarningMsg:
logMessage = "Warning: " + logMessage;
break;
case QtCriticalMsg:
logMessage = "Critical: " + logMessage;
break;
case QtFatalMsg:
logMessage = "Fatal: " + logMessage;
break;
}
if (logFile && logFile->isOpen()) {
QTextStream stream(logFile);
stream << logMessage << Qt::endl;
stream.flush();
}
if (type == QtCriticalMsg || type == QtFatalMsg || !logFile || !logFile->isOpen()) {
fprintf(stderr, "%s\n", logMessage.toLocal8Bit().constData());
}
}
// Signal handler for graceful shutdown
static void quit(int)
{
if (processTimer) {
processTimer->stop();
delete processTimer;
processTimer = nullptr;
}
if (grabber != nullptr) {
delete grabber;
grabber = nullptr;
}
if (processor != nullptr) {
delete processor;
processor = nullptr;
}
if (wledClient != nullptr) {
delete wledClient;
wledClient = nullptr;
}
if (logFile) {
logFile->close();
delete logFile;
logFile = nullptr;
}
if (qapp != nullptr) {
qapp->exit();
}
}
// Slot to receive and store the latest image from HyperionGrabber
void onImageReady(const QImage &image)
{
currentImage = image;
}
// Slot to process the current image and send to WLED (triggered by timer)
void processCurrentFrame()
{
if (currentImage.isNull()) {
qDebug() << "No image available yet for processing.";
return;
}
// Process the image with HyperionProcessor
QVector<QColor> ledColors = processor->process(currentImage);
// Send colors to WLED
wledClient->setLedsColor(ledColors);
qDebug() << "Processed frame and sent to WLED.";
}
int main(int argc, char *argv[])
{
// Initialize logging to file
logFile = new QFile("output.log");
if (!logFile->open(QFile::WriteOnly | QFile::Text | QFile::Truncate)) {
fprintf(stderr, "Warning: Could not open log file output.log for writing.\n");
delete logFile;
logFile = nullptr;
}
qInstallMessageHandler(customMessageOutput);
qapp = new QApplication(argc, argv);
signal(SIGINT, quit);
signal(SIGTERM, quit);
QApplication::setApplicationName("HyperionGrabber");
QApplication::setApplicationVersion("0.2");
// Read configuration from environment variables
QString wledAddress = qgetenv("HYPERION_GRABBER_WLED_ADDRESS");
if (wledAddress.isEmpty()) {
qCritical() << "Error: HYPERION_GRABBER_WLED_ADDRESS environment variable not set.";
return 1;
}
quint16 wledPort = qgetenv("HYPERION_GRABBER_WLED_PORT").toUShort();
if (wledPort == 0) wledPort = 21324; // Default WLED UDP Realtime port
QHash<QString, QString> grabberOpts;
grabberOpts.insert("scale", QString::number(qgetenv("HYPERION_GRABBER_SCALE").toInt()));
grabberOpts.insert("frameskip", QString::number(qgetenv("HYPERION_GRABBER_FRAMESKIP").toInt()));
grabberOpts.insert("changeThreshold", QString::number(qgetenv("HYPERION_GRABBER_CHANGE_THRESHOLD").toInt()));
LedLayout layout;
layout.bottom = qgetenv("HYPERION_GRABBER_LEDS_BOTTOM").toInt();
layout.right = qgetenv("HYPERION_GRABBER_LEDS_RIGHT").toInt();
layout.top = qgetenv("HYPERION_GRABBER_LEDS_TOP").toInt();
layout.left = qgetenv("HYPERION_GRABBER_LEDS_LEFT").toInt();
QJsonObject processorConfig;
processorConfig["blackBorderThreshold"] = qgetenv("HYPERION_GRABBER_BLACK_BORDER_THRESHOLD").toDouble();
processorConfig["smoothingFactor"] = qgetenv("HYPERION_GRABBER_SMOOTHING_FACTOR").toDouble();
// Apply default values if environment variables are not set or invalid
if (grabberOpts["scale"].isEmpty()) grabberOpts["scale"] = "8";
if (grabberOpts["frameskip"].isEmpty()) grabberOpts["frameskip"] = "0";
if (grabberOpts["changeThreshold"].isEmpty()) grabberOpts["changeThreshold"] = "100";
if (layout.bottom == 0) layout.bottom = 70;
if (layout.right == 0) layout.right = 20;
if (layout.top == 0) layout.top = 70;
if (layout.left == 0) layout.left = 20;
if (!processorConfig.contains("blackBorderThreshold")) processorConfig["blackBorderThreshold"] = 0.2;
if (!processorConfig.contains("smoothingFactor")) processorConfig["smoothingFactor"] = 0.1;
// Instantiate components
grabber = new HyperionGrabber(grabberOpts);
processor = new HyperionProcessor(layout, processorConfig);
wledClient = new WledClient(wledAddress, wledPort);
// Connect grabber to image receiver slot
QObject::connect(grabber, &HyperionGrabber::imageReady, &onImageReady);
// Setup timer for processing frames
processTimer = new QTimer();
processTimer->setInterval(1000); // Process every 1 second
QObject::connect(processTimer, &QTimer::timeout, &processCurrentFrame);
processTimer->start();
qInfo() << "HyperionGrabber started. Sending LED colors to WLED device:" << wledAddress << ":" << wledPort;
return qapp->exec();
}