1777 lines
65 KiB
C++
Executable File
1777 lines
65 KiB
C++
Executable File
/**
|
|
* 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 <Common/player_callback.h>
|
|
#include <Common/settings.h>
|
|
#include <Common/identifierstruct.h>
|
|
#include <Common/participantinformation.h>
|
|
|
|
#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 <ClockTimeStruct.h>
|
|
//#include <math.h>
|
|
|
|
#include <NGSException.h>
|
|
|
|
#include <QTimer>
|
|
#include <QDateTime>
|
|
#include <QtCore/QtEndian>
|
|
#include <LtsCoordTrans/lts_coord_trans.h>
|
|
#include <Core/core_enums.hpp>
|
|
|
|
|
|
#include <SimulatedWorldAPI.h>
|
|
#include <EventIdentifierStruct.h>
|
|
#include <RelativePositionStruct.h>
|
|
#include <SpatialStruct.h>
|
|
#include <EnvironmentObjectTypeStruct.h>
|
|
|
|
#include <IObserverHumanObjClass.h>
|
|
#include <IPlayerHumanObjClass.h>
|
|
#include <IPlayerHumanObjInstance.h>
|
|
|
|
#include <IObserverGroundVehicleObjClass.h>
|
|
#include <IPlayerGroundVehicleObjClass.h>
|
|
#include <IPlayerGroundVehicleObjInstance.h>
|
|
|
|
#include <IObserverLongBowApacheObjClass.h>
|
|
// We should not instantiate Apaches. Therefore "addLongbowApache()" should be deprecated and these includes are unnecessary:
|
|
#include <IPlayerLongBowApacheObjClass.h>
|
|
#include <IPlayerLongBowApacheObjInstance.h>
|
|
|
|
#include <IObserverDesignatorObjClass.h>
|
|
|
|
#include <IObserverEngagementReplyIntClass.h>
|
|
#include <IObserverEngagementReplyIntInstance.h>
|
|
|
|
#include <IObserverEventReportIntClass.h>
|
|
#include <IObserverEventReportIntInstance.h>
|
|
|
|
#include <IObserverMunitionDetonationIntClass.h>
|
|
#include <IObserverMunitionDetonationIntInstance.h>
|
|
|
|
#include <IObserverWeaponFireIntClass.h>
|
|
#include <IObserverWeaponFireIntInstance.h>
|
|
|
|
#include <IPlayerActionRequestIntClass.h>
|
|
#include <IPlayerActionRequestIntInstance.h>
|
|
|
|
#include <IPlayerDataIntClass.h>
|
|
#include <IPlayerDataIntInstance.h>
|
|
|
|
#include <IPlayerEngagementReplyIntClass.h>
|
|
#include <IPlayerEngagementReplyIntInstance.h>
|
|
|
|
#include <IPlayerEventReportIntClass.h>
|
|
#include <IPlayerEventReportIntInstance.h>
|
|
|
|
#include <IPlayerMunitionDetonationIntClass.h>
|
|
#include <IPlayerMunitionDetonationIntInstance.h>
|
|
|
|
#include <IPlayerWeaponFireIntClass.h>
|
|
#include <IPlayerWeaponFireIntInstance.h>
|
|
|
|
#include <IPlayerSOM.h>
|
|
|
|
#include <ArrayOfVariableDatumStructDynamicHLAvariableArray.h>
|
|
#include <VariableDatumSetStruct.h>
|
|
|
|
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<rpr2::IPlayerSOM *>(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<rpr2::IPlayerPhysicalEntityObjInstance *>(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<quint64>((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<const rpr2::IObserverBaseEntityObjInstance *>(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<const rpr2::IObserverBaseEntityObjInstance *>(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<rpr2::IPlayerPhysicalEntityObjInstance *>(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<const rpr2::IObserverBaseEntityObjInstance *>(&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<const rpr2::IObserverEmbeddedSystemObjInstance *>(&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<const rpr2::IObserverBaseEntityObjInstance *>(&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<const rpr2::IObserverEmbeddedSystemObjInstance *>(&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<const rpr2::IObserverDesignatorObjInstance *>(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<typename T>
|
|
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 <class T>
|
|
static inline T compass2rad(T degree)
|
|
{
|
|
static_assert(!std::is_integral<T>::value, "Floating Point Value Required! Integer may be in Centidegree?!?");
|
|
return (M_PI / 2.0) - (degree * M_PI / 180.0);
|
|
}
|
|
|
|
template <class T>
|
|
static inline T deg2rad(T degree)
|
|
{
|
|
static_assert(!std::is_integral<T>::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<const rpr2::IObserverDesignatorObjInstance *>(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<const rpr2::IObserverDesignatorObjInstance *>(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<const rpr2::IObserverDesignatorObjInstance *>(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<TrainingAreaBase::GearCharge> charges;
|
|
QMap<int, TrainingAreaBase::Gear> 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<TrainingAreaBase::Gear> 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<ngs::UINT16>::max();
|
|
evis.IssuingObjectIdentifier.ID = std::to_string(IdentifierStruct(0).toInt64());
|
|
if (m_eventCount > std::numeric_limits<ngs::UINT16>::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<rpr2::IPlayerPhysicalEntityObjInstance *>(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<const rpr2::IObserverBaseEntityObjInstance* >(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
|