Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/Lts/Common/LtsPluginManager/lts_plugin_loader.cpp
T
torstenandCopilot 7a6be7474e Qt6 migration: Fix BUILD_RTI=OFF build errors in TrainingAreaBase
- training_area_telegrams.cpp: Fixed QJsonObject initializer syntax (Qt6 requires {{}} not ({}))
- training_area_telegrams.cpp: Fixed fromJson() methods to access m_* members directly
- training_area_telegrams.cpp: Fixed brace mismatches in multiple toJson() methods
- training_area_telegrams.h: Changed Header struct to public members for consistency
- tx_mixin.cpp: Fixed getEquipmentNumber() template for direct member access
- lts_coordinate.cpp: Added missing #include <iomanip>
- MIGRATION_STATUS.md: Added migration progress tracking document

C++17 constraint enforced due to RTI Connext DDS 7.7.0 incompatibility with C++20+

Build status: BUILD_RTI=OFF successful (0 errors, warnings only)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-23 20:49:23 +02:00

417 lines
13 KiB
C++

#include "lts_plugin_loader.h"
#include <LtsAppDescriptor/lts_app_descriptor.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QDirIterator>
#include <QtCore/QFileInfo>
#include <QtCore/QJsonObject>
#include <QMutableListIterator>
#include <QtCore/QPluginLoader>
#include <utility>
namespace LtsPluginManager
{
struct LtsPluginLoaderHandler
{
LtsPluginLoader ltsPluginLoader;
static NGL::INGLLogger& logger()
{
return NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsPluginLoader");
}
static void cleanupPluginLoader();
};
Q_GLOBAL_STATIC(LtsPluginLoaderHandler, s_ltsPluginLoader)
void LtsPluginLoaderHandler::cleanupPluginLoader()
{
if (s_ltsPluginLoader.exists())
s_ltsPluginLoader->ltsPluginLoader.unloadAllPlugins();
}
LtsPluginLoader* LtsPluginLoader::getInstance()
{
if (!s_ltsPluginLoader.exists())
qAddPostRoutine(&LtsPluginLoaderHandler::cleanupPluginLoader);
return &s_ltsPluginLoader->ltsPluginLoader;
}
LtsPluginLoader::~LtsPluginLoader()
{
unloadAllPlugins();
}
void LtsPluginLoader::unloadAllPlugins()
{
for (auto& pi : std::as_const(m_loadedPlugins))
{
QPluginLoader* pluginLoader = pi.pluginLoader;
delete pluginLoader->instance();
if (!pluginLoader->unload())
qWarning() << "~LtsPluginLoader: could not unload" << pi.pluginName << pluginLoader->errorString();
delete pluginLoader;
}
m_loadedPlugins.clear();
}
void LtsPluginLoader::checkPlugins(const QVector<LtsAppDescriptor::AppDescription>& appVector, QStringList& errors)
{
NGL::INGLLogger& logger = LtsPluginLoaderHandler::logger();
if (appVector.isEmpty())
{
errors += QStringLiteral("No Apps available");
return;
}
QVector<PluginInstance> dataPlugins;
QVector<PluginInstance> guiPlugins;
auto addDataPluginFn = [&](const PluginInstance& pi) {
dataPlugins.append(pi);
return true;
};
auto addGuiPluginFn = [&](const PluginInstance& pi) {
guiPlugins.append(pi);
return true;
};
if (!loadPlugins(PluginType::DATA, addDataPluginFn))
{
errors += QStringLiteral("failed to load data plugins");
return;
}
if (!loadPlugins(PluginType::GUI, addGuiPluginFn))
{
errors += QStringLiteral("failed to load gui plugins");
return;
}
auto checkDuplicateIidsFn = [&](const QVector<PluginInstance>& plugins, const QString& pluginType) {
typedef QMap<QString, QString> IidMap;
IidMap iids;
for (auto i3 = plugins.constBegin(); i3 != plugins.constEnd(); ++i3)
{
const PluginInstance& pin = *i3;
auto iidFound = iids.constFind(pin.pluginName);
if (iidFound != iids.constEnd())
errors += QStringLiteral("Iid duplicate for %1 plugin '%2'").arg(pluginType, pin.pluginName);
else
iids.insert(pin.pluginName, pin.fullFileName);
}
};
checkDuplicateIidsFn(dataPlugins, QStringLiteral("data"));
checkDuplicateIidsFn(guiPlugins, QStringLiteral("gui"));
QVector<PluginInstance> plugins(dataPlugins + guiPlugins);
for (auto i1 = appVector.constBegin(); i1 != appVector.constEnd(); ++i1)
{
const LtsAppDescriptor::AppDescription& app = *i1;
const QVector<LtsAppDescriptor::PluginInfo>& piVector = app.pluginInfos;
for (auto i2 = piVector.constBegin(); i2 != piVector.constEnd(); i2++)
{
const LtsAppDescriptor::PluginInfo& pi = *i2;
bool found = false;
for (auto i3 = plugins.constBegin(); i3 != plugins.constEnd(); ++i3)
{
const PluginInstance& pin = *i3;
if (pi.iid == pin.pluginName)
{
found = true;
break;
}
}
if (!found)
{
ERROR_LOG(logger) << "Plugin with iid '" << pi.iid << "' was requested but was not loaded!";
errors +=
QStringLiteral("App '%1' requires plugin '%2' that could not be found.").arg(app.appName, pi.iid);
}
}
}
return;
}
bool LtsPluginLoader::loadPlugins(PluginType requestedPluginType,
const std::function<bool(const PluginInstance&)>& loadPlugin,
const QStringList& requiredPlugins, const QString& specialTopicTypeFilter,
const QStringList& optionalPlugins)
{
NGL::INGLLogger& logger = LtsPluginLoaderHandler::logger();
Q_ASSERT(requestedPluginType != PluginType::UNKNOWN);
QStringList requiredPluginsList = requiredPlugins;
QStringList optionalPluginsList = optionalPlugins;
bool checkPluginsLists = (!requiredPluginsList.isEmpty()) || (!optionalPluginsList.isEmpty());
if (checkPluginsLists && !m_loadedPlugins.isEmpty())
{
{
QMutableListIterator<QString> i1(requiredPluginsList);
while (i1.hasNext())
{
const QString pluginName = i1.next().toLower();
QMap<PluginKey, PluginInstance>::iterator i2 =
m_loadedPlugins.find(PluginKey(requestedPluginType, pluginName));
if (i2 != m_loadedPlugins.end())
{
DEBUG_LOG(logger) << "Plugin " << pluginName << " already loaded, using that instance";
if (loadPlugin)
loadPlugin(i2.value());
i1.remove();
}
}
}
{
QMutableListIterator<QString> i1(optionalPluginsList);
while (i1.hasNext())
{
const QString pluginName = i1.next().toLower();
QMap<PluginKey, PluginInstance>::iterator i2 =
m_loadedPlugins.find(PluginKey(requestedPluginType, pluginName));
if (i2 != m_loadedPlugins.end())
{
DEBUG_LOG(logger) << "Plugin " << pluginName << " already loaded, using that instance";
if (loadPlugin)
loadPlugin(i2.value());
i1.remove();
}
}
}
}
QFileInfo libPathInfo(QCoreApplication::applicationDirPath() + QStringLiteral("/../lib/"));
QString libPath(libPathInfo.absolutePath());
if (!libPathInfo.exists())
{
libPathInfo.setFile(QCoreApplication::applicationFilePath());
libPath = libPathInfo.absolutePath();
}
#ifdef Q_OS_WIN
const QStringList nameFilter = QStringList(QStringLiteral("*Plugin*.dll"));
#else
const QStringList nameFilter = QStringList(QStringLiteral("*Plugin*.so"));
#endif
static const QStringList pluginTypes({QStringLiteral("data"), QStringLiteral("gui"), QStringLiteral("topic_type"),
QStringLiteral("project"), QStringLiteral("style"),
QStringLiteral("special")});
QDirIterator pluginDir(libPath, nameFilter, QDir::Files);
while (pluginDir.hasNext())
{
pluginDir.next();
const QString filename = pluginDir.fileName();
if (filename.contains(QStringLiteral("PluginInterface"), Qt::CaseSensitive))
continue;
const QString absoluteFilePath = pluginDir.filePath();
DEBUG_LOG(logger) << "File Path: " << absoluteFilePath;
QScopedPointer<QPluginLoader> pluginLoader(new QPluginLoader);
pluginLoader->setFileName(absoluteFilePath);
const QJsonObject jObj = pluginLoader->metaData();
QJsonObject::const_iterator iid = jObj.find(QStringLiteral("IID"));
if (iid == jObj.end())
{
DEBUG_LOG(logger) << "Plugin '" << filename << "' has no IID !";
continue;
}
const QString jsonIid = iid.value().toString().toLower();
const QStringList iidParameters = QString(jsonIid).split(QLatin1Char(':'));
DEBUG_LOG(logger) << "Plugin " << absoluteFilePath << " provides iid '" << jsonIid;
if (iidParameters.count() != 3)
{
WARNING_LOG(logger) << "Invalid iid " << jsonIid << " for plugin " << filename;
continue;
}
bool ok = false;
int pluginLoaderVersion = iidParameters.at(1).toInt(&ok);
if (!ok || pluginLoaderVersion != PLUGIN_LOADER_VERSION)
{
WARNING_LOG(logger) << "Invalid plugin loader version" << pluginLoaderVersion << "for plugin:" << filename;
continue;
}
PluginType pluginType = PluginType::UNKNOWN;
const QString pluginName = iidParameters.at(2);
const QString pluginTypeString = iidParameters.at(0);
switch (pluginTypes.indexOf(pluginTypeString))
{
case 0:
pluginType = PluginType::DATA;
break;
case 1:
pluginType = PluginType::GUI;
break;
case 2:
pluginType = PluginType::TOPIC_TYPE;
break;
case 3:
pluginType = PluginType::PROJECT;
break;
case 4:
pluginType = PluginType::STYLE;
break;
case 5:
pluginType = PluginType::SPECIAL;
break;
default:
break;
}
if (pluginType != requestedPluginType)
{
DEBUG_LOG(logger) << "Plugin provides not the requested type " << (int)requestedPluginType
<< " is: " << (int)pluginType;
continue;
}
if (pluginType == PluginType::SPECIAL && !specialTopicTypeFilter.isEmpty())
{
if (!pluginName.startsWith(specialTopicTypeFilter))
{
DEBUG_LOG(logger) << "Special plugin, but filter does not match: " << pluginName << " - "
<< specialTopicTypeFilter;
continue;
}
}
if (checkPluginsLists)
{
bool found = false;
{
QMutableListIterator<QString> i(requiredPluginsList);
while (i.hasNext())
{
const QString requiredPlugin = i.next().toLower();
if (pluginName == requiredPlugin)
{
found = true;
i.remove();
break;
}
}
}
if (!found)
{
for (auto i = optionalPluginsList.constBegin(); i != optionalPluginsList.constEnd(); i++)
{
const QString optionalPlugin = (*i).toLower();
if (pluginName == optionalPlugin)
{
found = true;
break;
}
}
}
if (!found)
{
DEBUG_LOG(logger) << "Plugin " << pluginName << " was not requested";
continue;
}
}
if (loadPlugin && !m_loadedPlugins.isEmpty())
{
QMap<PluginKey, PluginInstance>::iterator i =
m_loadedPlugins.find(PluginKey(requestedPluginType, pluginName));
if (i != m_loadedPlugins.end())
{
DEBUG_LOG(logger) << "Plugin " << pluginName << " already loaded, using that instance ("
<< i.value().plugin->metaObject()->className() << ")";
loadPlugin(i.value());
continue;
}
}
QObject* plugin = pluginLoader->instance();
if (!plugin)
{
ERROR_LOG(logger) << "Failed to load " << filename << " - " << pluginLoader->errorString();
continue;
}
PluginInstance pi;
pi.pluginName = pluginName;
pi.fileName = filename;
pi.fullFileName = absoluteFilePath;
pi.plugin = plugin;
pi.pluginLoader = pluginLoader.data();
if (loadPlugin && !loadPlugin(pi))
{
DEBUG_LOG(logger) << "Plugin " << pluginName << " unloaded";
delete pluginLoader->instance();
pluginLoader->unload();
continue;
}
pi.pluginLoader = pluginLoader.take();
m_loadedPlugins.insert(PluginKey(requestedPluginType, pluginName), pi);
DEBUG_LOG(logger) << "Loaded " << jsonIid << " - File: " << filename;
}
if (checkPluginsLists && !requiredPluginsList.isEmpty())
{
WARNING_LOG(logger) << "These plugins could not be found for Plugin Type " << (int)requestedPluginType << ": "
<< requiredPluginsList.join(QStringLiteral(", "));
return false;
}
return true;
}
} // namespace LtsPluginManager