/** * @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 #include #include #include #include #include #include #include #include namespace AdlerJson { JsonDispatcher::JsonDispatcher(TrainingAreaBase::Plugin::DataProvider *pdp, const std::shared_ptr &recvFromTabCb, const std::shared_ptr &sendToTabCb, NGL::Settings *settings, std::shared_ptr &ddsHandler, std::shared_ptr &flmHandler, MainDispatcher *mainDispatcher) : TrainingAreaBase::Interface::Fts::AdlerCalculateTrajectoryRequestHandler(ddsHandler->ftsTab()) , m_recvFromTabCb(recvFromTabCb) , m_recvFromWebsocket(std::make_shared(recvFromTabCb, sendToTabCb, this, mainDispatcher->aesHandler().get(), mainDispatcher->ppmHandler().get())) , m_sendToWebsocket(std::make_shared(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()) , 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(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 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 JsonDispatcher::sendToTab() { return std::static_pointer_cast(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 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 &saa) { return saa.get()->getPartyId() == partyId; }); return it != m_saaInterfaces.end() ? *it : nullptr; } std::shared_ptr JsonDispatcher::getSAAByName(const QString &saaName) { auto it = std::find_if(m_saaInterfaces.begin(), m_saaInterfaces.end(), [saaName](const std::shared_ptr &saa) { return saa.get()->getSAAName() == saaName; }); return it != m_saaInterfaces.end() ? *it : nullptr; } std::shared_ptr 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 &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(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(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","Show Participant Info
")) .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", "Status: %1
Description: %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","Show Participant Info
Unit: %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(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(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 = 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(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("
"); const int partyId = saa->getPartyId(); const ParticipantManagement::Party party = m_ppmHandler->getDtc()->party(partyId); const QString partyStr = QString::fromStdString(party.name()); std::vector 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 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 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