Remove max LED number constraint from Matrix layout (#1805)

This commit is contained in:
LordGrey
2024-12-09 06:21:53 +01:00
committed by GitHub
parent e8e102c25d
commit 179ee316d0
28 changed files with 231 additions and 76 deletions

View File

@@ -85,6 +85,11 @@ JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject
_jsonCB = QSharedPointer<JsonCallbacks>(new JsonCallbacks( _log, _peerAddress, parent));
}
QSharedPointer<JsonCallbacks> JsonAPI::getCallBack() const
{
return _jsonCB;
}
void JsonAPI::initialize()
{
// init API, REQUIRED!
@@ -97,9 +102,6 @@ void JsonAPI::initialize()
// listen for killed instances
connect(_instanceManager, &HyperionIManager::instanceStateChanged, this, &JsonAPI::handleInstanceStateChange);
// pipe callbacks from subscriptions to parent
connect(_jsonCB.data(), &JsonCallbacks::newCallback, this, &JsonAPI::callbackMessage);
// notify hyperion about a jsonMessageForward
if (_hyperion != nullptr)
{
@@ -1537,7 +1539,7 @@ void JsonAPI::sendSuccessReply(const JsonApiCommand& cmd)
void JsonAPI::sendSuccessReply(const QString &command, int tan, InstanceCmd::Type isInstanceCmd)
{
emit callbackMessage(getBasicCommandReply(true, command, tan , isInstanceCmd));
emit callbackReady(getBasicCommandReply(true, command, tan , isInstanceCmd));
}
void JsonAPI::sendSuccessDataReply(const QJsonValue &infoData, const JsonApiCommand& cmd)
@@ -1572,7 +1574,7 @@ void JsonAPI::sendSuccessDataReplyWithError(const QJsonValue &infoData, const QS
reply["errorData"] = errorsArray;
}
emit callbackMessage(reply);
emit callbackReady(reply);
}
void JsonAPI::sendErrorReply(const QString &error, const JsonApiCommand& cmd)
@@ -1601,7 +1603,7 @@ void JsonAPI::sendErrorReply(const QString &error, const QStringList& errorDetai
reply["errorData"] = errorsArray;
}
emit callbackMessage(reply);
emit callbackReady(reply);
}
void JsonAPI::sendNewRequest(const QJsonValue &infoData, const JsonApiCommand& cmd)
@@ -1621,7 +1623,7 @@ void JsonAPI::sendNewRequest(const QJsonValue &infoData, const QString &command,
request["info"] = infoData;
emit callbackMessage(request);
emit callbackReady(request);
}
void JsonAPI::sendNoAuthorization(const JsonApiCommand& cmd)

View File

