/** * Connect to ngs / hla and provide the basic interface * $Id$ * $HeadURL$ * $Author$ * Created by Christian Ehrlicher, SLSE, 2017-04-07 */ #include "ngs_connector.h" #include "ngs_handler.h" #include "ppm_handler.h" #include "damage_model.h" #include #include #include #include #include "rpr/rpr_observer_human_cb_notifier.h" #include "rpr/rpr_observer_groundvehicle_cb_notifier.h" #include "rpr/rpr_observer_longbow_apache_cb_notifier.h" #include "rpr/rpr_observer_designator_cb_notifier.h" #include "rpr/rpr_ia_actionrequest.h" #include "rpr/rpr_ia_data.h" #include "rpr/rpr_ia_engagementreply.h" #include "rpr/rpr_ia_eventreport.h" #include "rpr/rpr_ia_munitiondetonation.h" #include "rpr/rpr_ia_weaponfire.h" //#include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // We should not instantiate Apaches. Therefore "addLongbowApache()" should be deprecated and these includes are unnecessary: #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace AreaEffectSimulation; namespace HighLevelArchitecture { static inline qint64 simTimeToMSec(const ngs::SimTime &st) { qint64 mSecs = st.getSeconds() * 1000LL; mSecs += st.getMicroSeconds() / 1000; return mSecs; } static inline rpr2::SpatialStruct spatialStructFromWorldLocation(const rpr2::WorldLocationStruct &wls) { rpr2::SpatialStaticStruct sss; sss.WorldLocation = wls; sss.IsFrozen = false; sss.Orientation.Phi = 3.138309f; sss.Orientation.Psi = 2.446645f; sss.Orientation.Theta = -0.879588f; rpr2::SpatialStruct ss; ss.DeadReckoningAlgorithm__A__Alternatives.discriminant(rpr2::DeadReckoningAlgorithmEnum8::Static); ss.DeadReckoningAlgorithm__A__Alternatives.SpatialStatic(sss); return ss; } // // NgsConnector // NgsConnector::NgsConnector(const QString &fedName, NgsHandler *ngsHandler) : m_ngsHandler(ngsHandler) , m_fedName(fedName) , m_swadmin(&ngs::SimulatedWorldAPI::getSimulatedWorldAdmin()) , m_master(nullptr) , m_somPlayer(nullptr) , m_playerCallback(new PlayerCallback()) , m_humanCallback(new ObserverHumanCBNotifier()) , m_groundVehicleCallback(new ObserverGroundVehicleCBNotifier()) , m_longBowApacheCallback(new ObserverLongBowApacheCBNotifier()) , m_designatorCallback(new ObserverDesignatorCBNotifier()) , m_iaEngagementReplyCallback(new ObserverEngagementReplyCBNotifier()) , m_iaEventReportCallback(new ObserverEventReportCBNotifier()) , m_iaMunitionDetonationCallback(new ObserverMunitionDetonationCBNotifier()) , m_iaWeaponFireCallback(new ObserverWeaponFireCBNotifier()) , m_bInitialized(false) , m_bCanPublishData(false) , m_timer(new QTimer(this)) // , m_FederateIdentifierStructRME({ Settings::siteId(), Settings::applicationId() }) , m_logger(getLogger("NgsConnector")) { connect(m_timer, &QTimer::timeout, this, &NgsConnector::go); connect(m_playerCallback, &PlayerCallback::firstTick, this, &NgsConnector::onFirstTickFinished); // Ground Vehicle connectObserverGroundVehicleObjectSignals(m_groundVehicleCallback); // Human connectObserverHumanObjectSignals(m_humanCallback); // LongBow Apache connectObserverLongBowApacheObjectSignals(m_longBowApacheCallback); // Designator connectObserverDesignatorObjectSignals(m_designatorCallback); // Engagement Reply connect(m_iaEngagementReplyCallback, &ObserverEngagementReplyCBNotifier::newEngagementReply, this, &NgsConnector::onNewEngagementReply); // Event Report connect(m_iaEventReportCallback, &ObserverEventReportCBNotifier::newEventReport, this, &NgsConnector::onNewEventReport); // Munition Detonation connect(m_iaMunitionDetonationCallback, &ObserverMunitionDetonationCBNotifier::newMunitionDetonation, this, &NgsConnector::onNewMunitionDetonation); // Weapon Fire connect(m_iaWeaponFireCallback, &ObserverWeaponFireCBNotifier::newWeaponFire, this, &NgsConnector::onNewWeaponFire); } NgsConnector::~NgsConnector() { uninitialize(); delete m_playerCallback; delete m_humanCallback; delete m_groundVehicleCallback; delete m_longBowApacheCallback; delete m_designatorCallback; delete m_iaEngagementReplyCallback; delete m_iaEventReportCallback; delete m_iaMunitionDetonationCallback; delete m_iaWeaponFireCallback; delete m_timer; } bool NgsConnector::initialize(ngs::IObserver::ProtocolType protocol, const QString &playerName, const QString &appDirPath) { QString swconf = appDirPath + QLatin1Char('/') + Settings::swConfFileName(); m_bInitialized = true; // NGS Initialisation try { m_master = &m_swadmin->getIFederationMaster(protocol, qPrintable(swconf), Settings::fomIdentifier().toStdString(), Settings::rtiIdentifier().toStdString()); // "", "MAK" } catch (const ngs::NGSSimWorldException &e) { ERROR_LOG(m_logger) << "caught exception during init (getIFederationMaster): " << e.print(); return false; } ngs::IPlayer *player = nullptr; switch (protocol) { case ngs::IObserver::HLA: { try { m_master->createFederation(m_fedName.toStdString()); } catch (const NGSException &e) { ERROR_LOG(m_logger) << "caught exception during init (createFederation): " << e.print(); return false; } player = &m_swadmin->getIPlayer(m_fedName.toStdString(), playerName.toStdString(), *m_playerCallback, Settings::swConfFileName().toLatin1().data(), Settings::fomIdentifier().toStdString(), Settings::rtiIdentifier().toStdString()); break; } case ngs::IObserver::CORBANOTIFY: case ngs::IObserver::DIS: { unsigned char federationId = m_fedName.toUShort(); m_master->createFederation(federationId); player = &m_swadmin->getIPlayer(federationId, playerName.toStdString(), *m_playerCallback, protocol, Settings::swConfFileName().toLatin1().data(), Settings::fomIdentifier().toStdString()); break; } } m_somPlayer = dynamic_cast(player); m_somPlayer->configureSimTime(ngs::IObserver::REALTIMECLOCK, ngs::SimTime(0, 0)); m_timer->start(10); return true; } void NgsConnector::uninitialize() { if (!m_bInitialized) return; m_bInitialized = false; m_timer->stop(); unsubscribeFromNgs(); if (m_swadmin) m_swadmin->resignInterface(); if (m_somPlayer) m_somPlayer->resignSimulatedWorld(); if (m_master) { m_master->destroyFederation(m_fedName.toStdString()); m_master->resignInterface(); m_master = nullptr; } } void NgsConnector::subscribeAtNgs() { m_enitityNumberCounter = 1; // restart m_eventCount = 1; INFO_LOG(m_logger) << "Subscribe at NGS - Human Objects"; rpr2::IObserverHumanObjClass &humanObjClass = m_somPlayer->getObserverHumanObjClass(*m_humanCallback); humanObjClass.subscribeAllAttributes(); humanObjClass.subscribe(); INFO_LOG(m_logger) << "Subscribe at NGS - GroundVehicle Objects"; rpr2::IObserverGroundVehicleObjClass &groundVehicleObjClass = m_somPlayer->getObserverGroundVehicleObjClass(*m_groundVehicleCallback); groundVehicleObjClass.subscribeAllAttributes(); groundVehicleObjClass.subscribe(); INFO_LOG(m_logger) << "Subscribe at NGS - LongBowApache Objects"; rpr2::IObserverLongBowApacheObjClass &longBowApacheObjClass = m_somPlayer->getObserverLongBowApacheObjClass(*m_longBowApacheCallback); longBowApacheObjClass.subscribeAllAttributes(); longBowApacheObjClass.subscribe(); INFO_LOG(m_logger) << "Subscribe at NGS - Designator Objects"; rpr2::IObserverDesignatorObjClass &designatorObjClass = m_somPlayer->getObserverDesignatorObjClass(*m_designatorCallback); designatorObjClass.subscribeAllAttributes(); designatorObjClass.subscribe(); rpr2::IObserverEngagementReplyIntClass &enr = m_somPlayer->getObserverEngagementReplyIntClass(*m_iaEngagementReplyCallback); enr.subscribe(); rpr2::IObserverEventReportIntClass &evr = m_somPlayer->getObserverEventReportIntClass(*m_iaEventReportCallback); evr.subscribe(); rpr2::IObserverMunitionDetonationIntClass &md = m_somPlayer->getObserverMunitionDetonationIntClass(*m_iaMunitionDetonationCallback); md.subscribe(); rpr2::IObserverWeaponFireIntClass &wf = m_somPlayer->getObserverWeaponFireIntClass(*m_iaWeaponFireCallback); wf.subscribe(); m_timer->start(); m_bCanPublishData = true; } void NgsConnector::unsubscribeFromNgs() { m_bCanPublishData = false; if (m_somPlayer) { INFO_LOG(m_logger) << "Unsubscribe at NGS - Human Objects"; rpr2::IObserverHumanObjClass &humanObjClass = m_somPlayer->getObserverHumanObjClass(*m_humanCallback); humanObjClass.unsubscribe(); INFO_LOG(m_logger) << "Unsubscribe at NGS - GroundVehicle Objects"; rpr2::IObserverGroundVehicleObjClass &groundVehicleObjClass = m_somPlayer->getObserverGroundVehicleObjClass(*m_groundVehicleCallback); groundVehicleObjClass.unsubscribe(); INFO_LOG(m_logger) << "Unsubscribe at NGS - LongBowApache Objects"; rpr2::IObserverLongBowApacheObjClass &longBowApacheObjClass = m_somPlayer->getObserverLongBowApacheObjClass(*m_longBowApacheCallback); longBowApacheObjClass.unsubscribe(); INFO_LOG(m_logger) << "Unsubscribe at NGS - Designator Objects"; rpr2::IObserverDesignatorObjClass &designatorObjectClass = m_somPlayer->getObserverDesignatorObjClass(*m_designatorCallback); designatorObjectClass.unsubscribe(); INFO_LOG(m_logger) << "Unsubscribe at NGS - Engagement Reply Interaction"; rpr2::IObserverEngagementReplyIntClass &enr = m_somPlayer->getObserverEngagementReplyIntClass(*m_iaEngagementReplyCallback); enr.unsubscribe(); INFO_LOG(m_logger) << "Unsubscribe at NGS - Event Report Interaction"; rpr2::IObserverEventReportIntClass &evr = m_somPlayer->getObserverEventReportIntClass(*m_iaEventReportCallback); evr.unsubscribe(); INFO_LOG(m_logger) << "Unsubscribe at NGS - Munition Detonation Interaction"; rpr2::IObserverMunitionDetonationIntClass &md = m_somPlayer->getObserverMunitionDetonationIntClass(*m_iaMunitionDetonationCallback); md.unsubscribe(); INFO_LOG(m_logger) << "Unsubscribe at NGS - Weapon Fire Interaction"; rpr2::IObserverWeaponFireIntClass &wf = m_somPlayer->getObserverWeaponFireIntClass(*m_iaWeaponFireCallback); wf.unsubscribe(); } for (rpr2::IPlayerBaseEntityObjInstance *player : qAsConst(m_playerMap)) player->deleteObjectInstance(getSimTime()); m_playerMap.clear(); for (auto it = m_observerBaseEntityInstances.cbegin(); it != m_observerBaseEntityInstances.cend(); ++it) emit virtualParticipantDeleted(it.key()); for (auto it = m_observerEmbeddedSystemInstances.cbegin(); it != m_observerEmbeddedSystemInstances.cend(); ++it) emit virtualEmbeddedSystemDeleted(it.key()); m_observerPositions.clear(); m_observerEmbeddedSystemInstances.clear(); m_newBaseEntitesWithoutParticipantIdSetIdSet.clear(); m_observerBaseEntityInstances.clear(); m_enitityNumberCounter = 1; // restart m_eventCount = 1; } bool NgsConnector::addHuman(const ParticipantInformation &participantData, const ProjectDataManagement::VulnerabilityState &state) { if (m_playerMap.contains(participantData.equipmentNo)) return false; DEBUG_LOG(getLogger("NgsConnector.Outbound.Startup")) << "addHuman(" << participantData.equipmentNo << ", " << participantData.partyId << ", " << participantData.typeStruct.toString() << ", " << state << ")"; rpr2::IPlayerHumanObjClass &player = m_somPlayer->getPlayerHumanObjClass(); rpr2::IPlayerHumanObjInstance *playerInstance = &player.newHumanObjInstance(); DamageModel dm(state); playerInstance->setDamageState(dm.damageState(-1)); playerInstance->setImmobilized(dm.immobilized(-1)); playerInstance->setFirePowerDisabled(dm.firePowerDisabled(-1)); return addPhysicalEntity(playerInstance, participantData); } bool NgsConnector::addLongBowApache(const ParticipantInformation &participantData, const ProjectDataManagement::VulnerabilityState &state) { if (m_playerMap.contains(participantData.equipmentNo)) return false; DEBUG_LOG(getLogger("NgsConnector.Outbound.Startup")) << "addLongBowApache(" << participantData.equipmentNo << ", " << participantData.partyId << ", " << participantData.typeStruct.toString() << ")"; rpr2::IPlayerLongBowApacheObjClass &player = m_somPlayer->getPlayerLongBowApacheObjClass(); rpr2::IPlayerLongBowApacheObjInstance *playerInstance = &player.newLongBowApacheObjInstance(); DamageModel dm(state); playerInstance->setDamageState(dm.damageState(-1)); playerInstance->setImmobilized(dm.immobilized(-1)); playerInstance->setFirePowerDisabled(dm.firePowerDisabled(-1)); return addPhysicalEntity(playerInstance, participantData); } bool NgsConnector::addGroundVehicle(const ParticipantInformation &participantData, const ProjectDataManagement::VulnerabilityState &state) { if (m_playerMap.contains(participantData.equipmentNo)) return false; DEBUG_LOG(getLogger("NgsConnector.Outbound.Startup")) << "addGroundVehicle(" << participantData.equipmentNo << ", " << participantData.partyId << ", " << participantData.typeStruct.toString() << ", " << state << ")"; rpr2::IPlayerGroundVehicleObjClass &player = m_somPlayer->getPlayerGroundVehicleObjClass(); rpr2::IPlayerGroundVehicleObjInstance *playerInstance = &player.newGroundVehicleObjInstance(); DamageModel dm(state); playerInstance->setDamageState(dm.damageState(-1)); playerInstance->setImmobilized(dm.immobilized(-1)); playerInstance->setFirePowerDisabled(dm.firePowerDisabled(-1)); return addPhysicalEntity(playerInstance, participantData); } bool NgsConnector::addBaseEntity(rpr2::IPlayerBaseEntityObjInstance *playerInstance, const ParticipantInformation &participantData) { if (m_playerMap.contains(participantData.equipmentNo)) return false; if (participantData.equipmentNo == 0) return false; IdentifierStruct is(participantData.equipmentNo); m_playerMap.insert(participantData.equipmentNo, playerInstance); playerInstance->setEntityType(participantData.typeStruct.toETS()); playerInstance->setEntityIdentifier(is.toEIS()); // setParticipantPartyId(*playerInstance, participantData.id, participantData.partyId); playerInstance->publish(getSimTime(participantData.position.msec)); DEBUG_LOG(getLogger("NgsConnector.Outbound.Startup")) << "Published Participant " << is.toString(); return true; } bool NgsConnector::addPhysicalEntity(rpr2::IPlayerPhysicalEntityObjInstance *playerInstance, const ParticipantInformation &playerData) { rpr2::MarkingStruct ms = toMarkingStruct(playerData.designator); playerInstance->setMarking(ms); rpr2::ForceIdentifierEnum8::ET forceId = m_ngsHandler->ppmHandler()->forceIdentifierFromPartyId(playerData.partyId); playerInstance->setForceIdentifier(forceId); rpr2::SpatialStruct ss(spatialStructFromWorldLocation(playerData.position.toWorldLocation())); playerInstance->setSpatial(ss); return addBaseEntity(playerInstance, playerData); } bool NgsConnector::removeBaseEntity(int eqNo) { if (!m_playerMap.contains(eqNo)) return false; m_playerMap[eqNo]->deleteObjectInstance(getSimTime()); return (m_playerMap.remove(eqNo) > 0); } bool NgsConnector::setParticipantName(int eqNo, const QString &designator) { rpr2::IPlayerPhysicalEntityObjInstance *playerInstance = dynamic_cast(m_playerMap.value(eqNo, nullptr)); if (!playerInstance) return false; playerInstance->setMarking(toMarkingStruct(designator)); playerInstance->publish(getSimTime()); return true; } // Excon -> HLA bool NgsConnector::sendData(int eqNo, const QByteArray& data, int requestIdentifier) { rpr2::IPlayerDataIntClass &ic = m_somPlayer->getPlayerDataIntClass(); rpr2::IPlayerDataIntInstance &icb = ic.newDataIntInstance(); rpr2::VariableDatumSetStruct vdss; vdss.NumberOfVariableDatums__A__Padding__A__VariableDatums.resize(1); rpr2::VariableDatumStruct &vds = vdss.NumberOfVariableDatums__A__Padding__A__VariableDatums[0]; vds.NumberOfLongLongs__A__Padding__A__DatumValue.resize(data.size() / 8 + 1); vds.DatumLengthInBits = 8 * data.size(); TRACE_LOG(m_logger) << "--> Data. Receiver: " << eqNo << ", ReqID: " << requestIdentifier << ", Data: " << data; for (unsigned int i=0; i< vds.NumberOfLongLongs__A__Padding__A__DatumValue.size(); ++i) { QByteArray ba = data.mid(8*i, 8).leftJustified(8,0); quint64 value = qFromBigEndian((uchar*)ba.data()); vds.NumberOfLongLongs__A__Padding__A__DatumValue[i] = value; } icb.setVariableDatumSet(vdss); rpr2::EntityIdentifierStruct v = IdentifierStruct(Settings::hlaPlayerIdExcon()).toEIS(); icb.setOriginatingEntity(v); icb.setRequestIdentifier(requestIdentifier); rpr2::EntityIdentifierStruct s = IdentifierStruct(eqNo).toEIS(); icb.setReceivingEntity(s); icb.publish(); return true; } // DDS -> HLA void NgsConnector::sendMunitionDetonation(int targetEqId, ProjectDataManagement::ChargeType ct, int sourceEqId, rpr2::DetonationResultCodeEnum8::ET drc, qint64 timestamp) { if (!m_playerMap.contains(sourceEqId) && sourceEqId != 0) return; DEBUG_LOG(getLogger("NgsConnector.Outbound.MunitionDetonation")) << "Munition Detonation: " << "Charge: " << ct.name() << ", Source EqId: " << sourceEqId << ", Target EqId: " << targetEqId << ", Detonation Result Code: " << drc << ", Time: " << timestamp; rpr2::IPlayerMunitionDetonationIntClass &icx = m_somPlayer->getPlayerMunitionDetonationIntClass(); rpr2::IPlayerMunitionDetonationIntInstance *icb = &icx.newMunitionDetonationIntInstance(); icb->setDetonationLocation(m_observerPositions[targetEqId].toWorldLocation()); icb->setDetonationResultCode(drc); icb->setEventIdentifier(getEventIdentifierStruct()); rpr2::RTIObjectIdStruct os; if (sourceEqId == 0) os.ID = "EXCON"; else os.ID = getObjectIdentifierByEquipmentNo(sourceEqId); icb->setFiringObjectIdentifier(os); if (ct.id() > 0) icb->setMunitionType(TypeStruct::fromString(QString::fromStdString(ct.disEntity())).toETS()); else icb->setMunitionType(rpr2::EntityTypeStruct()); icb->setQuantityFired(1); os.ID = getObjectIdentifierByEquipmentNo(targetEqId); icb->setTargetObjectIdentifier(os); // Not used by ICE: // icb->setRateOfFire(0); // icb->setFuseType(rpr2::FuseTypeEnum16::Contact); // icb->setWarheadType(rpr2::WarheadTypeEnum16::Kinetic); icb->publish(getSimTime(timestamp)); } IdentifierStruct NgsConnector::getIdentifierStructByEquipmentNo(const int eqId) const { const rpr2::IObserverBaseEntityObjInstance *beObject = nullptr; if (m_observerBaseEntityInstances.contains(eqId)) beObject = dynamic_cast(m_observerBaseEntityInstances[eqId]); else if (m_playerMap.contains(eqId)) beObject = m_playerMap[eqId]; if (!beObject || !beObject->isValidEntityIdentifier()) return IdentifierStruct(); return IdentifierStruct(beObject->getEntityIdentifier()); } rpr2::OMT13string NgsConnector::getObjectIdentifierByEquipmentNo(const int eqId) const { const rpr2::IObserverBaseEntityObjInstance *beObject = nullptr; if (m_observerBaseEntityInstances.contains(eqId)) beObject = dynamic_cast(m_observerBaseEntityInstances[eqId]); else if (m_playerMap.contains(eqId)) beObject = m_playerMap[eqId]; if (!beObject || !beObject->isValidObjectName()) return rpr2::OMT13string(); return beObject->getObjectName(); } void NgsConnector::sendParticipantInitRequest(int eqNo, bool resetManipulation, bool resetVulnerabilityState, bool restockCharges, qint64 simTimeMSecs) { Q_UNUSED(simTimeMSecs) IdentifierStruct is = getIdentifierStructByEquipmentNo(eqNo); if (is.isNull() || is.isOwnElement()) return; if (!resetVulnerabilityState) { WARNING_LOG(getLogger("NgsConnector.Outbound.GuezInit")) << "Request to " << (resetManipulation ? "reset Manipulation" : "") << (resetManipulation && restockCharges ? " and " : "") << (restockCharges ? "restock Charges" : "") << " without resetting the Player " << eqNo << " to CombatReady. Not supported."; return; } DEBUG_LOG(getLogger("NgsConnector.Outbound.GuezInit")) << "Request to " << (restockCharges ? "resurrect": "reset (=restock Charges)") << " Player " << eqNo << " to Combat Ready."; rpr2::IPlayerActionRequestIntClass &icx = m_somPlayer->getPlayerActionRequestIntClass(); rpr2::IPlayerActionRequestIntInstance *icb = &icx.newActionRequestIntInstance(); icb->setActionRequestCode(restockCharges ? rpr2::ActionEnum32::CTC_Action_Reset : rpr2::ActionEnum32::CTC_Action_Resurrcect); icb->setOriginatingEntity(IdentifierStruct(Settings::hlaPlayerIdExcon()).toEIS()); icb->setReceivingEntity(is.toEIS()); icb->publish(getSimTime(simTimeMSecs)); } bool NgsConnector::setWorldLocation(int eqNo, const ParticipantPosition &pos) { rpr2::IPlayerPhysicalEntityObjInstance *playerInstance = dynamic_cast(m_playerMap.value(eqNo, nullptr)); if (!playerInstance) return false; rpr2::SpatialStruct ss(spatialStructFromWorldLocation(pos.toWorldLocation())); playerInstance->setSpatial(ss); playerInstance->publish(getSimTime(pos.msec)); DEBUG_LOG(getLogger("NgsConnector.Outbound.Player.Position")) << "Player: " << eqNo << ", Position: " << pos.toString() << ", Time: " << pos.msec; return true; } ngs::SimTime NgsConnector::getSimTime(qint64 msecsSinceEpoch) { if (msecsSinceEpoch == 0) msecsSinceEpoch = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch(); return ngs::SimTime(msecsSinceEpoch / 1000, (msecsSinceEpoch % 1000) * 1000); } ngs::SimTime NgsConnector::getSimTime(const QDateTime &dateTime) { return getSimTime(dateTime.toMSecsSinceEpoch()); } void NgsConnector::go() { if (!m_somPlayer) return; try { m_somPlayer->go(ngs::IObserver::GOSTEP); } catch (const NGSException &ex) { ERROR_LOG(m_logger) << "NGS Exception caught in Event Loop" << ex.print(); } catch (const std::exception &e) { ERROR_LOG(m_logger) << "Standard Exception caught in Event Loop: " << e.what(); } catch (...) { // ERROR_LOG(m_logger) << "Generic Exception caught in Event Loop"; } } void NgsConnector::onFirstTickFinished() { unsubscribeFromNgs(); m_bCanPublishData = true; subscribeAtNgs(); emit firstTickFinished(); } void NgsConnector::connectObserverObjectSignals(ObserverObjectCB *entity) { connect(entity, &ObserverObjectCB::newObjectInstance2, this, &NgsConnector::addObserverObjectInstance, Qt::UniqueConnection); connect(entity, &ObserverObjectCB::objectInstanceDeleted, this, &NgsConnector::removeObserverObjectInstance, Qt::UniqueConnection); } void NgsConnector::connectObserverBaseEntityObjectSignals(ObserverBaseEntityObjectCB *entity) { connectObserverObjectSignals(entity); connect(entity, &ObserverBaseEntityObjectCB::entityIdentifierChanged, this, &NgsConnector::onBaseEntityEntityIdentifierChanged, Qt::UniqueConnection); connect(entity, &ObserverBaseEntityObjectCB::spatialChanged, this, &NgsConnector::onSpatialChanged, Qt::UniqueConnection); connect(entity, &ObserverBaseEntityObjectCB::entityTypeChanged, this, &NgsConnector::onEntityTypeChanged, Qt::UniqueConnection); } void NgsConnector::connectObserverPhysicalEntityObjectSignals(ObserverPhysicalEntityObjectCB *entity) { connectObserverBaseEntityObjectSignals(entity); connect(entity, &ObserverPhysicalEntityObjectCB::markingChanged, this, &NgsConnector::onMarkingChanged, Qt::UniqueConnection); connect(entity, &ObserverPhysicalEntityObjectCB::forceIdentifierChanged, this, &NgsConnector::onForceIdentifierChanged, Qt::UniqueConnection); connect(entity, &ObserverPhysicalEntityObjectCB::damageStateChanged, this, &NgsConnector::onDamageStateChanged, Qt::UniqueConnection); } void NgsConnector::connectObserverPlatformObjectSignals(ObserverPlatformObjectCB *entity) { connectObserverPhysicalEntityObjectSignals(entity); // No extra signals } void NgsConnector::connectObserverGroundVehicleObjectSignals(ObserverGroundVehicleObjectCB *entity) { connectObserverPlatformObjectSignals(entity); // No extra signals } void NgsConnector::connectObserverLifeformObjectSignals(ObserverLifeformObjectCB *entity) { connectObserverPhysicalEntityObjectSignals(entity); // No extra signals } void NgsConnector::connectObserverHumanObjectSignals(ObserverHumanObjectCB *entity) { connectObserverLifeformObjectSignals(entity); // No extra signals } void NgsConnector::connectObserverAircraftObjectSignals(ObserverAircraftObjectCB *entity) { connectObserverPlatformObjectSignals(entity); // No extra signals } void NgsConnector::connectObserverLongBowApacheObjectSignals(ObserverLongBowApacheObjectCB *entity) { connectObserverAircraftObjectSignals(entity); connect(entity, &ObserverLongBowApacheObjectCB::sensorChanged, this, &NgsConnector::onSensorChanged, Qt::UniqueConnection); } void NgsConnector::connectObserverEmbeddedSystemObjectSignals(ObserverEmbeddedSystemObjectCB *entity) { connectObserverObjectSignals(entity); connect(entity, &ObserverEmbeddedSystemObjectCB::entityIdentifierChanged, this, &NgsConnector::onEmbeddedSystemEntityIdentifierChanged); } void NgsConnector::connectObserverDesignatorObjectSignals(ObserverDesignatorObjectCB *entity) { connectObserverEmbeddedSystemObjectSignals(entity); connect(entity, &ObserverDesignatorObjectCB::designatorCodeChanged, this, &NgsConnector::onDesignatorCodeChanged, Qt::UniqueConnection); connect(entity, &ObserverDesignatorObjectCB::designatorOutputPowerChanged, this, &NgsConnector::onDesignatorOutputPowerChanged, Qt::UniqueConnection); connect(entity, &ObserverDesignatorObjectCB::designatorSpotLocationChanged, this, &NgsConnector::onDesignatorSpotLocationChanged, Qt::UniqueConnection); } void NgsConnector::addObserverObjectInstance(const ngs::IObserverObjectInstance &obj) { // Put it into the Observer list later on, when the slot "onEntityIdentifierChanged()" is called // Then we can be sure, the EntityIdentifier is definetely set. const rpr2::IObserverBaseEntityObjInstance *beObject = dynamic_cast(&obj); if (beObject && beObject->isValidEntityIdentifier()) { IdentifierStruct is(beObject->getEntityIdentifier()); DEBUG_LOG(getLogger("NgsConnector.Inbound.Player")) << "Player: " << is.toString() << " added."; m_newBaseEntitesWithoutParticipantIdSetIdSet.insert(&obj); onBaseEntityEntityIdentifierChanged(beObject->getEntityIdentifier(), beObject, QDateTime::currentMSecsSinceEpoch()); } const rpr2::IObserverEmbeddedSystemObjInstance *esObject = dynamic_cast(&obj); if (esObject && esObject->isValidEntityIdentifier()) { IdentifierStruct is(esObject->getEntityIdentifier()); DEBUG_LOG(getLogger("NgsConnector.Inbound.EmbeddedSystem")) << "Embedded System for Player: " << is.toString() << " added."; m_newEmbeddedSystemsWithoutParticipantIdSet.insert(&obj); onEmbeddedSystemEntityIdentifierChanged(esObject->getEntityIdentifier(), esObject, QDateTime::currentMSecsSinceEpoch()); } } void NgsConnector::removeObserverObjectInstance(const ngs::IObserverObjectInstance &obj) { const rpr2::IObserverBaseEntityObjInstance *beObject = dynamic_cast(&obj); if (beObject && beObject->isValidEntityIdentifier()) { IdentifierStruct is(beObject->getEntityIdentifier()); DEBUG_LOG(getLogger("NgsConnector.Inbound.Player")) << "Player: " << is.toString() << " removed."; if (is.isOwnElement()) emit virtualParticipantDeleted(is.entityNumber()); m_newBaseEntitesWithoutParticipantIdSetIdSet.remove(&obj); m_observerBaseEntityInstances.remove(is.entityNumber()); auto it = m_damageModelMap.find(is.entityNumber()); if (it != m_damageModelMap.end()) { delete it.value(); m_damageModelMap.erase(it); } } const rpr2::IObserverEmbeddedSystemObjInstance *esObject = dynamic_cast(&obj); if (esObject && esObject->isValidEntityIdentifier()) { IdentifierStruct is(esObject->getEntityIdentifier()); DEBUG_LOG(getLogger("NgsConnector.Inbound.Player")) << "Player: " << is.toString() << " removed."; if (is.isOwnElement()) emit virtualEmbeddedSystemDeleted(is.entityNumber()); const rpr2::IObserverDesignatorObjInstance *instance = dynamic_cast(esObject); TrainingAreaBase::LaserDesignation ld = getLaserDesignation(is, instance, QDateTime::currentMSecsSinceEpoch()); ld.outputPower(0.0); DEBUG_LOG(getLogger("NgsConnector.Inbound.LaserDesignation")) << "Remove Designation:" << " Source: " << ld.equipmentNumber() << ", Code: " << ld.code() << ", Output Power: " << ld.outputPower() << ", Target Position: " << ParticipantPosition(ld.spotLocation()).toString() << ")"; emit laserDesignation(ld); m_newEmbeddedSystemsWithoutParticipantIdSet.remove(&obj); m_observerEmbeddedSystemInstances.remove(is.entityNumber()); } } void NgsConnector::ensureDamageModelExists(const IdentifierStruct &is) { if (m_damageModelMap.contains(is.entityNumber())) return; DamageModel *dmgModel(new DamageModel(is)); m_damageModelMap.insert(is.entityNumber(), dmgModel); } rpr2::MarkingStruct NgsConnector::toMarkingStruct(const QString &designator) { rpr2::MarkingStruct ms; const QString string = designator.leftJustified(ms.MarkingData.size(), QLatin1Char('\0'), true); const QByteArray ba = string.toUtf8(); for (size_t i = 0; i < ms.MarkingData.size(); ++i) ms.MarkingData[i] = ba.at(i); // this is wrong but there is no 'UTF8' ... ms.MarkingEncodingType = rpr2::MarkingEncodingEnum8::ASCII; return ms; } rpr2::ClockTimeStruct NgsConnector::getClockTimeStruct(qint64 msecsSinceEpoch) { // Todo: Wrong! rpr2::ClockTimeStruct cts; cts.Hours = msecsSinceEpoch / (1000 * 60 * 60); cts.TimePastTheHour = msecsSinceEpoch - (cts.Hours * 1000 * 60 * 60) ; // This is wrong! return cts; } qint64 NgsConnector::getMSecsSinceEpoch(const rpr2::ClockTimeStruct &cts) { // Todo: Wrong! qint64 time = 0; time += (1000LL * 60 * 60 * cts.Hours); time += cts.TimePastTheHour; // Contains MSecs at the moment return time; } rpr2::EntityTypeStruct NgsConnector::getEtsCharge(int chargeType) const { const auto ct = m_ngsHandler->ppmHandler()->chargeTypeFromId(chargeType); if (ct.disEntity().size() == 0) return TypeStruct::fromString(QLatin1String("0.0.0.0.0.0.0")).toETS(); // Default return TypeStruct::fromString(QString::fromStdString(ct.disEntity())).toETS(); } template rpr2::ArrayOfWorldLocationStructDynamicHLAlengthlessVarArray NgsConnector::positionArray(const T &data) { rpr2::ArrayOfWorldLocationStructDynamicHLAlengthlessVarArray positions; if (data.size() == 0) return positions; positions.resize(data.size()); unsigned int i = 0; for (const auto &item : data) positions[i++] = ParticipantPosition(item).toWorldLocation(); return positions; } template static inline T compass2rad(T degree) { static_assert(!std::is_integral::value, "Floating Point Value Required! Integer may be in Centidegree?!?"); return (M_PI / 2.0) - (degree * M_PI / 180.0); } template static inline T deg2rad(T degree) { static_assert(!std::is_integral::value, "Floating Point Value Required! Integer may be in Centidegree?!?"); return degree * M_PI / 180.0; } QPolygonF NgsConnector::utmToGeogrphic(const QPolygonF &polyUtm, const LtsCoordTrans::UtmCoordinate &base) { QPolygonF poly; poly.reserve(polyUtm.size()); for (const QPointF &point : qAsConst(polyUtm)) poly << base.geographic(point); return poly; } void NgsConnector::onBaseEntityEntityIdentifierChanged(const rpr2::EntityIdentifierStruct &eis, const rpr2::IObserverBaseEntityObjInstance *obj, qint64 simTimeMSecs) { IdentifierStruct is(eis); // Own players are irrelevant here if (is.isOwnElement()) { m_newBaseEntitesWithoutParticipantIdSetIdSet.remove(obj); return; } // We cannot do anything here then. Who was it before? if (m_observerBaseEntityInstances.contains(is.entityNumber())) return; DEBUG_LOG(getLogger("NgsConnector.Inbound.Player")) << "Player: " << is.toString() << " will be added."; m_observerBaseEntityInstances.insert(is.entityNumber(), obj); m_newBaseEntitesWithoutParticipantIdSetIdSet.remove(obj); GuezHitStruct hs; hs.originatorType = ProjectDataManagement::StateChangeOriginator_def::Initialization; hs.sampleTime = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch(); // We don't have anything better hs.targetId = is.entityNumber(); hs.vulnerabilityState = ProjectDataManagement::VulnerabilityState_def::VulnerabilityBitsChanged; // --> Evaluate State from Bits (done in PPM Service). QTimer::singleShot(STATUS_WAIT_INTERVAL, this, [this, hs, obj, simTimeMSecs]() mutable { auto ptr = m_damageModelMap.value(hs.targetId); if (!ptr) { ERROR_LOG(getLogger("NgsConnector.Inbound.Player")) << "Damage State of entity " << hs.targetId << " didn't exist 50msecs after EntityIdentifier Changed. Setting Initial Status not possible!"; return; } DEBUG_LOG(getLogger("NgsConnector.Inbound.Player")) << "Player: " << hs.targetId << ". Setting initial Status, initial Position and Manipulation."; // Todo: Something to set initial State // const auto vulnerabilityBits = ptr->totalState(hs.sampleTime); // hs.vulnerabilityBits = (vulnerabilityBits & ~rpr2::TotalStatusEnum32::Manipulation); // DEBUG_LOG(getLogger("NgsConnector.Inbound.Player.Status")) // << "Player: " << hs.targetId // << " New Total Status: " << hs.vulnerabilityBits; // emit guezHit(hs); ParticipantPosition pos(obj->getSpatial().DeadReckoningAlgorithm__A__Alternatives.SpatialStatic().WorldLocation); DEBUG_LOG(getLogger("NgsConnector.Inbound.Player.Position")) << "Player: " << hs.targetId << ", Initial Position: " << pos.toString() << ", Time: " << pos.msec; emit spatialChanged(hs.targetId, pos, simTimeMSecs); }); } void NgsConnector::onEmbeddedSystemEntityIdentifierChanged(const rpr2::EntityIdentifierStruct &eis, const rpr2::IObserverEmbeddedSystemObjInstance *obj, qint64 simTimeMSecs) { IdentifierStruct is(eis); // Own players are irrelevant here if (is.isOwnElement()) { m_newEmbeddedSystemsWithoutParticipantIdSet.remove(obj); return; } // We cannot do anything here then. Who was it before? if (m_observerEmbeddedSystemInstances.contains(is.entityNumber())) return; DEBUG_LOG(getLogger("NgsConnector.Inbound.EmbeddedSystem")) << "Embedded System for Player: " << is.toString() << " will be added."; m_observerEmbeddedSystemInstances.insert(is.entityNumber(), obj); m_newEmbeddedSystemsWithoutParticipantIdSet.remove(obj); GuezHitStruct hs; hs.originatorType = ProjectDataManagement::StateChangeOriginator_def::Initialization; hs.sampleTime = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch(); // We don't have anything better hs.targetId = is.entityNumber(); hs.vulnerabilityState = ProjectDataManagement::VulnerabilityState_def::VulnerabilityBitsChanged; // --> Evaluate State from Bits (done in PPM Service). QTimer::singleShot(STATUS_WAIT_INTERVAL, this, [this, hs, obj, simTimeMSecs]() mutable { DEBUG_LOG(getLogger("NgsConnector.Inbound.Player")) << "Player: " << hs.targetId << ". Setting initial Status, initial Position and Manipulation."; }); } bool NgsConnector::onSpatialChanged(const rpr2::EntityIdentifierStruct &eis, const rpr2::WorldLocationStruct &wl, qint64 simTimeMSecs) { IdentifierStruct is(eis); ParticipantPosition newParticipantPosition(wl); newParticipantPosition.z *= -1; if (is.isOwnElement()) return true; ParticipantPosition &posInMapRef = m_observerPositions[is.entityNumber()]; if (!posInMapRef.hasMoved(newParticipantPosition)) //Hat sich die Position zum letzen Mal signifkant geƤndert? return true; DEBUG_LOG(getLogger("NgsConnector.Inbound.Player.Position")) << "Player: " << is.toString() << ", Position: " << newParticipantPosition.toString() << ", Time: " << newParticipantPosition.msec; posInMapRef = newParticipantPosition; emit spatialChanged(is.entityNumber(), newParticipantPosition, simTimeMSecs); return true; } void NgsConnector::onEntityTypeChanged(const rpr2::EntityIdentifierStruct &eis, const rpr2::EntityTypeStruct &ets, qint64 simTimeMSecs) { Q_UNUSED(simTimeMSecs); IdentifierStruct is(eis); if (is.isOwnElement()) return; TypeStruct ts(ets); WARNING_LOG(getLogger("NgsConnector.Inbound.Player")) << "Player: " << is.toString() << ". New Entity Type: " << ts.toString() << ". This has no effect!"; // Todo? } bool NgsConnector::onMarkingChanged(const rpr2::EntityIdentifierStruct &eis, const rpr2::MarkingStruct &marking, qint64 simTimeMSecs) { IdentifierStruct is(eis); if (is.isOwnElement()) return true; QByteArray ba; for (size_t i = 0; i< marking.MarkingData.size(); ++i) ba += marking.MarkingData[i]; const QString str(QString::fromUtf8(ba)); DEBUG_LOG(getLogger("NgsConnector.Inbound.Object")) << "Object: " << is.toString() << " has new Marking: '" << str << "'."; emit markingChanged(eis.EntityNumber, str, simTimeMSecs); return true; } bool NgsConnector::onForceIdentifierChanged(const rpr2::EntityIdentifierStruct &eis, rpr2::ForceIdentifierEnum8::ET identifier, qint64 simTimeMSecs) { Q_UNUSED(simTimeMSecs); IdentifierStruct is(eis); if (is.isOwnElement()) return true; WARNING_LOG(getLogger("NgsConnector.Inbound.Player")) << "Player: " << is.toString() << " has new Force Identifier: " << identifier << ". This has NO EFFECT."; return true; } bool NgsConnector::onDamageStateChanged(const rpr2::EntityIdentifierStruct &eis, rpr2::DamageStatusEnum32::ET status, qint64 simTimeMSecs) { IdentifierStruct is(eis); if (is.isOwnElement()) return true; ensureDamageModelExists(is); m_damageModelMap[is.entityNumber()]->setDamageState(status, simTimeMSecs); DEBUG_LOG(getLogger("NgsConnector.Inbound.Player.Status")) << "Player: " << is.toString() << " New Total Status: " << status; // Todo: Tactical Status Change on SAAB players can occur at any time (without known reason). Therefore, we MUST handle this. // Change of Tactical Status with no other Interaction cannot be handled (at the moment). The Interaction forwards the status change to PPM. // Note: We could start a timer per eqNo. If the Timer is not deleted by an interaction that handles it, send it out. GuezHitStruct hitStruct; hitStruct.originatorChargeTypeEtsString = TypeStruct::fromDisInt(0).toString(); hitStruct.originatorId = 0; hitStruct.originatorType = ProjectDataManagement::StateChangeOriginator_def::Unknown; hitStruct.position = m_observerPositions[is.entityNumber()].toGeoPosition(); hitStruct.sampleTime = simTimeMSecs; hitStruct.targetId = is.entityNumber(); hitStruct.vulnerabilityState = ProjectDataManagement::VulnerabilityState_def::VulnerabilityBitsChanged; // --> Evaluate State from Bits (done in PPM Service). hitStruct.vulnerabilityKind = 0; // const auto vulnerabilityBits = m_damageModelMap[is.entityNumber()]->totalState(hitStruct.sampleTime); // hitStruct.vulnerabilityBits = (vulnerabilityBits & ~rpr2::TotalStatusEnum32::Manipulation); // emit guezHit(hitStruct); return true; } bool NgsConnector::onSensorChanged(const rpr2::EntityIdentifierStruct &eis, rpr2::SensorFieldEnum32::ET value, qint64 simTimeMSecs) { TrainingAreaBase::SpecialSensorData ssd; ssd.equipmentNumber(eis.EntityNumber); ssd.operationalState(value | 0xff00); // Hack to have any bit set for display of the LEDs, ICE is using 0x00ff range only. ssd.sampleTime(simTimeMSecs); DEBUG_LOG(getLogger("NgsConnector.Inbound.SensorField")) << "SensorField:" << " Source: " << ssd.equipmentNumber() << ", Value (lower Byte!): " << value << ")"; emit sensorChanged(ssd); return true; } TrainingAreaBase::LaserDesignation NgsConnector::getLaserDesignation(const IdentifierStruct &is, const rpr2::IObserverDesignatorObjInstance *instance, qint64 simTimeMSecs) const { TrainingAreaBase::LaserDesignation ld; ld.equipmentNumber(is.entityNumber()); ld.sampleTime(simTimeMSecs); ld.outputPower(0.0); if (!instance) return ld; if (instance->isValidDesignatorCode()) ld.code(instance->getDesignatorCode()); if (instance->isValidDesignatorOutputPower()) ld.outputPower(instance->getDesignatorOutputPower()); if (instance->isValidDesignatorSpotLocation()) ld.spotLocation(ParticipantPosition(instance->getDesignatorSpotLocation()).toGeoPosition2D()); DEBUG_LOG(getLogger("NgsConnector.Inbound.LaserDesignation")) << "Designation:" << " Source: " << ld.equipmentNumber() << ", Code: " << ld.code() << ", Output Power: " << ld.outputPower() << ", Target Position: " << ParticipantPosition(ld.spotLocation()).toString() << ")"; return ld; } bool NgsConnector::onDesignatorCodeChanged(const rpr2::EntityIdentifierStruct &eis, rpr2::DesignatorCodeEnum16::ET value, qint64 simTimeMSecs) { IdentifierStruct is(eis); if (!m_observerBaseEntityInstances.contains(is.entityNumber())) return false; const rpr2::IObserverDesignatorObjInstance *instance = dynamic_cast(m_observerEmbeddedSystemInstances[is.entityNumber()]); TrainingAreaBase::LaserDesignation ld = getLaserDesignation(is, instance, simTimeMSecs); ld.code(value); DEBUG_LOG(getLogger("NgsConnector.Inbound.LaserDesignation")) << "Designation:" << " Source: " << ld.equipmentNumber() << ", Code: " << ld.code() << ", Output Power: " << ld.outputPower() << ", Target Position: " << ParticipantPosition(ld.spotLocation()).toString() << ")"; emit laserDesignation(ld); return true; } bool NgsConnector::onDesignatorOutputPowerChanged(const rpr2::EntityIdentifierStruct &eis, ngs::FLOAT32 value, qint64 simTimeMSecs) { IdentifierStruct is(eis); if (!m_observerBaseEntityInstances.contains(is.entityNumber())) return false; const rpr2::IObserverDesignatorObjInstance *instance = dynamic_cast(m_observerEmbeddedSystemInstances[is.entityNumber()]); TrainingAreaBase::LaserDesignation ld = getLaserDesignation(is, instance, simTimeMSecs); ld.outputPower(value); DEBUG_LOG(getLogger("NgsConnector.Inbound.LaserDesignation")) << "Designation:" << " Source: " << ld.equipmentNumber() << ", Code: " << ld.code() << ", Output Power: " << ld.outputPower() << ", Target Position: " << ParticipantPosition(ld.spotLocation()).toString() << ")"; emit laserDesignation(ld); return true; } bool NgsConnector::onDesignatorSpotLocationChanged(const rpr2::EntityIdentifierStruct &eis, const rpr2::WorldLocationStruct &value, qint64 simTimeMSecs) { IdentifierStruct is(eis); if (!m_observerBaseEntityInstances.contains(is.entityNumber())) return false; const rpr2::IObserverDesignatorObjInstance *instance = dynamic_cast(m_observerEmbeddedSystemInstances[is.entityNumber()]); TrainingAreaBase::LaserDesignation ld = getLaserDesignation(is, instance, simTimeMSecs); ld.spotLocation(ParticipantPosition(value).toGeoPosition2D()); DEBUG_LOG(getLogger("NgsConnector.Inbound.LaserDesignation")) << "Designation:" << " Source: " << ld.equipmentNumber() << ", Code: " << ld.code() << ", Output Power: " << ld.outputPower() << ", Target Position: " << ParticipantPosition(ld.spotLocation()).toString() << ")"; emit laserDesignation(ld); return true; } void NgsConnector::onNewEngagementReply(const rpr2::IObserverEngagementReplyIntInstance &interaction) { // Sanity: if (!interaction.isValidFiringObjectIdentifier() || !interaction.isValidTargetObjectIdentifier() || !interaction.isValidMunitionType() || !interaction.isValidResultingDamageState()) { ERROR_LOG(getLogger("NgsConnector.Inbound.EngagementReply")) << "Mandatory data not filled:" << (interaction.isValidFiringObjectIdentifier() ? "" : " [Firing Object Identifier] ") << (interaction.isValidTargetObjectIdentifier() ? "" : " [Target Object Identifier] ") << (interaction.isValidResultingDamageState() ? "" : " [Resulting Damage State] ") << (interaction.isValidMunitionType() ? "" : " [Munition Type] "); return; } int sender = interaction.isValidFiringObjectIdentifier() ? getEquipmentNoByRTIObjectId(interaction.getFiringObjectIdentifier().ID) : -1; int receiver = interaction.isValidTargetObjectIdentifier() ? getEquipmentNoByRTIObjectId(interaction.getTargetObjectIdentifier().ID) : -1; const auto ct = interaction.isValidMunitionType() ? m_ngsHandler->ppmHandler()->chargeTypeFromDisEntity(TypeStruct(interaction.getMunitionType()).toString()) : ProjectDataManagement::ChargeType(); ParticipantPosition pPos = interaction.isValidDetonationLocation() ? ParticipantPosition(interaction.getDetonationLocation()) : m_observerPositions.value(receiver); TrainingAreaBase::TabPosition tPos; tPos.position(pPos.toGeoPosition()); tPos.reportingSource(Core::PositionReportingSource_def::External); TrainingAreaBase::EquipmentState state; state.position(tPos); QString source = QLatin1String("unknown"); // Trefferereignis DUS state.equipmentNumber(receiver); state.originatorChargeTypeId(ct.id()); if (sender > 0) { state.originatorId(sender); state.originatorType(ProjectDataManagement::StateChangeOriginator_def::Participant); } else { if (sender == 0) source = QString::fromStdString(interaction.getFiringObjectIdentifier().ID).toLower(); if (source == QLatin1String("excon")) { state.originatorId(0); state.originatorType(ProjectDataManagement::StateChangeOriginator_def::ExerciseControl); } else if (source == QLatin1String("cis")) { state.originatorId(1); state.originatorType(ProjectDataManagement::StateChangeOriginator_def::ExerciseControl); } else if (source == QLatin1String("greenkey")) { state.originatorId(0); state.originatorType(ProjectDataManagement::StateChangeOriginator_def::ObserverController); } else if (source == QLatin1String("optical")) { state.originatorId(0); state.originatorType(ProjectDataManagement::StateChangeOriginator_def::ObserverController); } else { // Unknown source. I.e. Back to COMMS, Power Up,... state.originatorId(2); state.originatorType(ProjectDataManagement::StateChangeOriginator_def::ExerciseControl); } } state.sampleTime(simTimeToMSec(interaction.getReceiveTimeStamp())); // Todo: Put ResultingDamageState to the correct fields: DamageModel dm(IdentifierStruct(0), interaction.getResultingDamageState()); const auto ds = dm.vulnerabilityState(QDateTime::currentMSecsSinceEpoch()); state.vulnerabilityBits(1 << ds.underlying()); state.vulnerabilityKindId(); state.vulnerabilityState(ds); DEBUG_LOG(getLogger("NgsConnector.Inbound.EngagementReply")) << "EngagementReply: " << "Target: " << receiver << ", Source: " << (sender > 0 ? tr("Player %1").arg(sender) : source) << ", Charge Type: " << ct.name() << " (ID: " << ct.id() << ", DIS: " << ct.disEntity() << ")" << ", Position: " << pPos.toString() << ", Vulnerability Bits: " << state.vulnerabilityBits() << ")"; emit equipmentState(state); } void NgsConnector::onNewEventReport(const rpr2::IObserverEventReportIntInstance &interaction) { // Sanity: if (!interaction.isValidOriginatingEntity() || !interaction.isValidEventType() || !interaction.isValidVariableDatumSet()) { ERROR_LOG(getLogger("NgsConnector.Inbound.EventReport")) << "Mandatory data not filled:" << (interaction.isValidOriginatingEntity() ? "" : " [Originating Entity] ") << (interaction.isValidEventType() ? "" : " [Event Type] ") << (interaction.isValidVariableDatumSet() ? "" : " [Variable Datum Set] "); return; } // Self issued? if (IdentifierStruct(interaction.getOriginatingEntity()).toInt64() == IdentifierStruct(0).toInt64()) return; if (interaction.getEventType() != rpr2::EventTypeEnum32::CTC_Apache_Ammo_Message) { INFO_LOG(getLogger("NgsConnector.Inbound.EventReport")) << "Received EventReport other than 'CTC_Apache_Ammo_Message'. Type: " << interaction.getEventType(); return; } std::vector charges; QMap gears; const IdentifierStruct is(interaction.getOriginatingEntity()); quint64 simTimeMSecs = simTimeToMSec(interaction.getReceiveTimeStamp()); const rpr2::ArrayOfVariableDatumStructDynamicHLAvariableArray &variableDatum = interaction.getVariableDatumSet().NumberOfVariableDatums__A__Padding__A__VariableDatums; QString chargesString; for (uint i=0; i < variableDatum.size(); ++i) { const rpr2::VariableDatumStruct &datum = variableDatum[i]; if (datum.DatumLengthInBits != (64 + 32)) { ERROR_LOG(getLogger("NgsConnector.Inbound.EventReport")) << "Length of Ammunition Message not correct! Has " << datum.DatumLengthInBits << " bits!"; continue; } quint64 etsAsULong = datum.NumberOfLongLongs__A__Padding__A__DatumValue[0]; quint64 count = datum.NumberOfLongLongs__A__Padding__A__DatumValue[1]; quint32 roundCount = (count & 0xFFFFFFFF00000000) >> 32; const auto ct = m_ngsHandler->ppmHandler()->chargeTypeFromDisEntity(TypeStruct::fromULong(etsAsULong).toString()); int gearTypeId = m_ngsHandler->ppmHandler()->gearTypeIdFromChargeTypeId(ct.id()); TrainingAreaBase::GearCharge gc; gc.chargeTypeId(ct.id()); gc.gearId(((65536 * is.entityNumber()) + gearTypeId)); // Something "unique"... gc.gearTypeId(gearTypeId); gc.remain(roundCount); chargesString.append(tr("[%1 (%2): %3] ").arg(QString::fromStdString(ct.name())).arg(ct.id()).arg(roundCount)); charges.push_back(gc); TrainingAreaBase::Gear gear; gear.gearId(gc.gearId()); gear.chargeCounting(true); gear.gearTypeId(gearTypeId); gears[gc.gearId()] = gear; } TrainingAreaBase::EquipmentGearChargeState egcs; egcs.charge(charges); egcs.equipmentNumber(is.entityNumber()); egcs.sampleTime(simTimeMSecs); DEBUG_LOG(getLogger("NgsConnector.Inbound.EventReport")) << "EventReport (ApacheAmmoMessage / Charges): " << " Player: " << egcs.equipmentNumber() << ", Charges: " << chargesString << ")"; emit gearChargeState(egcs); std::vector vecGears; vecGears.reserve(gears.size()); QString gearsString; for (const auto &gear : qAsConst(gears)) { vecGears.push_back(gear); gearsString.append(tr("[GearId: %1, GearTypeId; %2] ").arg(gear.gearId()).arg(gear.gearTypeId())); } TrainingAreaBase::EquipmentGearAssignment ega; ega.equipmentNumber(is.entityNumber()); ega.gears(vecGears); ega.sampleTime(simTimeMSecs); DEBUG_LOG(getLogger("NgsConnector.Inbound.EventReport")) << "EventReport (ApacheAmmoMessage / Gears): " << " Player: " << egcs.equipmentNumber() << ", Gears: " << gearsString << ")"; emit gearAssignment(ega); } // HLA -> DDS void NgsConnector::onNewMunitionDetonation(const rpr2::IObserverMunitionDetonationIntInstance &interaction) { // Sanity: if (!interaction.isValidDetonationResultCode() || !interaction.isValidFiringObjectIdentifier() || !interaction.isValidEventIdentifier() || !interaction.isValidMunitionType()) { ERROR_LOG(getLogger("NgsConnector.Inbound.MunitionDetonation")) << "Mandatory data not filled:" << (interaction.isValidDetonationResultCode() ? "" : " [Detonation Result Code] ") << (interaction.isValidFiringObjectIdentifier() ? "" : " [Firing Object Identifier] ") << (interaction.isValidEventIdentifier() ? "" : " [Event Identifier] ") << (interaction.isValidMunitionType() ? "" : " [Munition Type] "); return; } // Self issued? const auto &objectIdentifier = interaction.getEventIdentifier().IssuingObjectIdentifier; int eqId = getEquipmentNoByRTIObjectId(objectIdentifier.ID); DEBUG_LOG(m_logger) << "Engagement Reply, Issuer: " << eqId; if (m_playerMap.contains(eqId) || objectIdentifier.ID == std::to_string(IdentifierStruct(0).toInt64())) // Self issued return; const auto result = interaction.getDetonationResultCode(); const auto ct = m_ngsHandler->ppmHandler()->chargeTypeFromDisEntity(TypeStruct(interaction.getMunitionType()).toString()); int firingEqId = getEquipmentNoByRTIObjectId(interaction.getFiringObjectIdentifier().ID); if (result == rpr2::DetonationResultCodeEnum8::EntityImpact || result == rpr2::DetonationResultCodeEnum8::EntityProximateDetonation) { // Sanity: if (!interaction.isValidTargetObjectIdentifier()) { ERROR_LOG(getLogger("NgsConnector.Inbound.MunitionDetonation")) << "Mandatory data not filled: [Target Object Identifier]"; return; } TrainingAreaBase::SendCesHitInformation request; // Ausloesung DUSTreffer request.distanceToOrignator(interaction.isValidQuantityFired() ? interaction.getQuantityFired() : 1); request.directionDegree(0); request.effectPercent((result == rpr2::DetonationResultCodeEnum8::EntityProximateDetonation) ? 0 : 100); request.effectTime(simTimeToMSec(interaction.getReceiveTimeStamp())); request.equipmentNumber(getEquipmentNoByRTIObjectId(interaction.getTargetObjectIdentifier().ID)); request.chargeTypeId(ct.id()); request.originatorEquipmentNumber(firingEqId); DEBUG_LOG(getLogger("NgsConnector.Inbound.MunitionDetonation")) << "MunitionDetonation (Target Hit):" << ", Charge Type: " << ct.name() << " (ID: " << ct.id() << ", DIS: " << ct.disEntity() << ")" << ", Shooter: " << request.originatorEquipmentNumber() << ", Target: " << request.equipmentNumber() << ", Rounds: " << request.distanceToOrignator() << ", FullHit/IsProximate : " << (result == rpr2::DetonationResultCodeEnum8::EntityProximateDetonation ? "Proximate" : "Full Hit") << ")"; emit cesHitInformation(request); } else if (result == rpr2::DetonationResultCodeEnum8::Detonation || result == rpr2::DetonationResultCodeEnum8::GroundImpact) { // Sanity: if (!interaction.isValidDetonationLocation()) { ERROR_LOG(getLogger("NgsConnector.Inbound.MunitionDetonation")) << "Mandatory data not filled: [Detonation Location] "; return; } ParticipantPosition pos(interaction.getDetonationLocation()); AirSupportImpactModifyRequest asim; asim.chargeTypeId(ct.id()); asim.effectStart(simTimeToMSec(interaction.getReceiveTimeStamp())); asim.externalId(interaction.getEventIdentifier().EventCount); asim.flightTime(0); asim.impactPosition(pos.toGeoPosition2D()); asim.name(tr("Ground Impact by Apache %1").arg(firingEqId).toStdString()); asim.participantId(0); DEBUG_LOG(getLogger("NgsConnector.Inbound.MunitionDetonation")) << "MunitionDetonation (Ground Hit): EventID: " << asim.externalId() << ", Charge Type: " << ct.name() << " (ID: " << ct.id() << ", DIS: " << ct.disEntity() << ")" << ", Shooter: " << firingEqId << ", Target Position: " << pos.toString() << ")"; emit airSupportImpact(asim, firingEqId); } } int NgsConnector::getEquipmentNoByRTIObjectId(const std::string &sender) const { // Own Players for (auto it(m_playerMap.cbegin()); it != m_playerMap.cend(); ++it) { if (it.value()->isValidObjectName()) { if (it.value()->getObjectName() == sender) return it.key(); } } // ICE Players for (auto it(m_observerBaseEntityInstances.cbegin()); it != m_observerBaseEntityInstances.cend(); ++it) { if (it.value()->isValidObjectName()) { if (it.value()->getObjectName() == sender) return it.key(); } } // Embedded Systems for (auto it(m_observerEmbeddedSystemInstances.cbegin()); it != m_observerEmbeddedSystemInstances.cend(); ++it) { if (it.value()->isValidObjectName()) { if (it.value()->getObjectName() == sender) return it.key(); } } return 0; } // HLA -> DDS void NgsConnector::onNewWeaponFire(const rpr2::IObserverWeaponFireIntInstance &interaction) { // Sanity: if (!interaction.isValidQuantityFired() || !interaction.isValidFiringObjectIdentifier() || !interaction.isValidEventIdentifier() || !interaction.isValidMunitionType()) { ERROR_LOG(getLogger("NgsConnector.Inbound.WeaponFire")) << "Mandatory data not filled:" << (interaction.isValidQuantityFired() ? "" : " [Quantity Fired] ") << (interaction.isValidFiringObjectIdentifier() ? "" : " [Firing Object Identifier] ") << (interaction.isValidEventIdentifier() ? "" : " [Event Identifier] ") << (interaction.isValidMunitionType() ? "" : " [Munition Type] "); return; } TrainingAreaBase::EquipmentGearEvent event; event.equipmentNumber(getEquipmentNoByRTIObjectId(interaction.getFiringObjectIdentifier().ID)); // only send for others, not for own players if (m_playerMap.contains(event.equipmentNumber())) return; TrainingAreaBase::GearCharge charge; const auto ct = m_ngsHandler->ppmHandler()->chargeTypeFromDisEntity(TypeStruct(interaction.getMunitionType()).toString()); int gearTypeId = m_ngsHandler->ppmHandler()->gearTypeIdFromChargeTypeId(ct.id()); charge.chargeTypeId(ct.id()); charge.gearId(((65536 * event.equipmentNumber()) + gearTypeId)); // Something "unique"... charge.gearTypeId(gearTypeId); charge.remain(-1); event.charge(charge); event.chargeAmount(interaction.getQuantityFired()); event.sampleTime(simTimeToMSec(interaction.getReceiveTimeStamp())); DEBUG_LOG(getLogger("NgsConnector.Inbound.WeaponFire")) << "Weapon Fire: EventID: " << interaction.getEventIdentifier().EventCount << ", Charge Type: " << ct.name() << " (ID: " << ct.id() << ", DIS: " << ct.disEntity() << ")" << ", Shooter: " << event.equipmentNumber() << ", Rounds: " << event.chargeAmount() << ")"; emit weaponFire(event); } rpr2::EventIdentifierStruct NgsConnector::getEventIdentifierStruct() { rpr2::EventIdentifierStruct evis; evis.EventCount = m_eventCount++ % std::numeric_limits::max(); evis.IssuingObjectIdentifier.ID = std::to_string(IdentifierStruct(0).toInt64()); if (m_eventCount > std::numeric_limits::max()) emit eventCountOverflow(); return evis; } ProjectDataManagement::VulnerabilityState NgsConnector::getVulnerabilityStateFromState(const ParticipantManagement::StateInformation &state) { ProjectDataManagement::VulnerabilityState vs; vs = state.vulnerabilityState(); return vs; } bool NgsConnector::setVulnerabilityState(int eqNo, const ParticipantManagement::StateInformation &state) { rpr2::IPlayerPhysicalEntityObjInstance *playerInstance = dynamic_cast(m_playerMap.value(eqNo, nullptr)); if (!playerInstance) return false; DamageModel dm(state.vulnerabilityState()); playerInstance->setDamageState(dm.damageState(-1)); playerInstance->setImmobilized(dm.immobilized(-1)); playerInstance->setFirePowerDisabled(dm.firePowerDisabled(-1)); DEBUG_LOG(getLogger("NgsConnector.Outbound.Player.Status")) << "Player: " << eqNo << ", New Total Status: " << state.vulnerabilityState(); playerInstance->publish(getSimTime(state.stateTime())); return true; } // Hit from Participant Management bool NgsConnector::setParticipantHit(int eqNo, const ParticipantManagement::StateInformation &state, const ParticipantPosition &partPos, int chargeType, ProjectDataManagement::StateChangeOriginator originatorType, int originatorEqNo) { Q_UNUSED(partPos) // Set Tactical Status on Player auto *playerInstanceDest = m_playerMap.value(eqNo, nullptr); if (!playerInstanceDest || !playerInstanceDest->isValidEntityIdentifier()) return false; if (!setVulnerabilityState(eqNo, state)) return false; // Put Munition Detonation on HLA // rpr2::IPlayerGuezHitIntClass &ic = m_somPlayer->getPlayerGuezHitIntClass(); // rpr2::IPlayerGuezHitIntInstance &icb = ic.newGuezHitIntInstance(); // icb.setMunition(getEtsCharge(chargeType)); // icb.setTargetIdentifier(playerInstanceDest->getEntityIdentifier()); // icb.setTargetLocation(partPos.toWorldLocation()); IdentifierStruct is(0); // own excon // rpr2::OriginatorTypeEnum16::ET originatorTypeRpr = rpr2::OriginatorTypeEnum16::Unknown; switch (originatorType.underlying()) { case ProjectDataManagement::StateChangeOriginator_def::AreaEffectSimulation: { break; } case ProjectDataManagement::StateChangeOriginator_def::ExerciseControl: break; case ProjectDataManagement::StateChangeOriginator_def::ObserverController: break; case ProjectDataManagement::StateChangeOriginator_def::Participant: if (originatorType == ProjectDataManagement::StateChangeOriginator_def::Participant) { if (m_playerMap.contains(originatorEqNo)) is = IdentifierStruct(originatorEqNo); else if (m_observerBaseEntityInstances.contains(originatorEqNo)) { // Works as long as externalID == equipmentNo const rpr2::IObserverBaseEntityObjInstance *instance = dynamic_cast(m_observerBaseEntityInstances[originatorEqNo]); if (instance->isValidEntityIdentifier()) is = IdentifierStruct(instance->getEntityIdentifier()); } break; } Q_FALLTHROUGH(); case ProjectDataManagement::StateChangeOriginator_def::Initialization: case ProjectDataManagement::StateChangeOriginator_def::FunctionalArea: case ProjectDataManagement::StateChangeOriginator_def::MaintainanceDevice: case ProjectDataManagement::StateChangeOriginator_def::Unknown: case ProjectDataManagement::StateChangeOriginator_def::WoundTreatmentDevice: // Todo: Check, if IED and DUSMine actually can be handled correctly: case ProjectDataManagement::StateChangeOriginator_def::ImprovisedExplosiveDevice: case ProjectDataManagement::StateChangeOriginator_def::DusMine: // originatorType = rpr2::OriginatorTypeEnum16::Unknown; break; } // icb.setOriginatorIdentifier(is.toEIS()); // icb.setOriginatorType(originatorTypeRpr); const auto ct = m_ngsHandler->ppmHandler()->chargeTypeFromId(chargeType); DEBUG_LOG(getLogger("NgsConnector.Outbound.GuezHit")) << "Player: " << eqNo << ", Originator Type: " << originatorType << ", Originator Id: " << originatorEqNo << ", Charge Type: " << ct.name() << " (ID: " << ct.id() << ", DIS: " << ct.disEntity() << ")" << ", Vulnerability State: " << state.vulnerabilityState(); try { // icb.publish(getSimTime(state.stateTime())); } catch (...) { ERROR_LOG(getLogger("NgsConnector.Outbound.GuezHit")) << "Player: " << eqNo << ", could not publish GuezHit!"; } return true; } } // namespace HighLevelArchitecture