On PoE sites the MIPI-DSI panel can lose its init on a supply dip while the RK3566 keeps running; the UI may also hang or exit. Three layers: - hardware watchdog: systemd RuntimeWatchdogSec=20s (system.conf.d) - GUI: SdWatchdog pings systemd per swapped frame (WATCHDOG_USEC=30s, enabled by the binary so older versions run under the same unit) and writes run/qt.frame; buttontask.service gets Restart=always, NotifyAccess=main, TimeoutAbortSec=10s - bt-displaywatch (root, buttontask-displaywatch.service): kernel DRM/DSI timeouts, eth link down->up (PoE dip hint), stale UI frames, restart storms -> panel re-init (stop UI, fb0 blank 4->0, start UI), repeated -> reboot (max 3/day, never during OTA); incident log in /var/log/buttontask/ Manual recovery: hold one finger 10 s in the bottom-left corner (panels are single-touch). Delivered by OTA via bt-update sync_system; the web service runs `bt-update sync-system` at start for devices updated by an older bt-update. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
75 lines
2.6 KiB
C++
75 lines
2.6 KiB
C++
#include <QGuiApplication>
|
|
#include <QQmlApplicationEngine>
|
|
#include <QQmlContext>
|
|
#include <QCommandLineParser>
|
|
#include <QFileInfo>
|
|
#include <QDir>
|
|
#include <QDebug>
|
|
|
|
#include "configmanager.h"
|
|
#include "buttonsmodel.h"
|
|
#include "buttoncontroller.h"
|
|
#include "settingscontainer.h"
|
|
#include "systeminfo.h"
|
|
#include "sensorsbridge.h"
|
|
#include "uiidlewatch.h"
|
|
#include "sdwatchdog.h"
|
|
#include "recoverygesture.h"
|
|
|
|
#include <QQuickWindow>
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
qputenv("QT_IM_MODULE", "qtvirtualkeyboard");
|
|
QGuiApplication app(argc, argv);
|
|
QCoreApplication::setApplicationName("ButtonTask");
|
|
QCoreApplication::setOrganizationName("ButtonTask");
|
|
QCoreApplication::setApplicationVersion("2.5");
|
|
|
|
QCommandLineParser parser;
|
|
parser.setApplicationDescription("ButtonTask v2 - configurable button panel");
|
|
parser.addHelpOption();
|
|
parser.addVersionOption();
|
|
QCommandLineOption configOpt(QStringList() << "c" << "config",
|
|
"Path to config.json",
|
|
"path");
|
|
parser.addOption(configOpt);
|
|
parser.process(app);
|
|
|
|
QString configPath = parser.value(configOpt);
|
|
if (configPath.isEmpty())
|
|
configPath = qEnvironmentVariable("BUTTONTASK_CONFIG");
|
|
if (configPath.isEmpty())
|
|
configPath = QCoreApplication::applicationDirPath() + "/config/config.json";
|
|
|
|
qInfo() << "Using config:" << configPath;
|
|
|
|
ConfigManager config(configPath);
|
|
ButtonsModel model(&config);
|
|
ButtonController controller(&config, &model);
|
|
SettingsContainer settings(&config);
|
|
SystemInfo systemInfo(&config);
|
|
SensorsBridge sensorsBridge;
|
|
UiIdleWatch uiIdle;
|
|
|
|
QQmlApplicationEngine engine;
|
|
engine.rootContext()->setContextProperty("config", &config);
|
|
engine.rootContext()->setContextProperty("buttonsModel", &model);
|
|
engine.rootContext()->setContextProperty("btnController", &controller);
|
|
engine.rootContext()->setContextProperty("settingsContainer", &settings);
|
|
engine.rootContext()->setContextProperty("systemInfo", &systemInfo);
|
|
engine.rootContext()->setContextProperty("sensorsBridge", &sensorsBridge);
|
|
engine.rootContext()->setContextProperty("uiIdle", &uiIdle);
|
|
|
|
engine.load(QUrl(QStringLiteral("qrc:/qml/Main.qml")));
|
|
if (engine.rootObjects().isEmpty())
|
|
return -1;
|
|
|
|
// Display/GUI watchdog (see bt-displaywatch): systemd ping per real frame
|
|
// + 10 s hold in the bottom-left corner to request a panel re-init.
|
|
SdWatchdog sdWatchdog(qobject_cast<QQuickWindow *>(engine.rootObjects().first()));
|
|
RecoveryGesture recoveryGesture;
|
|
|
|
return app.exec();
|
|
}
|