@@ -282,22 +282,43 @@ QStringList JsonCallbacks::getSubscribedCommands() const
}
void JsonCallbacks::doCallback(Subscription::Type cmd, const QVariant& data)
{
if (data.userType() == QMetaType::QJsonArray)
{
doCallback(cmd, data.toJsonArray());
}
else
{
doCallback(cmd, data.toJsonObject());
}
}
void JsonCallbacks::doCallback(Subscription::Type cmd, const QJsonArray& data)
{
QJsonObject obj;
obj["command"] = Subscription::toString(cmd);
if (Subscription::isInstanceSpecific(cmd))
{
obj["instance"] = _hyperion->getInstanceIndex();
obj.insert("instance", _hyperion->getInstanceIndex());
}
obj.insert("data", data);
if (data.userType() == QMetaType::QJsonArray) {
obj["data"] = data.toJsonArray();
} else {
obj["data"] = data.toJsonObject();
emit callbackReady(obj);
}
void JsonCallbacks::doCallback(Subscription::Type cmd, const QJsonObject& data)
{
QJsonObject obj;
obj["command"] = Subscription::toString(cmd);
if (Subscription::isInstanceSpecific(cmd))
{
obj.insert("instance", _hyperion->getInstanceIndex());
}
obj.insert("data", data);
emit newCallback(obj);
emit callbackReady(obj);
}
void JsonCallbacks::handleComponentState(hyperion::Components comp, bool state)
@@ -306,7 +327,7 @@ void JsonCallbacks::handleComponentState(hyperion::Components comp, bool state)
data["name"] = componentToIdString(comp);
data["enabled"] = state;
doCallback(Subscription::ComponentsUpdate, QVariant(data));
doCallback(Subscription::ComponentsUpdate, data);
}
void JsonCallbacks::handlePriorityUpdate(int currentPriority, const PriorityMuxer::InputsMap& activeInputs)
@@ -315,7 +336,7 @@ void JsonCallbacks::handlePriorityUpdate(int currentPriority, const PriorityMuxe
data["priorities"] = JsonInfo::getPrioritiestInfo(currentPriority, activeInputs);
data["priorities_autoselect"] = _hyperion->sourceAutoSelectEnabled();
doCallback(Subscription::PrioritiesUpdate, QVariant(data));
doCallback(Subscription::PrioritiesUpdate, data);
}
void JsonCallbacks::handleImageToLedsMappingChange(int mappingType)
@@ -323,7 +344,7 @@ void JsonCallbacks::handleImageToLedsMappingChange(int mappingType)
QJsonObject data;
data["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(mappingType);
doCallback(Subscription::ImageToLedMappingUpdate, QVariant(data));
doCallback(Subscription::ImageToLedMappingUpdate, data);
}
void JsonCallbacks::handleAdjustmentChange()
@@ -335,7 +356,7 @@ void JsonCallbacks::handleVideoModeChange(VideoMode mode)
{
QJsonObject data;
data["videomode"] = QString(videoMode2String(mode));
doCallback(Subscription::VideomodeUpdate, QVariant(data));
doCallback(Subscription::VideomodeUpdate, data);
}
#if defined(ENABLE_EFFECTENGINE)
@@ -343,29 +364,29 @@ void JsonCallbacks::handleEffectListChange()
{
QJsonObject effects;
effects["effects"] = JsonInfo::getEffects(_hyperion);
doCallback(Subscription::EffectsUpdate, QVariant(effects));
doCallback(Subscription::EffectsUpdate, effects);
}
#endif
void JsonCallbacks::handleSettingsChange(settings::type type, const QJsonDocument& data)
{
QJsonObject dat;
QJsonObject obj;
if(data.isObject()) {
dat[typeToString(type)] = data.object();
obj[typeToString(type)] = data.object();
} else {
dat[typeToString(type)] = data.array();
obj[typeToString(type)] = data.array();
}
doCallback(Subscription::SettingsUpdate, QVariant(dat));
doCallback(Subscription::SettingsUpdate, obj);
}
void JsonCallbacks::handleLedsConfigChange(settings::type type, const QJsonDocument& data)
{
if(type == settings::LEDS)
{
QJsonObject dat;
dat[typeToString(type)] = data.array();
doCallback(Subscription::LedsUpdate, QVariant(dat));
QJsonObject obj;
obj[typeToString(type)] = data.array();
doCallback(Subscription::LedsUpdate, obj);
}
}
@@ -385,7 +406,7 @@ void JsonCallbacks::handleTokenChange(const QVector<AuthManager::AuthDefinition>
sub["last_use"] = entry.lastUse;
arr.push_back(sub);
}
doCallback(Subscription::TokenUpdate, QVariant(arr));
doCallback(Subscription::TokenUpdate, arr);
}
void JsonCallbacks::handleLedColorUpdate(const std::vector<ColorRgb> &ledColors)
@@ -393,13 +414,16 @@ void JsonCallbacks::handleLedColorUpdate(const std::vector<ColorRgb> &ledColors)
QJsonObject result;
QJsonArray leds;
for (const auto &color : ledColors)
// Avoid copying by appending RGB values directly
for (const auto& color : ledColors)
{
leds << QJsonValue(color.red) << QJsonValue(color.green) << QJsonValue(color.blue);
leds.append(QJsonValue(color.red));
leds.append(QJsonValue(color.green));
leds.append(QJsonValue(color.blue));
}
result["leds"] = leds;
doCallback(Subscription::LedColorsUpdate, QVariant(result));
doCallback(Subscription::LedColorsUpdate, result);
}
void JsonCallbacks::handleImageUpdate(const Image<ColorRgb> &image)
@@ -413,7 +437,7 @@ void JsonCallbacks::handleImageUpdate(const Image<ColorRgb> &image)
QJsonObject result;
result["image"] = "data:image/jpg;base64," + QString(byteArray.toBase64());
doCallback(Subscription::ImageUpdate, QVariant(result));
doCallback(Subscription::ImageUpdate, result);
}
void JsonCallbacks::handleLogMessageUpdate(const Logger::T_LOG_MESSAGE &msg)
@@ -445,7 +469,7 @@ void JsonCallbacks::handleLogMessageUpdate(const Logger::T_LOG_MESSAGE &msg)
}
result.insert("messages", messageArray);
doCallback(Subscription::LogMsgUpdate, QVariant(result));
doCallback(Subscription::LogMsgUpdate, result);
}
void JsonCallbacks::handleEventUpdate(const Event &event)
@@ -454,6 +478,6 @@ void JsonCallbacks::handleEventUpdate(const Event &event)
result["event"] = eventToString(event);
doCallback(Subscription::EventUpdate, QVariant(result));
doCallback(Subscription::EventUpdate, result);
}

