/** * @RDE_HEADER@ * PpmHandler for AdlerJson Plugin * $Id: ppm_handler.cpp 62899 2024-05-29 11:08:50Z ewert_j $ * $HeadURL: svn://slssvn/TDS/branches/product/features/ufom/Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/ppm_handler.cpp $ * $Author: ewert_j $ * Created by Julius Ewert, ESIM, 2024-03-20 */ #include "ppm_handler.h" #include "Core/core.hpp" #include #include "json_dispatcher.h" #include #include #include namespace AdlerJson { // See PPM::SpecialSensorStatus static const int ADLER_OPERATIONAL = 125; // static const int ADLER_CONDITIONALLY_OPERATIONAL = 126; static const int ADLER_NOT_OPERATIONAL = 127; PpmHandler::PpmHandler(QObject *parent) : QObject(parent) , m_logger(getLogger("PpmHandler")) , m_sendParticipantDataTimer(new QTimer(this)) { } bool PpmHandler::init(MainDispatcher *mainDispatcher) { m_mainDispatcher = mainDispatcher; m_ftcPpm = ParticipantManagement::Interface::Ftc::create(m_mainDispatcher->ddsHandler()->ddsConnector()); m_ftcPpm->enableModifyParticipantData(); m_ftcPpm->enableModifyPartyData(); m_ftcPpm->enableSetSimulatedPosition(); m_dtcPpm = ParticipantManagement::Interface::Dtc::create(m_mainDispatcher->ddsHandler()->ddsConnector()); DEBUG_LOG(m_logger) << "-------------<><><>------" << m_mainDispatcher->exerciseId(); if (!m_dtcPpm->enableParticipant() || !m_dtcPpm->enableDynamicParticipantData() || !m_dtcPpm->enableParticipantPosition()) { ERROR_LOG(m_logger) << "Can not read data from Ppm"; return false; } m_dtcPpm->enableType(); m_dtcPpm->waitForHistoricalDataParticipant(2000); m_dtcPpm->waitForHistoricalDataDynamicParticipantData(1000); m_dtcPpm->waitForHistoricalDataParticipantPosition(1000); m_sendParticipantDataTimer->setInterval(2 * 1000); connect(m_sendParticipantDataTimer, &QTimer::timeout, this, &PpmHandler::onSendParticipantDataTimeout); connect(m_dtcPpm.get(), &ParticipantManagement::Interface::Dtc::partyChanged, this, &PpmHandler::onPartyChanged); connect(m_dtcPpm.get(), &ParticipantManagement::Interface::Dtc::participantChanged, this, &PpmHandler::onParticipantChanged); connect(m_dtcPpm.get(), &ParticipantManagement::Interface::Dtc::participantPositionChanged, this, &PpmHandler::onParticipantPositionChanged); connect(m_dtcPpm.get(), &ParticipantManagement::Interface::Dtc::dynamicParticipantDataChanged, this, &PpmHandler::onDynamicParticipantDataChanged); return true; } void PpmHandler::modifyPartyAdlerProperties(const int wsId, const int assetId, const int partyId, const QString &saaName, const QString &szenarName, const ParticipantManagement::ConnectState &connectState, const bool isVerbund) { DEBUG_LOG(m_logger) << "modifyParty" << " assetId=" << assetId << " partyId=" << partyId << " saaName=" << saaName << " szenarName=" << szenarName << " connectState=" << connectState << " isVerbund=" << isVerbund; ParticipantManagement::Party requestParty; requestParty = m_dtcPpm->party(partyId); // Get old properties and then update them QJsonObject origObj = QJsonDocument::fromJson(requestParty.properties().c_str()).object(); // This might be empty after creation, but it's okay QJsonObject adlerPropertiesObj = origObj[QStringLiteral("Adler")].toObject(); adlerPropertiesObj[QStringLiteral("saaConnectedState")] = connectState.underlying(); adlerPropertiesObj[QStringLiteral("szenarName")] = szenarName; adlerPropertiesObj[QStringLiteral("saaName")] = saaName; adlerPropertiesObj[QStringLiteral("wsId")] = wsId; adlerPropertiesObj[QStringLiteral("assetId")] = ((connectState == ParticipantManagement::ConnectState::Connected) ? assetId : -1); // sicherung adlerPropertiesObj[QStringLiteral("isVerbund")] = isVerbund; const QByteArray &propertiesBa = requestParty.properties().c_str(); QJsonParseError parserError; QJsonDocument doc(QJsonDocument::fromJson(propertiesBa, &parserError)); if (parserError.error != QJsonParseError::NoError) { ERROR_LOG(m_logger) << "Error parsing json document: " << parserError.errorString() << " '" << propertiesBa << "'"; doc = QJsonDocument(); } DEBUG_LOG(m_logger) << "json to parse: " << doc.toJson(); QJsonObject obj(doc.object()); obj[QStringLiteral("Adler")] = adlerPropertiesObj; const QByteArray propertiesStr = QJsonDocument(obj).toJson(QJsonDocument::Compact); requestParty.properties() = propertiesStr.data(); m_ftcPpm->modifyPartyDataRequest(requestParty); } void PpmHandler::parseSQFS(const Feuerstellungsdaten &exSQFS, const int wsId) { DEBUG_LOG(m_logger) << "Start parse SQFS"; auto saa = m_mainDispatcher->jsonDispatcher()->getSAAByWs(wsId); if (!saa) { ERROR_LOG(m_logger) << "SQ WS from SAA but SAA with id " << wsId << " not found"; return; } const QString &verein = exSQFS.feuereinheit.verein; const ProjectDataManagement::L26_EffectingSystemType &internalType = exSQFS.typ; // The random number helps us identifying this message and its content inside the temp maps int rndNmbr = QRandomGenerator::global()->generate(); ParticipantManagement::ParticipantData pUdata = findParticipantDataByInternalName(rndNmbr, verein, saa->getIsVerbund()); // Here we Need to save the charges auto chargeMap = m_tempChargesMap.value(rndNmbr); chargeMap.insert(verein, QVector(exSQFS.geschosse)); m_tempChargesMap.insert(rndNmbr, chargeMap); QMap readyMap; readyMap.insert(verein, exSQFS.status); m_tempReadyMessage.insert(rndNmbr, readyMap); // we will use the mid point coordinate for the unit position auto posdMap = m_tempPositionMap.value(rndNmbr); AdlerJson::KoordinateMitHoehe middle; middle.koordinate.longitude = exSQFS.mittelpunkt.koordinate.longitude; middle.koordinate.latitude = exSQFS.mittelpunkt.koordinate.latitude; posdMap.insert(verein, middle); m_tempPositionMap.insert(rndNmbr, posdMap); // add custom function to get participant for own party! int superiorModId = 1000000007; ParticipantManagement::Participant pUnit = findParticipantByInternalName(verein, saa->getPartyId(), saa->getIsVerbund()); // We already had a "fake" Unit, we just need the tempmaps if (pUdata.modId() > 0) { m_sendParticipantDataTimer->start(); return; } // We have either an existing unit or not even a fake unit: then we will update or create one if (pUnit.id() > 0) { // Update UnitPos //what about others? // Nothing to do as we dont Update Unit Symbols by itself. // But Update Charges // It still applies, that units never publsh their positions.. // see Lts/Services/ParticipantManagement/ParticipantHandling/participant.cpp:37 sendChargesFromTempMap(rndNmbr, saa->getPartyId(), saa->getIsVerbund()); sendReadyMessageFromTempMap(rndNmbr, saa->getPartyId(), saa->getIsVerbund()); sendPositionFromTempMap(rndNmbr, saa->getPartyId(), saa->getIsVerbund()); // then remove again here m_tempChargesMap.remove(rndNmbr); m_tempPositionMap.remove(rndNmbr); m_tempinternalNameMap.remove(rndNmbr); m_tempReadyMessage.remove(rndNmbr); } else { std::vector list; // Get UnitType const ParticipantManagement::Type unitType = m_dtcPpm->getTypeFromAdlerType(waffensystemtypMap.key(internalType.underlying()), true, saa->getIsVerbund()); // find parent First // modId will be 0 if not found and set if found // append root if set const auto root = findRootUnit(saa->getPartyId()); if (root.id() > 0) { DEBUG_LOG(m_logger) << "Append FS Unit to root Unit"; list.push_back(root); } ParticipantManagement::ParticipantData data = findParticipantDataByInternalName(rndNmbr, verein, saa->getIsVerbund()); // update data.id(0); data.partyId(saa->getPartyId()); data.externalId(0); data.hierarchyNumber(1); data.name(verein.toStdString()); data.designator(verein.toStdString()); data.symbol(unitType.symbol()); data.tacticalIdentifier(verein.toStdString()); data.typeId(unitType.id()); data.modId(superiorModId); data.isDeleted(false); data.isSimulated(false); data.superiorModId(root.modId()); list.push_back(data); ParticipantManagement::ModifyParticipantDataRequest newRequest; newRequest.requestId(rndNmbr); // we store the random number here to adress later for the postion newRequest.data(list); // add to m_tempPartDataList m_tempPartDataList.insert(rndNmbr, list); // Send Request m_ftcPpm->modifyParticipantDataRequest(newRequest, this); } } void PpmHandler::parseSQWS(const Waffensystemdaten &exSQWS, const int wsId) { DEBUG_LOG(m_logger) << "Start parse SQWS"; auto saa = m_mainDispatcher->jsonDispatcher()->getSAAByWs(wsId); if (!saa) { ERROR_LOG(m_logger) << "SQ WS from SAA but SAA with id " << wsId << " not found"; return; } const ProjectDataManagement::L27_EffectorId &internalName = exSQWS.kenner; const ProjectDataManagement::L26_EffectingSystemType &internalType = exSQWS.typ; const auto &charges = exSQWS.geschosse; const QString &verein = exSQWS.einheit.verein; // The random number helps us identifying this message and its content inside the temp maps int currentRandomNumber = QRandomGenerator::global()->generate(); // overrides currentRandomNumber if the verin is already inside ParticipantManagement::ParticipantData pUdata = findParticipantDataByInternalName(currentRandomNumber, verein, saa->getIsVerbund()); // Save Name for later auto nameVec = m_tempinternalNameMap.value(currentRandomNumber); nameVec.append(waffensystemkennerMap.key(internalName)); m_tempinternalNameMap.insert(currentRandomNumber, nameVec); QMap> map = m_tempChargesMap.value(currentRandomNumber); map.insert(waffensystemkennerMap.key(internalName), charges); m_tempChargesMap.insert(currentRandomNumber, map); // add custom function to get participant for own party! ParticipantManagement::Participant pp = findParticipantByInternalName(waffensystemkennerMap.key(internalName), saa->getPartyId(), saa->getIsVerbund()); // Save Position and internal name in temp Maps: auto posMap = m_tempPositionMap.value(currentRandomNumber); posMap.insert(waffensystemkennerMap.key(internalName), exSQWS.koordinate); m_tempPositionMap.insert(currentRandomNumber, posMap); if (pp.id() > 0) { // Update DEBUG_LOG(m_logger) << "Found Participant only update pos"; sendPositionFromTempMap(currentRandomNumber, saa->getPartyId(), saa->getIsVerbund()); sendChargesFromTempMap(currentRandomNumber, saa->getPartyId(), saa->getIsVerbund()); // then remove again here m_tempChargesMap.remove(currentRandomNumber); m_tempPositionMap.remove(currentRandomNumber); m_tempinternalNameMap.remove(currentRandomNumber); m_randomNumbers.removeAll(currentRandomNumber); } else { // need to map those inside the ppmdtc const ParticipantManagement::Type type = m_dtcPpm->getTypeFromAdlerType(waffensystemtypMap.key(internalType.underlying()), false, saa->getIsVerbund()); DEBUG_LOG(m_logger) << "found type with id: " << type.id() << " for name: " << waffensystemtypMap.key(internalType); if (type.id() <= 0) { ERROR_LOG(m_logger) << "NO Type Found"; return; } std::vector list = m_tempPartDataList.value(currentRandomNumber); // Find Unit first) uint superiorModId = QRandomGenerator::global()->generate() / 2; ParticipantManagement::Participant pUnit = findParticipantByInternalName(verein, saa->getPartyId(), saa->getIsVerbund()); DEBUG_LOG(m_logger) << "Looking for Verein: " << verein << " found Unit: " << pUnit.id() << " foundData: " << pUdata.modId(); if (pUdata.modId() > 0) { superiorModId = pUdata.modId(); DEBUG_LOG(m_logger) << "FOUND UNIT in list use their modId " << superiorModId << " RND: " << currentRandomNumber; } else if (pUnit.id() > 0) { // Mod Unit ParticipantManagement::ParticipantData pUData = m_dtcPpm->convertParticipantToParticipantData(pUnit); pUData.modId(superiorModId); pUData.hierarchyNumber(1); list.push_back(pUData); m_tempPartDataList.insert(currentRandomNumber, list); DEBUG_LOG(m_logger) << "CREATE Uit from ppm " << superiorModId << " RND: " << currentRandomNumber; ParticipantManagement::Participant tmp = pUnit; while (tmp.superiorId() > 0) { tmp = m_dtcPpm->participant(tmp.superiorId()); pUData = m_dtcPpm->convertParticipantToParticipantData(tmp); list.push_back(pUData); } } else { // create new // find parent First // modId will be 0 if not found and set if found // append root if set const auto root = findRootUnit(saa->getPartyId()); if (root.id() > 0) { DEBUG_LOG(m_logger) << "Append to Unit" << superiorModId << " RND: " << currentRandomNumber; list.push_back(root); } const ParticipantManagement::Type unitType = m_dtcPpm->getTypeFromAdlerType(waffensystemtypMap.key(internalType), true, saa->getIsVerbund()); DEBUG_LOG(m_logger) << "CREATE NEW Unit use supId" << superiorModId << " RND: " << currentRandomNumber; ParticipantManagement::ParticipantData data; data.id(0); data.partyId(saa->getPartyId()); data.externalId(0); data.hierarchyNumber(1); data.name(verein.toStdString()); data.designator(verein.toStdString()); data.symbol(unitType.symbol()); data.tacticalIdentifier(verein.toStdString()); data.typeId(unitType.id()); // use korrect type data.modId(superiorModId); data.isDeleted(false); data.isSimulated(false); data.superiorModId(root.modId()); // Add to request List list.push_back(data); } // Create DEBUG_LOG(m_logger) << "Create new Participant" << " RND: " << currentRandomNumber; ParticipantManagement::ParticipantData data; data.id(0); data.partyId(saa->getPartyId()); data.externalId(0); data.hierarchyNumber(list.size()); data.name(waffensystemkennerMap.key(internalName).toStdString()); data.designator(waffensystemkennerMap.key(internalName).toStdString()); data.symbol(type.symbol()); data.tacticalIdentifier(verein.toStdString()); data.typeId(type.id()); // use korrect type data.modId(QRandomGenerator::global()->generate() / 2); data.isDeleted(false); data.isSimulated(false); data.superiorModId(superiorModId); // ParticipantManagement::ModifyParticipantDataRequest *newRequest = new ParticipantManagement::ModifyParticipantDataRequest(); // newRequest->requestId(rndNmbr); //we store the random number here to adress later for the postion list.push_back(data); m_tempPartDataList.insert(currentRandomNumber, list); m_randomNumbers.append(currentRandomNumber); m_sendParticipantDataTimer->start(); } } void PpmHandler::parseSQLP(const Ladeplatzdaten &exSQLP, const int wsId) { DEBUG_LOG(m_logger) << "START parse SQLP"; auto saa = m_mainDispatcher->jsonDispatcher()->getSAAByWs(wsId); if (!saa) { ERROR_LOG(m_logger) << "SQ LP from SAA but SAA with id " << wsId << " not found"; return; } const QString &verein = exSQLP.einheit.verein; // lets assume that there is no "unit for that and can be added manualy int rndNmbr = QRandomGenerator::global()->generate(); // Save Position and internal name in temp Maps: auto posdMap = m_tempPositionMap.value(rndNmbr); posdMap.insert(verein, exSQLP.koordinate); m_tempPositionMap.insert(rndNmbr, posdMap); QMap> map; map.insert(verein, QVector(exSQLP.geschosse)); m_tempChargesMap.insert(rndNmbr, map); auto nameVec = m_tempinternalNameMap.value(rndNmbr); nameVec.append(verein); m_tempinternalNameMap.insert(rndNmbr, nameVec); // add custom function to get participant for own party! ParticipantManagement::Participant pp = findParticipantByInternalName(verein, saa->getPartyId(), saa->getIsVerbund()); const ParticipantManagement::Type type = m_dtcPpm->getTypeFromAdlerType(QStringLiteral("LP"), false, saa->getIsVerbund()); if (pp.id() > 0) { // Update pos sendPositionFromTempMap(rndNmbr, saa->getPartyId(), saa->getIsVerbund()); // update charge sendChargesFromTempMap(rndNmbr, saa->getPartyId(), saa->getIsVerbund()); // then remove again here m_tempChargesMap.remove(rndNmbr); m_tempPositionMap.remove(rndNmbr); m_tempinternalNameMap.remove(rndNmbr); } else { // Create new Participant // Create DEBUG_LOG(m_logger) << "Create new Participant LP"; std::vector list; // find parent First // modId will be 0 if not found and set if found // append root if set const auto root = findRootUnit(saa->getPartyId()); if (root.id() > 0) { DEBUG_LOG(m_logger) << "Append LP to Unit"; list.push_back(root); } ParticipantManagement::ParticipantData data; data.id(0); data.partyId(saa->getPartyId()); data.externalId(0); data.hierarchyNumber(1); data.name(verein.toStdString()); data.designator(verein.toStdString()); data.symbol(type.symbol()); data.tacticalIdentifier(verein.toStdString()); data.typeId(type.id()); // use korrect type data.modId(1000000001); data.isDeleted(false); data.isSimulated(false); data.superiorModId(root.modId()); ParticipantManagement::ModifyParticipantDataRequest *newRequest = new ParticipantManagement::ModifyParticipantDataRequest(); newRequest->requestId(rndNmbr); // we store the random number here to adress later for the postion list.push_back(data); newRequest->data(list); // add to m_tempPartDataList m_tempPartDataList.insert(rndNmbr, list); m_ftcPpm->modifyParticipantDataRequest(*newRequest, this); } } void PpmHandler::removeAllSAAParticipants(const int partyId) { // find all participants frpom this SAA const auto &participants = findAllSAAParticipants(partyId); if (participants.isEmpty()) return; ParticipantManagement::ModifyParticipantDataRequest req; std::vector list; for (const auto &part : qAsConst(participants)) { const ParticipantManagement::Type &participantType = m_dtcPpm->type(part.typeId()); bool isUnitType = (participantType.typeClass() == ParticipantManagement::TypeClass::Unit) || (participantType.typeClass() == ParticipantManagement::TypeClass::SupportUnit); if (isUnitType) continue; ParticipantManagement::ParticipantData pToDel; pToDel.id() = part.id(); pToDel.isDeleted() = true; list.push_back(pToDel); } req.data(list); DEBUG_LOG(m_logger) << "Remove all participants (without Units): " << list.size(); m_ftcPpm->modifyParticipantDataRequest(req, this); } // void PpmHandler::onPartyChanged(const ParticipantManagement::Party &data, DataModification modType) // { // } void PpmHandler::onCreateParticipant() { ParticipantManagement::ParticipantData data; data.id(0); data.partyId(3); data.externalId(0); data.hierarchyNumber(1); data.name("Noah Thiering"); data.designator("Hund"); data.symbol("AP6D|CMPL|10011000001308000000"); data.tacticalIdentifier("14"); data.typeId(177); data.modId(1000000001); data.isDeleted(false); data.isSimulated(false); data.superiorModId(0); ParticipantManagement::ModifyParticipantDataRequest *newRequest = new ParticipantManagement::ModifyParticipantDataRequest(); newRequest->requestId(2); std::vector liste; liste.push_back(data); newRequest->data(liste); // ParticipantManagement::ParticipantData data; // int32_t id, // int32_t partyId, // int32_t hierarchyNumber, // const ::dds::core::optional< int32_t >& equipmentNumber, // int32_t typeId, // ::rti::core::int64 externalId, // const std::string& symbol, // const std::string& name, // const std::string& identification, // const std::string& designator, // const std::string& tacticalIdentifier, // const std::string& info, // const ParticipantManagement::ParticipantAssignState& assignState, // bool isSimulated, // bool isDeleted, // const std::string& properties, // int32_t modId, // int32_t superiorModId); // data.id(0); // data.partyId(1); // data.typeId(5); // ParticipantManagement::ModifyParticipantDataRequest *newRequest = new ParticipantManagement::ModifyParticipantDataRequest(); // newRequest->requestId(9999); // std::vector liste; // liste.push_back(data); // newRequest->data(liste); // m_ftcPpm->modifyParticipantDataRequest(*newRequest); m_ftcPpm->modifyParticipantDataRequest(*newRequest); } void PpmHandler::getParticipants() { if (!m_dtcPpm) return; for (const auto &participant : m_dtcPpm->participants()) { onParticipantChanged(participant, DataModification::eAdded); } } bool PpmHandler::isEqNumberInExercise(const int equipmentNumber) const { bool result = m_dtcPpm->participantIdByEquipmentNumber(equipmentNumber) > 0; return result; } void PpmHandler::onParticipantChanged(const ParticipantManagement::Participant &participant, DataModification modType) { if (!participant.equipmentNumber().is_set()) { for (auto it = m_participantIdsByEqNo.begin(); it != m_participantIdsByEqNo.end(); ++it) if (it.value() == participant.id()) { m_participantIdsByEqNo.remove(it.key()); break; } return; } if (participant.externalId() > 0) return; QJsonObject headerJson; headerJson[QStringLiteral("equipmentNumber")] = participant.equipmentNumber().get(); headerJson[QStringLiteral("timeStamp")] = m_mainDispatcher->ddsHandler()->partitionTime(); if (modType == DataModification::eRemoved) { m_participantIdsByEqNo.remove(participant.equipmentNumber().get()); QJsonObject telegram; QJsonObject data; data[QStringLiteral("header")] = headerJson; telegram[QStringLiteral("data")] = data; telegram[QStringLiteral("type")] = QStringLiteral("UFomHlaRemoveParticipant"); // telegram[QStringLiteral("typeId")] = TrainingAreaBase::TetraIcd::TelegramId::UFOMHLA_REMOVE_PARTICIPANT; emit participantChanged(telegram); return; } if (m_participantIdsByEqNo.contains(participant.equipmentNumber().get())) { if (m_participantIdsByEqNo[participant.equipmentNumber().get()] != participant.id()) { for (auto it = m_participantIdsByEqNo.begin(); it != m_participantIdsByEqNo.end(); ++it) if (it.value() == participant.id()) { m_participantIdsByEqNo.remove(it.key()); break; } m_participantIdsByEqNo[participant.equipmentNumber().get()] = participant.id(); } } else m_participantIdsByEqNo[participant.equipmentNumber().get()] = participant.id(); ParticipantManagement::DynamicParticipantData dynamicData = m_dtcPpm->dynamicParticipantData(participant.id()); ParticipantManagement::Party party = m_dtcPpm->party(participant.partyId()); ParticipantManagement::Type type = m_dtcPpm->type(participant.typeId()); ParticipantManagement::ParticipantPosition partPos = m_dtcPpm->participantPosition(participant.id()); QJsonObject telegram; QJsonObject geoPositionObj; geoPositionObj[QStringLiteral("lon")] = partPos.pos().lon(); geoPositionObj[QStringLiteral("lat")] = partPos.pos().lat(); geoPositionObj[QStringLiteral("z")] = partPos.pos().z(); QJsonObject data; data[QStringLiteral("header")] = headerJson; data[QStringLiteral("status")] = static_cast(m_dtcPpm->youngestStateInformation(dynamicData.stateHistory()).vulnerabilityState().underlying()); data[QStringLiteral("forceIdentifier")] = static_cast(party.hlaForceIdentifier().underlying()); data[QStringLiteral("geoPosition")] = geoPositionObj; telegram[QStringLiteral("data")] = data; telegram[QStringLiteral("type")] = QStringLiteral("UFomHlaAddParticipant"); // telegram[QStringLiteral("typeId")] = TrainingAreaBase::TetraIcd::TelegramId::UFOMHLA_ADD_PARTICIPANT; emit participantChanged(telegram); } void PpmHandler::onParticipantPositionChanged(const ParticipantManagement::ParticipantPosition &position, DataModification modType) { if (modType == DataModification::eRemoved) return; const bool participantIsExternal = m_dtcPpm->participant(position.participantId()).externalId() > 0; if (participantIsExternal) { handleParticipantPositionChangedExternal(position); } else { handleParticipantPositionChangedInternal(position); } } void PpmHandler::onDynamicParticipantDataChanged(const ParticipantManagement::DynamicParticipantData &dynPartData, DataModification modType) { if (modType == DataModification::eRemoved) return; ParticipantManagement::Participant part = m_dtcPpm->participant(dynPartData.participantId()); if (part.externalId() > 0 || !part.equipmentNumber().is_set()) return; QJsonObject telegram; // telegram[QStringLiteral("typeId")] = TrainingAreaBase::TetraIcd::TelegramId::UFOMHLA_VULNERABILITY_STATE_CHANGED; telegram[QStringLiteral("type")] = QStringLiteral("UFomHlaVulnerabilityStateChanged"); QJsonObject headerJson; headerJson[QStringLiteral("equipmentNumber")] = part.equipmentNumber().get(); headerJson[QStringLiteral("timeStamp")] = m_mainDispatcher->ddsHandler()->partitionTime(); QJsonObject data; data[QStringLiteral("header")] = headerJson; data[QStringLiteral("status")] = static_cast(m_dtcPpm->youngestStateInformation(dynPartData.stateHistory()).vulnerabilityState().underlying()); telegram[QStringLiteral("data")] = data; emit participantChanged(telegram); } void PpmHandler::onSendParticipantDataTimeout() { m_sendParticipantDataTimer->stop(); if (m_randomNumbers.empty()) ERROR_LOG(m_logger) << "Cant Send ParticipantData RandomNumbers is empty"; ParticipantManagement::ModifyParticipantDataRequest *newRequest = new ParticipantManagement::ModifyParticipantDataRequest(); int currentRandomNumber = m_randomNumbers.first(); newRequest->requestId(currentRandomNumber); // we store the random number here to adress later for the postion newRequest->data(m_tempPartDataList.value(currentRandomNumber)); m_ftcPpm->modifyParticipantDataRequest(*newRequest, this); } void PpmHandler::handleParticipantPositionChangedExternal(const ParticipantManagement::ParticipantPosition &position) { ParticipantManagement::Participant part = m_dtcPpm->participant(position.participantId()); const bool participantPreviouslyMounted = m_mountedParticipants.contains(part.equipmentNumber().get()); if (!participantPreviouslyMounted) return; const bool mountedOnOtherParticipant = position.mountedOn().is_set(); if (!mountedOnOtherParticipant) { sendParticipantUnmount(part); return; } const bool newMountedParticipantIsExternal = m_dtcPpm->participant(position.mountedOn().get()).externalId() != 0; if (newMountedParticipantIsExternal) { // If the new mounted participant is internal the entry in the Hashmap is still used. So dont remove it! m_mountedParticipants.remove(part.equipmentNumber().get()); } } void PpmHandler::handleParticipantPositionChangedInternal(const ParticipantManagement::ParticipantPosition &position) { DEBUG_LOG(m_logger) << "handleParticipantPositionChangedInternal"; ParticipantManagement::Participant part = m_dtcPpm->participant(position.participantId()); const bool hasMountedParticipants = position.mountedParticipants().is_set() && position.mountedParticipants().get().size() > 0; if (hasMountedParticipants) { const QVector mountedParticipantIds(position.mountedParticipants().get().begin(), position.mountedParticipants().get().end()); for (int mountedParticipantId : qAsConst(mountedParticipantIds)) { ParticipantManagement::Participant mountedParticipant = m_dtcPpm->participant(mountedParticipantId); const bool isExternal = mountedParticipant.externalId() > 0; const bool wasMountedOnThisPart = m_mountedParticipants.contains(mountedParticipant.equipmentNumber().get()) && m_mountedParticipants[mountedParticipant.equipmentNumber().get()] == part.equipmentNumber().get(); if (!isExternal || wasMountedOnThisPart) continue; if (!part.equipmentNumber().is_set()) continue; sendParticipantMount(mountedParticipant, part); } if (part.equipmentNumber().is_set()) { sendParticipantPositionChanged(position); } } } void PpmHandler::sendParticipantMount(const ParticipantManagement::Participant &part, const ParticipantManagement::Participant &partToBeMounted) // Passanger, Vehicle { m_mountedParticipants.insert(part.equipmentNumber().get(), partToBeMounted.equipmentNumber().get()); ERROR_LOG(m_logger) << "Mount Participant"; QJsonObject telegram; // telegram[QStringLiteral("typeId")] = TrainingAreaBase::TetraIcd::TelegramId::UFOMHLA_PARTICIPANT_MOUNT; telegram[QStringLiteral("type")] = QStringLiteral("UFomHlaParticipantMount"); QJsonObject headerJson; headerJson[QStringLiteral("equipmentNumber")] = part.equipmentNumber().get(); headerJson[QStringLiteral("timeStamp")] = m_mainDispatcher->ddsHandler()->partitionTime(); QJsonObject data; data[QStringLiteral("header")] = headerJson; data[QStringLiteral("mountedOn")] = QJsonValue(partToBeMounted.equipmentNumber().get()); telegram[QStringLiteral("data")] = data; emit participantMount(telegram); } void PpmHandler::sendParticipantUnmount(const ParticipantManagement::Participant &participant) { m_mountedParticipants.remove(participant.equipmentNumber().get()); DEBUG_LOG(m_logger) << "Unmount Participant"; QJsonObject telegram; // telegram[QStringLiteral("typeId")] = TrainingAreaBase::TetraIcd::TelegramId::UFOMHLA_PARTICIPANT_UNMOUNT; telegram[QStringLiteral("type")] = QStringLiteral("UFomHlaParticipantUnmount"); QJsonObject headerJson; headerJson[QStringLiteral("equipmentNumber")] = participant.equipmentNumber().get(); headerJson[QStringLiteral("timeStamp")] = m_mainDispatcher->ddsHandler()->partitionTime(); QJsonObject data; data[QStringLiteral("header")] = headerJson; telegram[QStringLiteral("data")] = data; emit participantUnmount(telegram); } void PpmHandler::sendParticipantPositionChanged(const ParticipantManagement::ParticipantPosition &position) { ParticipantManagement::Participant part = m_dtcPpm->participant(position.participantId()); QJsonObject telegram; // telegram[QStringLiteral("typeId")] = TrainingAreaBase::TetraIcd::TelegramId::UFOMHLA_POSITION_CHANGED; telegram[QStringLiteral("type")] = QStringLiteral("UFomHlaPositionChanged"); QJsonObject headerJson; headerJson[QStringLiteral("equipmentNumber")] = part.equipmentNumber().get(); headerJson[QStringLiteral("timeStamp")] = m_mainDispatcher->ddsHandler()->partitionTime(); QJsonObject data; data[QStringLiteral("header")] = headerJson; data[QStringLiteral("lon")] = position.pos().lon(); data[QStringLiteral("lat")] = position.pos().lat(); data[QStringLiteral("z")] = position.pos().z(); telegram[QStringLiteral("data")] = data; emit participantChanged(telegram); } void PpmHandler::onModifyParticipantDataReply(const Core::FunctionReplyObject &reply) { DEBUG_LOG(m_logger) << "Got Function Reply " << reply.str(); if (!reply.error()) { if (!m_tempPartDataList.contains(reply.id())) return; const int partyId = m_tempPartDataList.value(reply.id()).data()->partyId(); if (!m_mainDispatcher->jsonDispatcher()->getSAAByParty(partyId)) return; const bool isVerbund = m_mainDispatcher->jsonDispatcher()->getSAAByParty(partyId)->getIsVerbund(); DEBUG_LOG(m_logger) << "Got ModifyParticipant Reply SEND from temp Maps (partyId: " << partyId << " , isVerbund: " << isVerbund << ")"; if (m_tempPositionMap.contains(reply.id())) sendPositionFromTempMap(reply.id(), partyId, isVerbund); if (m_tempChargesMap.contains(reply.id())) sendChargesFromTempMap(reply.id(), partyId, isVerbund); if (m_tempReadyMessage.contains(reply.id())) sendReadyMessageFromTempMap(reply.id(), partyId, isVerbund); } else { ERROR_LOG(m_logger) << "GOT ModifyParticipant Replay with error " << reply.translated(); } // then remove any entries from Map m_tempChargesMap.remove(reply.id()); m_tempPositionMap.remove(reply.id()); m_tempinternalNameMap.remove(reply.id()); m_tempReadyMessage.remove(reply.id()); if (m_randomNumbers.contains(reply.id())) { m_randomNumbers.removeAll(reply.id()); if (!m_randomNumbers.isEmpty() && !m_sendParticipantDataTimer->isActive()) m_sendParticipantDataTimer->start(); } m_tempPartDataList.remove(reply.id()); } void PpmHandler::onModifyPartyDataReply(const Core::FunctionReplyObject &reply) { DEBUG_LOG(m_logger) << "Got Function Reply " << reply.str(); } QList PpmHandler::findAllSAAParticipants(const int partyId) { QList ret; const auto &list = m_dtcPpm->participants(); for (const auto &part : qAsConst(list)) { if (part.partyId() != partyId || part.isDeleted()) continue; if (!m_dtcPpm->isArtilleryType(part.typeId())) continue; if (part.name().empty()) continue; ret.append(part); } return ret; } ParticipantManagement::ParticipantData PpmHandler::findParticipantDataByInternalName(int &randomNumberInOut, const QString &internalName, const bool isVerbund) { ParticipantManagement::ParticipantData ret; for (const auto &randomNumber : m_tempPartDataList.keys()) { for (const auto &part : m_tempPartDataList.value(randomNumber)) { if (part.name() != internalName.toStdString()) continue; if (m_dtcPpm->isAdlerVerbund(part.typeId()) != isVerbund) continue; randomNumberInOut = randomNumber; return part; } } return ret; } ParticipantManagement::ParticipantData PpmHandler::findRootUnit(const int partyId) { ParticipantManagement::ParticipantData ret; const auto &list = m_dtcPpm->participants(); for (const auto &part : qAsConst(list)) { if (part.partyId() != partyId || part.isDeleted()) continue; if (m_dtcPpm->type(part.typeId()).typeClass() != ParticipantManagement::TypeClass::Unit) continue; if (m_dtcPpm->isArtilleryType(part.typeId())) continue; if (part.superiorId() > 0) continue; ret = m_dtcPpm->convertParticipantToParticipantData(part); } return ret; } ParticipantManagement::Participant PpmHandler::findParticipantByInternalName(const QString &internalName, const int partyId, const bool isVerbund) { ParticipantManagement::Participant pp; DEBUG_LOG(m_logger) << "START find participant: " << internalName; auto participantMap = m_dtcPpm->participants(); for (const auto &part : qAsConst(participantMap)) { if (part.partyId() != partyId || part.isDeleted()) continue; if (!m_dtcPpm->isArtilleryType(part.typeId())) continue; if (m_dtcPpm->isAdlerVerbund(part.typeId()) != isVerbund) continue; if (part.name() != internalName.toStdString()) continue; pp = part; } return pp; } void PpmHandler::onPartyChanged(const ParticipantManagement::Party &data, DataModification modType) { DEBUG_LOG(m_logger) << LOGFUNCTION << ", modType: " << modType; if (modType == DataModification::eRemoved) return; const ParticipantManagement::ConnectState connectState = m_dtcPpm->saaConnectedState(data.id()); DEBUG_LOG(m_logger) << "++++ connectState: " << connectState; // only in connect state disconnecting: delete all ADLER objects and force the disconnection if (connectState != ParticipantManagement::ConnectState::Disconnecting) return; auto saa = m_mainDispatcher->jsonDispatcher()->getSAAByParty(data.id()); if (!saa) return; DEBUG_LOG(m_logger) << "++++ szenarName: " << saa->getSzenarName() << ", wsId: " << saa->getWsId(); EXStatus status; status.status = Szenarstatus::Gestoppt; m_mainDispatcher->jsonDispatcher()->onSAAStatus(saa->getWsId(), status); m_mainDispatcher->jsonDispatcher()->onSAADeregistration(saa->getWsId(), data.id()); } void PpmHandler::sendPositionFromTempMap(int rndNmbr, const int partyId, const bool isVerbund) { const QMap posMap = m_tempPositionMap.value(rndNmbr); const QList partList; // we need the participant Id now const auto &nameVec = m_tempinternalNameMap.value(rndNmbr); QMapIterator it(posMap); while (it.hasNext()) { it.next(); ParticipantManagement::Participant pp = findParticipantByInternalName(it.key(), partyId, isVerbund); if (pp.id() <= 0) return; TrainingAreaBase::EquipmentPosition equipPosReq; Core::GeoPosition tlnPos; TrainingAreaBase::TabPosition tabPos; tlnPos.lat(it.value().koordinate.latitude * (M_PI / 180.0)); tlnPos.lon(it.value().koordinate.longitude * (M_PI / 180.0)); // Ignore Adler Z Value due to different elevation maps // tlnPos.z(it.value().hoehe); tabPos.position(tlnPos); tabPos.reportingSource(Core::PositionReportingSource::SetByOperator); equipPosReq.sampleTime(m_mainDispatcher->ddsHandler()->partitionTime()); equipPosReq.position(tabPos); equipPosReq.equipmentNumber(-1 * pp.id()); // equipPosReq.mountedParticipants(); // equipPosReq.orientation(); m_mainDispatcher->ddsHandler()->dtsTab()->publish(equipPosReq); } } void PpmHandler::sendChargesFromTempMap(const int rndNmbr, const int partyId, const bool isVerbund) { // No ammunition supply is displayed in ADLER standalone mode if (!isVerbund) return; const auto &charges = m_tempChargesMap.value(rndNmbr); const ParticipantManagement::Type type = m_dtcPpm->getTypeFromAdlerType(QStringLiteral("LP"), false, isVerbund); for (const auto &charge : charges.toStdMap()) { TrainingAreaBase::EquipmentGearChargeState gearChargeState; TrainingAreaBase::GearCharge gc; ParticipantManagement::Participant participant = findParticipantByInternalName(charge.first, partyId, isVerbund); const QVector geschosse = sumUpAnzahlForSameGeschoss(charge.second); for (auto c : geschosse) { int chargeTypeId = m_mainDispatcher->pdmHandler()->getDtc()->getChargeTypeIdFromAdlerCharges(c.geschosssorte); // if geschosssorte is null, we cannot get a valid chargeTypeId for it // this happens when the SAA is sending "Ladung" without a valid geschosssorte) if (chargeTypeId <= 0) continue; DEBUG_LOG(m_logger) << "---- participant.id:" << participant.id() << (participant.typeId() == type.id() ? " (Ladeplatz)" : ""); QVector gearTypesIds = m_mainDispatcher->pdmHandler()->getDtc()->gearTypeIdFromChargeTypeId(chargeTypeId); // Here only one gearType should be found gc.gearTypeId() = !gearTypesIds.isEmpty() ? gearTypesIds.first() : 0; gc.gearId(participant.id()); gc.chargeTypeId(chargeTypeId); gc.remain() = c.anzahl; gearChargeState.charge().push_back(gc); } gearChargeState.sampleTime(m_mainDispatcher->ddsHandler()->partitionTime()); gearChargeState.equipmentNumber(-1 * participant.id()); m_mainDispatcher->ddsHandler()->dtsTab()->publish(gearChargeState); } m_tempChargesMap.remove(rndNmbr); } void PpmHandler::sendReadyMessageFromTempMap(int rndNmbr, const int partyId, const bool isVerbund) { const auto &readyMap = m_tempReadyMessage.value(rndNmbr); for (const auto &readyMsg : readyMap.toStdMap()) { ParticipantManagement::Participant participant = findParticipantByInternalName(readyMsg.first, partyId, isVerbund); TrainingAreaBase::SpecialSensorData ssData; ssData.sampleTime(m_mainDispatcher->ddsHandler()->partitionTime()); ssData.operationalState(convertStatus_L28(readyMsg.second)); ssData.equipmentNumber(-1 * participant.id()); m_mainDispatcher->ddsHandler()->dtsTab()->publish(ssData); } m_tempReadyMessage.remove(rndNmbr); } int PpmHandler::convertStatus_L28(const ProjectDataManagement::L28_Status &status) { switch (status.underlying()) { case ProjectDataManagement::L28_Status::Einsatzbereit: return ADLER_OPERATIONAL; case ProjectDataManagement::L28_Status::NichtEinsatzbereit: return ADLER_NOT_OPERATIONAL; default: WARNING_LOG(m_logger) << "Status " << status << " not handled in switch"; break; } return -1; } QVector PpmHandler::sumUpAnzahlForSameGeschoss(const QVector &geschosse) const { using GeschossInfo = std::tuple; QMap summedUpGeschosseMap; for (const Geschoss &geschoss : geschosse) { const GeschossInfo geschossInfo(geschoss.geschosssorte, geschoss.ladung, geschoss.zuender); if (summedUpGeschosseMap.contains(geschossInfo)) { summedUpGeschosseMap[geschossInfo].anzahl += geschoss.anzahl; continue; } summedUpGeschosseMap.insert(geschossInfo, geschoss); } return summedUpGeschosseMap.values().toVector(); } } // namespace AdlerJson