Files
qt5-legatus-legacy/Lts/Application/OpApp/main.cpp
T
2026-08-13 18:33:10 +02:00

524 lines
19 KiB
C++
Executable File

/**
* @RDE_HEADER@
* main for OperatorApplication
* $Id$
* $HeadURL$
* $Author$
*/
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_manager.h>
#include <LtsAppManager/lts_app_manager.h>
#include <LtsPluginManager/lts_plugin_loader.h>
#include <LtsSession/lts_session_data.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <LtsMultiMonitor/lts_multi_monitor.h>
#include <StyleManager/style_manager.h>
#include <LtsWidgets/lts_global_wait_dialog.h>
#include <LtsWidgets/lts_splash.h>
#include <LtsWidgets/lts_combo_box.h>
#include <LtsProjectionWorkstation/lts_projection_workstation.h>
#include <LtsUtilsSettings/settings.h>
#include <nglapplication/nglapplication.h>
#include <nglapplication/nglloggerhandler.h>
#include <nglsettings/nglsettings_settings.h>
#include <ngltranslator/ngltranslator.h>
#include <NGLLogger/INGLLogger.h>
#include <rti/config/Logger.hpp>
#include <QPixmap>
#include <QProcess>
#include <QDir>
#include <QString>
#include <QMessageBox>
#include <QSurfaceFormat>
#include <QScrollerProperties>
#include <QInputDialog>
#include <QNetworkProxy>
#include <QTimer>
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser(ProvideCommandLineParameterOptions(CreateDomainIdFromIp | DomainId | ExerciseId | TcpConnectionUrl))
{
addOption({{QStringLiteral("a"), QStringLiteral("application")},
QStringLiteral("Specify the initial application to start (Default: Situation [Demo / Dev - otherwise NOT SPECIFIED => starts AppChooser]"), QStringLiteral("Application"),
QStringLiteral("Situation")
}); // TODO: remove default on release
addOption({{QStringLiteral("b"), QStringLiteral("basePlugin")},
QStringLiteral("Base Project Plugin Iid to start the application with (Default: <not specified>)"), QStringLiteral("Base Project Plugin Iid"),
QString()
}); // TODO: remove default on release
addOption({{QStringLiteral("s"), QStringLiteral("stylePlugin")},
QStringLiteral("Style Plugin to start the application with (Default: rme.light)"), QStringLiteral("Style Plugin Iid"),
QStringLiteral("rme.light")
});
addOption({{QStringLiteral("u"), QStringLiteral("username")},
QStringLiteral("Preset Username to skip login (also specify -p !)"),
QStringLiteral("username")
});
addOption({{QStringLiteral("x"), QStringLiteral("tabletmode")},
QStringLiteral("Enforce Tablet Mode"),
QStringLiteral("false")
});
addOption({{QStringLiteral("w"), QStringLiteral("workstationmode")},
QStringLiteral("Enforce Workstation Mode"),
QStringLiteral("false")
});
addOption({{QStringLiteral("q"), QStringLiteral("qos_file")},
QStringLiteral("Define QoS Profile "),
QStringLiteral("qos_file")
});
addOption({{QStringLiteral("p"), QStringLiteral("password")},
QStringLiteral("Preset Password to skip login (also specify -u !)"),
QStringLiteral("password")
});
addOption({{QStringLiteral("k"), QStringLiteral("skipScreen")},
QStringLiteral("Tell the Multi Monitor mode to not put anything on the screen with id n (example: 1 (skips entire screen 1)) - or on sub screen 0 or 1 (left or right) for ultra wide screens (example: 1.1 (skips the right side of screen 1)) (Default: (none))"),
QStringLiteral("screen id"),
});
addOption({{QStringLiteral("m"), QStringLiteral("multi-monitor-mode")},
QStringLiteral("Use the Multi Monitor mode to place Base Layouts on Screens in Fullscreen")
});
#ifdef LTS_DEBUG
addOption({QStringLiteral("dbgmmm"),
QStringLiteral("Start the multi-monitor-mode with reduced size for use on development maschines")
});
#endif
addOption({{QStringLiteral("r"), QStringLiteral("replayId")},
QStringLiteral("Default: -1"),
QStringLiteral("Replay ID"),
QStringLiteral("-1")
});
addOption({{QStringLiteral("l"), QStringLiteral("lang")},
QStringLiteral("Language iso code to use (de_DE, en_GB, en_US)"),
QStringLiteral("<locale iso code>")
});
addOption({QStringLiteral("ignore-plugins"),
QStringLiteral("Ignore these plugins, which otherwise would be loaded, "
"specified by apps.json of the selected Base Plugin. "
"Example: \"--ignore-plugins data:avm,data:bdv,gui:avm\""),
QStringLiteral("ignore-plugins") // <= value
});
addOption({QStringLiteral("projection"),
QStringLiteral("Application is started on an Projection Workstation."),
});
addOption({QStringLiteral("check-requirements"),
QStringLiteral("Lets the Plugin Loader check if every App's requirements are met before starting"),
});
addOption({QStringLiteral("enable-experimental-web-platform-features"),
QString(),
});
addOption({QStringLiteral("script"),
QStringLiteral("Id of a script. The script has to exist in the given exercise."),
QStringLiteral("ScriptId"),
QStringLiteral("0")
});
addOption({QStringLiteral("place-script-screens"),
QStringLiteral("Let the screen 1 and screen 2 of the script-editor be placed automatically on startup.")
});
m_additionalHelpStr =
QStringLiteral("\n\n\e[33m example OWS : %1 -m -a0 -d1 -b legatus -t EXCTDS01:7400 -l de_DE \n\n\e[0m").arg(QCoreApplication::arguments().at(0));
// fprintf(stdout, "\n\n\e[33m example OWS : %s -m -a0 -d1 -b legatus -t EXCTDS01:7400 -l de_DE \n\n\e[0m", QCoreApplication::arguments().at(0).toLatin1().data());
}
QString operatorName() const { return value(QStringLiteral("u")); }
QString password() const { return value(QStringLiteral("p")); }
int replayId() const { return value(QStringLiteral("r")).toInt(); }
QString projectPluginIid() const { return value(QStringLiteral("b")); }
QString stylePluginIid() const { return value(QStringLiteral("s")); }
int applicationId() const { bool idk = false; int id = value(QStringLiteral("a")).toInt(&idk); if (!idk) { return -1; } return id; }
bool multiMonitorMode() const { return isSet(QStringLiteral("m")); }
bool isProjectionWorkstation() const { return isSet(QStringLiteral("projection")); }
bool isForcedTabletMode() const { return isSet(QStringLiteral("x")); }
bool isForcedWSMode() const { return isSet(QStringLiteral("w")); }
QString langIsoCode() const { return value(QStringLiteral("l")); }
QStringList ignoredPlugins() const { return value(QStringLiteral("ignore-plugins")).split(QLatin1Char(','), QString::SkipEmptyParts); }
bool checkRequirements() const { return isSet(QStringLiteral("check-requirements")); }
QString skipScreen() const { return value(QStringLiteral("k")); }
QString qosFile() const { return value(QStringLiteral("q")).size() > 0 ? value(QStringLiteral("q")) : QString(); }
int scriptId() const { return value(QStringLiteral("script")).toInt(); }
bool placeScriptScreens() const { return isSet(QStringLiteral("place-script-screens")); }
#ifdef LTS_DEBUG
bool debugMultiMonitorMode() const { return isSet(QStringLiteral("dbgmmm")); }
#endif
};
// ------------------------------------------------------------------
int main(int argc, char **argv) // TODO: json strings to lower
{
qsrand(time(nullptr));
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
QScrollerProperties props;
props.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
props.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
QScrollerProperties::setDefaultScrollerProperties(props);
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
//LtsApp app(argc, argv);
NGLApplication app(argc, argv);
app.createLogger("opApp");
QApplication::setQuitOnLastWindowClosed(false); // we are cool enough to handle it ourselves 8)
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QNetworkProxy::setApplicationProxy(QNetworkProxy()); // do NOT use proxies anywhere!
NGL::LoggerHandler *lh = NGL::LoggerHandler::getLoggerHandler();
if (lh)
{
const QByteArray postfix = lh->loggerNamePostfix().toLocal8Bit() + "_dds_";
const QByteArray prefix = lh->loggerNamePrefix().toLocal8Bit();
rti::config::Logger &ddsLogger = rti::config::Logger::instance();
//ddsLogger.output_file(fdopen(STDOUT_FILENO, "w"));
ddsLogger.output_file_set(postfix.data(), prefix.data(), 10 * 1024 * 1024, 10);
ddsLogger.verbosity(rti::config::Verbosity_def::WARNING);
//ddsLogger.verbosity_by_category(rti::config::LogCategory_def::COMMUNICATION, rti::config::Verbosity_def::STATUS_ALL);
}
// TODO: replace all exit(x) with qApp.quit() and an error msg
LtsWidgets::LtsGlobalWaitDialog::getInstance()->setVisible(false); // avoid wait screen, we have the splash for this reason!
#ifdef __linux__
// svcSituationRep - pgrep/pkill length limitation
QProcess::startDetached(QLatin1String("pkill -9 svcSituationRep")); // kill it!
QProcess::startDetached(QLatin1String("pkill -9 ffmpeg")); // otherwise a stray ffmpeg may record until the the disk is full
#elif _WIN32
QProcess::startDetached(QLatin1String("taskkill /im svcSituationReplay /f"));
#endif
// Parse Options
CmdLineParser options;
if (!options.init())
return -1;
// Set Translation
QString langIsoCode = options.langIsoCode();
if (langIsoCode.isEmpty())
langIsoCode = QLocale::system().name();
NGL::setTranslationLocale(langIsoCode.toLatin1());
// Set Style
QString stylePluginName = options.stylePluginIid();
if (stylePluginName.isEmpty())
{
ERROR_LOG(app.baseLogger()) << "No Style Plugin Iid specified - abort.";
return -1;
}
// .. to keep some compatibility to old resource structure TODO: rm !
QDir::setSearchPaths(QLatin1String("icon"), QStringList(QLatin1String(":/style/icons/")));
QDir::setSearchPaths(QLatin1String("icon"), QStringList(QLatin1String(":/style/"))); // FIXME: does qt check for recursion ??
LtsUtils::StyleManager::getInstance()->loadStyle(stylePluginName);
// Load Plugins
LtsPluginManager::LtsPluginLoader *pluginLoader = LtsPluginManager::LtsPluginLoader::getInstance();
QString projectPluginName = options.projectPluginIid();
if (projectPluginName.isEmpty())
{
bool k;
projectPluginName = QInputDialog::getText(nullptr,
QCoreApplication::tr("No Project Plugin specified"),
QCoreApplication::tr("Please specify a Project Plugin:"), QLineEdit::Normal,
QString(), &k); // blocking, no event loop needed
if (!k || projectPluginName.isEmpty())
{
ERROR_LOG(app.baseLogger()) << "No Project Plugin Iid specified - abort.";
return -1;
}
}
// load project plugin
if (!pluginLoader->loadPlugins(LtsPluginManager::PluginType::PROJECT, nullptr, {projectPluginName}))
{
ERROR_LOG(app.baseLogger()) << "Error loading Project Plugin - abort.";
return -1;
}
LtsUtils::ProjectSettings::initFromFile(QStringLiteral(":project/project_settings.json"));
// Setup Application
if (options.multiMonitorMode())
LtsMultiMonitor::LtsMultiMonitor::getInstance()->setEnableMultiMonitorMode();
#ifdef LTS_DEBUG
if (options.debugMultiMonitorMode())
LtsMultiMonitor::LtsMultiMonitor::getInstance()->setDebugMultiMonitorMode();
#endif
// show splash
LtsWidgets::Splash::getInstance()->show();
LtsAppDescriptor::App applicationId = LtsAppDescriptor::LtsAppDescriptor::appFromInteger(options.applicationId());
int exerciseId = options.exerciseId(); // -1 = invalid, 0 = admin, >0 = exercise
int replayId = options.replayId(); // -1 = invalid, TODO: comment doku
int scriptId = options.scriptId(); // <0 = invalid, 0 = default = ignore, >0 script
QString operatorName = options.operatorName();
QString password = options.password();
QStringList qos_file = QStringList(options.qosFile());
// TODO: log4cxx/NGLLlogger does not flush here !! messages not seen !
if (scriptId < 0)
{
ERROR_LOG(app.baseLogger()) << "Given scriptId " << scriptId << " is invalid - abort.";
return -1;
}
if (scriptId > 0 && applicationId != LtsAppDescriptor::App::Scripts)
{
ERROR_LOG(app.baseLogger()) << "ScriptId set but given app is not Scripts - abort.";
return -1;
}
if (scriptId > 0 && exerciseId < 1)
{
ERROR_LOG(app.baseLogger()) << "ScriptId set but no exercise is given - abort.";
return -1;
}
if (scriptId > 0 && exerciseId > 0 && applicationId == LtsAppDescriptor::App::Scripts)
{
LtsSessionData::LtsSessionData::getInstance()->setScriptId(scriptId);
}
if (options.placeScriptScreens() && applicationId != LtsAppDescriptor::App::Scripts)
{
ERROR_LOG(app.baseLogger()) << "PlaceScriptScreensn is set but given app is not Scripts - abort.";
return -1;
}
LtsSessionData::LtsSessionData::getInstance()->setPlaceScriptScreens(options.placeScriptScreens());
if (operatorName.isEmpty() != password.isEmpty()) // check for xor and warn user
{
ERROR_LOG(app.baseLogger()) << "Specify -u if -p is specified & vise versa - abort.";
return 1;
}
if (options.domainId() < 0)
{
ERROR_LOG(app.baseLogger()) << "Invalid configuration for domainId (" << options.domainId() << ") from LtsConfig - abort.";
QMessageBox::critical(nullptr, QCoreApplication::tr("Invalid Domain Id"), QCoreApplication::tr("Domain #%1 is not valid - abort.").arg(options.domainId()));
return -1;
}
// }
LtsSessionData::LtsSessionData *sessionData = LtsSessionData::LtsSessionData::getInstance();
sessionData->setDomainIdPreset(options.domainId());
sessionData->setRequestedAppId(applicationId);
if (options.useTcpConnection())
sessionData->setTcpConnectionUrl(options.tcpConnectionUrl());
if (options.checkRequirements())
sessionData->setDebugFlagValue(QStringLiteral("PluginManager.CheckAppRequirements"), true);
// load JSON app description file
QFile appsFile(QStringLiteral(":project/apps.json")); // TODO: convert Json => Binary Json
if (!appsFile.open(QFile::ReadOnly))
{
ERROR_LOG(app.baseLogger()) << "Could not read App Description File";
return -1;
}
QStringList lastErrors;
QString loginPluginIid;
LtsAppDescriptor::AppDescriptionVector appDescs = LtsAppDescriptor::LtsAppDescriptor::getAppDescriptionVector(appsFile.readAll(),
lastErrors,
options.ignoredPlugins(),
loginPluginIid);
if (loginPluginIid.isEmpty())
loginPluginIid = QStringLiteral("opm.login");
QString skipScreen = options.skipScreen();
int skipScreenId = -1;
int skipSubScreenId = -1;
if (!skipScreen.isEmpty())
{
QStringList skipScreenList(skipScreen.split(QLatin1Char('.'), QString::SkipEmptyParts));
int skipScreenListCount = skipScreenList.count();
if (skipScreenListCount > 2)
{
qWarning() << "invalid skip screen syntax:" << skipScreen;
return -1;
}
if (skipScreenListCount > 0)
{
bool k = false;
skipScreenId = skipScreenList.at(0).toInt(&k);
if (!k)
{
qWarning() << "invalid skip screen syntax:" << skipScreen;
return -1;
}
}
if (skipScreenListCount > 1)
{
bool k = false;
skipSubScreenId = skipScreenList.at(1).toInt(&k);
if (!k)
{
qWarning() << "invalid skip screen syntax:" << skipScreen;
return -1;
}
}
}
LtsMultiMonitor::LtsMultiMonitor::getInstance()->setSkipScreen(skipScreenId, skipSubScreenId);
appsFile.close();
if (!lastErrors.isEmpty())
{
QMessageBox::critical(nullptr, QString(), lastErrors.join(QStringLiteral("\r\n\r\n")));
// TODO: log precise msg !!
return -1;
}
if(options.isForcedTabletMode())
sessionData->setAsTablet();
else if (options.isForcedWSMode())
sessionData->setAsTablet(false);
else if (!QTouchDevice::devices().isEmpty())
sessionData->setAsTablet();
LtsWidgets::LtsComboBox::setUseSelectionDialog(sessionData->isTablet());
LtsAppManager::LtsAppManager *appManager = LtsAppManager::LtsAppManager::getInstance();
appManager->setAvailableApps(appDescs);
// Projection Specific Setup
if (options.isProjectionWorkstation())
{
if (operatorName.isEmpty() && password.isEmpty())
{
ERROR_LOG(app.baseLogger()) << "Projection-User & Password has to be specified when --projection option is active!";
return 1;
}
applicationId = LtsAppDescriptor::App::Situation;
LtsProjectionWorkstation::LtsProjectionWorkstation::getInstance()->setProjectionOperatorPreset(operatorName, password);
}
// Normal Setup
else
{
if (applicationId == LtsAppDescriptor::App::Login)
INFO_LOG(app.baseLogger()) << "No Application specified - starting with App Chooser.";
// TODO: replayid can not life without exerciseId
if (exerciseId >= 0 && replayId >= 0) // do not set both!
{
ERROR_LOG(app.baseLogger()) << "Use either -e (exercise) or -r (replay) - abort.";
return 1;
}
if (!operatorName.isEmpty() && !password.isEmpty())
{
sessionData->setOperatorPreset(operatorName, password); // if -u and -p (no Login window !)
}
if (exerciseId == 0) // Admin Session was requested explicitly
{
//sessionManager->setGlobalSessionVisible();
sessionData->setPartitionPreset(exerciseId, IDL::PartitionType::Global);
}
if (!qos_file.isEmpty()) // use defined qos file
{
//sessionManager->setGlobalSessionVisible();
DEBUG_LOG(app.baseLogger()) << "QOS -> using new file" << qos_file.join(QStringLiteral(","));
sessionData->setQosFile(qos_file);
}
if (exerciseId > 0) // non Admin => Exercise
sessionData->setPartitionPreset(exerciseId, IDL::PartitionType::Exercise);
if (replayId >= 0) // Replay
sessionData->setPartitionPreset(replayId, IDL::PartitionType::Replay);
}
appManager->setLoginPluginIid(loginPluginIid);
appManager->setMainApp(applicationId);
if (applicationId == LtsAppDescriptor::App::ThpSituationReplay)
{
LtsSessionData::LtsSessionData::getInstance()->setVisibleParties({});
LtsSessionData::LtsSessionData::getInstance()->setOperatorRights({-1});
QTimer::singleShot(0, appManager, [qos_file, appManager]()
{
LtsSession::DdsConnectionParams params;
params.domainId = 1;
params.exerciseId = 2;
params.partitionId = 2;
if (!qos_file.isEmpty())
params.qos_file = qos_file;
params.partitionType = IDL::PartitionType::Replay;
appManager->createApp(LtsAppDescriptor::App::Situation, params, {}, false, true);
} );
}
else
{
appManager->showLogin();
}
// Startup Application
STARTUP_LOG(app.baseLogger());
int iRet = app.exec();
SHUTDOWN_LOG(app.baseLogger());
return iRet;
}