View File

@@ -13,7 +13,13 @@
// python utils
#include <python/PythonProgram.h>
Effect::Effect(Hyperion* hyperion, int priority, int timeout, const QString& script, const QString& name, const QJsonObject& args, const QString& imageData)
// Constants
namespace {
int DEFAULT_MAX_UPDATE_RATE_HZ { 200 };
} //End of constants
Effect::Effect(Hyperion *hyperion, int priority, int timeout, const QString &script, const QString &name, const QJsonObject &args, const QString &imageData)
: QThread()
, _hyperion(hyperion)
, _priority(priority)
@@ -26,7 +32,8 @@ Effect::Effect(Hyperion* hyperion, int priority, int timeout, const QString& scr
, _endTime(-1)
, _interupt(false)
, _imageSize(hyperion->getLedGridSize())
, _image(_imageSize, QImage::Format_ARGB32_Premultiplied)
, _image(_imageSize,QImage::Format_ARGB32_Premultiplied)
, _lowestUpdateIntervalInSeconds(1/static_cast<double>(DEFAULT_MAX_UPDATE_RATE_HZ))
{
_colors.resize(_hyperion->getLedCount());
_colors.fill(ColorRgb::BLACK);

View File

@@ -184,6 +184,7 @@ PyMethodDef EffectModule::effectMethods[] = {
{"imageCOffset" , EffectModule::wrapImageCOffset , METH_VARARGS, "Add offset to the coordinate system"},
{"imageCShear" , EffectModule::wrapImageCShear , METH_VARARGS, "Shear of coordinate system by the given horizontal/vertical axis"},
{"imageResetT" , EffectModule::wrapImageResetT , METH_NOARGS, "Resets all coords modifications (rotate,offset,shear)"},
{"lowestUpdateInterval" , EffectModule::wrapLowestUpdateInterval , METH_NOARGS, "Gets the lowest permissible interval time in seconds"},
{NULL, NULL, 0, NULL}
};
@@ -1106,3 +1107,10 @@ PyObject* EffectModule::wrapImageResetT(PyObject* self, PyObject* args)
getEffect()->_painter->resetTransform();
Py_RETURN_NONE;
}
PyObject* EffectModule::wrapLowestUpdateInterval(PyObject* self, PyObject* args)
{
qDebug() << "_lowestUpdateIntervalInSeconds: " << getEffect()->_lowestUpdateIntervalInSeconds;
return Py_BuildValue("d", getEffect()->_lowestUpdateIntervalInSeconds);
}

View File

@@ -49,6 +49,13 @@
#include <boblightserver/BoblightServer.h>
#endif
// Constants
namespace {
constexpr std::chrono::milliseconds DEFAULT_MAX_IMAGE_EMISSION_INTERVAL{ 40 }; // 25 Hz
constexpr std::chrono::milliseconds DEFAULT_MAX_RAW_LED_DATA_EMISSION_INTERVAL{ 25 }; // 40 Hz
constexpr std::chrono::milliseconds DEFAULT_MAX_LED_DEVICE_DATA_EMISSION_INTERVAL{ 5 }; // 200 Hz
} //End of constants
Hyperion::Hyperion(quint8 instance)
: QObject()
, _instIndex(instance)
@@ -75,6 +82,12 @@ Hyperion::Hyperion(quint8 instance)
#if defined(ENABLE_BOBLIGHT_SERVER)
, _boblightServer(nullptr)
#endif
, _lastImageEmission(0)
, _lastRawLedDataEmission(0)
, _lastLedDeviceDataEmission(0)
, _imageEmissionInterval(DEFAULT_MAX_IMAGE_EMISSION_INTERVAL)
, _rawLedDataEmissionInterval(DEFAULT_MAX_RAW_LED_DATA_EMISSION_INTERVAL)
, _ledDeviceDataEmissionInterval(DEFAULT_MAX_LED_DEVICE_DATA_EMISSION_INTERVAL)
{
qRegisterMetaType<ComponentList>("ComponentList");
@@ -109,6 +122,8 @@ void Hyperion::start()
// handle hwLedCount
_hwLedCount = getSetting(settings::DEVICE).object()["hardwareLedCount"].toInt(getLedCount());
// Initialize colororder vector
for (const Led& led : _ledString.leds())
{
@@ -180,6 +195,15 @@ void Hyperion::start()
connect(GlobalSignals::getInstance(), &GlobalSignals::setGlobalColor, this, &Hyperion::setColor);
connect(GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage, this, &Hyperion::setInputImage);
// Limit LED data emission, if high rumber of LEDs configured
_rawLedDataEmissionInterval = (_ledString.leds().size() > 1000) ? 2 * DEFAULT_MAX_RAW_LED_DATA_EMISSION_INTERVAL : DEFAULT_MAX_RAW_LED_DATA_EMISSION_INTERVAL;
_ledDeviceDataEmissionInterval = (_hwLedCount > 1000) ? 2 * DEFAULT_MAX_LED_DEVICE_DATA_EMISSION_INTERVAL : DEFAULT_MAX_LED_DEVICE_DATA_EMISSION_INTERVAL;
// Set up timers to throttle specific signals
_imageTimer.start(); // Start the elapsed timer for image signal throttling
_rawLedDataTimer.start(); // Start the elapsed timer for rawLedColors throttling
_ledDeviceDataTimer.start(); // Start the elapsed timer for LED-Device data throttling
// if there is no startup / background effect and no sending capture interface we probably want to push once BLACK (as PrioMuxer won't emit a priority change)
update();
@@ -267,6 +291,7 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co
std::vector<ColorRgb> color(_ledString.leds().size(), ColorRgb{0,0,0});
_ledBuffer = color;
_ledStringColorOrder.clear();
for (const Led& led : _ledString.leds())
{
@@ -276,6 +301,10 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co
// handle hwLedCount update
_hwLedCount = getSetting(settings::DEVICE).object()["hardwareLedCount"].toInt(getLedCount());
// Limit LED data emission, if high rumber of LEDs configured
_rawLedDataEmissionInterval = (_ledString.leds().size() > 1000) ? 2 * DEFAULT_MAX_RAW_LED_DATA_EMISSION_INTERVAL : DEFAULT_MAX_RAW_LED_DATA_EMISSION_INTERVAL;
_ledDeviceDataEmissionInterval = (_hwLedCount > 1000) ? 2 * DEFAULT_MAX_LED_DEVICE_DATA_EMISSION_INTERVAL : DEFAULT_MAX_LED_DEVICE_DATA_EMISSION_INTERVAL;
// change in leds are also reflected in adjustment
delete _raw2ledAdjustment;
_raw2ledAdjustment = hyperion::createLedColorsAdjustment(static_cast<int>(_ledString.leds().size()), getSetting(settings::COLOR).object());
@@ -672,7 +701,14 @@ void Hyperion::update()
Image<ColorRgb> image = priorityInfo.image;
if (image.width() > 1 || image.height() > 1)
{
emit currentImage(image);
_imageEmissionInterval = (image.width() > 1280) ? 2 * DEFAULT_MAX_IMAGE_EMISSION_INTERVAL : DEFAULT_MAX_IMAGE_EMISSION_INTERVAL;
// Throttle the emission of currentImage(image) signal
qint64 elapsedImageEmissionTime = _imageTimer.elapsed();
if (elapsedImageEmissionTime - _lastImageEmission >= _imageEmissionInterval.count())
{
_lastImageEmission = elapsedImageEmissionTime;
emit currentImage(image); // Emit the image signal at the controlled rate
}
_ledBuffer = _imageProcessor->process(image);
}
else
@@ -692,9 +728,15 @@ void Hyperion::update()
}
}
// emit rawLedColors before transform
emit rawLedColors(_ledBuffer);
// Throttle the emission of rawLedColors(_ledBuffer) signal
qint64 elapsedRawLedDataEmissionTime = _rawLedDataTimer.elapsed();
if (elapsedRawLedDataEmissionTime - _lastRawLedDataEmission >= _rawLedDataEmissionInterval.count())
{
_lastRawLedDataEmission = elapsedRawLedDataEmissionTime;
emit rawLedColors(_ledBuffer); // Emit the rawLedColors signal at the controlled rate
}
// Start transformations
_raw2ledAdjustment->applyAdjustment(_ledBuffer);
int i = 0;
@@ -740,7 +782,13 @@ void Hyperion::update()
// Smoothing is disabled
if (! _deviceSmooth->enabled())
{
emit ledDeviceData(_ledBuffer);
// Throttle the emission of LED-Device data signal
qint64 elapsedLedDeviceDataEmissionTime = _ledDeviceDataTimer.elapsed();
if (elapsedLedDeviceDataEmissionTime - _lastLedDeviceDataEmission >= _ledDeviceDataEmissionInterval.count())
{
_lastLedDeviceDataEmission = elapsedLedDeviceDataEmissionTime;
emit ledDeviceData(_ledBuffer);
}
}
else
{

View File

@@ -124,13 +124,11 @@
"ledshoriz": {
"type": "integer",
"minimum": 0,
"maximum": 50,
"default": 0
},
"ledsvert": {
"type": "integer",
"minimum": 0,
"maximum": 50,
"default": 0
},
"cabling": {

View File

@@ -1,6 +1,7 @@
// project includes
#include "JsonClientConnection.h"
#include <api/JsonAPI.h>
#include <api/JsonCallbacks.h>
// qt inc
#include <QTcpSocket>
@@ -17,8 +18,9 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket, bool localConnect
// create a new instance of JsonAPI
_jsonAPI = new JsonAPI(socket->peerAddress().toString(), _log, localConnection, this);
// get the callback messages from JsonAPI and send it to the client
connect(_jsonAPI, &JsonAPI::callbackMessage, this , &JsonClientConnection::sendMessage);
connect(_jsonAPI, &JsonAPI::callbackReady, this , &JsonClientConnection::sendMessage);
connect(_jsonAPI, &JsonAPI::forceClose, this , [&](){ _socket->close(); } );
connect(_jsonAPI->getCallBack().get(), &JsonCallbacks::callbackReady, this, &JsonClientConnection::sendMessage);
_jsonAPI->initialize();
}
@@ -47,7 +49,8 @@ void JsonClientConnection::readRequest()
qint64 JsonClientConnection::sendMessage(QJsonObject message)
{
QJsonDocument writer(message);
QByteArray data = writer.toJson(QJsonDocument::Compact) + "\n";
QByteArray data = writer.toJson(QJsonDocument::Compact);
data.append('\n');
if (!_socket || (_socket->state() != QAbstractSocket::ConnectedState)) return 0;
return _socket->write(data.data(), data.size());

View File

@@ -5,6 +5,7 @@
#include "QtHttpClientWrapper.h"
#include <api/JsonAPI.h>
#include <api/JsonCallbacks.h>
WebJsonRpc::WebJsonRpc(QtHttpRequest* request, QtHttpServer* server, bool localConnection, QtHttpClientWrapper* parent)
: QObject(parent)
@@ -14,8 +15,10 @@ WebJsonRpc::WebJsonRpc(QtHttpRequest* request, QtHttpServer* server, bool localC
{
const QString client = request->getClientInfo().clientAddress.toString();
_jsonAPI = new JsonAPI(client, _log, localConnection, this, true);
connect(_jsonAPI, &JsonAPI::callbackMessage, this, &WebJsonRpc::handleCallback);
connect(_jsonAPI, &JsonAPI::callbackReady, this, &WebJsonRpc::sendCallbackMessage);
connect(_jsonAPI, &JsonAPI::forceClose, [&]() { _wrapper->closeConnection(); _stopHandle = true; });
connect(_jsonAPI->getCallBack().get(), &JsonCallbacks::callbackReady, this, &WebJsonRpc::sendCallbackMessage);
_jsonAPI->initialize();
}
@@ -31,7 +34,7 @@ void WebJsonRpc::handleMessage(QtHttpRequest* request)
}
}
void WebJsonRpc::handleCallback(QJsonObject obj)
void WebJsonRpc::sendCallbackMessage(QJsonObject obj)
{
// guard against wrong callbacks; TODO: Remove when JSONAPI is more solid
if(!_unlocked) return;

View File

@@ -26,5 +26,5 @@ private:
bool _unlocked = false;
private slots:
void handleCallback(QJsonObject obj);
void sendCallbackMessage(QJsonObject obj);
};

View File

@@ -4,6 +4,7 @@
#include <hyperion/Hyperion.h>
#include <api/JsonAPI.h>
#include <api/JsonCallbacks.h>
#include <QTcpSocket>
#include <QtEndian>
@@ -24,9 +25,11 @@ WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, bool
// Json processor
_jsonAPI.reset(new JsonAPI(client, _log, localConnection, this));
connect(_jsonAPI.get(), &JsonAPI::callbackMessage, this, &WebSocketClient::sendMessage);
connect(_jsonAPI.get(), &JsonAPI::callbackReady, this, &WebSocketClient::sendMessage);
connect(_jsonAPI.get(), &JsonAPI::forceClose, this,[this]() { this->sendClose(CLOSECODE::NORMAL); });
connect(_jsonAPI->getCallBack().get(), &JsonCallbacks::callbackReady, this, &WebSocketClient::sendMessage);
connect(this, &WebSocketClient::handleMessage, _jsonAPI.get(), &JsonAPI::handleMessage);
Debug(_log, "New connection from %s", QSTRING_CSTR(client));