Files
qt5-legatus-legacy/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/json_dispatcher.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

1004 lines
42 KiB
C++
Executable File

/**
* @RDE_HEADER@
* JsonDispatcher for Apache Plugin
* $Id: hla_dispatcher.cpp 62304 2024-03-21 08:17:45Z ewert_j $
* $HeadURL: svn://slssvn/TDS/branches/product/features/ufom/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/hla_dispatcher.cpp $
* $Author: ewert_j $
* Created by Julius Ewert, ESIM, 2024-03-20
*/
#include "json_dispatcher.h"
#include "json_handler_rx.h"
#include "json_handler_tx.h"
#include "settings.h"
#include "ws_handler.h"
#include <AreaEffectSimulation/aes_operator_rights.h>
#include <QHostInfo>
#include <QJsonDocument>
#include <QSettings>
#include <QStringList>
#include <QTimer>
#include <QWebSocket>
#include <nglsettings/nglsettings_settings.h>
#include <vector>
namespace AdlerJson
{
JsonDispatcher::JsonDispatcher(TrainingAreaBase::Plugin::DataProvider *pdp,
const std::shared_ptr<TrainingAreaBase::RecvFromTab> &recvFromTabCb,
const std::shared_ptr<TrainingAreaBase::TxMixin> &sendToTabCb,
NGL::Settings *settings,
std::shared_ptr<TrainingAreaBase::DdsHandler> &ddsHandler,
std::shared_ptr<FlmHandler> &flmHandler,
MainDispatcher *mainDispatcher)
: TrainingAreaBase::Interface::Fts::AdlerCalculateTrajectoryRequestHandler(ddsHandler->ftsTab())
, m_recvFromTabCb(recvFromTabCb)
, m_recvFromWebsocket(std::make_shared<JsonHandlerRx>(recvFromTabCb, sendToTabCb, this, mainDispatcher->aesHandler().get(), mainDispatcher->ppmHandler().get()))
, m_sendToWebsocket(std::make_shared<JsonHandlerTx>(pdp, mainDispatcher))
// , m_ddsHandler(ddsHandler)
, m_mainDispatcher(mainDispatcher)
, m_flmHandler(flmHandler)
, m_ppmHandler(mainDispatcher->ppmHandler())
, m_ftsTab(ddsHandler->ftsTab())
, m_dtsTab(ddsHandler->dtsTab())
, m_ftcNom(ddsHandler->ftcNom())
, m_settings(settings)
, m_webSocket(new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this))
, m_webSocketInterface(std::make_shared<TrainingAreaBase::WebSocketHandler>())
, m_webSocketReconnectTimer(new QTimer(this))
, m_bWarnedOnce(false)
, m_logger(getLogger("JsonDispatcher"))
{
DEBUG_LOG(m_logger) << "JsonDispatcher::JsonDispatcher";
m_sendToWebsocket->setWSInterface(m_webSocketInterface);
connect(m_webSocketInterface.get(), &TrainingAreaBase::WebSocketInterface::recivedTelegram, this, &JsonDispatcher::onRecivedTelegram, Qt::UniqueConnection);
connect(m_webSocketInterface.get(), &TrainingAreaBase::WebSocketInterface::newConnection, this, &JsonDispatcher::onNewWebsocketConnected, Qt::UniqueConnection);
connect(m_webSocketInterface.get(), &TrainingAreaBase::WebSocketInterface::wsDisconnected, this, &JsonDispatcher::onWSDisconnected);
}
bool JsonDispatcher::init() { return true; }
void JsonDispatcher::connectWebsocket()
{
DEBUG_LOG(m_logger) << "JsonDispatcher::connectWebsocket()";
if (!m_webSocketInterface->init(Settings::getWebSocketServerPortRange(3)))
{
DEBUG_LOG(m_logger) << "Adler Could not open WebSocketInterface";
}
else
{
DEBUG_LOG(m_logger) << "Adler Could open WebSocketInterface on port " << m_webSocketInterface->getPort();
}
emit websocketConnected(m_webSocketInterface->getPort());
const QUrl url(webSocketUrl());
if (!m_bWarnedOnce)
DEBUG_LOG(m_logger) << "Adler: Using WebSocket url: " << url.toString();
if (m_webSocket->state() != QAbstractSocket::ConnectedState)
m_webSocket->open(url);
}
void JsonDispatcher::onKeepAliveTimerTimeout()
{
QTimer *timer = qobject_cast<QTimer *>(sender());
int wsId = m_connectionKeepaliveTimer.key(timer);
DEBUG_LOG(m_logger) << "onKeepAliveTimerTimeout Timeout from " << wsId << " still in list " << (getSAAByWs(wsId) != nullptr);
auto saa = getSAAByWs(wsId);
if (saa)
{
const QUrl saaUrl = m_webSocketInterface->getPeerUrl(wsId);
const ParticipantManagement::ConnectState connectState = ParticipantManagement::ConnectState::NoHeartbeat;
const bool isSaaConnected = (connectState == ParticipantManagement::ConnectState::Connected);
const int assetId = getAssetId(saa->getSAAName());
DEBUG_LOG(m_logger) << "Timeout from " << saa->getSAAName() << " ==> SAA URL: " << saaUrl.toString();
m_ppmHandler
->modifyPartyAdlerProperties(wsId, assetId, saa->getPartyId(), saa->getSAAName(), saa->getSzenarName(), connectState, saa->getIsVerbund());
m_mainDispatcher->asmHandler()->modifyAssetAdlerProperties(assetId, isSaaConnected);
saa->setIsAlive(false);
}
else
ERROR_LOG(m_logger) << "Trying to remove an saa after timeout with the websocketId: " << wsId << " that is not connected";
m_connectionKeepaliveTimer.remove(wsId);
timer->stop();
timer->deleteLater();
}
int JsonDispatcher::getAssetId(const QString &saaName) const
{
std::function<QString(const std::string &)> saaNameFromProps = [](const std::string &props) -> QString
{
const QJsonObject propObj = QJsonDocument::fromJson(QByteArray::fromStdString(props)).object();
if (!propObj.contains(QStringLiteral("credentials")))
return {};
const QJsonObject adlerObj = propObj[QStringLiteral("credentials")].toObject();
return adlerObj.value(QStringLiteral("saaName")).toString();
};
for (const auto &currAsset : m_mainDispatcher->asmHandler()->getDtc()->assets())
{
if (currAsset.assetTypeId() != AssetManagement::AssetType_def::AdlerSAA)
continue;
const auto &dbSaa = saaNameFromProps(currAsset.properties());
if (dbSaa.toLower() != saaName.toLower())
continue;
return currAsset.id();
}
return -1;
}
std::shared_ptr<TrainingAreaBase::SendToTab> JsonDispatcher::sendToTab() { return std::static_pointer_cast<TrainingAreaBase::SendToTab>(m_sendToWebsocket); }
bool JsonDispatcher::canSendData() const { return m_webSocket && m_webSocket->state() == QAbstractSocket::ConnectedState; }
void JsonDispatcher::onNewWebsocketConnected(const int wsId, const QString &address, qint16 port)
{
DEBUG_LOG(m_logger) << "New Connection from: " << address << ", port: " << port << ", with id: " << wsId;
}
void JsonDispatcher::onWSDisconnected(const int wsId)
{
DEBUG_LOG(m_logger) << "Connection disconnect wsId: " << wsId;
auto saa = getSAAByWs(wsId);
if (saa)
onSAADeregistration(wsId, saa->getPartyId());
}
std::shared_ptr<SAAInterface> JsonDispatcher::getSAAByParty(const int partyId)
{
DEBUG_LOG(m_logger) << "getSAAByParty partyId: " << partyId;
if (m_saaInterfaces.empty())
return nullptr;
auto it
= std::find_if(m_saaInterfaces.begin(), m_saaInterfaces.end(), [partyId](const std::shared_ptr<SAAInterface> &saa) { return saa.get()->getPartyId() == partyId; });
return it != m_saaInterfaces.end() ? *it : nullptr;
}
std::shared_ptr<SAAInterface> JsonDispatcher::getSAAByName(const QString &saaName)
{
auto it
= std::find_if(m_saaInterfaces.begin(), m_saaInterfaces.end(), [saaName](const std::shared_ptr<SAAInterface> &saa) { return saa.get()->getSAAName() == saaName; });
return it != m_saaInterfaces.end() ? *it : nullptr;
}
std::shared_ptr<SAAInterface> JsonDispatcher::getSAAByWs(const int wsId)
{
TRACE_LOG(m_logger) << "m_saaInterfaces.size :" << m_saaInterfaces.size() << ", wsId=" << wsId;
for (int i = 0; i < m_saaInterfaces.size(); i++)
TRACE_LOG(m_logger) << "i=" << i << ", getPartyId :" << m_saaInterfaces[i]->getPartyId() << ", getWsId :" << m_saaInterfaces[i]->getWsId();
auto it = std::find_if(m_saaInterfaces.begin(), m_saaInterfaces.end(), [wsId](const std::shared_ptr<SAAInterface> &saa) { return saa.get()->getWsId() == wsId; });
return it != m_saaInterfaces.end() ? *it : nullptr;
}
bool JsonDispatcher::sendExFeuerbereit(const ExFeuerbereit &exFBDataIn, const int partyId)
{
DEBUG_LOG(m_logger) << "TRY SEND EXFeuerbereit attempt for party " << partyId;
auto saa = getSAAByParty(partyId);
if (!saa)
{
ERROR_LOG(m_logger) << "sendExFeuerbereit no saa for paty:" << partyId;
return false;
}
ExFeuerbereit exFB = exFBDataIn;
exFB.sender = QStringLiteral("EXCON@GUEZ.DE");
exFB.receiver = saa->getSAAName();
exFB.timestamp = QDateTime::currentDateTime().toString(Qt::DateFormat::ISODate);
WARNING_LOG(m_logger) << "TRY SEND EXFeuerbereit attempt for SAA NAME " << saa->getSAAName() << " esid " << saa->getWsId();
m_sendToWebsocket->onGenericTelegram1(saa->getWsId(), exFB.toJson());
return true;
}
bool JsonDispatcher::sendExAbgefeuert(const ExAbgefeuert &exAFDataIn, const int partyId)
{
DEBUG_LOG(m_logger) << "TRY SEND EXFeuerbereit attempt for party " << partyId;
auto saa = getSAAByParty(partyId);
if (!saa)
return false;
ExAbgefeuert exAF = exAFDataIn;
exAF.sender = QStringLiteral("EXCON@GUEZ.DE");
exAF.receiver = saa->getSAAName();
exAF.timestamp = QDateTime::currentDateTime().toString(Qt::DateFormat::ISODate);
WARNING_LOG(m_logger) << "TRY SEND sendExAbgefeuert attempt for SAA NAME " << saa->getSAAName() << " esid " << saa->getWsId();
m_sendToWebsocket->onGenericTelegram1(saa->getWsId(), exAF.toJson());
return true;
}
bool JsonDispatcher::sendExMqManuelleQuittung(const ExMqManuelleQuittung &exMQDataIn, int partyId)
{
DEBUG_LOG(m_logger) << "TRY SEND ExMqManuelleQuittung attempt for party " << partyId;
auto saa = getSAAByParty(partyId);
if (!saa)
return false;
ExMqManuelleQuittung exMQ = exMQDataIn;
exMQ.sender = QStringLiteral("EXCON@GUEZ.DE");
exMQ.receiver = saa->getSAAName();
exMQ.timestamp = QDateTime::currentDateTime().toString(Qt::DateFormat::ISODate);
DEBUG_LOG(m_logger) << "TRY SEND ExMqManuelleQuittung attempt for SAA NAME " << saa->getSAAName() << " esid " << saa->getWsId();
m_sendToWebsocket->onGenericTelegram1(saa->getWsId(), exMQ.toJson());
return true;
}
bool JsonDispatcher::sendExTqTechnischeQuittung(const ExTqTechnischeQuittung &exTQDataIn, int partyId)
{
DEBUG_LOG(m_logger) << "TRY SEND ExTqTechnischeQuittung attempt for party " << partyId;
auto saa = getSAAByParty(partyId);
if (!saa)
return false;
ExTqTechnischeQuittung exMQ = exTQDataIn;
exMQ.sender = QStringLiteral("EXCON@GUEZ.DE");
exMQ.receiver = saa->getSAAName();
exMQ.timestamp = QDateTime::currentDateTime().toString(Qt::DateFormat::ISODate);
DEBUG_LOG(m_logger) << "TRY SEND ExMqManuelleQuittung attempt for SAA NAME " << saa->getSAAName() << " esid " << saa->getWsId();
m_sendToWebsocket->onGenericTelegram1(saa->getWsId(), exMQ.toJson());
return true;
}
bool JsonDispatcher::sendExVollzugsmeldung(const ExVollzugsmeldung &exVZDataIn, int partyId)
{
DEBUG_LOG(m_logger) << "TRY SEND ExVollzugsmeldung attempt for party " << partyId;
auto saa = getSAAByParty(partyId);
if (!saa)
return false;
ExVollzugsmeldung exVZ = exVZDataIn;
exVZ.sender = QStringLiteral("EXCON@GUEZ.DE");
exVZ.receiver = saa->getSAAName();
exVZ.timestamp = QDateTime::currentDateTime().toString(Qt::DateFormat::ISODate);
WARNING_LOG(m_logger) << "TRY SEND ExVollzugsmeldung attempt for SAA NAME " << saa->getSAAName() << " WSID " << saa->getWsId();
m_sendToWebsocket->onGenericTelegram1(saa->getWsId(), exVZ.toJson());
return true;
}
void JsonDispatcher::onSAARegistration(const int wsId, const int partyId, const QString &saaName, const QString &szenarName, bool isVerbund, const QString &uuid)
{
DEBUG_LOG(m_logger) << "Registration attempt from " << saaName << " for party " << partyId << ", saaName: " << saaName << ", uuid: " << uuid << ", wsId: " << wsId;
ExAnmeldebestaetigung exRC;
exRC.referenceUuid = uuid;
exRC.timestamp = QDateTime::currentDateTime().toString(Qt::DateFormat::ISODate);
exRC.anBestaetigungsdaten.error = AdlerJson::L45_AnmeldebestaetigungFehlercode::Ok;
auto saa = getSAAByName(saaName);
DEBUG_LOG(m_logger) << "is saa " << saa;
if (saa)
{
DEBUG_LOG(m_logger) << "saa.get()->getPartyId(): " << saa.get()->getPartyId() << " partyId: " << partyId;
if (saa.get()->getPartyId() == partyId)
{
DEBUG_LOG(m_logger) << "saa already connected, reconnect!";
// exRC.anBestaetigungsdaten.error = AdlerJson::L45_AnmeldebestaetigungFehlercode::Doppelt;
onSAADeregistration(saa.get()->getWsId(), partyId);
// TODO: emit or answer with saa already connected
}
else
{
DEBUG_LOG(m_logger) << "other saa connected to this party";
// exRC.anBestaetigungsdaten.error = AdlerJson::L45_AnmeldebestaetigungFehlercode::Mehrfach;
// TODO: emit or answer with other saa connected to this party
}
}
else if (getSAAByParty(partyId))
{
DEBUG_LOG(m_logger) << "party is used by other saa";
// exRC.anBestaetigungsdaten.error = AdlerJson::L45_AnmeldebestaetigungFehlercode::Belegt;
// TODO: emit or answer party is used by other saa
}
// else
{
auto newSaa = std::make_shared<SAAInterface>(wsId, partyId, saaName, szenarName, isVerbund);
newSaa->init(m_ppmHandler, m_mainDispatcher->pdmHandler(), m_mainDispatcher->aesHandler(), m_dtsTab);
// cleanUp first
cleanupSaaInterfaces(saaName, wsId, partyId);
m_saaInterfaces.append(newSaa);
// We created a new SAA, start keep alive procedure
int adlerOffset = m_settings->settings()->value(QLatin1String("TrainingAreaBase/AdlerKeepalive"), 30000).toInt();
DEBUG_LOG(m_logger) << "adlerOffset =" << adlerOffset;
if (adlerOffset > 0)
{
DEBUG_LOG(m_logger) << "Started SAA with Keppalive Timer: " << adlerOffset;
QTimer *keepaliveTimer = new QTimer(this);
keepaliveTimer->setInterval(adlerOffset);
m_connectionKeepaliveTimer.insert(wsId, keepaliveTimer);
connect(keepaliveTimer, &QTimer::timeout, this, &JsonDispatcher::onKeepAliveTimerTimeout);
keepaliveTimer->start();
}
saa = newSaa;
// Won't cleanup in Debug as the simulator won't send continous EX_VerbindugsAnfrage
DEBUG_LOG(m_logger) << "emit or answer without error wsId: " << wsId;
}
exRC.sender = getSender();
exRC.receiver = saa->getSAAName();
// Send Answer to SAA
m_sendToWebsocket->onGenericTelegram1(wsId, exRC.toJson());
// if we declined the registration don't update ppm/asm
if (exRC.anBestaetigungsdaten.error != AdlerJson::L45_AnmeldebestaetigungFehlercode::Ok)
return;
// Initialize HQ-Folders if not exist
m_flmHandler->initHQFolders(partyId);
// Modify Party
const ParticipantManagement::ConnectState connectState = ParticipantManagement::ConnectState::Connected;
const bool isSaaConnected = (connectState == ParticipantManagement::ConnectState::Connected);
const int assetId = getAssetId(saaName);
DEBUG_LOG(m_logger) << "onSAARegistration modifyParty"
<< " ==> SAA name " << saaName;
createNotificationRequest(wsId, QString::fromUtf8(tr_lts("Notification", "Reconnect to SAA")),
NotificationManagement::MessageType_def::Success,
NotificationManagement::NotificationType::AdlerNotification,
saaName,
{});
m_ppmHandler->modifyPartyAdlerProperties(wsId, assetId, partyId, saaName, szenarName, connectState, saa->getIsVerbund());
m_mainDispatcher->asmHandler()->modifyAssetAdlerProperties(assetId, isSaaConnected);
}
void JsonDispatcher::onSAADeregistration(const int wsId, const int partyId)
{
DEBUG_LOG(m_logger) << "onSAADeregistration"
<< " wsId: " << wsId << ", partyId: " << partyId;
auto saa = getSAAByWs(wsId);
if (saa)
{
const ParticipantManagement::ConnectState connectState = ParticipantManagement::ConnectState::Disconnected;
const bool isSaaConnected = (connectState == ParticipantManagement::ConnectState::Connected);
const int assetId = getAssetId(saa->getSAAName());
DEBUG_LOG(m_logger) << "Deregistation from " << saa->getSAAName();
createNotificationRequest(wsId, QString::fromUtf8(tr_lts("Notification", "SAADeregistration")),
NotificationManagement::MessageType_def::Info,
NotificationManagement::NotificationType::AdlerNotification,
saa->getSAAName(),
{});
m_ppmHandler->modifyPartyAdlerProperties(wsId, assetId, partyId, {}, {}, connectState, saa->getIsVerbund());
m_mainDispatcher->asmHandler()->modifyAssetAdlerProperties(assetId, isSaaConnected);
m_saaInterfaces.removeOne(saa);
}
else
ERROR_LOG(m_logger) << "Trying to remove an saa with the websocketId: " << wsId << " that is not connected";
}
void JsonDispatcher::onSAAConnectionTest(const int wsId, const QString &uuid)
{
DEBUG_LOG(m_logger) << "onSAAConnectionTest"
<< " wsId: " << wsId << ", uuid: " << uuid;
auto saa = getSAAByWs(wsId);
if (saa)
{
if (!saa->getIsAlive())
{
saa->setIsAlive(true);
}
QTimer *connectionTimer = m_connectionKeepaliveTimer.value(wsId);
if (connectionTimer)
{
DEBUG_LOG(m_logger) << "connectionTimer->start()";
connectionTimer->start(); // Restarts the KeepAlive Timer
}
DEBUG_LOG(m_logger) << "ConnectionTest from " << saa->getSAAName();
EXConnectionTestReply exCTR;
exCTR.referenceUuid = uuid;
exCTR.timestamp = QDateTime::currentDateTime().toString(Qt::DateFormat::ISODate);
QJsonObject reply = exCTR.toJson();
m_sendToWebsocket->onGenericTelegram1(wsId, reply);
}
}
void JsonDispatcher::onSAAStatus(const int wsId, const EXStatus &status)
{
DEBUG_LOG(m_logger) << "onSAAStatus"
<< " wsId: " << wsId << ", status: " << static_cast<int>(status.status);
auto saa = getSAAByWs(wsId);
if (saa)
{
DEBUG_LOG(m_logger) << "Status from " << saa->getSAAName() << AdlerJson::szenarstatusToStr(status.status);
saa->setStatus(status.status);
QString title;
switch (status.status)
{
case AdlerJson::Szenarstatus::Inaktiv:
title = QString::fromUtf8(tr_lts("Notification", "Inactive"));
break;
case AdlerJson::Szenarstatus::Geplant:
title = QString::fromUtf8(tr_lts("Notification", "Planned"));
break;
case AdlerJson::Szenarstatus::Aktiv:
title = QString::fromUtf8(tr_lts("Notification", "Active"));
break;
case AdlerJson::Szenarstatus::Gestoppt:
title = QString::fromUtf8(tr_lts("Notification", "Stopped"));
break;
}
createNotificationRequest(wsId,
title,
NotificationManagement::MessageType_def::Success,
NotificationManagement::NotificationType::AdlerNotification,
saa->getSAAName(),
{});
}
}
QString JsonDispatcher::getParticipantInfoLink(const QString &verein, const int partyId, const bool isVerbund)
{
const ParticipantManagement::Participant &part = m_ppmHandler->findParticipantByInternalName(verein, partyId, isVerbund);
DEBUG_LOG(m_logger) << "ParticipantId=" << part.id();
QString ppmlink = QString::fromUtf8(tr_lts("Notification","<a href='lts://ParticipantInfo/%1'>Show Participant Info</a><br />"))
.arg(part.id());
return ppmlink;
}
void JsonDispatcher::onSAAExceptionStatus(const int wsId, const TechnischeDaten &exceptionStatus)
{
DEBUG_LOG(m_logger) << "onSAAExceptionStatus"
<< ", wsId: " << wsId << ", status: " << StatusL32Map.key(exceptionStatus.status) << ", beschreibung: " << exceptionStatus.beschreibung;
const auto saa = getSAAByWs(wsId);
DEBUG_LOG(m_logger) << "Sende Notification Technische Quittung";
QString content = QString::fromUtf8(tr_lts("Notification", "<B>Status: </B>%1<br /><B> Description: </B>%2"));
QStringList args;
// INV = Meldung nicht gültig nach JSON-Schema.
// DAT = Datenkonsistenzfehler.
if (StatusL32Map.key(exceptionStatus.status) == QStringLiteral("INV"))
args << QString::fromUtf8(tr_lts("Notification", "Message is not valid according to JSON schema."));
else
args << QString::fromUtf8(tr_lts("Notification", "Data Consistency Error"));
args << exceptionStatus.beschreibung;
createNotificationRequest(wsId, QString::fromUtf8(tr_lts("Notification","Error Message from SAA")),
NotificationManagement::MessageType_def::Error,
NotificationManagement::NotificationType::AdlerNotification,
content,
args);
}
void JsonDispatcher::onSAASQWS(const int wsId, const Waffensystemdaten &exSQWS)
{
DEBUG_LOG(m_logger) << "onSAASQWS";
const bool isVerbund = true;
auto saa = getSAAByWs(wsId);
int partyId = saa->getPartyId();
const ParticipantManagement::Participant &part = m_ppmHandler->findParticipantByInternalName(exSQWS.einheit.verein, partyId, isVerbund);
m_ppmHandler->parseSQWS(exSQWS, wsId);
if (part.id() > 0)
{
sendParticipantInfoNotification(wsId, partyId, isVerbund, exSQWS.einheit);
return;
}
QTimer::singleShot(5000, this, [this, wsId, partyId, isVerbund, exSQWS]() {
sendParticipantInfoNotification(wsId, partyId, isVerbund, exSQWS.einheit);
});
}
void JsonDispatcher::onSAASQFS(const int wsId, const Feuerstellungsdaten &exSQFS)
{
DEBUG_LOG(m_logger) << "onSAASQFS";
auto saa = getSAAByWs(wsId);
int partyId = saa->getPartyId();
const bool isVerbund = true;
m_ppmHandler->parseSQFS(exSQFS, wsId);
const ParticipantManagement::Participant &part = m_ppmHandler->findParticipantByInternalName(exSQFS.feuereinheit.verein, partyId, isVerbund);
if (part.id() > 0)
{
sendParticipantInfoNotification(wsId, partyId, isVerbund, exSQFS.feuereinheit);
return;
}
QTimer::singleShot(5000, this, [this, wsId, partyId, isVerbund, exSQFS]() {
sendParticipantInfoNotification(wsId, partyId, isVerbund, exSQFS.feuereinheit);
});
}
void JsonDispatcher::onSAASQLP(const int wsId, const Ladeplatzdaten &exSQLP)
{
DEBUG_LOG(m_logger) << "onSAASQLP";
auto saa = getSAAByWs(wsId);
int partyId = saa->getPartyId();
const bool isVerbund = true;
const ParticipantManagement::Participant &part = m_ppmHandler->findParticipantByInternalName(exSQLP.einheit.verein, partyId, isVerbund);
m_ppmHandler->parseSQLP(exSQLP, wsId);
if (part.id() > 0)
{
sendParticipantInfoNotification(wsId, partyId, isVerbund, exSQLP.einheit);
return;
}
QTimer::singleShot(5000, this, [this, wsId, partyId, exSQLP]() {
sendParticipantInfoNotification(wsId, partyId, isVerbund, exSQLP.einheit);
});
}
void JsonDispatcher::sendParticipantInfoNotification(const int wsId, const int partyId, const bool isVerbund, const Einheit &einheit)
{
DEBUG_LOG(m_logger) << "sendParticipantInfoNotification";
QStringList args;
const ParticipantManagement::Participant &part = m_ppmHandler->findParticipantByInternalName(einheit.verein, partyId, isVerbund);
DEBUG_LOG(m_logger) << "sendParticipantInfoNotification ParticipantId=" << part.id();
QString content = QString::fromUtf8(tr_lts("Notification","<a href='lts://ParticipantInfo/%1'>Show Participant Info</a><br /><B>Unit:</B> %2/%3/%4"));
args.append(QString::number(part.id()));
args.append(ortsIdMap.key(einheit.oi));
args.append(QString::number(einheit.r));
args.append(einheit.verein);
createNotificationRequest(wsId,
QString::fromUtf8(tr_lts("Notification","Loading Point Position Report")),
NotificationManagement::MessageType_def::Info,
NotificationManagement::NotificationType::AdlerNotification,
content,
args);
DEBUG_LOG(m_logger) << "Stellungsmeldung content=" << content;
}
void JsonDispatcher::onSAATrajectoryData(const int wsId, const Flugbahndaten &exFlugbahndaten, const QString referenceUuid)
{
DEBUG_LOG(m_logger) << "onSAATrajectoryData wsId=" << wsId << ", referenceUuid :" << referenceUuid;
auto saa = getSAAByWs(wsId);
if (!saa)
{
ERROR_LOG(m_logger) << "onSAATrajectoryData but SAA with wsId " << wsId << " not found";
return;
}
QString referenceUuidAlt = referenceUuid;
referenceUuidAlt = referenceUuidAlt.replace(QStringLiteral("-"), QStringLiteral(""));
if (!m_trajectoryIdentifier.contains(referenceUuidAlt))
{
DEBUG_LOG(m_logger) << "referenceUuid not found in m_trajectoryIdentifier"
<< ", referenceUuid : " << referenceUuidAlt;
for (auto it = m_trajectoryIdentifier.constBegin(); it != m_trajectoryIdentifier.constEnd(); ++it)
{
DEBUG_LOG(m_logger) << "m_trajectoryIdentifier key : " << it.key() << ", value aesItemId :" << it.value().aesItemId << ", explosionId :" << it.value().explosionId;
}
}
TrajectoryIdentifier trajectoryIdentifier = m_trajectoryIdentifier.value(referenceUuidAlt);
m_trajectoryIdentifier.remove(referenceUuidAlt);
// Parse in party SAA
const Core::GeoPosition positionLaunch = m_positionsLaunch.value(referenceUuidAlt);
m_positionsLaunch.remove(referenceUuidAlt);
saa->parseFlugbahndaten(exFlugbahndaten, trajectoryIdentifier.aesItemId, trajectoryIdentifier.explosionId, positionLaunch);
}
void JsonDispatcher::onSAAHXGuidingLines(const int wsId, const Koordinierungsdaten &koordinierungsdaten)
{
auto saa = getSAAByWs(wsId);
if (!saa)
{
ERROR_LOG(m_logger) << "SQ HX (K) from SAA but SAA with id " << wsId << " not found";
return;
}
int partyId = saa->getPartyId();
QJsonObject obj;
koordinierungsdatenToJson(obj, koordinierungsdaten);
m_flmHandler->processHQMessage(partyId, koordinierungsdaten.name, koordinierungsdaten.meldungsnummer, obj);
}
void JsonDispatcher::onSAAHXGuidingLinesAndBoundaries(const int wsId, const Fuehrungsdaten &fuehrungsdaten)
{
auto saa = getSAAByWs(wsId);
if (!saa)
{
ERROR_LOG(m_logger) << "SQ HX (G) from SAA but SAA with id " << wsId << " not found";
return;
}
int partyId = saa->getPartyId();
QJsonObject obj;
fuehrungsdatenToJson(obj, fuehrungsdaten);
m_flmHandler->processHQMessage(partyId, fuehrungsdaten.name, fuehrungsdaten.meldungsnummer, obj);
}
void JsonDispatcher::onSSAEXFIREHALT(const int wsId, const FeuerHaltDaten &feuerhaltdaten)
{
const auto saa = getSAAByWs(wsId);
if (!saa)
{
ERROR_LOG(m_logger) << "EX-FH from SSA but SAA with id " << wsId << " not found";
}
DEBUG_LOG(m_logger) << "[EX-FH] data from SSA " << wsId << " : "
<< "verein: " << feuerhaltdaten.verein << " "
<< "kenner: " << feuerhaltdaten.kenner.underlying() << " "
<< "kategorie: " << feuerhaltdaten.kategorie.underlying() << " "
<< "feuerkampfReferenz: " << feuerhaltdaten.feuerkampfReferenz << " "
<< "zpNummer: " << feuerhaltdaten.zpNummer << " "
<< "koordinaten: "
<< "{ lat: " << feuerhaltdaten.koordinate.latitude << " | lng: " << feuerhaltdaten.koordinate.longitude << "} "
<< "radius: " << feuerhaltdaten.radius << " "
<< "entladenSchuss: " << feuerhaltdaten.entladenSchuss << " ";
const int partyId = saa->getPartyId();
const ProjectDataManagement::L22_FireHaltCategory kategorie = feuerhaltdaten.kategorie;
const ProjectDataManagement::L27_EffectorId kenner = feuerhaltdaten.kenner;
AreaEffectSimulation::EffectClusterFireHaltRequest req;
req.partyId(partyId);
req.fireHaltCategory(kategorie);
req.kenner(kenner);
req.longitude(feuerhaltdaten.koordinate.longitude);
req.latitude(feuerhaltdaten.koordinate.latitude);
req.radius(feuerhaltdaten.radius);
req.entladenSchuss(feuerhaltdaten.entladenSchuss);
const bool bRet = m_mainDispatcher->aesHandler()->getFtc()->effectClusterFireHaltRequest(req);
if (bRet && kategorie == ProjectDataManagement::L22_FireHaltCategory::E && !feuerhaltdaten.entladenSchuss) //we can send the EXVollzugsmeldung immediatly
{
Feuerkampfdaten fkd;
fkd.feuerkampfReferenz = feuerhaltdaten.feuerkampfReferenz;
fkd.kenner = feuerhaltdaten.kenner;
fkd.verein = feuerhaltdaten.verein;
ExVollzugsmeldung exvm;
exvm.feuerkampfdaten = fkd;
sendExVollzugsmeldung(exvm, partyId);
}
createNotificationRequest(wsId,
QString::fromUtf8(tr_lts("Notification","Hold your fire")),
NotificationManagement::MessageType_def::Info,
NotificationManagement::NotificationType::CombatOrder,
saa->getSAAName(),
{});
}
void JsonDispatcher::onWebSocketConnected()
{
QWebSocket *ws = qobject_cast<QWebSocket *>(sender());
m_sendToWebsocket->onWebSocketConnected(ws);
m_webSocketReconnectTimer->stop();
m_bWarnedOnce = false;
}
void JsonDispatcher::onWebsocketExternalConnectionStateChanged(bool bConnected)
{
m_sendToWebsocket->onWebsocketConnectedToExternalSystem(bConnected);
emit connectionState(QString::fromUtf8(name()), bConnected);
}
void JsonDispatcher::onWebSocketError()
{
if (!m_webSocket)
return;
if (!m_bWarnedOnce)
WARNING_LOG(m_logger) << "Error connecting to " << webSocketUrl().toString() << ": " << m_webSocket->errorString();
m_webSocket->close();
m_webSocketReconnectTimer->start(5000);
m_bWarnedOnce = true;
emit connectionState(QString::fromUtf8(name()), false);
}
bool JsonDispatcher::isConnectionTest(QJsonDocument &doc)
{
QJsonObject obj = doc.object();
QString key = QStringLiteral("type");
QString value = QStringLiteral("EX-Verbindungsanfrage");
if (obj.contains(key))
{
QJsonValue val = obj.value(key);
if (val.toString() == value)
{
return true;
}
}
return false;
}
void JsonDispatcher::onRecivedTelegram(int wsId, const QJsonObject &telegram)
{
QJsonDocument doc(telegram);
if (!isConnectionTest(doc))
{
DEBUG_LOG(m_logger) << "Text Message received: " << doc.toJson() << ", wsId: " << wsId;
}
else
{
TRACE_LOG(m_logger) << "Text Message received: " << doc.toJson() << ", wsId: " << wsId;
}
// convert from json to internal representation, send to TAB
int errorcode = 0;
m_recvFromWebsocket->handleTelegram(wsId, telegram, errorcode);
// now that it's handled look at the error code and if it's EX_FLUGDATEN
// then return errorcode
TRACE_LOG(m_logger) << "errorcode = " << errorcode;
const int typeId = telegram[JsonKeys::TYPE_ID()].toInt();
if (typeId != TelegramIds::EX_FLUGBAHNDATEN())
return;
if (errorcode > 0)
{
FlugbahnberechnungFehlercode fehlercode = static_cast<FlugbahnberechnungFehlercode>(errorcode);
QString errorStr = FehlercodeMap.key(fehlercode);
QString completeErrorStr = QStringLiteral("Calculate Trajectory failed: ") + errorStr;
DEBUG_LOG(m_logger) << "errorStr :" << errorStr << ", completeErrorStr :" << completeErrorStr;
Core::FunctionReplyObject reply = Core::FunctionReplyObject(-1, tr_lts("dds", completeErrorStr.toStdString()), {errorStr.toStdString()});
reply.setId(m_explosionIndex);
m_ftsTab->adlerCalculateTrajectoryReply(m_cookieCalculateTrajectoryAdler, reply);
}
else
{
Core::FunctionReplyObject reply;
reply.setId(m_explosionIndex);
m_ftsTab->adlerCalculateTrajectoryReply(m_cookieCalculateTrajectoryAdler, reply);
}
}
void JsonDispatcher::onTextMessageReceived(const QByteArray &message)
{
DEBUG_LOG(m_logger) << "Text Message received: " << message;
// convert from json to internal representation, send to TAB
m_recvFromWebsocket->handleTelegram(message);
}
QUrl JsonDispatcher::webSocketUrl() const { return QUrl(QStringLiteral("ws://127.0.0.1:7069")); }
void JsonDispatcher::onAdlerCalculateTrajectoryRequest(const rti::core::SampleIdentity &cookie, const AdlerCalculateTrajectoryRequest &request)
{
Core::FunctionReplyObject ret;
m_explosionIndex = request.explosionIndex();
m_cookieCalculateTrajectoryAdler = cookie;
DEBUG_LOG(m_logger) << "Adler Plugin onSendCalculateTrajectoryAdlerRequest"
<< ", m_saaInterfaces.size=" << m_saaInterfaces.size();
int wsId = 0;
std::shared_ptr<SAAInterface> saaInterface = getSAAByParty(request.partyId());
if (saaInterface)
wsId = saaInterface->getWsId();
if (wsId == 0)
{
DEBUG_LOG(m_logger) << " No wsId found wsId=" << wsId << " return";
ret.setId(request.explosionIndex());
m_ftsTab->adlerCalculateTrajectoryReply(cookie, ret);
return;
}
DEBUG_LOG(m_logger) << "Adler Plugin onSendCalculateTrajectoryAdlerRequest"
<< " connect to SAA and send data"
<< ", partyId :" << request.partyId() << ", wsId :" << wsId;
ExFlugbahnberechnung exFlugbahnberechnung;
exFlugbahnberechnung.sender = QStringLiteral("EXCON@GUEZ.DE");
auto saa = getSAAByWs(wsId);
if (!saa)
{
ERROR_LOG(m_logger) << "SQ WS from SAA but SAA with id " << wsId << " not found";
return;
}
exFlugbahnberechnung.receiver = saa->getSAAName();
exFlugbahnberechnung.timestamp = QDateTime::currentDateTime().toString(Qt::DateFormat::ISODate);
exFlugbahnberechnung.version = QStringLiteral("0.0.5");
exFlugbahnberechnung.flugbahnberechnungDaten.standort.koordinate.latitude = request.positionLaunch().lat() * 180.0 / M_PI;
exFlugbahnberechnung.flugbahnberechnungDaten.standort.koordinate.longitude = request.positionLaunch().lon() * 180.0 / M_PI;
exFlugbahnberechnung.flugbahnberechnungDaten.standort.hoehe = request.positionLaunch().z();
exFlugbahnberechnung.flugbahnberechnungDaten.zielkoordinate.koordinate.latitude = request.positionTarget().lat() * 180.0 / M_PI;
exFlugbahnberechnung.flugbahnberechnungDaten.zielkoordinate.koordinate.longitude = request.positionTarget().lon() * 180.0 / M_PI;
exFlugbahnberechnung.flugbahnberechnungDaten.zielkoordinate.hoehe = request.positionTarget().z();
exFlugbahnberechnung.flugbahnberechnungDaten.waffensystem = ProjectDataManagement::L26_EffectingSystemType::inner_enum(request.weaponSystemType().underlying() + 1);
DEBUG_LOG(m_logger) << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!77 weaponSystemType " << request.weaponSystemType().underlying();
DEBUG_LOG(m_logger) << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!77 L26_EffectingSystemType " << exFlugbahnberechnung.flugbahnberechnungDaten.waffensystem;
exFlugbahnberechnung.flugbahnberechnungDaten.geschoss.geschosssorte
= m_mainDispatcher->pdmHandler()->getDtc()->adlerChargeTypeName(request.trajectory().chargeTypeId());
DEBUG_LOG(m_logger) << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!77 chargeTypeId " << request.trajectory().chargeTypeId();
DEBUG_LOG(m_logger) << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!77 geschosssorte " << exFlugbahnberechnung.flugbahnberechnungDaten.geschoss.geschosssorte;
exFlugbahnberechnung.flugbahnberechnungDaten.geschoss.zuender = m_mainDispatcher->pdmHandler()->getDtc()->adlerIgniterTypeName(request.trajectory().igniterTypeId());
DEBUG_LOG(m_logger) << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!77 igniterTypeId " << request.trajectory().igniterTypeId();
DEBUG_LOG(m_logger) << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!77 zuender " << exFlugbahnberechnung.flugbahnberechnungDaten.geschoss.zuender;
exFlugbahnberechnung.flugbahnberechnungDaten.geschoss.zuenderFunktion
= m_mainDispatcher->pdmHandler()->getDtc()->adlerIgniterFunctionName(request.trajectory().igniterFunctionId());
DEBUG_LOG(m_logger) << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!77 igniterFunctionId " << request.trajectory().igniterFunctionId();
DEBUG_LOG(m_logger) << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!77 zuenderFunktion " << exFlugbahnberechnung.flugbahnberechnungDaten.geschoss.zuenderFunktion;
if (request.trajectory().loadTypeId() > 0)
exFlugbahnberechnung.flugbahnberechnungDaten.geschoss.ladung = m_mainDispatcher->pdmHandler()->getDtc()->adlerLoadTypeName(request.trajectory().loadTypeId());
DEBUG_LOG(m_logger) << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!77 ladung " << exFlugbahnberechnung.flugbahnberechnungDaten.geschoss.ladung;
exFlugbahnberechnung.flugbahnberechnungDaten.sprenghoehe = static_cast<int>(request.explosionHeight());
exFlugbahnberechnung.flugbahnberechnungDaten.winkelgruppe = AdlerJson::Winkelgruppe(request.angleGroup().underlying());
//////////////////////////////////////////////////
/// Daten aus dem ESG-Simulator
if (request.properties().empty())
ERROR_LOG(m_logger) << "No properties available";
DEBUG_LOG(m_logger) << ", request.aesItemId() :" << request.aesItemId();
//////////////////////////////////////////////////
DEBUG_LOG(m_logger) << "UUID=" << exFlugbahnberechnung.uuid;
TrajectoryIdentifier trajectoryIdentifier;
trajectoryIdentifier.aesItemId = request.aesItemId();
trajectoryIdentifier.explosionId = request.explosionIndex();
QString uuidCleaned = exFlugbahnberechnung.uuid.remove(QStringLiteral("-"));
m_trajectoryIdentifier.insert(uuidCleaned, trajectoryIdentifier);
m_positionsLaunch.insert(uuidCleaned, request.positionLaunch());
QJsonObject telegram;
telegram = exFlugbahnberechnung.toJson();
DEBUG_LOG(m_logger) << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!77 " << QJsonDocument(telegram).toJson(QJsonDocument::JsonFormat::Compact);
m_sendToWebsocket->onGenericTelegram1(wsId, telegram);
}
void JsonDispatcher::cleanupSaaInterfaces(const QString &saaName, int wsId, int partyId)
{
// as the saaInterfaces are a vector, you can append multiple saa by the same name or ws id or party Id and it will always return the first one.
// so we add a cleanup function
DEBUG_LOG(m_logger) << "cleanup saa partyId: " << partyId << " name: " << saaName << " wsId " << wsId;
const auto copy = m_saaInterfaces;
for (const auto &saa : copy)
{
if ((partyId > 0 && saa.get()->getPartyId() == partyId) || (!saaName.isEmpty() && saa.get()->getSAAName() == saaName) // the name should be uniqe!
|| (wsId > 0 && saa.get()->getWsId() == wsId))
{
DEBUG_LOG(m_logger) << "Cleanup SAA " << saa->getSAAName() << " for Party: " << saa->getPartyId();
m_saaInterfaces.removeOne(saa);
}
}
}
//-----------------------------------------------------------------------------
// Notifications:
//-----------------------------------------------------------------------------
void JsonDispatcher::getCreateNotificationRequestData(const int wsId,
const QString &title,
NotificationManagement::CreateNotification &data,
const QString &content,
const QStringList &contentArgs)
{
const qint64 currentTime = m_mainDispatcher->ddsHandler()->partitionTime();
// 100 seconds
const qint64 expiryTime = currentTime + 100 * 1000;
NotificationManagement::NomNotification notif;
notif.title(title.toStdString());
const auto saa = getSAAByWs(wsId);
if (!saa)
{
WARNING_LOG(m_logger) << "No valid SAA for wsId";
return;
}
//QString content = QStringLiteral("[Open Notification List ](//nomEditGuiPlugin/)") + QStringLiteral("<br />");
const int partyId = saa->getPartyId();
const ParticipantManagement::Party party = m_ppmHandler->getDtc()->party(partyId);
const QString partyStr = QString::fromStdString(party.name());
std::vector<std::string> args;
args.reserve(contentArgs.size());
for (const auto & str : qAsConst(contentArgs))
args.emplace_back(str.toStdString());
notif.content(content.toStdString());
notif.contentArgs(args);
notif.timeStamp(currentTime);
notif.expiryTime(expiryTime);
notif.priority(NotificationManagement::Priority_def::High);
notif.processedPriority(NotificationManagement::Priority_def::High);
notif.exerciseId(m_mainDispatcher->exerciseId());
const QVector<int> recipientIds = m_mainDispatcher->ddsHandler()->dtcOpm()->getNotificationRecipents(m_mainDispatcher->exerciseId(),
partyId,
OperatorRights::AdlerExplosionClusterEdit,
data.notification().notificationType().underlying());
DEBUG_LOG(m_logger) << "getCreateNotificationRequestData recipientIds.size=" << recipientIds.size()
<< ", exerciseId=" << m_mainDispatcher->exerciseId();
std::vector<NotificationManagement::Address> recipientIdsDds;
recipientIdsDds.reserve(recipientIds.size());
for (const auto &recipientId : qAsConst(recipientIds))
{
NotificationManagement::Address addr;
addr.addressId(recipientId);
addr.isRead(false);
const OperatorManagement::User user = m_mainDispatcher->ddsHandler()->dtcOpm()->user(recipientId);
addr.name(user.name());
addr.addressType(NotificationManagement::AddressType_def::Operator);
notif.recipientIds().push_back(addr);
DEBUG_LOG(m_logger) << "recipientId=" << addr.name()
<< ", addr.addressId=" << addr.addressId();
}
NotificationManagement::Address addrSender;
addrSender.name(saa->getSAAName().toStdString());
addrSender.addressType(NotificationManagement::AddressType_def::Service);
notif.sender(addrSender);
data.notification(notif);
} // end of getCreateNotificationRequestData()
void JsonDispatcher::createNotificationRequest(const int wsId,
const QString &title,
const NotificationManagement::MessageType messageType,
const NotificationManagement::NotificationType notificationType,
const QString &content,
const QStringList &args)
{
DEBUG_LOG(m_logger) << "createNotificationRequest"
<< ", content=" << content;
NotificationManagement::CreateNotification req;
getCreateNotificationRequestData(wsId, title, req, content, args);
if (req.notification().content().empty())
{
INFO_LOG(m_logger) << "req.notification().content().empty()";
return;
}
req.notification().messageType(messageType);
req.notification().notificationType(notificationType);
INFO_LOG(m_logger) << "trying to send CreateNotificationRequest";
if (req.notification().recipientIds().size() == 0)
{
INFO_LOG(m_logger) << "req.notification().recipientIds().size() == 0";
return;
}
INFO_LOG(m_logger) << "CreateNotificationRequest";
if (!m_ftcNom->createNotificationRequest(req, this))
{
ERROR_LOG(m_logger) << "Error requesting create data";
}
}
//-----------------------------------------------------------------------------
void JsonDispatcher::onCreateNotificationReply(const Core::FunctionReplyObject &replyObject)
{
INFO_LOG(m_logger) << "Got createReply with id " << replyObject.id() << " and errorString " << replyObject.translated();
if (replyObject.error())
{
ERROR_LOG(m_logger) << "Error requesting CreateNotification";
return;
}
else
{
DEBUG_LOG(m_logger) << "Requesting create data ok. Got new id:" << replyObject.id() << " ... ok.";
}
} // end of onNotificationCreateReply()
} // namespace AdlerJson