Files
qt5-legatus-legacy/Lts/Proxies/TrainingAreaBase/main_handler.cpp
T
torstenandCopilot 3028302f7b Qt6 Migration: Merge latest Qt5 devel content and apply migration fixes
- Merged 292 changed files from dlbo snapshot
- Fixed IDL enum collisions (PortType, PdmEventType)
- Fixed Grantlee template for C++11 scoped enums
- Fixed CMake VERSION_GREATER syntax
- Added missing directories (THP, LtsRestClient, etc.)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-15 18:20:51 +00:00

532 lines
17 KiB
C++
Executable File

/**
* MainHandler for TetraRadio
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-14
*/
#include "main_handler.h"
#include "settings.h"
#include "logger.h"
#include "WebSocketInterface/Messages/reply_message.h"
#include "WebSocketInterface/json_keys.h"
#include <AssetManagement/asm_dtc.h>
#include <DDS/Core/pacemaker.h>
#include <ExerciseManagement/exm_dtc.h>
#include <LtsPluginManager/lts_plugin_loader.h>
#include <TrainingAreaBase/tab_record_replay_plugin.h>
#include <TrainingAreaBase/tab_record_replay_plugin_custom.h>
#include <nglsettings/nglsettings_connections.h>
#include <ngldb/nsqlquery.h>
#include <QUuid>
#include <QJsonDocument>
#include <QSqlDatabase>
namespace TrainingAreaBase
{
MainHandler::MainHandler(QObject *parent)
: QObject(parent)
, m_ddsHandler(nullptr)
, m_handlerWebSocket(nullptr)
, m_logger(getLogger("MainHandler"))
{}
MainHandler::~MainHandler()
{
LtsPluginManager::LtsPluginLoader::getInstance()->unloadAllPlugins();
m_interfaces.clear();
m_ifcConnectionState.clear();
m_rxHandler.reset();
m_txHandler.reset();
m_ftsHandler.reset();
m_posHandler.reset();
m_ppmHandler.reset();
m_pdmHandler.reset();
m_exmHandler.reset();
m_ismHandler.reset();
m_aesHandler.reset();
m_ddsHandler.reset();
m_dbHandler.reset();
}
bool MainHandler::init(int domainId, int exerciseId)
{
// initialize database
m_dbHandler = std::make_shared<DbHandler>();
if (!m_dbHandler->init(exerciseId, Settings::settings()))
return false;
// initialize DDS
m_ddsHandler = std::make_shared<DdsHandler>(domainId, exerciseId);
if (!m_ddsHandler->init())
return false;
m_exmHandler = std::make_shared<ExmHandler>(this);
connect(m_exmHandler.get(), &ExmHandler::exerciseChanged,
this, &MainHandler::onExerciseChanged);
connect(m_exmHandler.get(), &ExmHandler::exercise2RadioGroupChanged,
this, &MainHandler::onExercise2RadioGroupChanged);
m_exmHandler->init();
m_pdmHandler = std::make_shared<PdmHandler>(this);
if (!m_pdmHandler->init())
return false;
if (!m_pdmHandler->initializePdm())
return false;
m_ppmHandler = std::make_shared<PpmHandler>(this);
connect(m_ppmHandler.get(), &PpmHandler::position,
this, &MainHandler::onPositionChanged);
if (!m_ppmHandler->init())
return false;
if (exerciseId > 0)
{
m_ismHandler = std::make_shared<IsmHandler>(this);
if (!m_ismHandler->init())
return false;
/*
* Es gibt den Fall der Initialisierung, wenn der TAB Prozess neu gestartet wird.
* Ist ein ABG zu diesem Zeitpunkt angemeldet und wird danach ein anderer Teilnehmer mit dem selben ABG angemeldet,
* bleibt das Risiko bestehen, dass zwei Teilnehmer dasselbe ABG zum gleichen Zeitpunkt anzeigen.
*
*
NSqlQuery q(databaseConnection());
TrainingAreaBase::RecordReplayPluginCustom *tabRecordReplay;
tabRecordReplay = new TrainingAreaBase::RecordReplayPluginCustom;
tabRecordReplay->initDtc(nullptr, exerciseId);
TrainingAreaBase::RecordReplayPlugin recordReplay;
recordReplay.initDtc(nullptr, exerciseId);
if (!recordReplay.readParticipantSanitaryMaterialStockReportAt(q, QDateTime::currentMSecsSinceEpoch()))
{
ERROR_LOG(m_logger) << "Could not read ParticipantSanitaryMaterialStockReport";
// return false;
}
else
{
const auto &latestStock = tabRecordReplay->getParticipantSanitaryMaterialStockReportMap();
ERROR_LOG(m_logger) << "Starting to latest ParticipantSanitaryMaterialStock. Size: "
<< latestStock.size();
for (const auto &stock : latestStock)
{
ERROR_LOG(m_logger) << "Stock for equipment number " << stock.equipmentNumber();
if (stock.uniqueId() > 0 && !m_uniqueId2EquipmentNumber.contains(stock.uniqueId()))
m_uniqueId2EquipmentNumber.insert(stock.uniqueId(), stock.equipmentNumber());
}
}
tabRecordReplay->deleteLater();
*/
}
m_posHandler = std::make_shared<PositionHandler>(this);
if (!m_posHandler->init())
return false;
// to TrainingArea
m_txHandler = std::make_shared<TxMixin>();
// from DDS
m_ftsHandler = std::make_shared<RecvFromDdsFts>(this,
m_ddsHandler->ftsTab(),
m_txHandler);
if (exerciseId > 0)
{
m_aesHandler = std::make_shared<RecvFromDdsAes>(this,
m_txHandler);
if (!m_aesHandler->init())
return false;
}
// to DDS
m_rxHandler = std::make_shared<SendToDds>(this);
// to/from TrainingArea
auto cb = [&](const LtsPluginManager::PluginInstance &pi) { return loadPlugin(pi); };
LtsPluginManager::LtsPluginLoader::getInstance()->loadPlugins(LtsPluginManager::PluginType::SPECIAL,
cb,
QStringList(),
QStringLiteral("tab"));
if (m_interfaces.empty())
{
WARNING_LOG(m_logger) << "Could not load any training area plugin - can not send data to training area";
return false;
}
for (const std::shared_ptr<Plugin::Interface> &ifc : m_interfaces)
m_txHandler->addTxHandler(ifc->sendToTab());
// WebSocketServer
m_handlerWebSocket = std::make_shared<WebSocketInterface::WebSocketInterfaceHandler>(this);
DEBUG_LOG(m_logger) << "Reset WebSocketInterfaceHandler successful.";
if (!m_handlerWebSocket->init())
{
ERROR_LOG(m_logger) << "Initialisation WebSocketInterfaceHandler failed.";
return false;
}
m_ddsHandler->pacemaker()->addPort(m_handlerWebSocket->serverWebSocketPort(), Core::PortType::WebSocketServer, QStringLiteral("{\"id\":\"TAB\"}"));
DEBUG_LOG(m_logger) << "Initialisation WebSocketInterfaceHandler successful.";
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::Running);
return true;
}
bool MainHandler::useTetraScrambling() const
{
return Settings::useTetraScramling();
}
ProjectDataManagement::RadioGroup MainHandler::exerciseRadioGroup() const
{
if (exerciseId() == 0)
{
// for exercise 0 we have to look for a base grp
const QMap<int, ProjectDataManagement::RadioGroup> radioGrps = m_pdmHandler->radioGroups();
for (auto it = radioGrps.begin(); it != radioGrps.end(); ++it)
if (it.value().base_group())
return it.value();
return ProjectDataManagement::RadioGroup();
}
const auto ex = m_exmHandler->exercise(exerciseId());
if (ex.radioGroupId().is_set())
return m_pdmHandler->radioGroup(ex.radioGroupId().get());
return ProjectDataManagement::RadioGroup();
}
bool MainHandler::loadPlugin(const LtsPluginManager::PluginInstance &pi)
{
INFO_LOG(m_logger) << "Trying to load plugin '" << pi.fullFileName << "'";
Plugin::Plugin *tabPlugin = qobject_cast<Plugin::Plugin *>(pi.plugin);
if (!tabPlugin)
{
WARNING_LOG(m_logger) << "failed loading a training area plugin - not a TrainingAreaPlugin object!";
return false;
}
INFO_LOG(m_logger) << "... success";
std::shared_ptr<Plugin::Interface> ifc = tabPlugin->createInterface(this, m_rxHandler, m_txHandler, m_ddsHandler, Settings::settings());
if (!ifc || !ifc->init())
{
WARNING_LOG(m_logger) << "Could not initialize training area plugin" << pi.pluginName << pi.fileName;
return false;
}
connect(ifc.get(), &Plugin::Interface::connectionState, this, &MainHandler::onConnectionState);
connect(ifc.get(), &Plugin::Interface::directMessage, this, &MainHandler::onDirectMessage);
m_interfaces.push_back(ifc);
m_ccsMap = m_pdmHandler->dtc()->canComponentStrings();
m_bctMap = m_pdmHandler->dtc()->biteComponentTexts();
return true;
}
TetraIcd::ExerciseEnvironmentData MainHandler::exerciseEnvironmentData() const
{
TetraIcd::ExerciseEnvironmentData exData;
exData.exerciseId = exerciseId();
const auto &baseCoord = m_posHandler->baseCoordinate();
// 32UPD
const QString zone = baseCoord.mgrs(0, false);
if (zone.isEmpty())
return exData;
exData.utmZone = baseCoord.zone;
exData.grid = zone.mid(3, 2).toStdString();
exData.utmBand = zone.mid(2, 1).at(0).toLatin1();
exData.timeZone = m_exerciseData.defaultTimeZone();
return exData;
}
TetraIcd::ExerciseStateData MainHandler::exerciseStateData() const
{
TetraIcd::ExerciseStateData exData;
if (m_exerciseData.id() != exerciseId())
return exData;
exData.exerciseId = m_exerciseData.id();
exData.exerciseState = static_cast<TetraIcd::ExerciseStateData::ExerciseState>(m_exerciseData.state().underlying());
return exData;
}
TetraIcd::ExerciseEquipmentAssignmentComplete MainHandler::exerciseEquipmentAssignment() const
{
TetraIcd::ExerciseEquipmentAssignmentComplete eeac;
eeac.exerciseEquipmentAssignments = m_ppmHandler->exerciseEquipmentAssignment();
const auto exercises = m_exmHandler->exercise2RadioGroup();
const QMap<int, ProjectDataManagement::RadioGroup> radioGroups = m_pdmHandler->radioGroups();
for (const auto &it : exercises)
{
const ProjectDataManagement::RadioGroup rg = radioGroups.value(it.second);
if (rg.id() == 0)
{
WARNING_LOG(m_logger) << "Got a radio group id " << it.second << " for exercise " << it.first
<< " but could not find valid data for it - ignoring!";
continue;
}
TrainingAreaBase::TetraIcd::GroupIssiInformation gissi;
gissi.exerciseId = it.first;
gissi.serverIssi = rg.issi();
gissi.broadcastIssi1 = rg.group_issi1();
gissi.broadcastIssi2 = rg.group_issi2();
eeac.groupIssiInformation.push_back(std::move(gissi));
}
return eeac;
}
QVector<TetraIcd::TrainingAreaTelegram*> MainHandler::aesTelegrams() const
{
return m_aesHandler ? m_aesHandler->sentAesTelegrams() : QVector<TetraIcd::TrainingAreaTelegram*>();
}
QSqlDatabase MainHandler::databaseConnection() const
{
if (!m_dbHandler || !m_dbHandler->db())
return QSqlDatabase();
return m_dbHandler->db()->database();
}
QString MainHandler::databaseSchemaName() const
{
if (!m_dbHandler || !m_dbHandler->db())
return QString();
return m_dbHandler->db()->schemaName();
}
int MainHandler::domainId() const
{
if (!m_ddsHandler)
return 0;
if (!m_ddsHandler->ddsConnector())
return 0;
return m_ddsHandler->ddsConnector()->getDomainId();
}
void MainHandler::onExerciseChanged(const ExerciseManagement::Exercise &data, DataModification modType)
{
if (data.id() != exerciseId())
return;
m_exerciseData = data;
if (modType == DataModification::eModified)
m_txHandler->txData(exerciseStateData());
// update Interfaces
for (const std::shared_ptr<Plugin::Interface> &ifc : qAsConst(m_interfaces))
ifc->exerciseChanged();
}
void MainHandler::onExercise2RadioGroupChanged()
{
m_txHandler->txData(exerciseEquipmentAssignment());
}
void MainHandler::onConnectionState(const QString &name, bool connectionState)
{
auto itC = m_ifcConnectionState.find(name);
if (itC != m_ifcConnectionState.end())
{
if (itC.value() == connectionState)
return; // already send to DDS
itC.value() = connectionState;
}
else
{
m_ifcConnectionState.insert(name, connectionState);
}
DEBUG_LOG(m_logger) << "Publishing connection states: " << m_ifcConnectionState.size();
TrainingAreaBase::Connections connections;
connections.id(1);
for (auto it = m_ifcConnectionState.cbegin(); it != m_ifcConnectionState.cend(); ++it)
connections.connections().push_back(
TrainingAreaBase::ConnectionState(it.key().toStdString(), it.value()));
m_ddsHandler->dtsTab()->publish(connections);
}
void MainHandler::onDirectMessage(const QByteArray &jsonMessage)
{
DEBUG_LOG(m_logger) << "Got direct message " << jsonMessage;
WebSocketInterface::Messages::BiteDataMessage::BiteData biteData;
QJsonObject obj = QJsonDocument::fromJson(jsonMessage).object();
biteData.equipNo = obj.value(WebSocketInterface::JsonKeys::EQUIP_NO()).toInt();
biteData.isLicensed = obj.value(QStringLiteral("doLicence")).toBool();
qint64 biteState = obj.value(QStringLiteral("biteState")).toInt();
qint64 configState = obj.value(QStringLiteral("configState")).toInt();
qint64 batteryState = obj.value(QStringLiteral("batteryState")).toInt();
qint64 configError = obj.value(QStringLiteral("configError")).toInt();
QJsonArray biteError = obj.value(QStringLiteral("biteError")).toArray();
if (biteData.equipNo <= 0)
return;
// configuration errors
for (int it = 0 ; it < 32; it++)
{
quint32 bitValue = 1U << it;
QString name;
QString isOk;
for (const ProjectDataManagement::CanComponentString &ccs : qAsConst(m_ccsMap))
{
if (ccs.bitMask() == bitValue)
{
name = m_pdmHandler->dtc()->trCanComponentStringName(ccs.id());
break;
}
}
if (((biteState & bitValue) == bitValue))
{
if (((configState & bitValue) != bitValue))
{
isOk = tr("Error");
}
}
else if (((configState & bitValue) == bitValue))
{
isOk = tr("Communication error");
}
if(!name.isEmpty() && !isOk.isEmpty())
{
WebSocketInterface::Messages::BiteDataMessage::ConfigurationError error;
error.componentName = name;
error.text = isOk;
biteData.configurationErrors.append(error);
}
}
// basic errors
if(configError != 0)
{
WebSocketInterface::Messages::BiteDataMessage::BasicError error;
error.text = tr("Configcheck error");
biteData.basicErrors.append(error);
}
if (batteryState != 0)
{
WebSocketInterface::Messages::BiteDataMessage::BasicError error;
error.text = tr("Battery Warning");
biteData.basicErrors.append(error);
}
// componentErrors
for (const auto &ele : qAsConst(biteError))
{
QString text;
QJsonObject error = ele.toObject();
ParticipantManagement::BiteError bitError;
bitError.canDeviceNumber(error.value(QStringLiteral("canDeviceNumber")).toInt());
bitError.errorNumber(error.value(QStringLiteral("errorNumber")).toInt());
bitError.subdeviceNumber(error.value(QStringLiteral("subdeviceNumber")).toInt());
bitError.uniqueId(error.value(QStringLiteral("uniqueId")).toInt());
if (bitError.subdeviceNumber() != 0)
text = (tr("Error: %1 \nComponent: %2 \nSubdev: %3 \nUId: %4 ")
.arg(getErrorString(bitError),
getCanDeviceName(bitError.canDeviceNumber()),
getSubDeviceName(bitError.subdeviceNumber()))
.arg(QString::number( bitError.uniqueId(), 16 ).toUpper()));
else if (bitError.canDeviceNumber() != 0)
text = (tr("Error: %1 \nComponent: %2")
.arg(getErrorString(bitError),
getCanDeviceName(bitError.canDeviceNumber())));
WebSocketInterface::Messages::BiteDataMessage::BiteError biteErrorLocal;
biteErrorLocal.deviceId = bitError.canDeviceNumber();
biteErrorLocal.subDevice = bitError.subdeviceNumber();
biteErrorLocal.errorNo = bitError.errorNumber();
biteErrorLocal.uniqueId = bitError.uniqueId();
WebSocketInterface::Messages::BiteDataMessage::ErrorText errorT;
errorT.lang = QStringLiteral("de_DE");
errorT.text = text;
biteErrorLocal.errorTexts.append(errorT);
biteData.biteErrors.append(biteErrorLocal);
}
WebSocketInterface::Messages::BiteDataMessage *msg = new WebSocketInterface::Messages::BiteDataMessage(biteData, QStringLiteral("7777"));
if (m_handlerWebSocket != nullptr)
{
m_handlerWebSocket->sendToAllClients(msg);
}
}
QString MainHandler::getErrorString(const ParticipantManagement::BiteError &error) const
{
const bool subDevice = (error.subdeviceNumber() != 0);
for (const ProjectDataManagement::BiteComponentText &bct : m_bctMap)
{
if (((subDevice && bct.subDeviceId() == error.subdeviceNumber()) ||
(bct.canDeviceId() == error.canDeviceNumber())) &&
(bct.errorNumber() == error.errorNumber()))
return m_pdmHandler->dtc()->trBiteComponentTextName(bct.id());
}
return tr("Unknown (%1)").arg(error.errorNumber());
}
QString MainHandler::getCanDeviceName(int id) const
{
for (const ProjectDataManagement::CanComponentString &ccs : m_ccsMap)
{
if (ccs.canDeviceId() == id)
return m_pdmHandler->dtc()->trCanComponentStringName(ccs.id());
}
return tr("Unknown (%1)").arg(id);
}
QString MainHandler::getSubDeviceName(int id) const
{
for (const ProjectDataManagement::CanComponentString &ccs : m_ccsMap)
{
if (ccs.subDeviceId() == id)
return m_pdmHandler->dtc()->trCanComponentStringName(ccs.id());
}
return tr("Unknown (%1)").arg(id);
}
void MainHandler::onPositionChanged(int equipNo, const ParticipantManagement::ParticipantPosition &pos)
{
WebSocketInterface::Messages::PositionMessage::PositionData positionData;
positionData.equipNo = equipNo;
positionData.pos = pos.pos();
positionData.gpsTime = pos.sampleTime();
WebSocketInterface::Messages::PositionMessage *msg = new WebSocketInterface::Messages::PositionMessage(positionData, QUuid::createUuid().toString());
if (m_handlerWebSocket != nullptr)
{
m_handlerWebSocket->sendToAllClients(msg);
}
}
} // namespace TrainingAreaBase